Snap for 9550355 from 63029511fde8a5c5aafd908fda720bf006250960 to sdk-release

Change-Id: I74026733172bce7cf3b259f63a5dcf6f60751d69
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
new file mode 100644
index 0000000..e8a7879
--- /dev/null
+++ b/.git-blame-ignore-revs
@@ -0,0 +1,2 @@
+# Replace block comments with line comments to appease eslint
+e56b1a91dcc4638262843eaead4347d7bf5a73b0
diff --git a/.gitignore b/.gitignore
index 3bd8337..b275b2ec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,3 +31,6 @@
 TAGS
 /*.pftrace
 examples/sdk/build/
+GPATH
+GRTAGS
+GTAGS
\ No newline at end of file
diff --git a/Android.bp b/Android.bp
index 3121f00..420723f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -18,6 +18,7 @@
 cc_binary {
     name: "heapprofd",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_version",
@@ -200,6 +201,7 @@
 cc_library_shared {
     name: "heapprofd_client",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_public_abi_base",
@@ -232,6 +234,7 @@
 cc_library_shared {
     name: "heapprofd_client_api",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_public_abi_base",
@@ -280,6 +283,7 @@
 cc_library_shared {
     name: "heapprofd_standalone_client",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_version",
@@ -455,6 +459,7 @@
 cc_binary_host {
     name: "ipc_plugin",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_public_abi_base",
@@ -478,6 +483,7 @@
 cc_library_shared {
     name: "libperfetto",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_version",
@@ -675,7 +681,7 @@
     ],
     shared_libs: [
         "android.hardware.atrace@1.0",
-        "android.hardware.health-V1-ndk",
+        "android.hardware.health-V2-ndk",
         "android.hardware.health@2.0",
         "android.hardware.power.stats-V1-cpp",
         "android.hardware.power.stats@1.0",
@@ -704,6 +710,7 @@
 cc_library_static {
     name: "libperfetto_client_experimental",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_version",
@@ -912,14 +919,16 @@
         "//apex_available:platform",
         "com.android.art",
         "com.android.art.debug",
+        "com.android.tethering",
     ],
-    min_sdk_version: "S",
+    min_sdk_version: "30",
 }
 
 // GN: //src/perfetto_cmd:perfetto
 cc_binary {
     name: "perfetto",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_version",
@@ -1069,10 +1078,19 @@
     ],
 }
 
+// GN: //src/base:perfetto_base_default_platform
+filegroup {
+    name: "perfetto_base_default_platform",
+    srcs: [
+        "src/base/default_platform.cc",
+    ],
+}
+
 // GN: //test/cts:perfetto_cts_deps
 cc_library_static {
     name: "perfetto_cts_deps",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_version",
@@ -1363,6 +1381,7 @@
 cc_library_static {
     name: "perfetto_cts_jni_deps",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_version",
@@ -1695,6 +1714,11 @@
     name: "perfetto_include_perfetto_ext_base_http_http",
 }
 
+// GN: //include/perfetto/ext/base/threading:threading
+filegroup {
+    name: "perfetto_include_perfetto_ext_base_threading_threading",
+}
+
 // GN: //include/perfetto/ext/base:version
 filegroup {
     name: "perfetto_include_perfetto_ext_base_version",
@@ -1799,10 +1823,12 @@
 cc_test {
     name: "perfetto_integrationtests",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_version",
         ":perfetto_include_perfetto_ext_ipc_ipc",
+        ":perfetto_include_perfetto_ext_trace_processor_demangle",
         ":perfetto_include_perfetto_ext_trace_processor_export_json",
         ":perfetto_include_perfetto_ext_trace_processor_importers_memory_tracker_memory_tracker",
         ":perfetto_include_perfetto_ext_traced_sys_stats_counters",
@@ -1917,6 +1943,7 @@
         ":perfetto_protos_perfetto_trace_translation_cpp_gen",
         ":perfetto_protos_perfetto_trace_translation_lite_gen",
         ":perfetto_protos_perfetto_trace_translation_zero_gen",
+        ":perfetto_protos_third_party_pprof_zero_gen",
         ":perfetto_protos_third_party_statsd_config_zero_gen",
         ":perfetto_src_android_internal_headers",
         ":perfetto_src_android_internal_lazy_library_loader",
@@ -1952,33 +1979,57 @@
         ":perfetto_src_protozero_filtering_message_filter",
         ":perfetto_src_protozero_proto_ring_buffer",
         ":perfetto_src_protozero_protozero",
-        ":perfetto_src_trace_processor_analysis_analysis",
         ":perfetto_src_trace_processor_containers_containers",
         ":perfetto_src_trace_processor_db_db",
         ":perfetto_src_trace_processor_dynamic_dynamic",
         ":perfetto_src_trace_processor_export_json",
-        ":perfetto_src_trace_processor_ftrace_descriptors",
         ":perfetto_src_trace_processor_importers_android_bugreport_android_bugreport",
         ":perfetto_src_trace_processor_importers_common_common",
+        ":perfetto_src_trace_processor_importers_common_parser_types",
+        ":perfetto_src_trace_processor_importers_common_trace_parser_hdr",
+        ":perfetto_src_trace_processor_importers_ftrace_ftrace_descriptors",
+        ":perfetto_src_trace_processor_importers_ftrace_full",
+        ":perfetto_src_trace_processor_importers_ftrace_minimal",
+        ":perfetto_src_trace_processor_importers_fuchsia_fuchsia_record",
+        ":perfetto_src_trace_processor_importers_fuchsia_full",
+        ":perfetto_src_trace_processor_importers_fuchsia_minimal",
+        ":perfetto_src_trace_processor_importers_gzip_full",
+        ":perfetto_src_trace_processor_importers_i2c_full",
+        ":perfetto_src_trace_processor_importers_json_full",
+        ":perfetto_src_trace_processor_importers_json_minimal",
         ":perfetto_src_trace_processor_importers_memory_tracker_graph_processor",
-        ":perfetto_src_trace_processor_importers_proto_storage_full",
-        ":perfetto_src_trace_processor_importers_proto_storage_minimal",
+        ":perfetto_src_trace_processor_importers_ninja_ninja",
+        ":perfetto_src_trace_processor_importers_proto_full",
+        ":perfetto_src_trace_processor_importers_proto_minimal",
+        ":perfetto_src_trace_processor_importers_proto_packet_sequence_state_generation_hdr",
+        ":perfetto_src_trace_processor_importers_proto_proto_importer_module",
+        ":perfetto_src_trace_processor_importers_syscalls_full",
+        ":perfetto_src_trace_processor_importers_systrace_full",
+        ":perfetto_src_trace_processor_importers_systrace_systrace_line",
+        ":perfetto_src_trace_processor_importers_systrace_systrace_parser",
         ":perfetto_src_trace_processor_lib",
         ":perfetto_src_trace_processor_metatrace",
         ":perfetto_src_trace_processor_metrics_metrics",
+        ":perfetto_src_trace_processor_prelude_functions_functions",
+        ":perfetto_src_trace_processor_prelude_operators_operators",
+        ":perfetto_src_trace_processor_sorter_sorter",
         ":perfetto_src_trace_processor_sqlite_sqlite",
         ":perfetto_src_trace_processor_sqlite_sqlite_minimal",
-        ":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_types_types",
         ":perfetto_src_trace_processor_util_descriptors",
+        ":perfetto_src_trace_processor_util_glob",
         ":perfetto_src_trace_processor_util_gzip",
         ":perfetto_src_trace_processor_util_interned_message_view",
+        ":perfetto_src_trace_processor_util_profile_builder",
+        ":perfetto_src_trace_processor_util_proto_profiler",
         ":perfetto_src_trace_processor_util_proto_to_args_parser",
         ":perfetto_src_trace_processor_util_protozero_to_text",
+        ":perfetto_src_trace_processor_util_sql_argument",
         ":perfetto_src_trace_processor_util_stack_traces_util",
+        ":perfetto_src_trace_processor_util_stdlib",
         ":perfetto_src_trace_processor_util_util",
         ":perfetto_src_trace_processor_util_zip_reader",
         ":perfetto_src_trace_processor_views_views",
@@ -2009,6 +2060,7 @@
         ":perfetto_src_tracing_core_service",
         ":perfetto_src_tracing_core_test_support",
         ":perfetto_src_tracing_in_process_backend",
+        ":perfetto_src_tracing_integrationtests",
         ":perfetto_src_tracing_ipc_common",
         ":perfetto_src_tracing_ipc_consumer_consumer",
         ":perfetto_src_tracing_ipc_default_socket",
@@ -2040,6 +2092,7 @@
         "libgtest",
         "libperfetto_client_experimental",
         "perfetto_src_trace_processor_demangle",
+        "sqlite_ext_percentile",
     ],
     whole_static_libs: [
         "perfetto_gtest_logcat_printer",
@@ -2143,15 +2196,19 @@
         "perfetto_protos_perfetto_trace_translation_cpp_gen_headers",
         "perfetto_protos_perfetto_trace_translation_lite_gen_headers",
         "perfetto_protos_perfetto_trace_translation_zero_gen_headers",
+        "perfetto_protos_third_party_pprof_zero_gen_headers",
         "perfetto_protos_third_party_statsd_config_zero_gen_headers",
         "perfetto_src_base_version_gen_h",
-        "perfetto_src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
-        "perfetto_src_trace_processor_importers_gen_cc_config_descriptor",
-        "perfetto_src_trace_processor_importers_gen_cc_statsd_atoms_descriptor",
-        "perfetto_src_trace_processor_importers_gen_cc_track_event_descriptor",
+        "perfetto_src_trace_processor_importers_proto_gen_cc_chrome_track_event_descriptor",
+        "perfetto_src_trace_processor_importers_proto_gen_cc_config_descriptor",
+        "perfetto_src_trace_processor_importers_proto_gen_cc_statsd_atoms_descriptor",
+        "perfetto_src_trace_processor_importers_proto_gen_cc_trace_descriptor",
+        "perfetto_src_trace_processor_importers_proto_gen_cc_track_event_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_sql_gen_amalgamated_sql_metrics",
+        "perfetto_src_trace_processor_stdlib_gen_amalgamated_stdlib",
+        "perfetto_src_trace_processor_tables_tables_python",
     ],
     defaults: [
         "perfetto_defaults",
@@ -2464,6 +2521,7 @@
         "protos/perfetto/config/android/android_log_config.proto",
         "protos/perfetto/config/android/android_polled_state_config.proto",
         "protos/perfetto/config/android/android_system_property_config.proto",
+        "protos/perfetto/config/android/network_trace_config.proto",
         "protos/perfetto/config/android/packages_list_config.proto",
     ],
     tools: [
@@ -2476,6 +2534,7 @@
         "external/perfetto/protos/perfetto/config/android/android_log_config.gen.cc",
         "external/perfetto/protos/perfetto/config/android/android_polled_state_config.gen.cc",
         "external/perfetto/protos/perfetto/config/android/android_system_property_config.gen.cc",
+        "external/perfetto/protos/perfetto/config/android/network_trace_config.gen.cc",
         "external/perfetto/protos/perfetto/config/android/packages_list_config.gen.cc",
     ],
 }
@@ -2488,6 +2547,7 @@
         "protos/perfetto/config/android/android_log_config.proto",
         "protos/perfetto/config/android/android_polled_state_config.proto",
         "protos/perfetto/config/android/android_system_property_config.proto",
+        "protos/perfetto/config/android/network_trace_config.proto",
         "protos/perfetto/config/android/packages_list_config.proto",
     ],
     tools: [
@@ -2500,6 +2560,7 @@
         "external/perfetto/protos/perfetto/config/android/android_log_config.gen.h",
         "external/perfetto/protos/perfetto/config/android/android_polled_state_config.gen.h",
         "external/perfetto/protos/perfetto/config/android/android_system_property_config.gen.h",
+        "external/perfetto/protos/perfetto/config/android/network_trace_config.gen.h",
         "external/perfetto/protos/perfetto/config/android/packages_list_config.gen.h",
     ],
     export_include_dirs: [
@@ -2516,6 +2577,7 @@
         "protos/perfetto/config/android/android_log_config.proto",
         "protos/perfetto/config/android/android_polled_state_config.proto",
         "protos/perfetto/config/android/android_system_property_config.proto",
+        "protos/perfetto/config/android/network_trace_config.proto",
         "protos/perfetto/config/android/packages_list_config.proto",
     ],
     tools: [
@@ -2527,6 +2589,7 @@
         "external/perfetto/protos/perfetto/config/android/android_log_config.pb.cc",
         "external/perfetto/protos/perfetto/config/android/android_polled_state_config.pb.cc",
         "external/perfetto/protos/perfetto/config/android/android_system_property_config.pb.cc",
+        "external/perfetto/protos/perfetto/config/android/network_trace_config.pb.cc",
         "external/perfetto/protos/perfetto/config/android/packages_list_config.pb.cc",
     ],
 }
@@ -2539,6 +2602,7 @@
         "protos/perfetto/config/android/android_log_config.proto",
         "protos/perfetto/config/android/android_polled_state_config.proto",
         "protos/perfetto/config/android/android_system_property_config.proto",
+        "protos/perfetto/config/android/network_trace_config.proto",
         "protos/perfetto/config/android/packages_list_config.proto",
     ],
     tools: [
@@ -2550,6 +2614,7 @@
         "external/perfetto/protos/perfetto/config/android/android_log_config.pb.h",
         "external/perfetto/protos/perfetto/config/android/android_polled_state_config.pb.h",
         "external/perfetto/protos/perfetto/config/android/android_system_property_config.pb.h",
+        "external/perfetto/protos/perfetto/config/android/network_trace_config.pb.h",
         "external/perfetto/protos/perfetto/config/android/packages_list_config.pb.h",
     ],
     export_include_dirs: [
@@ -2566,6 +2631,7 @@
         "protos/perfetto/config/android/android_log_config.proto",
         "protos/perfetto/config/android/android_polled_state_config.proto",
         "protos/perfetto/config/android/android_system_property_config.proto",
+        "protos/perfetto/config/android/network_trace_config.proto",
         "protos/perfetto/config/android/packages_list_config.proto",
     ],
     tools: [
@@ -2578,6 +2644,7 @@
         "external/perfetto/protos/perfetto/config/android/android_log_config.pbzero.cc",
         "external/perfetto/protos/perfetto/config/android/android_polled_state_config.pbzero.cc",
         "external/perfetto/protos/perfetto/config/android/android_system_property_config.pbzero.cc",
+        "external/perfetto/protos/perfetto/config/android/network_trace_config.pbzero.cc",
         "external/perfetto/protos/perfetto/config/android/packages_list_config.pbzero.cc",
     ],
 }
@@ -2590,6 +2657,7 @@
         "protos/perfetto/config/android/android_log_config.proto",
         "protos/perfetto/config/android/android_polled_state_config.proto",
         "protos/perfetto/config/android/android_system_property_config.proto",
+        "protos/perfetto/config/android/network_trace_config.proto",
         "protos/perfetto/config/android/packages_list_config.proto",
     ],
     tools: [
@@ -2602,6 +2670,7 @@
         "external/perfetto/protos/perfetto/config/android/android_log_config.pbzero.h",
         "external/perfetto/protos/perfetto/config/android/android_polled_state_config.pbzero.h",
         "external/perfetto/protos/perfetto/config/android/android_system_property_config.pbzero.h",
+        "external/perfetto/protos/perfetto/config/android/network_trace_config.pbzero.h",
         "external/perfetto/protos/perfetto/config/android/packages_list_config.pbzero.h",
     ],
     export_include_dirs: [
@@ -2690,6 +2759,7 @@
         "protos/perfetto/config/android/android_log_config.proto",
         "protos/perfetto/config/android/android_polled_state_config.proto",
         "protos/perfetto/config/android/android_system_property_config.proto",
+        "protos/perfetto/config/android/network_trace_config.proto",
         "protos/perfetto/config/android/packages_list_config.proto",
         "protos/perfetto/config/chrome/chrome_config.proto",
         "protos/perfetto/config/data_source_config.proto",
@@ -4201,12 +4271,12 @@
         "protos/perfetto/metrics/android/simpleperf.proto",
         "protos/perfetto/metrics/android/startup_metric.proto",
         "protos/perfetto/metrics/android/surfaceflinger.proto",
-        "protos/perfetto/metrics/android/sysui_cuj_metrics.proto",
         "protos/perfetto/metrics/android/task_names.proto",
         "protos/perfetto/metrics/android/thread_time_in_state_metric.proto",
         "protos/perfetto/metrics/android/trace_quality.proto",
         "protos/perfetto/metrics/android/unsymbolized_frames.proto",
         "protos/perfetto/metrics/chrome/all_chrome_metrics.proto",
+        "protos/perfetto/metrics/chrome/args_class_names.proto",
         "protos/perfetto/metrics/chrome/blink_gc_metric.proto",
         "protos/perfetto/metrics/chrome/dropped_frames.proto",
         "protos/perfetto/metrics/chrome/frame_times.proto",
@@ -4272,7 +4342,6 @@
         "protos/perfetto/metrics/android/simpleperf.proto",
         "protos/perfetto/metrics/android/startup_metric.proto",
         "protos/perfetto/metrics/android/surfaceflinger.proto",
-        "protos/perfetto/metrics/android/sysui_cuj_metrics.proto",
         "protos/perfetto/metrics/android/task_names.proto",
         "protos/perfetto/metrics/android/thread_time_in_state_metric.proto",
         "protos/perfetto/metrics/android/trace_quality.proto",
@@ -4300,6 +4369,7 @@
         "protos/perfetto/trace/android/gpu_mem_event.proto",
         "protos/perfetto/trace/android/graphics_frame_event.proto",
         "protos/perfetto/trace/android/initial_display_state.proto",
+        "protos/perfetto/trace/android/network_trace.proto",
         "protos/perfetto/trace/android/packages_list.proto",
     ],
     tools: [
@@ -4316,6 +4386,7 @@
         "external/perfetto/protos/perfetto/trace/android/gpu_mem_event.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/network_trace.gen.cc",
         "external/perfetto/protos/perfetto/trace/android/packages_list.gen.cc",
     ],
 }
@@ -4332,6 +4403,7 @@
         "protos/perfetto/trace/android/gpu_mem_event.proto",
         "protos/perfetto/trace/android/graphics_frame_event.proto",
         "protos/perfetto/trace/android/initial_display_state.proto",
+        "protos/perfetto/trace/android/network_trace.proto",
         "protos/perfetto/trace/android/packages_list.proto",
     ],
     tools: [
@@ -4348,6 +4420,7 @@
         "external/perfetto/protos/perfetto/trace/android/gpu_mem_event.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/network_trace.gen.h",
         "external/perfetto/protos/perfetto/trace/android/packages_list.gen.h",
     ],
     export_include_dirs: [
@@ -4368,6 +4441,7 @@
         "protos/perfetto/trace/android/gpu_mem_event.proto",
         "protos/perfetto/trace/android/graphics_frame_event.proto",
         "protos/perfetto/trace/android/initial_display_state.proto",
+        "protos/perfetto/trace/android/network_trace.proto",
         "protos/perfetto/trace/android/packages_list.proto",
     ],
     tools: [
@@ -4383,6 +4457,7 @@
         "external/perfetto/protos/perfetto/trace/android/gpu_mem_event.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/network_trace.pb.cc",
         "external/perfetto/protos/perfetto/trace/android/packages_list.pb.cc",
     ],
 }
@@ -4399,6 +4474,7 @@
         "protos/perfetto/trace/android/gpu_mem_event.proto",
         "protos/perfetto/trace/android/graphics_frame_event.proto",
         "protos/perfetto/trace/android/initial_display_state.proto",
+        "protos/perfetto/trace/android/network_trace.proto",
         "protos/perfetto/trace/android/packages_list.proto",
     ],
     tools: [
@@ -4414,6 +4490,7 @@
         "external/perfetto/protos/perfetto/trace/android/gpu_mem_event.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/network_trace.pb.h",
         "external/perfetto/protos/perfetto/trace/android/packages_list.pb.h",
     ],
     export_include_dirs: [
@@ -4434,6 +4511,7 @@
         "protos/perfetto/trace/android/gpu_mem_event.proto",
         "protos/perfetto/trace/android/graphics_frame_event.proto",
         "protos/perfetto/trace/android/initial_display_state.proto",
+        "protos/perfetto/trace/android/network_trace.proto",
         "protos/perfetto/trace/android/packages_list.proto",
     ],
     tools: [
@@ -4450,6 +4528,7 @@
         "external/perfetto/protos/perfetto/trace/android/gpu_mem_event.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/network_trace.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/android/packages_list.pbzero.cc",
     ],
 }
@@ -4466,6 +4545,7 @@
         "protos/perfetto/trace/android/gpu_mem_event.proto",
         "protos/perfetto/trace/android/graphics_frame_event.proto",
         "protos/perfetto/trace/android/initial_display_state.proto",
+        "protos/perfetto/trace/android/network_trace.proto",
         "protos/perfetto/trace/android/packages_list.proto",
     ],
     tools: [
@@ -4482,6 +4562,7 @@
         "external/perfetto/protos/perfetto/trace/android/gpu_mem_event.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/network_trace.pbzero.h",
         "external/perfetto/protos/perfetto/trace/android/packages_list.pbzero.h",
     ],
     export_include_dirs: [
@@ -4644,6 +4725,7 @@
         "protos/perfetto/config/android/android_log_config.proto",
         "protos/perfetto/config/android/android_polled_state_config.proto",
         "protos/perfetto/config/android/android_system_property_config.proto",
+        "protos/perfetto/config/android/network_trace_config.proto",
         "protos/perfetto/config/android/packages_list_config.proto",
         "protos/perfetto/config/chrome/chrome_config.proto",
         "protos/perfetto/config/data_source_config.proto",
@@ -4674,6 +4756,7 @@
         "protos/perfetto/trace/android/gpu_mem_event.proto",
         "protos/perfetto/trace/android/graphics_frame_event.proto",
         "protos/perfetto/trace/android/initial_display_state.proto",
+        "protos/perfetto/trace/android/network_trace.proto",
         "protos/perfetto/trace/android/packages_list.proto",
         "protos/perfetto/trace/chrome/chrome_benchmark_metadata.proto",
         "protos/perfetto/trace/chrome/chrome_metadata.proto",
@@ -4681,10 +4764,12 @@
         "protos/perfetto/trace/clock_snapshot.proto",
         "protos/perfetto/trace/extension_descriptor.proto",
         "protos/perfetto/trace/filesystem/inode_file_map.proto",
+        "protos/perfetto/trace/ftrace/android_fs.proto",
         "protos/perfetto/trace/ftrace/binder.proto",
         "protos/perfetto/trace/ftrace/block.proto",
         "protos/perfetto/trace/ftrace/cgroup.proto",
         "protos/perfetto/trace/ftrace/clk.proto",
+        "protos/perfetto/trace/ftrace/cma.proto",
         "protos/perfetto/trace/ftrace/compaction.proto",
         "protos/perfetto/trace/ftrace/cpuhp.proto",
         "protos/perfetto/trace/ftrace/cros_ec.proto",
@@ -4712,11 +4797,13 @@
         "protos/perfetto/trace/ftrace/kmem.proto",
         "protos/perfetto/trace/ftrace/kvm.proto",
         "protos/perfetto/trace/ftrace/lowmemorykiller.proto",
+        "protos/perfetto/trace/ftrace/lwis.proto",
         "protos/perfetto/trace/ftrace/mali.proto",
         "protos/perfetto/trace/ftrace/mdss.proto",
         "protos/perfetto/trace/ftrace/mm_event.proto",
         "protos/perfetto/trace/ftrace/net.proto",
         "protos/perfetto/trace/ftrace/oom.proto",
+        "protos/perfetto/trace/ftrace/panel.proto",
         "protos/perfetto/trace/ftrace/power.proto",
         "protos/perfetto/trace/ftrace/printk.proto",
         "protos/perfetto/trace/ftrace/raw_syscalls.proto",
@@ -4734,8 +4821,11 @@
         "protos/perfetto/trace/ftrace/tcp.proto",
         "protos/perfetto/trace/ftrace/test_bundle_wrapper.proto",
         "protos/perfetto/trace/ftrace/thermal.proto",
+        "protos/perfetto/trace/ftrace/trusty.proto",
         "protos/perfetto/trace/ftrace/ufs.proto",
         "protos/perfetto/trace/ftrace/v4l2.proto",
+        "protos/perfetto/trace/ftrace/virtio_gpu.proto",
+        "protos/perfetto/trace/ftrace/virtio_video.proto",
         "protos/perfetto/trace/ftrace/vmscan.proto",
         "protos/perfetto/trace/ftrace/workqueue.proto",
         "protos/perfetto/trace/gpu/gpu_counter_event.proto",
@@ -4766,6 +4856,8 @@
         "protos/perfetto/trace/trace.proto",
         "protos/perfetto/trace/trace_packet.proto",
         "protos/perfetto/trace/trace_packet_defaults.proto",
+        "protos/perfetto/trace/trace_uuid.proto",
+        "protos/perfetto/trace/track_event/chrome_active_processes.proto",
         "protos/perfetto/trace/track_event/chrome_application_state_info.proto",
         "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto",
         "protos/perfetto/trace/track_event/chrome_content_settings_event_info.proto",
@@ -4785,6 +4877,7 @@
         "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/range_of_interest.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",
@@ -4913,10 +5006,12 @@
 genrule {
     name: "perfetto_protos_perfetto_trace_ftrace_cpp_gen",
     srcs: [
+        "protos/perfetto/trace/ftrace/android_fs.proto",
         "protos/perfetto/trace/ftrace/binder.proto",
         "protos/perfetto/trace/ftrace/block.proto",
         "protos/perfetto/trace/ftrace/cgroup.proto",
         "protos/perfetto/trace/ftrace/clk.proto",
+        "protos/perfetto/trace/ftrace/cma.proto",
         "protos/perfetto/trace/ftrace/compaction.proto",
         "protos/perfetto/trace/ftrace/cpuhp.proto",
         "protos/perfetto/trace/ftrace/cros_ec.proto",
@@ -4944,11 +5039,13 @@
         "protos/perfetto/trace/ftrace/kmem.proto",
         "protos/perfetto/trace/ftrace/kvm.proto",
         "protos/perfetto/trace/ftrace/lowmemorykiller.proto",
+        "protos/perfetto/trace/ftrace/lwis.proto",
         "protos/perfetto/trace/ftrace/mali.proto",
         "protos/perfetto/trace/ftrace/mdss.proto",
         "protos/perfetto/trace/ftrace/mm_event.proto",
         "protos/perfetto/trace/ftrace/net.proto",
         "protos/perfetto/trace/ftrace/oom.proto",
+        "protos/perfetto/trace/ftrace/panel.proto",
         "protos/perfetto/trace/ftrace/power.proto",
         "protos/perfetto/trace/ftrace/printk.proto",
         "protos/perfetto/trace/ftrace/raw_syscalls.proto",
@@ -4966,8 +5063,11 @@
         "protos/perfetto/trace/ftrace/tcp.proto",
         "protos/perfetto/trace/ftrace/test_bundle_wrapper.proto",
         "protos/perfetto/trace/ftrace/thermal.proto",
+        "protos/perfetto/trace/ftrace/trusty.proto",
         "protos/perfetto/trace/ftrace/ufs.proto",
         "protos/perfetto/trace/ftrace/v4l2.proto",
+        "protos/perfetto/trace/ftrace/virtio_gpu.proto",
+        "protos/perfetto/trace/ftrace/virtio_video.proto",
         "protos/perfetto/trace/ftrace/vmscan.proto",
         "protos/perfetto/trace/ftrace/workqueue.proto",
     ],
@@ -4977,10 +5077,12 @@
     ],
     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/ftrace/android_fs.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/binder.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/block.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/cgroup.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/clk.gen.cc",
+        "external/perfetto/protos/perfetto/trace/ftrace/cma.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/compaction.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/cpuhp.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/cros_ec.gen.cc",
@@ -5008,11 +5110,13 @@
         "external/perfetto/protos/perfetto/trace/ftrace/kmem.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/kvm.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/lowmemorykiller.gen.cc",
+        "external/perfetto/protos/perfetto/trace/ftrace/lwis.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/mali.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/mdss.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/mm_event.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/net.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/oom.gen.cc",
+        "external/perfetto/protos/perfetto/trace/ftrace/panel.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/power.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/printk.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/raw_syscalls.gen.cc",
@@ -5030,8 +5134,11 @@
         "external/perfetto/protos/perfetto/trace/ftrace/tcp.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/test_bundle_wrapper.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/thermal.gen.cc",
+        "external/perfetto/protos/perfetto/trace/ftrace/trusty.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/ufs.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/v4l2.gen.cc",
+        "external/perfetto/protos/perfetto/trace/ftrace/virtio_gpu.gen.cc",
+        "external/perfetto/protos/perfetto/trace/ftrace/virtio_video.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/vmscan.gen.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/workqueue.gen.cc",
     ],
@@ -5041,10 +5148,12 @@
 genrule {
     name: "perfetto_protos_perfetto_trace_ftrace_cpp_gen_headers",
     srcs: [
+        "protos/perfetto/trace/ftrace/android_fs.proto",
         "protos/perfetto/trace/ftrace/binder.proto",
         "protos/perfetto/trace/ftrace/block.proto",
         "protos/perfetto/trace/ftrace/cgroup.proto",
         "protos/perfetto/trace/ftrace/clk.proto",
+        "protos/perfetto/trace/ftrace/cma.proto",
         "protos/perfetto/trace/ftrace/compaction.proto",
         "protos/perfetto/trace/ftrace/cpuhp.proto",
         "protos/perfetto/trace/ftrace/cros_ec.proto",
@@ -5072,11 +5181,13 @@
         "protos/perfetto/trace/ftrace/kmem.proto",
         "protos/perfetto/trace/ftrace/kvm.proto",
         "protos/perfetto/trace/ftrace/lowmemorykiller.proto",
+        "protos/perfetto/trace/ftrace/lwis.proto",
         "protos/perfetto/trace/ftrace/mali.proto",
         "protos/perfetto/trace/ftrace/mdss.proto",
         "protos/perfetto/trace/ftrace/mm_event.proto",
         "protos/perfetto/trace/ftrace/net.proto",
         "protos/perfetto/trace/ftrace/oom.proto",
+        "protos/perfetto/trace/ftrace/panel.proto",
         "protos/perfetto/trace/ftrace/power.proto",
         "protos/perfetto/trace/ftrace/printk.proto",
         "protos/perfetto/trace/ftrace/raw_syscalls.proto",
@@ -5094,8 +5205,11 @@
         "protos/perfetto/trace/ftrace/tcp.proto",
         "protos/perfetto/trace/ftrace/test_bundle_wrapper.proto",
         "protos/perfetto/trace/ftrace/thermal.proto",
+        "protos/perfetto/trace/ftrace/trusty.proto",
         "protos/perfetto/trace/ftrace/ufs.proto",
         "protos/perfetto/trace/ftrace/v4l2.proto",
+        "protos/perfetto/trace/ftrace/virtio_gpu.proto",
+        "protos/perfetto/trace/ftrace/virtio_video.proto",
         "protos/perfetto/trace/ftrace/vmscan.proto",
         "protos/perfetto/trace/ftrace/workqueue.proto",
     ],
@@ -5105,10 +5219,12 @@
     ],
     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/ftrace/android_fs.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/binder.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/block.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/cgroup.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/clk.gen.h",
+        "external/perfetto/protos/perfetto/trace/ftrace/cma.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/compaction.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/cpuhp.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/cros_ec.gen.h",
@@ -5136,11 +5252,13 @@
         "external/perfetto/protos/perfetto/trace/ftrace/kmem.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/kvm.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/lowmemorykiller.gen.h",
+        "external/perfetto/protos/perfetto/trace/ftrace/lwis.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/mali.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/mdss.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/mm_event.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/net.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/oom.gen.h",
+        "external/perfetto/protos/perfetto/trace/ftrace/panel.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/power.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/printk.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/raw_syscalls.gen.h",
@@ -5158,8 +5276,11 @@
         "external/perfetto/protos/perfetto/trace/ftrace/tcp.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/test_bundle_wrapper.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/thermal.gen.h",
+        "external/perfetto/protos/perfetto/trace/ftrace/trusty.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/ufs.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/v4l2.gen.h",
+        "external/perfetto/protos/perfetto/trace/ftrace/virtio_gpu.gen.h",
+        "external/perfetto/protos/perfetto/trace/ftrace/virtio_video.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/vmscan.gen.h",
         "external/perfetto/protos/perfetto/trace/ftrace/workqueue.gen.h",
     ],
@@ -5173,10 +5294,12 @@
 genrule {
     name: "perfetto_protos_perfetto_trace_ftrace_lite_gen",
     srcs: [
+        "protos/perfetto/trace/ftrace/android_fs.proto",
         "protos/perfetto/trace/ftrace/binder.proto",
         "protos/perfetto/trace/ftrace/block.proto",
         "protos/perfetto/trace/ftrace/cgroup.proto",
         "protos/perfetto/trace/ftrace/clk.proto",
+        "protos/perfetto/trace/ftrace/cma.proto",
         "protos/perfetto/trace/ftrace/compaction.proto",
         "protos/perfetto/trace/ftrace/cpuhp.proto",
         "protos/perfetto/trace/ftrace/cros_ec.proto",
@@ -5204,11 +5327,13 @@
         "protos/perfetto/trace/ftrace/kmem.proto",
         "protos/perfetto/trace/ftrace/kvm.proto",
         "protos/perfetto/trace/ftrace/lowmemorykiller.proto",
+        "protos/perfetto/trace/ftrace/lwis.proto",
         "protos/perfetto/trace/ftrace/mali.proto",
         "protos/perfetto/trace/ftrace/mdss.proto",
         "protos/perfetto/trace/ftrace/mm_event.proto",
         "protos/perfetto/trace/ftrace/net.proto",
         "protos/perfetto/trace/ftrace/oom.proto",
+        "protos/perfetto/trace/ftrace/panel.proto",
         "protos/perfetto/trace/ftrace/power.proto",
         "protos/perfetto/trace/ftrace/printk.proto",
         "protos/perfetto/trace/ftrace/raw_syscalls.proto",
@@ -5226,8 +5351,11 @@
         "protos/perfetto/trace/ftrace/tcp.proto",
         "protos/perfetto/trace/ftrace/test_bundle_wrapper.proto",
         "protos/perfetto/trace/ftrace/thermal.proto",
+        "protos/perfetto/trace/ftrace/trusty.proto",
         "protos/perfetto/trace/ftrace/ufs.proto",
         "protos/perfetto/trace/ftrace/v4l2.proto",
+        "protos/perfetto/trace/ftrace/virtio_gpu.proto",
+        "protos/perfetto/trace/ftrace/virtio_video.proto",
         "protos/perfetto/trace/ftrace/vmscan.proto",
         "protos/perfetto/trace/ftrace/workqueue.proto",
     ],
@@ -5236,10 +5364,12 @@
     ],
     cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
     out: [
+        "external/perfetto/protos/perfetto/trace/ftrace/android_fs.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/binder.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/block.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/cgroup.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/clk.pb.cc",
+        "external/perfetto/protos/perfetto/trace/ftrace/cma.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/compaction.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/cpuhp.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/cros_ec.pb.cc",
@@ -5267,11 +5397,13 @@
         "external/perfetto/protos/perfetto/trace/ftrace/kmem.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/kvm.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/lowmemorykiller.pb.cc",
+        "external/perfetto/protos/perfetto/trace/ftrace/lwis.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/mali.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/mdss.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/mm_event.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/net.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/oom.pb.cc",
+        "external/perfetto/protos/perfetto/trace/ftrace/panel.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/power.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/printk.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/raw_syscalls.pb.cc",
@@ -5289,8 +5421,11 @@
         "external/perfetto/protos/perfetto/trace/ftrace/tcp.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/test_bundle_wrapper.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/thermal.pb.cc",
+        "external/perfetto/protos/perfetto/trace/ftrace/trusty.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/ufs.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/v4l2.pb.cc",
+        "external/perfetto/protos/perfetto/trace/ftrace/virtio_gpu.pb.cc",
+        "external/perfetto/protos/perfetto/trace/ftrace/virtio_video.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/vmscan.pb.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/workqueue.pb.cc",
     ],
@@ -5300,10 +5435,12 @@
 genrule {
     name: "perfetto_protos_perfetto_trace_ftrace_lite_gen_headers",
     srcs: [
+        "protos/perfetto/trace/ftrace/android_fs.proto",
         "protos/perfetto/trace/ftrace/binder.proto",
         "protos/perfetto/trace/ftrace/block.proto",
         "protos/perfetto/trace/ftrace/cgroup.proto",
         "protos/perfetto/trace/ftrace/clk.proto",
+        "protos/perfetto/trace/ftrace/cma.proto",
         "protos/perfetto/trace/ftrace/compaction.proto",
         "protos/perfetto/trace/ftrace/cpuhp.proto",
         "protos/perfetto/trace/ftrace/cros_ec.proto",
@@ -5331,11 +5468,13 @@
         "protos/perfetto/trace/ftrace/kmem.proto",
         "protos/perfetto/trace/ftrace/kvm.proto",
         "protos/perfetto/trace/ftrace/lowmemorykiller.proto",
+        "protos/perfetto/trace/ftrace/lwis.proto",
         "protos/perfetto/trace/ftrace/mali.proto",
         "protos/perfetto/trace/ftrace/mdss.proto",
         "protos/perfetto/trace/ftrace/mm_event.proto",
         "protos/perfetto/trace/ftrace/net.proto",
         "protos/perfetto/trace/ftrace/oom.proto",
+        "protos/perfetto/trace/ftrace/panel.proto",
         "protos/perfetto/trace/ftrace/power.proto",
         "protos/perfetto/trace/ftrace/printk.proto",
         "protos/perfetto/trace/ftrace/raw_syscalls.proto",
@@ -5353,8 +5492,11 @@
         "protos/perfetto/trace/ftrace/tcp.proto",
         "protos/perfetto/trace/ftrace/test_bundle_wrapper.proto",
         "protos/perfetto/trace/ftrace/thermal.proto",
+        "protos/perfetto/trace/ftrace/trusty.proto",
         "protos/perfetto/trace/ftrace/ufs.proto",
         "protos/perfetto/trace/ftrace/v4l2.proto",
+        "protos/perfetto/trace/ftrace/virtio_gpu.proto",
+        "protos/perfetto/trace/ftrace/virtio_video.proto",
         "protos/perfetto/trace/ftrace/vmscan.proto",
         "protos/perfetto/trace/ftrace/workqueue.proto",
     ],
@@ -5363,10 +5505,12 @@
     ],
     cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(in)",
     out: [
+        "external/perfetto/protos/perfetto/trace/ftrace/android_fs.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/binder.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/block.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/cgroup.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/clk.pb.h",
+        "external/perfetto/protos/perfetto/trace/ftrace/cma.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/compaction.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/cpuhp.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/cros_ec.pb.h",
@@ -5394,11 +5538,13 @@
         "external/perfetto/protos/perfetto/trace/ftrace/kmem.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/kvm.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/lowmemorykiller.pb.h",
+        "external/perfetto/protos/perfetto/trace/ftrace/lwis.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/mali.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/mdss.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/mm_event.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/net.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/oom.pb.h",
+        "external/perfetto/protos/perfetto/trace/ftrace/panel.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/power.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/printk.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/raw_syscalls.pb.h",
@@ -5416,8 +5562,11 @@
         "external/perfetto/protos/perfetto/trace/ftrace/tcp.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/test_bundle_wrapper.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/thermal.pb.h",
+        "external/perfetto/protos/perfetto/trace/ftrace/trusty.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/ufs.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/v4l2.pb.h",
+        "external/perfetto/protos/perfetto/trace/ftrace/virtio_gpu.pb.h",
+        "external/perfetto/protos/perfetto/trace/ftrace/virtio_video.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/vmscan.pb.h",
         "external/perfetto/protos/perfetto/trace/ftrace/workqueue.pb.h",
     ],
@@ -5431,10 +5580,12 @@
 genrule {
     name: "perfetto_protos_perfetto_trace_ftrace_zero_gen",
     srcs: [
+        "protos/perfetto/trace/ftrace/android_fs.proto",
         "protos/perfetto/trace/ftrace/binder.proto",
         "protos/perfetto/trace/ftrace/block.proto",
         "protos/perfetto/trace/ftrace/cgroup.proto",
         "protos/perfetto/trace/ftrace/clk.proto",
+        "protos/perfetto/trace/ftrace/cma.proto",
         "protos/perfetto/trace/ftrace/compaction.proto",
         "protos/perfetto/trace/ftrace/cpuhp.proto",
         "protos/perfetto/trace/ftrace/cros_ec.proto",
@@ -5462,11 +5613,13 @@
         "protos/perfetto/trace/ftrace/kmem.proto",
         "protos/perfetto/trace/ftrace/kvm.proto",
         "protos/perfetto/trace/ftrace/lowmemorykiller.proto",
+        "protos/perfetto/trace/ftrace/lwis.proto",
         "protos/perfetto/trace/ftrace/mali.proto",
         "protos/perfetto/trace/ftrace/mdss.proto",
         "protos/perfetto/trace/ftrace/mm_event.proto",
         "protos/perfetto/trace/ftrace/net.proto",
         "protos/perfetto/trace/ftrace/oom.proto",
+        "protos/perfetto/trace/ftrace/panel.proto",
         "protos/perfetto/trace/ftrace/power.proto",
         "protos/perfetto/trace/ftrace/printk.proto",
         "protos/perfetto/trace/ftrace/raw_syscalls.proto",
@@ -5484,8 +5637,11 @@
         "protos/perfetto/trace/ftrace/tcp.proto",
         "protos/perfetto/trace/ftrace/test_bundle_wrapper.proto",
         "protos/perfetto/trace/ftrace/thermal.proto",
+        "protos/perfetto/trace/ftrace/trusty.proto",
         "protos/perfetto/trace/ftrace/ufs.proto",
         "protos/perfetto/trace/ftrace/v4l2.proto",
+        "protos/perfetto/trace/ftrace/virtio_gpu.proto",
+        "protos/perfetto/trace/ftrace/virtio_video.proto",
         "protos/perfetto/trace/ftrace/vmscan.proto",
         "protos/perfetto/trace/ftrace/workqueue.proto",
     ],
@@ -5495,10 +5651,12 @@
     ],
     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/ftrace/android_fs.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/binder.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/block.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/cgroup.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/clk.pbzero.cc",
+        "external/perfetto/protos/perfetto/trace/ftrace/cma.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/compaction.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/cpuhp.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/cros_ec.pbzero.cc",
@@ -5526,11 +5684,13 @@
         "external/perfetto/protos/perfetto/trace/ftrace/kmem.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/kvm.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/lowmemorykiller.pbzero.cc",
+        "external/perfetto/protos/perfetto/trace/ftrace/lwis.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/mali.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/mdss.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/mm_event.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/net.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/oom.pbzero.cc",
+        "external/perfetto/protos/perfetto/trace/ftrace/panel.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/power.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/printk.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/raw_syscalls.pbzero.cc",
@@ -5548,8 +5708,11 @@
         "external/perfetto/protos/perfetto/trace/ftrace/tcp.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/test_bundle_wrapper.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/thermal.pbzero.cc",
+        "external/perfetto/protos/perfetto/trace/ftrace/trusty.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/ufs.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/v4l2.pbzero.cc",
+        "external/perfetto/protos/perfetto/trace/ftrace/virtio_gpu.pbzero.cc",
+        "external/perfetto/protos/perfetto/trace/ftrace/virtio_video.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/vmscan.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/ftrace/workqueue.pbzero.cc",
     ],
@@ -5559,10 +5722,12 @@
 genrule {
     name: "perfetto_protos_perfetto_trace_ftrace_zero_gen_headers",
     srcs: [
+        "protos/perfetto/trace/ftrace/android_fs.proto",
         "protos/perfetto/trace/ftrace/binder.proto",
         "protos/perfetto/trace/ftrace/block.proto",
         "protos/perfetto/trace/ftrace/cgroup.proto",
         "protos/perfetto/trace/ftrace/clk.proto",
+        "protos/perfetto/trace/ftrace/cma.proto",
         "protos/perfetto/trace/ftrace/compaction.proto",
         "protos/perfetto/trace/ftrace/cpuhp.proto",
         "protos/perfetto/trace/ftrace/cros_ec.proto",
@@ -5590,11 +5755,13 @@
         "protos/perfetto/trace/ftrace/kmem.proto",
         "protos/perfetto/trace/ftrace/kvm.proto",
         "protos/perfetto/trace/ftrace/lowmemorykiller.proto",
+        "protos/perfetto/trace/ftrace/lwis.proto",
         "protos/perfetto/trace/ftrace/mali.proto",
         "protos/perfetto/trace/ftrace/mdss.proto",
         "protos/perfetto/trace/ftrace/mm_event.proto",
         "protos/perfetto/trace/ftrace/net.proto",
         "protos/perfetto/trace/ftrace/oom.proto",
+        "protos/perfetto/trace/ftrace/panel.proto",
         "protos/perfetto/trace/ftrace/power.proto",
         "protos/perfetto/trace/ftrace/printk.proto",
         "protos/perfetto/trace/ftrace/raw_syscalls.proto",
@@ -5612,8 +5779,11 @@
         "protos/perfetto/trace/ftrace/tcp.proto",
         "protos/perfetto/trace/ftrace/test_bundle_wrapper.proto",
         "protos/perfetto/trace/ftrace/thermal.proto",
+        "protos/perfetto/trace/ftrace/trusty.proto",
         "protos/perfetto/trace/ftrace/ufs.proto",
         "protos/perfetto/trace/ftrace/v4l2.proto",
+        "protos/perfetto/trace/ftrace/virtio_gpu.proto",
+        "protos/perfetto/trace/ftrace/virtio_video.proto",
         "protos/perfetto/trace/ftrace/vmscan.proto",
         "protos/perfetto/trace/ftrace/workqueue.proto",
     ],
@@ -5623,10 +5793,12 @@
     ],
     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/ftrace/android_fs.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/binder.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/block.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/cgroup.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/clk.pbzero.h",
+        "external/perfetto/protos/perfetto/trace/ftrace/cma.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/compaction.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/cpuhp.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/cros_ec.pbzero.h",
@@ -5654,11 +5826,13 @@
         "external/perfetto/protos/perfetto/trace/ftrace/kmem.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/kvm.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/lowmemorykiller.pbzero.h",
+        "external/perfetto/protos/perfetto/trace/ftrace/lwis.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/mali.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/mdss.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/mm_event.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/net.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/oom.pbzero.h",
+        "external/perfetto/protos/perfetto/trace/ftrace/panel.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/power.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/printk.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/raw_syscalls.pbzero.h",
@@ -5676,8 +5850,11 @@
         "external/perfetto/protos/perfetto/trace/ftrace/tcp.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/test_bundle_wrapper.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/thermal.pbzero.h",
+        "external/perfetto/protos/perfetto/trace/ftrace/trusty.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/ufs.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/v4l2.pbzero.h",
+        "external/perfetto/protos/perfetto/trace/ftrace/virtio_gpu.pbzero.h",
+        "external/perfetto/protos/perfetto/trace/ftrace/virtio_video.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/vmscan.pbzero.h",
         "external/perfetto/protos/perfetto/trace/ftrace/workqueue.pbzero.h",
     ],
@@ -5953,6 +6130,7 @@
     srcs: [
         "protos/perfetto/trace/clock_snapshot.proto",
         "protos/perfetto/trace/system_info.proto",
+        "protos/perfetto/trace/trace_uuid.proto",
         "protos/perfetto/trace/trigger.proto",
     ],
     tools: [
@@ -5963,6 +6141,7 @@
     out: [
         "external/perfetto/protos/perfetto/trace/clock_snapshot.gen.cc",
         "external/perfetto/protos/perfetto/trace/system_info.gen.cc",
+        "external/perfetto/protos/perfetto/trace/trace_uuid.gen.cc",
         "external/perfetto/protos/perfetto/trace/trigger.gen.cc",
     ],
 }
@@ -5973,6 +6152,7 @@
     srcs: [
         "protos/perfetto/trace/clock_snapshot.proto",
         "protos/perfetto/trace/system_info.proto",
+        "protos/perfetto/trace/trace_uuid.proto",
         "protos/perfetto/trace/trigger.proto",
     ],
     tools: [
@@ -5983,6 +6163,7 @@
     out: [
         "external/perfetto/protos/perfetto/trace/clock_snapshot.gen.h",
         "external/perfetto/protos/perfetto/trace/system_info.gen.h",
+        "external/perfetto/protos/perfetto/trace/trace_uuid.gen.h",
         "external/perfetto/protos/perfetto/trace/trigger.gen.h",
     ],
     export_include_dirs: [
@@ -5997,6 +6178,7 @@
     srcs: [
         "protos/perfetto/trace/clock_snapshot.proto",
         "protos/perfetto/trace/system_info.proto",
+        "protos/perfetto/trace/trace_uuid.proto",
         "protos/perfetto/trace/trigger.proto",
     ],
     tools: [
@@ -6006,6 +6188,7 @@
     out: [
         "external/perfetto/protos/perfetto/trace/clock_snapshot.pb.cc",
         "external/perfetto/protos/perfetto/trace/system_info.pb.cc",
+        "external/perfetto/protos/perfetto/trace/trace_uuid.pb.cc",
         "external/perfetto/protos/perfetto/trace/trigger.pb.cc",
     ],
 }
@@ -6016,6 +6199,7 @@
     srcs: [
         "protos/perfetto/trace/clock_snapshot.proto",
         "protos/perfetto/trace/system_info.proto",
+        "protos/perfetto/trace/trace_uuid.proto",
         "protos/perfetto/trace/trigger.proto",
     ],
     tools: [
@@ -6025,6 +6209,7 @@
     out: [
         "external/perfetto/protos/perfetto/trace/clock_snapshot.pb.h",
         "external/perfetto/protos/perfetto/trace/system_info.pb.h",
+        "external/perfetto/protos/perfetto/trace/trace_uuid.pb.h",
         "external/perfetto/protos/perfetto/trace/trigger.pb.h",
     ],
     export_include_dirs: [
@@ -6039,6 +6224,7 @@
     srcs: [
         "protos/perfetto/trace/clock_snapshot.proto",
         "protos/perfetto/trace/system_info.proto",
+        "protos/perfetto/trace/trace_uuid.proto",
         "protos/perfetto/trace/trigger.proto",
     ],
     tools: [
@@ -6049,6 +6235,7 @@
     out: [
         "external/perfetto/protos/perfetto/trace/clock_snapshot.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/system_info.pbzero.cc",
+        "external/perfetto/protos/perfetto/trace/trace_uuid.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/trigger.pbzero.cc",
     ],
 }
@@ -6059,6 +6246,7 @@
     srcs: [
         "protos/perfetto/trace/clock_snapshot.proto",
         "protos/perfetto/trace/system_info.proto",
+        "protos/perfetto/trace/trace_uuid.proto",
         "protos/perfetto/trace/trigger.proto",
     ],
     tools: [
@@ -6069,6 +6257,7 @@
     out: [
         "external/perfetto/protos/perfetto/trace/clock_snapshot.pbzero.h",
         "external/perfetto/protos/perfetto/trace/system_info.pbzero.h",
+        "external/perfetto/protos/perfetto/trace/trace_uuid.pbzero.h",
         "external/perfetto/protos/perfetto/trace/trigger.pbzero.h",
     ],
     export_include_dirs: [
@@ -6555,6 +6744,7 @@
 genrule {
     name: "perfetto_protos_perfetto_trace_processor_zero_gen",
     srcs: [
+        "protos/perfetto/trace_processor/metatrace_categories.proto",
         "protos/perfetto/trace_processor/trace_processor.proto",
     ],
     tools: [
@@ -6563,6 +6753,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_processor/metatrace_categories.pbzero.cc",
         "external/perfetto/protos/perfetto/trace_processor/trace_processor.pbzero.cc",
     ],
 }
@@ -6571,6 +6762,7 @@
 genrule {
     name: "perfetto_protos_perfetto_trace_processor_zero_gen_headers",
     srcs: [
+        "protos/perfetto/trace_processor/metatrace_categories.proto",
         "protos/perfetto/trace_processor/trace_processor.proto",
     ],
     tools: [
@@ -6579,6 +6771,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_processor/metatrace_categories.pbzero.h",
         "external/perfetto/protos/perfetto/trace_processor/trace_processor.pbzero.h",
     ],
     export_include_dirs: [
@@ -7181,6 +7374,7 @@
 genrule {
     name: "perfetto_protos_perfetto_trace_track_event_cpp_gen",
     srcs: [
+        "protos/perfetto/trace/track_event/chrome_active_processes.proto",
         "protos/perfetto/trace/track_event/chrome_application_state_info.proto",
         "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto",
         "protos/perfetto/trace/track_event/chrome_content_settings_event_info.proto",
@@ -7200,6 +7394,7 @@
         "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/range_of_interest.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",
@@ -7212,6 +7407,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/track_event/chrome_active_processes.gen.cc",
         "external/perfetto/protos/perfetto/trace/track_event/chrome_application_state_info.gen.cc",
         "external/perfetto/protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.gen.cc",
         "external/perfetto/protos/perfetto/trace/track_event/chrome_content_settings_event_info.gen.cc",
@@ -7231,6 +7427,7 @@
         "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",
+        "external/perfetto/protos/perfetto/trace/track_event/range_of_interest.gen.cc",
         "external/perfetto/protos/perfetto/trace/track_event/source_location.gen.cc",
         "external/perfetto/protos/perfetto/trace/track_event/task_execution.gen.cc",
         "external/perfetto/protos/perfetto/trace/track_event/thread_descriptor.gen.cc",
@@ -7243,6 +7440,7 @@
 genrule {
     name: "perfetto_protos_perfetto_trace_track_event_cpp_gen_headers",
     srcs: [
+        "protos/perfetto/trace/track_event/chrome_active_processes.proto",
         "protos/perfetto/trace/track_event/chrome_application_state_info.proto",
         "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto",
         "protos/perfetto/trace/track_event/chrome_content_settings_event_info.proto",
@@ -7262,6 +7460,7 @@
         "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/range_of_interest.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",
@@ -7274,6 +7473,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/track_event/chrome_active_processes.gen.h",
         "external/perfetto/protos/perfetto/trace/track_event/chrome_application_state_info.gen.h",
         "external/perfetto/protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.gen.h",
         "external/perfetto/protos/perfetto/trace/track_event/chrome_content_settings_event_info.gen.h",
@@ -7293,6 +7493,7 @@
         "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",
+        "external/perfetto/protos/perfetto/trace/track_event/range_of_interest.gen.h",
         "external/perfetto/protos/perfetto/trace/track_event/source_location.gen.h",
         "external/perfetto/protos/perfetto/trace/track_event/task_execution.gen.h",
         "external/perfetto/protos/perfetto/trace/track_event/thread_descriptor.gen.h",
@@ -7309,6 +7510,7 @@
 genrule {
     name: "perfetto_protos_perfetto_trace_track_event_descriptor",
     srcs: [
+        "protos/perfetto/trace/track_event/chrome_active_processes.proto",
         "protos/perfetto/trace/track_event/chrome_application_state_info.proto",
         "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto",
         "protos/perfetto/trace/track_event/chrome_content_settings_event_info.proto",
@@ -7328,6 +7530,7 @@
         "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/range_of_interest.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",
@@ -7347,6 +7550,7 @@
 genrule {
     name: "perfetto_protos_perfetto_trace_track_event_lite_gen",
     srcs: [
+        "protos/perfetto/trace/track_event/chrome_active_processes.proto",
         "protos/perfetto/trace/track_event/chrome_application_state_info.proto",
         "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto",
         "protos/perfetto/trace/track_event/chrome_content_settings_event_info.proto",
@@ -7366,6 +7570,7 @@
         "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/range_of_interest.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",
@@ -7377,6 +7582,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/track_event/chrome_active_processes.pb.cc",
         "external/perfetto/protos/perfetto/trace/track_event/chrome_application_state_info.pb.cc",
         "external/perfetto/protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.pb.cc",
         "external/perfetto/protos/perfetto/trace/track_event/chrome_content_settings_event_info.pb.cc",
@@ -7396,6 +7602,7 @@
         "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",
+        "external/perfetto/protos/perfetto/trace/track_event/range_of_interest.pb.cc",
         "external/perfetto/protos/perfetto/trace/track_event/source_location.pb.cc",
         "external/perfetto/protos/perfetto/trace/track_event/task_execution.pb.cc",
         "external/perfetto/protos/perfetto/trace/track_event/thread_descriptor.pb.cc",
@@ -7408,6 +7615,7 @@
 genrule {
     name: "perfetto_protos_perfetto_trace_track_event_lite_gen_headers",
     srcs: [
+        "protos/perfetto/trace/track_event/chrome_active_processes.proto",
         "protos/perfetto/trace/track_event/chrome_application_state_info.proto",
         "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto",
         "protos/perfetto/trace/track_event/chrome_content_settings_event_info.proto",
@@ -7427,6 +7635,7 @@
         "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/range_of_interest.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",
@@ -7438,6 +7647,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/track_event/chrome_active_processes.pb.h",
         "external/perfetto/protos/perfetto/trace/track_event/chrome_application_state_info.pb.h",
         "external/perfetto/protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.pb.h",
         "external/perfetto/protos/perfetto/trace/track_event/chrome_content_settings_event_info.pb.h",
@@ -7457,6 +7667,7 @@
         "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",
+        "external/perfetto/protos/perfetto/trace/track_event/range_of_interest.pb.h",
         "external/perfetto/protos/perfetto/trace/track_event/source_location.pb.h",
         "external/perfetto/protos/perfetto/trace/track_event/task_execution.pb.h",
         "external/perfetto/protos/perfetto/trace/track_event/thread_descriptor.pb.h",
@@ -7473,6 +7684,7 @@
 genrule {
     name: "perfetto_protos_perfetto_trace_track_event_zero_gen",
     srcs: [
+        "protos/perfetto/trace/track_event/chrome_active_processes.proto",
         "protos/perfetto/trace/track_event/chrome_application_state_info.proto",
         "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto",
         "protos/perfetto/trace/track_event/chrome_content_settings_event_info.proto",
@@ -7492,6 +7704,7 @@
         "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/range_of_interest.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",
@@ -7504,6 +7717,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/track_event/chrome_active_processes.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/track_event/chrome_application_state_info.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/track_event/chrome_content_settings_event_info.pbzero.cc",
@@ -7523,6 +7737,7 @@
         "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",
+        "external/perfetto/protos/perfetto/trace/track_event/range_of_interest.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/track_event/source_location.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/track_event/task_execution.pbzero.cc",
         "external/perfetto/protos/perfetto/trace/track_event/thread_descriptor.pbzero.cc",
@@ -7535,6 +7750,7 @@
 genrule {
     name: "perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
     srcs: [
+        "protos/perfetto/trace/track_event/chrome_active_processes.proto",
         "protos/perfetto/trace/track_event/chrome_application_state_info.proto",
         "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto",
         "protos/perfetto/trace/track_event/chrome_content_settings_event_info.proto",
@@ -7554,6 +7770,7 @@
         "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/range_of_interest.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",
@@ -7566,6 +7783,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/track_event/chrome_active_processes.pbzero.h",
         "external/perfetto/protos/perfetto/trace/track_event/chrome_application_state_info.pbzero.h",
         "external/perfetto/protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.pbzero.h",
         "external/perfetto/protos/perfetto/trace/track_event/chrome_content_settings_event_info.pbzero.h",
@@ -7585,6 +7803,7 @@
         "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",
+        "external/perfetto/protos/perfetto/trace/track_event/range_of_interest.pbzero.h",
         "external/perfetto/protos/perfetto/trace/track_event/source_location.pbzero.h",
         "external/perfetto/protos/perfetto/trace/track_event/task_execution.pbzero.h",
         "external/perfetto/protos/perfetto/trace/track_event/thread_descriptor.pbzero.h",
@@ -7707,6 +7926,7 @@
 genrule {
     name: "perfetto_protos_third_party_chromium_descriptor",
     srcs: [
+        "protos/perfetto/trace/track_event/chrome_active_processes.proto",
         "protos/perfetto/trace/track_event/chrome_application_state_info.proto",
         "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto",
         "protos/perfetto/trace/track_event/chrome_content_settings_event_info.proto",
@@ -7726,6 +7946,7 @@
         "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/range_of_interest.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",
@@ -7866,6 +8087,7 @@
         "src/base/temp_file.cc",
         "src/base/thread_checker.cc",
         "src/base/thread_task_runner.cc",
+        "src/base/thread_utils.cc",
         "src/base/time.cc",
         "src/base/unix_task_runner.cc",
         "src/base/utils.cc",
@@ -7905,6 +8127,22 @@
     ],
 }
 
+// GN: //src/base/threading:threading
+filegroup {
+    name: "perfetto_src_base_threading_threading",
+    srcs: [
+        "src/base/threading/thread_pool.cc",
+    ],
+}
+
+// GN: //src/base/threading:unittests
+filegroup {
+    name: "perfetto_src_base_threading_unittests",
+    srcs: [
+        "src/base/threading/thread_pool_unittest.cc",
+    ],
+}
+
 // GN: //src/base:unittests
 filegroup {
     name: "perfetto_src_base_unittests",
@@ -7923,6 +8161,7 @@
         "src/base/periodic_task_unittest.cc",
         "src/base/scoped_file_unittest.cc",
         "src/base/small_vector_unittest.cc",
+        "src/base/status_or_unittest.cc",
         "src/base/string_splitter_unittest.cc",
         "src/base/string_utils_unittest.cc",
         "src/base/string_view_unittest.cc",
@@ -8585,6 +8824,7 @@
 cc_binary_host {
     name: "perfetto_src_protozero_protoc_plugin_cppgen_plugin",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_public_abi_base",
@@ -8813,14 +9053,6 @@
     ],
 }
 
-// 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",
@@ -8890,7 +9122,6 @@
         "src/trace_processor/dynamic/ancestor_generator.cc",
         "src/trace_processor/dynamic/connected_flow_generator.cc",
         "src/trace_processor/dynamic/descendant_generator.cc",
-        "src/trace_processor/dynamic/describe_slice_generator.cc",
         "src/trace_processor/dynamic/dynamic_table_generator.cc",
         "src/trace_processor/dynamic/experimental_annotated_stack_generator.cc",
         "src/trace_processor/dynamic/experimental_counter_dur_generator.cc",
@@ -8907,6 +9138,9 @@
 filegroup {
     name: "perfetto_src_trace_processor_dynamic_unittests",
     srcs: [
+        "src/trace_processor/dynamic/ancestor_generator_unittest.cc",
+        "src/trace_processor/dynamic/connected_flow_generator_unittest.cc",
+        "src/trace_processor/dynamic/descendant_generator_unittest.cc",
         "src/trace_processor/dynamic/experimental_counter_dur_generator_unittest.cc",
         "src/trace_processor/dynamic/experimental_flat_slice_generator_unittest.cc",
         "src/trace_processor/dynamic/experimental_slice_layout_generator_unittest.cc",
@@ -8921,14 +9155,6 @@
     ],
 }
 
-// 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:gen_cc_test_messages_descriptor
 genrule {
     name: "perfetto_src_trace_processor_gen_cc_test_messages_descriptor",
@@ -8967,7 +9193,9 @@
     srcs: [
         "src/trace_processor/importers/common/args_tracker.cc",
         "src/trace_processor/importers/common/args_translation_table.cc",
+        "src/trace_processor/importers/common/async_track_set_tracker.cc",
         "src/trace_processor/importers/common/clock_tracker.cc",
+        "src/trace_processor/importers/common/deobfuscation_mapping_table.cc",
         "src/trace_processor/importers/common/event_tracker.cc",
         "src/trace_processor/importers/common/flow_tracker.cc",
         "src/trace_processor/importers/common/global_args_tracker.cc",
@@ -8975,16 +9203,29 @@
         "src/trace_processor/importers/common/slice_tracker.cc",
         "src/trace_processor/importers/common/slice_translation_table.cc",
         "src/trace_processor/importers/common/system_info_tracker.cc",
+        "src/trace_processor/importers/common/trace_parser.cc",
         "src/trace_processor/importers/common/track_tracker.cc",
     ],
 }
 
+// GN: //src/trace_processor/importers/common:parser_types
+filegroup {
+    name: "perfetto_src_trace_processor_importers_common_parser_types",
+}
+
+// GN: //src/trace_processor/importers/common:trace_parser_hdr
+filegroup {
+    name: "perfetto_src_trace_processor_importers_common_trace_parser_hdr",
+}
+
 // GN: //src/trace_processor/importers/common:unittests
 filegroup {
     name: "perfetto_src_trace_processor_importers_common_unittests",
     srcs: [
         "src/trace_processor/importers/common/args_translation_table_unittest.cc",
+        "src/trace_processor/importers/common/async_track_set_tracker_unittest.cc",
         "src/trace_processor/importers/common/clock_tracker_unittest.cc",
+        "src/trace_processor/importers/common/deobfuscation_mapping_table_unittest.cc",
         "src/trace_processor/importers/common/event_tracker_unittest.cc",
         "src/trace_processor/importers/common/flow_tracker_unittest.cc",
         "src/trace_processor/importers/common/process_tracker_unittest.cc",
@@ -8993,63 +9234,114 @@
     ],
 }
 
-// GN: //src/trace_processor/importers:gen_cc_chrome_track_event_descriptor
-genrule {
-    name: "perfetto_src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
+// GN: //src/trace_processor/importers/ftrace:ftrace_descriptors
+filegroup {
+    name: "perfetto_src_trace_processor_importers_ftrace_ftrace_descriptors",
     srcs: [
-        ":perfetto_protos_third_party_chromium_descriptor",
-    ],
-    cmd: "$(location tools/gen_cc_proto_descriptor.py) --gen_dir=$(genDir) --cpp_out=$(out) $(in)",
-    out: [
-        "src/trace_processor/importers/chrome_track_event.descriptor.h",
-    ],
-    tool_files: [
-        "tools/gen_cc_proto_descriptor.py",
+        "src/trace_processor/importers/ftrace/ftrace_descriptors.cc",
     ],
 }
 
-// GN: //src/trace_processor/importers:gen_cc_config_descriptor
-genrule {
-    name: "perfetto_src_trace_processor_importers_gen_cc_config_descriptor",
+// GN: //src/trace_processor/importers/ftrace:full
+filegroup {
+    name: "perfetto_src_trace_processor_importers_ftrace_full",
     srcs: [
-        ":perfetto_protos_perfetto_config_descriptor",
-    ],
-    cmd: "$(location tools/gen_cc_proto_descriptor.py) --gen_dir=$(genDir) --cpp_out=$(out) $(in)",
-    out: [
-        "src/trace_processor/importers/config.descriptor.h",
-    ],
-    tool_files: [
-        "tools/gen_cc_proto_descriptor.py",
+        "src/trace_processor/importers/ftrace/binder_tracker.cc",
+        "src/trace_processor/importers/ftrace/drm_tracker.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/iostat_tracker.cc",
+        "src/trace_processor/importers/ftrace/mali_gpu_event_tracker.cc",
+        "src/trace_processor/importers/ftrace/rss_stat_tracker.cc",
+        "src/trace_processor/importers/ftrace/sched_event_tracker.cc",
+        "src/trace_processor/importers/ftrace/thread_state_tracker.cc",
+        "src/trace_processor/importers/ftrace/v4l2_tracker.cc",
+        "src/trace_processor/importers/ftrace/virtio_gpu_tracker.cc",
+        "src/trace_processor/importers/ftrace/virtio_video_tracker.cc",
     ],
 }
 
-// GN: //src/trace_processor/importers:gen_cc_statsd_atoms_descriptor
-genrule {
-    name: "perfetto_src_trace_processor_importers_gen_cc_statsd_atoms_descriptor",
+// GN: //src/trace_processor/importers/ftrace:minimal
+filegroup {
+    name: "perfetto_src_trace_processor_importers_ftrace_minimal",
     srcs: [
-        "src/trace_processor/importers/proto/atoms.descriptor",
-    ],
-    cmd: "$(location tools/gen_cc_proto_descriptor.py) --gen_dir=$(genDir) --cpp_out=$(out) $(in)",
-    out: [
-        "src/trace_processor/importers/atoms.descriptor.h",
-    ],
-    tool_files: [
-        "tools/gen_cc_proto_descriptor.py",
+        "src/trace_processor/importers/ftrace/ftrace_module.cc",
     ],
 }
 
-// GN: //src/trace_processor/importers:gen_cc_track_event_descriptor
-genrule {
-    name: "perfetto_src_trace_processor_importers_gen_cc_track_event_descriptor",
+// GN: //src/trace_processor/importers/ftrace:unittests
+filegroup {
+    name: "perfetto_src_trace_processor_importers_ftrace_unittests",
     srcs: [
-        ":perfetto_protos_perfetto_trace_track_event_descriptor",
+        "src/trace_processor/importers/ftrace/binder_tracker_unittest.cc",
+        "src/trace_processor/importers/ftrace/sched_event_tracker_unittest.cc",
+        "src/trace_processor/importers/ftrace/thread_state_tracker_unittest.cc",
     ],
-    cmd: "$(location tools/gen_cc_proto_descriptor.py) --gen_dir=$(genDir) --cpp_out=$(out) $(in)",
-    out: [
-        "src/trace_processor/importers/track_event.descriptor.h",
+}
+
+// GN: //src/trace_processor/importers/fuchsia:fuchsia_record
+filegroup {
+    name: "perfetto_src_trace_processor_importers_fuchsia_fuchsia_record",
+    srcs: [
+        "src/trace_processor/importers/fuchsia/fuchsia_record.cc",
     ],
-    tool_files: [
-        "tools/gen_cc_proto_descriptor.py",
+}
+
+// GN: //src/trace_processor/importers/fuchsia:full
+filegroup {
+    name: "perfetto_src_trace_processor_importers_fuchsia_full",
+    srcs: [
+        "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",
+    ],
+}
+
+// GN: //src/trace_processor/importers/fuchsia:minimal
+filegroup {
+    name: "perfetto_src_trace_processor_importers_fuchsia_minimal",
+}
+
+// GN: //src/trace_processor/importers/fuchsia:unittests
+filegroup {
+    name: "perfetto_src_trace_processor_importers_fuchsia_unittests",
+    srcs: [
+        "src/trace_processor/importers/fuchsia/fuchsia_parser_unittest.cc",
+        "src/trace_processor/importers/fuchsia/fuchsia_trace_utils_unittest.cc",
+    ],
+}
+
+// GN: //src/trace_processor/importers/gzip:full
+filegroup {
+    name: "perfetto_src_trace_processor_importers_gzip_full",
+    srcs: [
+        "src/trace_processor/importers/gzip/gzip_trace_parser.cc",
+    ],
+}
+
+// GN: //src/trace_processor/importers/i2c:full
+filegroup {
+    name: "perfetto_src_trace_processor_importers_i2c_full",
+    srcs: [
+        "src/trace_processor/importers/i2c/i2c_tracker.cc",
+    ],
+}
+
+// GN: //src/trace_processor/importers/json:full
+filegroup {
+    name: "perfetto_src_trace_processor_importers_json_full",
+    srcs: [
+        "src/trace_processor/importers/json/json_trace_parser.cc",
+        "src/trace_processor/importers/json/json_trace_tokenizer.cc",
+    ],
+}
+
+// GN: //src/trace_processor/importers/json:minimal
+filegroup {
+    name: "perfetto_src_trace_processor_importers_json_minimal",
+    srcs: [
+        "src/trace_processor/importers/json/json_utils.cc",
     ],
 }
 
@@ -9065,21 +9357,165 @@
     ],
 }
 
-// GN: //src/trace_processor/importers/proto:storage_full
+// GN: //src/trace_processor/importers/memory_tracker:unittests
 filegroup {
-    name: "perfetto_src_trace_processor_importers_proto_storage_full",
+    name: "perfetto_src_trace_processor_importers_memory_tracker_unittests",
     srcs: [
-        "src/trace_processor/importers/proto/heap_graph_tracker.cc",
+        "src/trace_processor/importers/memory_tracker/graph_processor_unittest.cc",
+        "src/trace_processor/importers/memory_tracker/graph_unittest.cc",
+        "src/trace_processor/importers/memory_tracker/raw_process_memory_node_unittest.cc",
     ],
 }
 
-// GN: //src/trace_processor/importers/proto:storage_minimal
+// GN: //src/trace_processor/importers/ninja:ninja
 filegroup {
-    name: "perfetto_src_trace_processor_importers_proto_storage_minimal",
+    name: "perfetto_src_trace_processor_importers_ninja_ninja",
     srcs: [
+        "src/trace_processor/importers/ninja/ninja_log_parser.cc",
+    ],
+}
+
+// GN: //src/trace_processor/importers/proto:full
+filegroup {
+    name: "perfetto_src_trace_processor_importers_proto_full",
+    srcs: [
+        "src/trace_processor/importers/proto/additional_modules.cc",
+        "src/trace_processor/importers/proto/android_camera_event_module.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/content_analyzer.cc",
+        "src/trace_processor/importers/proto/frame_timeline_event_parser.cc",
+        "src/trace_processor/importers/proto/gpu_event_parser.cc",
+        "src/trace_processor/importers/proto/graphics_event_module.cc",
+        "src/trace_processor/importers/proto/graphics_frame_event_parser.cc",
+        "src/trace_processor/importers/proto/heap_graph_module.cc",
+        "src/trace_processor/importers/proto/heap_graph_tracker.cc",
+        "src/trace_processor/importers/proto/metadata_module.cc",
+        "src/trace_processor/importers/proto/statsd_module.cc",
+        "src/trace_processor/importers/proto/system_probes_module.cc",
+        "src/trace_processor/importers/proto/system_probes_parser.cc",
+        "src/trace_processor/importers/proto/translation_table_module.cc",
+        "src/trace_processor/importers/proto/vulkan_memory_tracker.cc",
+    ],
+}
+
+// GN: //src/trace_processor/importers/proto:gen_cc_chrome_track_event_descriptor
+genrule {
+    name: "perfetto_src_trace_processor_importers_proto_gen_cc_chrome_track_event_descriptor",
+    srcs: [
+        ":perfetto_protos_third_party_chromium_descriptor",
+    ],
+    cmd: "$(location tools/gen_cc_proto_descriptor.py) --gen_dir=$(genDir) --cpp_out=$(out) $(in)",
+    out: [
+        "src/trace_processor/importers/proto/chrome_track_event.descriptor.h",
+    ],
+    tool_files: [
+        "tools/gen_cc_proto_descriptor.py",
+    ],
+}
+
+// GN: //src/trace_processor/importers/proto:gen_cc_config_descriptor
+genrule {
+    name: "perfetto_src_trace_processor_importers_proto_gen_cc_config_descriptor",
+    srcs: [
+        ":perfetto_protos_perfetto_config_descriptor",
+    ],
+    cmd: "$(location tools/gen_cc_proto_descriptor.py) --gen_dir=$(genDir) --cpp_out=$(out) $(in)",
+    out: [
+        "src/trace_processor/importers/proto/config.descriptor.h",
+    ],
+    tool_files: [
+        "tools/gen_cc_proto_descriptor.py",
+    ],
+}
+
+// GN: //src/trace_processor/importers/proto:gen_cc_statsd_atoms_descriptor
+genrule {
+    name: "perfetto_src_trace_processor_importers_proto_gen_cc_statsd_atoms_descriptor",
+    srcs: [
+        "src/trace_processor/importers/proto/atoms.descriptor",
+    ],
+    cmd: "$(location tools/gen_cc_proto_descriptor.py) --gen_dir=$(genDir) --cpp_out=$(out) $(in)",
+    out: [
+        "src/trace_processor/importers/proto/atoms.descriptor.h",
+    ],
+    tool_files: [
+        "tools/gen_cc_proto_descriptor.py",
+    ],
+}
+
+// GN: //src/trace_processor/importers/proto:gen_cc_trace_descriptor
+genrule {
+    name: "perfetto_src_trace_processor_importers_proto_gen_cc_trace_descriptor",
+    srcs: [
+        ":perfetto_protos_perfetto_trace_descriptor",
+    ],
+    cmd: "$(location tools/gen_cc_proto_descriptor.py) --gen_dir=$(genDir) --cpp_out=$(out) $(in)",
+    out: [
+        "src/trace_processor/importers/proto/trace.descriptor.h",
+    ],
+    tool_files: [
+        "tools/gen_cc_proto_descriptor.py",
+    ],
+}
+
+// GN: //src/trace_processor/importers/proto:gen_cc_track_event_descriptor
+genrule {
+    name: "perfetto_src_trace_processor_importers_proto_gen_cc_track_event_descriptor",
+    srcs: [
+        ":perfetto_protos_perfetto_trace_track_event_descriptor",
+    ],
+    cmd: "$(location tools/gen_cc_proto_descriptor.py) --gen_dir=$(genDir) --cpp_out=$(out) $(in)",
+    out: [
+        "src/trace_processor/importers/proto/track_event.descriptor.h",
+    ],
+    tool_files: [
+        "tools/gen_cc_proto_descriptor.py",
+    ],
+}
+
+// GN: //src/trace_processor/importers/proto:minimal
+filegroup {
+    name: "perfetto_src_trace_processor_importers_proto_minimal",
+    srcs: [
+        "src/trace_processor/importers/proto/active_chrome_processes_tracker.cc",
+        "src/trace_processor/importers/proto/chrome_string_lookup.cc",
+        "src/trace_processor/importers/proto/chrome_system_probes_module.cc",
+        "src/trace_processor/importers/proto/chrome_system_probes_parser.cc",
+        "src/trace_processor/importers/proto/default_modules.cc",
         "src/trace_processor/importers/proto/heap_profile_tracker.cc",
+        "src/trace_processor/importers/proto/memory_tracker_snapshot_module.cc",
+        "src/trace_processor/importers/proto/memory_tracker_snapshot_parser.cc",
+        "src/trace_processor/importers/proto/metadata_minimal_module.cc",
+        "src/trace_processor/importers/proto/metadata_tracker.cc",
+        "src/trace_processor/importers/proto/packet_analyzer.cc",
+        "src/trace_processor/importers/proto/packet_sequence_state_generation.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/profiler_util.cc",
+        "src/trace_processor/importers/proto/proto_trace_parser.cc",
+        "src/trace_processor/importers/proto/proto_trace_reader.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/importers/proto/track_event_tracker.cc",
+    ],
+}
+
+// GN: //src/trace_processor/importers/proto:packet_sequence_state_generation_hdr
+filegroup {
+    name: "perfetto_src_trace_processor_importers_proto_packet_sequence_state_generation_hdr",
+}
+
+// GN: //src/trace_processor/importers/proto:proto_importer_module
+filegroup {
+    name: "perfetto_src_trace_processor_importers_proto_proto_importer_module",
+    srcs: [
+        "src/trace_processor/importers/proto/proto_importer_module.cc",
     ],
 }
 
@@ -9087,8 +9523,58 @@
 filegroup {
     name: "perfetto_src_trace_processor_importers_proto_unittests",
     srcs: [
+        "src/trace_processor/importers/proto/active_chrome_processes_tracker_unittest.cc",
         "src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc",
         "src/trace_processor/importers/proto/heap_profile_tracker_unittest.cc",
+        "src/trace_processor/importers/proto/perf_sample_tracker_unittest.cc",
+        "src/trace_processor/importers/proto/proto_trace_parser_unittest.cc",
+    ],
+}
+
+// GN: //src/trace_processor/importers/syscalls:full
+filegroup {
+    name: "perfetto_src_trace_processor_importers_syscalls_full",
+    srcs: [
+        "src/trace_processor/importers/syscalls/syscall_tracker.cc",
+    ],
+}
+
+// GN: //src/trace_processor/importers/syscalls:unittests
+filegroup {
+    name: "perfetto_src_trace_processor_importers_syscalls_unittests",
+    srcs: [
+        "src/trace_processor/importers/syscalls/syscall_tracker_unittest.cc",
+    ],
+}
+
+// GN: //src/trace_processor/importers/systrace:full
+filegroup {
+    name: "perfetto_src_trace_processor_importers_systrace_full",
+    srcs: [
+        "src/trace_processor/importers/systrace/systrace_line_parser.cc",
+        "src/trace_processor/importers/systrace/systrace_line_tokenizer.cc",
+        "src/trace_processor/importers/systrace/systrace_trace_parser.cc",
+    ],
+}
+
+// GN: //src/trace_processor/importers/systrace:systrace_line
+filegroup {
+    name: "perfetto_src_trace_processor_importers_systrace_systrace_line",
+}
+
+// GN: //src/trace_processor/importers/systrace:systrace_parser
+filegroup {
+    name: "perfetto_src_trace_processor_importers_systrace_systrace_parser",
+    srcs: [
+        "src/trace_processor/importers/systrace/systrace_parser.cc",
+    ],
+}
+
+// GN: //src/trace_processor/importers/systrace:unittests
+filegroup {
+    name: "perfetto_src_trace_processor_importers_systrace_unittests",
+    srcs: [
+        "src/trace_processor/importers/systrace/systrace_parser_unittest.cc",
     ],
 }
 
@@ -9187,9 +9673,6 @@
         "src/trace_processor/metrics/sql/android/android_simpleperf.sql",
         "src/trace_processor/metrics/sql/android/android_startup.sql",
         "src/trace_processor/metrics/sql/android/android_surfaceflinger.sql",
-        "src/trace_processor/metrics/sql/android/android_sysui_cuj.sql",
-        "src/trace_processor/metrics/sql/android/android_sysui_cuj_jank_query.sql",
-        "src/trace_processor/metrics/sql/android/android_sysui_cuj_surfaceflinger.sql",
         "src/trace_processor/metrics/sql/android/android_task_names.sql",
         "src/trace_processor/metrics/sql/android/android_trace_quality.sql",
         "src/trace_processor/metrics/sql/android/android_trusty_workqueues.sql",
@@ -9201,6 +9684,7 @@
         "src/trace_processor/metrics/sql/android/g2d.sql",
         "src/trace_processor/metrics/sql/android/g2d_duration.sql",
         "src/trace_processor/metrics/sql/android/global_counter_span_view.sql",
+        "src/trace_processor/metrics/sql/android/global_counter_span_view_merged.sql",
         "src/trace_processor/metrics/sql/android/gpu_counter_span_view.sql",
         "src/trace_processor/metrics/sql/android/jank/cujs.sql",
         "src/trace_processor/metrics/sql/android/jank/cujs_boundaries.sql",
@@ -9209,6 +9693,7 @@
         "src/trace_processor/metrics/sql/android/jank/internal/derived_events.sql",
         "src/trace_processor/metrics/sql/android/jank/internal/query_base.sql",
         "src/trace_processor/metrics/sql/android/jank/internal/query_frame_slice.sql",
+        "src/trace_processor/metrics/sql/android/jank/params.sql",
         "src/trace_processor/metrics/sql/android/jank/query_functions.sql",
         "src/trace_processor/metrics/sql/android/jank/relevant_slices.sql",
         "src/trace_processor/metrics/sql/android/jank/relevant_threads.sql",
@@ -9216,8 +9701,25 @@
         "src/trace_processor/metrics/sql/android/java_heap_histogram.sql",
         "src/trace_processor/metrics/sql/android/java_heap_stats.sql",
         "src/trace_processor/metrics/sql/android/mem_stats_priority_breakdown.sql",
+        "src/trace_processor/metrics/sql/android/p_state.sql",
         "src/trace_processor/metrics/sql/android/power_drain_in_watts.sql",
         "src/trace_processor/metrics/sql/android/power_profile_data.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/barbet.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/bluejay.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/blueline.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/bonito.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/bramble.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/coral.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/crosshatch.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/flame.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/marlin.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/oriole.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/raven.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/redfin.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/sargo.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/sunfish.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/taimen.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/walleye.sql",
         "src/trace_processor/metrics/sql/android/process_counter_span_view.sql",
         "src/trace_processor/metrics/sql/android/process_mem.sql",
         "src/trace_processor/metrics/sql/android/process_metadata.sql",
@@ -9238,17 +9740,26 @@
         "src/trace_processor/metrics/sql/android/unsymbolized_frames.sql",
         "src/trace_processor/metrics/sql/chrome/actual_power_by_category.sql",
         "src/trace_processor/metrics/sql/chrome/actual_power_by_rail_mode.sql",
+        "src/trace_processor/metrics/sql/chrome/chrome_args_class_names.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_event_metadata.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_histogram_hashes.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals.sql",
+        "src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals_base.sql",
+        "src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals_template.sql",
+        "src/trace_processor/metrics/sql/chrome/chrome_long_tasks.sql",
+        "src/trace_processor/metrics/sql/chrome/chrome_long_tasks_delaying_input_processing.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_performance_mark_hashes.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_processes.sql",
+        "src/trace_processor/metrics/sql/chrome/chrome_reliable_range.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_scroll_inputs_per_frame.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_scroll_jank_caused_by_scheduling.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_slice_names.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_stack_samples_for_task.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_tasks.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_tasks_delaying_input_processing.sql",
+        "src/trace_processor/metrics/sql/chrome/chrome_tasks_delaying_input_processing_base.sql",
+        "src/trace_processor/metrics/sql/chrome/chrome_tasks_delaying_input_processing_template.sql",
+        "src/trace_processor/metrics/sql/chrome/chrome_tasks_template.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_thread_slice.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_unsymbolized_args.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_user_event_hashes.sql",
@@ -9256,6 +9767,10 @@
         "src/trace_processor/metrics/sql/chrome/cpu_time_by_rail_mode.sql",
         "src/trace_processor/metrics/sql/chrome/estimated_power_by_category.sql",
         "src/trace_processor/metrics/sql/chrome/estimated_power_by_rail_mode.sql",
+        "src/trace_processor/metrics/sql/chrome/event_latency_scroll_jank.sql",
+        "src/trace_processor/metrics/sql/chrome/event_latency_scroll_jank_cause.sql",
+        "src/trace_processor/metrics/sql/chrome/event_latency_to_breakdowns.sql",
+        "src/trace_processor/metrics/sql/chrome/experimental_reliable_chrome_tasks_delaying_input_processing.sql",
         "src/trace_processor/metrics/sql/chrome/gesture_flow_event.sql",
         "src/trace_processor/metrics/sql/chrome/gesture_flow_event_queuing_delay.sql",
         "src/trace_processor/metrics/sql/chrome/gesture_jank.sql",
@@ -9274,6 +9789,8 @@
         "src/trace_processor/metrics/sql/chrome/touch_flow_event.sql",
         "src/trace_processor/metrics/sql/chrome/touch_flow_event_queuing_delay.sql",
         "src/trace_processor/metrics/sql/chrome/touch_jank.sql",
+        "src/trace_processor/metrics/sql/chrome/vsync_intervals.sql",
+        "src/trace_processor/metrics/sql/common/parent_slice.sql",
         "src/trace_processor/metrics/sql/experimental/blink_gc_metric.sql",
         "src/trace_processor/metrics/sql/experimental/chrome_dropped_frames.sql",
         "src/trace_processor/metrics/sql/experimental/chrome_long_latency.sql",
@@ -9284,12 +9801,12 @@
         "src/trace_processor/metrics/sql/trace_stats.sql",
         "src/trace_processor/metrics/sql/webview/webview_power_usage.sql",
     ],
-    cmd: "$(location tools/gen_amalgamated_sql_metrics.py) --cpp_out=$(out) $(in)",
+    cmd: "$(location tools/gen_amalgamated_sql.py) --namespace=sql_metrics --cpp-out=$(out) $(in)",
     out: [
         "src/trace_processor/metrics/sql/amalgamated_sql_metrics.h",
     ],
     tool_files: [
-        "tools/gen_amalgamated_sql_metrics.py",
+        "tools/gen_amalgamated_sql.py",
     ],
 }
 
@@ -9301,6 +9818,45 @@
     ],
 }
 
+// GN: //src/trace_processor/prelude/functions:functions
+filegroup {
+    name: "perfetto_src_trace_processor_prelude_functions_functions",
+    srcs: [
+        "src/trace_processor/prelude/functions/create_function.cc",
+        "src/trace_processor/prelude/functions/create_function_internal.cc",
+        "src/trace_processor/prelude/functions/create_view_function.cc",
+        "src/trace_processor/prelude/functions/import.cc",
+        "src/trace_processor/prelude/functions/pprof_functions.cc",
+        "src/trace_processor/prelude/functions/register_function.cc",
+        "src/trace_processor/prelude/functions/sqlite3_str_split.cc",
+    ],
+}
+
+// GN: //src/trace_processor/prelude/functions:unittests
+filegroup {
+    name: "perfetto_src_trace_processor_prelude_functions_unittests",
+    srcs: [
+        "src/trace_processor/prelude/functions/sqlite3_str_split_unittest.cc",
+    ],
+}
+
+// GN: //src/trace_processor/prelude/operators:operators
+filegroup {
+    name: "perfetto_src_trace_processor_prelude_operators_operators",
+    srcs: [
+        "src/trace_processor/prelude/operators/span_join_operator.cc",
+        "src/trace_processor/prelude/operators/window_operator.cc",
+    ],
+}
+
+// GN: //src/trace_processor/prelude/operators:unittests
+filegroup {
+    name: "perfetto_src_trace_processor_prelude_operators_unittests",
+    srcs: [
+        "src/trace_processor/prelude/operators/span_join_operator_unittest.cc",
+    ],
+}
+
 // GN: //src/trace_processor/rpc:httpd
 filegroup {
     name: "perfetto_src_trace_processor_rpc_httpd",
@@ -9326,21 +9882,32 @@
     ],
 }
 
+// GN: //src/trace_processor/sorter:sorter
+filegroup {
+    name: "perfetto_src_trace_processor_sorter_sorter",
+    srcs: [
+        "src/trace_processor/sorter/trace_sorter.cc",
+    ],
+}
+
+// GN: //src/trace_processor/sorter:unittests
+filegroup {
+    name: "perfetto_src_trace_processor_sorter_unittests",
+    srcs: [
+        "src/trace_processor/sorter/trace_sorter_queue_unittest.cc",
+        "src/trace_processor/sorter/trace_sorter_unittest.cc",
+    ],
+}
+
 // GN: //src/trace_processor/sqlite:sqlite
 filegroup {
     name: "perfetto_src_trace_processor_sqlite_sqlite",
     srcs: [
-        "src/trace_processor/sqlite/create_function.cc",
-        "src/trace_processor/sqlite/create_function_internal.cc",
-        "src/trace_processor/sqlite/create_view_function.cc",
         "src/trace_processor/sqlite/db_sqlite_table.cc",
-        "src/trace_processor/sqlite/register_function.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_utils.cc",
         "src/trace_processor/sqlite/stats_table.cc",
-        "src/trace_processor/sqlite/window_operator_table.cc",
     ],
 }
 
@@ -9359,50 +9926,35 @@
     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",
         "src/trace_processor/sqlite/sqlite_utils_unittest.cc",
     ],
 }
 
-// GN: //src/trace_processor:storage_full
-filegroup {
-    name: "perfetto_src_trace_processor_storage_full",
+// GN: //src/trace_processor/stdlib:gen_amalgamated_stdlib
+genrule {
+    name: "perfetto_src_trace_processor_stdlib_gen_amalgamated_stdlib",
     srcs: [
-        "src/trace_processor/importers/additional_modules.cc",
-        "src/trace_processor/importers/ftrace/binder_tracker.cc",
-        "src/trace_processor/importers/ftrace/drm_tracker.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/iostat_tracker.cc",
-        "src/trace_processor/importers/ftrace/rss_stat_tracker.cc",
-        "src/trace_processor/importers/ftrace/sched_event_tracker.cc",
-        "src/trace_processor/importers/ftrace/thread_state_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/i2c/i2c_tracker.cc",
-        "src/trace_processor/importers/json/json_trace_parser.cc",
-        "src/trace_processor/importers/json/json_trace_tokenizer.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/frame_timeline_event_parser.cc",
-        "src/trace_processor/importers/proto/gpu_event_parser.cc",
-        "src/trace_processor/importers/proto/graphics_event_module.cc",
-        "src/trace_processor/importers/proto/graphics_frame_event_parser.cc",
-        "src/trace_processor/importers/proto/heap_graph_module.cc",
-        "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/stdlib/android/battery.sql",
+        "src/trace_processor/stdlib/android/binder.sql",
+        "src/trace_processor/stdlib/android/process_metadata.sql",
+        "src/trace_processor/stdlib/android/startup/internal_startups_maxsdk28.sql",
+        "src/trace_processor/stdlib/android/startup/internal_startups_minsdk29.sql",
+        "src/trace_processor/stdlib/android/startup/internal_startups_minsdk33.sql",
+        "src/trace_processor/stdlib/android/startup/startups.sql",
+        "src/trace_processor/stdlib/chrome/cpu_powerups.sql",
+        "src/trace_processor/stdlib/common/counters.sql",
+        "src/trace_processor/stdlib/common/metadata.sql",
+        "src/trace_processor/stdlib/common/percentiles.sql",
+        "src/trace_processor/stdlib/common/slices.sql",
+        "src/trace_processor/stdlib/common/timestamps.sql",
+        "src/trace_processor/stdlib/experimental/android_broadcast.sql",
+    ],
+    cmd: "$(location tools/gen_amalgamated_sql.py) --namespace=stdlib --cpp-out=$(out) $(in)",
+    out: [
+        "src/trace_processor/stdlib/amalgamated_stdlib.h",
+    ],
+    tool_files: [
+        "tools/gen_amalgamated_sql.py",
     ],
 }
 
@@ -9411,38 +9963,10 @@
     name: "perfetto_src_trace_processor_storage_minimal",
     srcs: [
         "src/trace_processor/forwarding_trace_parser.cc",
-        "src/trace_processor/importers/default_modules.cc",
-        "src/trace_processor/importers/ftrace/ftrace_module.cc",
-        "src/trace_processor/importers/json/json_utils.cc",
-        "src/trace_processor/importers/ninja/ninja_log_parser.cc",
-        "src/trace_processor/importers/proto/android_camera_event_module.cc",
-        "src/trace_processor/importers/proto/async_track_set_tracker.cc",
-        "src/trace_processor/importers/proto/chrome_string_lookup.cc",
-        "src/trace_processor/importers/proto/chrome_system_probes_module.cc",
-        "src/trace_processor/importers/proto/chrome_system_probes_parser.cc",
-        "src/trace_processor/importers/proto/memory_tracker_snapshot_module.cc",
-        "src/trace_processor/importers/proto/memory_tracker_snapshot_parser.cc",
-        "src/trace_processor/importers/proto/metadata_module.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_reader.cc",
-        "src/trace_processor/importers/proto/proto_trace_tokenizer.cc",
-        "src/trace_processor/importers/proto/statsd_module.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/importers/proto/track_event_tracker.cc",
-        "src/trace_processor/importers/proto/translation_table_module.cc",
         "src/trace_processor/trace_blob.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/virtual_destructors.cc",
     ],
 }
@@ -9455,6 +9979,34 @@
     ],
 }
 
+// GN: //src/trace_processor/tables:py_tables_unittest
+genrule {
+    name: "perfetto_src_trace_processor_tables_py_tables_unittest",
+    srcs: [
+        "src/trace_processor/tables/py_tables_unittest.py",
+    ],
+    tools: [
+        "perfetto_src_trace_processor_tables_py_tables_unittest_binary",
+    ],
+    cmd: "$(location perfetto_src_trace_processor_tables_py_tables_unittest_binary) --gen-dir=$(genDir) --inputs $(in) --outputs $(out)",
+    out: [
+        "src/trace_processor/tables/py_tables_unittest_py.h",
+    ],
+}
+
+// GN: //src/trace_processor/tables:py_tables_unittest
+python_binary_host {
+    name: "perfetto_src_trace_processor_tables_py_tables_unittest_binary",
+    srcs: [
+        "python/generators/trace_processor_table/public.py",
+        "python/generators/trace_processor_table/serialize.py",
+        "python/generators/trace_processor_table/util.py",
+        "src/trace_processor/tables/py_tables_unittest.py",
+        "tools/gen_tp_table_headers.py",
+    ],
+    main: "tools/gen_tp_table_headers.py",
+}
+
 // GN: //src/trace_processor/tables:tables
 filegroup {
     name: "perfetto_src_trace_processor_tables_tables",
@@ -9463,11 +10015,52 @@
     ],
 }
 
+// GN: //src/trace_processor/tables:tables_python
+genrule {
+    name: "perfetto_src_trace_processor_tables_tables_python",
+    srcs: [
+        "src/trace_processor/tables/android_tables.py",
+        "src/trace_processor/tables/metadata_tables.py",
+    ],
+    tools: [
+        "perfetto_src_trace_processor_tables_tables_python_binary",
+    ],
+    cmd: "$(location perfetto_src_trace_processor_tables_tables_python_binary) --gen-dir=$(genDir) --inputs $(in) --outputs $(out)",
+    out: [
+        "src/trace_processor/tables/android_tables_py.h",
+        "src/trace_processor/tables/metadata_tables_py.h",
+    ],
+}
+
+// GN: //src/trace_processor/tables:tables_python
+python_binary_host {
+    name: "perfetto_src_trace_processor_tables_tables_python_binary",
+    srcs: [
+        "python/generators/trace_processor_table/public.py",
+        "python/generators/trace_processor_table/serialize.py",
+        "python/generators/trace_processor_table/util.py",
+        "src/trace_processor/tables/android_tables.py",
+        "src/trace_processor/tables/metadata_tables.py",
+        "tools/gen_tp_table_headers.py",
+    ],
+    main: "tools/gen_tp_table_headers.py",
+}
+
 // GN: //src/trace_processor/tables:unittests
 filegroup {
     name: "perfetto_src_trace_processor_tables_unittests",
     srcs: [
         "src/trace_processor/tables/macros_unittest.cc",
+        "src/trace_processor/tables/py_tables_unittest.cc",
+    ],
+}
+
+// GN: //src/trace_processor:top_level_unittests
+filegroup {
+    name: "perfetto_src_trace_processor_top_level_unittests",
+    srcs: [
+        "src/trace_processor/forwarding_trace_parser_unittest.cc",
+        "src/trace_processor/ref_counted_unittest.cc",
     ],
 }
 
@@ -9493,23 +10086,13 @@
 // GN: //src/trace_processor:unittests
 filegroup {
     name: "perfetto_src_trace_processor_unittests",
+}
+
+// GN: //src/trace_processor/util:bump_allocator
+filegroup {
+    name: "perfetto_src_trace_processor_util_bump_allocator",
     srcs: [
-        "src/trace_processor/forwarding_trace_parser_unittest.cc",
-        "src/trace_processor/importers/ftrace/binder_tracker_unittest.cc",
-        "src/trace_processor/importers/ftrace/sched_event_tracker_unittest.cc",
-        "src/trace_processor/importers/ftrace/thread_state_tracker_unittest.cc",
-        "src/trace_processor/importers/fuchsia/fuchsia_trace_utils_unittest.cc",
-        "src/trace_processor/importers/memory_tracker/graph_processor_unittest.cc",
-        "src/trace_processor/importers/memory_tracker/graph_unittest.cc",
-        "src/trace_processor/importers/memory_tracker/raw_process_memory_node_unittest.cc",
-        "src/trace_processor/importers/proto/async_track_set_tracker_unittest.cc",
-        "src/trace_processor/importers/proto/perf_sample_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/ref_counted_unittest.cc",
-        "src/trace_processor/trace_sorter_queue_unittest.cc",
-        "src/trace_processor/trace_sorter_unittest.cc",
+        "src/trace_processor/util/bump_allocator.cc",
     ],
 }
 
@@ -9521,6 +10104,14 @@
     ],
 }
 
+// GN: //src/trace_processor/util:glob
+filegroup {
+    name: "perfetto_src_trace_processor_util_glob",
+    srcs: [
+        "src/trace_processor/util/glob.cc",
+    ],
+}
+
 // GN: //src/trace_processor/util:gzip
 filegroup {
     name: "perfetto_src_trace_processor_util_gzip",
@@ -9534,6 +10125,23 @@
     name: "perfetto_src_trace_processor_util_interned_message_view",
 }
 
+// GN: //src/trace_processor/util:profile_builder
+filegroup {
+    name: "perfetto_src_trace_processor_util_profile_builder",
+    srcs: [
+        "src/trace_processor/util/annotated_callsites.cc",
+        "src/trace_processor/util/profile_builder.cc",
+    ],
+}
+
+// GN: //src/trace_processor/util:proto_profiler
+filegroup {
+    name: "perfetto_src_trace_processor_util_proto_profiler",
+    srcs: [
+        "src/trace_processor/util/proto_profiler.cc",
+    ],
+}
+
 // GN: //src/trace_processor/util:proto_to_args_parser
 filegroup {
     name: "perfetto_src_trace_processor_util_proto_to_args_parser",
@@ -9551,6 +10159,14 @@
     ],
 }
 
+// GN: //src/trace_processor/util:sql_argument
+filegroup {
+    name: "perfetto_src_trace_processor_util_sql_argument",
+    srcs: [
+        "src/trace_processor/util/sql_argument.cc",
+    ],
+}
+
 // GN: //src/trace_processor/util:stack_traces_util
 filegroup {
     name: "perfetto_src_trace_processor_util_stack_traces_util",
@@ -9559,14 +10175,23 @@
     ],
 }
 
+// GN: //src/trace_processor/util:stdlib
+filegroup {
+    name: "perfetto_src_trace_processor_util_stdlib",
+}
+
 // GN: //src/trace_processor/util:unittests
 filegroup {
     name: "perfetto_src_trace_processor_util_unittests",
     srcs: [
+        "src/trace_processor/util/bump_allocator_unittest.cc",
         "src/trace_processor/util/debug_annotation_parser_unittest.cc",
+        "src/trace_processor/util/glob_unittest.cc",
         "src/trace_processor/util/gzip_utils_unittest.cc",
+        "src/trace_processor/util/proto_profiler_unittest.cc",
         "src/trace_processor/util/proto_to_args_parser_unittest.cc",
         "src/trace_processor/util/protozero_to_text_unittests.cc",
+        "src/trace_processor/util/sql_argument_unittest.cc",
         "src/trace_processor/util/streaming_line_reader_unittest.cc",
         "src/trace_processor/util/zip_reader_unittest.cc",
     ],
@@ -9786,16 +10411,17 @@
         "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",
         "src/traced/probes/ftrace/ftrace_config_utils.cc",
         "src/traced/probes/ftrace/ftrace_controller.cc",
         "src/traced/probes/ftrace/ftrace_data_source.cc",
+        "src/traced/probes/ftrace/ftrace_print_filter.cc",
         "src/traced/probes/ftrace/ftrace_stats.cc",
         "src/traced/probes/ftrace/printk_formats_parser.cc",
         "src/traced/probes/ftrace/proto_translation_table.cc",
+        "src/traced/probes/ftrace/vendor_tracepoints.cc",
     ],
 }
 
@@ -9935,14 +10561,15 @@
     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/ftrace_config_muxer_unittest.cc",
         "src/traced/probes/ftrace/ftrace_config_unittest.cc",
         "src/traced/probes/ftrace/ftrace_controller_unittest.cc",
+        "src/traced/probes/ftrace/ftrace_print_filter_unittest.cc",
         "src/traced/probes/ftrace/ftrace_procfs_unittest.cc",
         "src/traced/probes/ftrace/printk_formats_parser_unittest.cc",
         "src/traced/probes/ftrace/proto_translation_table_unittest.cc",
+        "src/traced/probes/ftrace/vendor_tracepoints_unittest.cc",
     ],
 }
 
@@ -10208,6 +10835,14 @@
     ],
 }
 
+// GN: //src/tracing:integrationtests
+filegroup {
+    name: "perfetto_src_tracing_integrationtests",
+    srcs: [
+        "src/tracing/internal/tracing_muxer_impl_integrationtest.cc",
+    ],
+}
+
 // GN: //src/tracing/ipc:common
 filegroup {
     name: "perfetto_src_tracing_ipc_common",
@@ -10290,8 +10925,10 @@
     name: "perfetto_src_tracing_test_client_api_integrationtests",
     srcs: [
         "src/tracing/test/api_integrationtest.cc",
+        "src/tracing/test/api_integrationtest_main.cc",
         "src/tracing/test/tracing_module.cc",
         "src/tracing/test/tracing_module2.cc",
+        "src/tracing/test/tracing_module3.cc",
     ],
 }
 
@@ -10380,6 +11017,7 @@
         "protos/perfetto/config/android/android_log_config.proto",
         "protos/perfetto/config/android/android_polled_state_config.proto",
         "protos/perfetto/config/android/android_system_property_config.proto",
+        "protos/perfetto/config/android/network_trace_config.proto",
         "protos/perfetto/config/android/packages_list_config.proto",
         "protos/perfetto/config/chrome/chrome_config.proto",
         "protos/perfetto/config/data_source_config.proto",
@@ -10410,6 +11048,7 @@
         "protos/perfetto/trace/android/gpu_mem_event.proto",
         "protos/perfetto/trace/android/graphics_frame_event.proto",
         "protos/perfetto/trace/android/initial_display_state.proto",
+        "protos/perfetto/trace/android/network_trace.proto",
         "protos/perfetto/trace/android/packages_list.proto",
         "protos/perfetto/trace/chrome/chrome_benchmark_metadata.proto",
         "protos/perfetto/trace/chrome/chrome_metadata.proto",
@@ -10417,10 +11056,12 @@
         "protos/perfetto/trace/clock_snapshot.proto",
         "protos/perfetto/trace/extension_descriptor.proto",
         "protos/perfetto/trace/filesystem/inode_file_map.proto",
+        "protos/perfetto/trace/ftrace/android_fs.proto",
         "protos/perfetto/trace/ftrace/binder.proto",
         "protos/perfetto/trace/ftrace/block.proto",
         "protos/perfetto/trace/ftrace/cgroup.proto",
         "protos/perfetto/trace/ftrace/clk.proto",
+        "protos/perfetto/trace/ftrace/cma.proto",
         "protos/perfetto/trace/ftrace/compaction.proto",
         "protos/perfetto/trace/ftrace/cpuhp.proto",
         "protos/perfetto/trace/ftrace/cros_ec.proto",
@@ -10448,11 +11089,13 @@
         "protos/perfetto/trace/ftrace/kmem.proto",
         "protos/perfetto/trace/ftrace/kvm.proto",
         "protos/perfetto/trace/ftrace/lowmemorykiller.proto",
+        "protos/perfetto/trace/ftrace/lwis.proto",
         "protos/perfetto/trace/ftrace/mali.proto",
         "protos/perfetto/trace/ftrace/mdss.proto",
         "protos/perfetto/trace/ftrace/mm_event.proto",
         "protos/perfetto/trace/ftrace/net.proto",
         "protos/perfetto/trace/ftrace/oom.proto",
+        "protos/perfetto/trace/ftrace/panel.proto",
         "protos/perfetto/trace/ftrace/power.proto",
         "protos/perfetto/trace/ftrace/printk.proto",
         "protos/perfetto/trace/ftrace/raw_syscalls.proto",
@@ -10470,8 +11113,11 @@
         "protos/perfetto/trace/ftrace/tcp.proto",
         "protos/perfetto/trace/ftrace/test_bundle_wrapper.proto",
         "protos/perfetto/trace/ftrace/thermal.proto",
+        "protos/perfetto/trace/ftrace/trusty.proto",
         "protos/perfetto/trace/ftrace/ufs.proto",
         "protos/perfetto/trace/ftrace/v4l2.proto",
+        "protos/perfetto/trace/ftrace/virtio_gpu.proto",
+        "protos/perfetto/trace/ftrace/virtio_video.proto",
         "protos/perfetto/trace/ftrace/vmscan.proto",
         "protos/perfetto/trace/ftrace/workqueue.proto",
         "protos/perfetto/trace/gpu/gpu_counter_event.proto",
@@ -10502,6 +11148,8 @@
         "protos/perfetto/trace/trace.proto",
         "protos/perfetto/trace/trace_packet.proto",
         "protos/perfetto/trace/trace_packet_defaults.proto",
+        "protos/perfetto/trace/trace_uuid.proto",
+        "protos/perfetto/trace/track_event/chrome_active_processes.proto",
         "protos/perfetto/trace/track_event/chrome_application_state_info.proto",
         "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto",
         "protos/perfetto/trace/track_event/chrome_content_settings_event_info.proto",
@@ -10521,6 +11169,7 @@
         "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/range_of_interest.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",
@@ -10661,11 +11310,14 @@
 cc_test {
     name: "perfetto_unittests",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_http_http",
+        ":perfetto_include_perfetto_ext_base_threading_threading",
         ":perfetto_include_perfetto_ext_base_version",
         ":perfetto_include_perfetto_ext_ipc_ipc",
+        ":perfetto_include_perfetto_ext_trace_processor_demangle",
         ":perfetto_include_perfetto_ext_trace_processor_export_json",
         ":perfetto_include_perfetto_ext_trace_processor_importers_memory_tracker_memory_tracker",
         ":perfetto_include_perfetto_ext_traced_sys_stats_counters",
@@ -10780,6 +11432,7 @@
         ":perfetto_protos_perfetto_trace_translation_cpp_gen",
         ":perfetto_protos_perfetto_trace_translation_lite_gen",
         ":perfetto_protos_perfetto_trace_translation_zero_gen",
+        ":perfetto_protos_third_party_pprof_zero_gen",
         ":perfetto_protos_third_party_statsd_config_zero_gen",
         ":perfetto_src_android_internal_headers",
         ":perfetto_src_android_internal_lazy_library_loader",
@@ -10789,6 +11442,8 @@
         ":perfetto_src_base_http_http",
         ":perfetto_src_base_http_unittests",
         ":perfetto_src_base_test_support",
+        ":perfetto_src_base_threading_threading",
+        ":perfetto_src_base_threading_unittests",
         ":perfetto_src_base_unittests",
         ":perfetto_src_base_unix_socket",
         ":perfetto_src_base_version",
@@ -10843,7 +11498,6 @@
         ":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_db",
@@ -10851,38 +11505,73 @@
         ":perfetto_src_trace_processor_dynamic_dynamic",
         ":perfetto_src_trace_processor_dynamic_unittests",
         ":perfetto_src_trace_processor_export_json",
-        ":perfetto_src_trace_processor_ftrace_descriptors",
         ":perfetto_src_trace_processor_importers_android_bugreport_android_bugreport",
         ":perfetto_src_trace_processor_importers_android_bugreport_unittests",
         ":perfetto_src_trace_processor_importers_common_common",
+        ":perfetto_src_trace_processor_importers_common_parser_types",
+        ":perfetto_src_trace_processor_importers_common_trace_parser_hdr",
         ":perfetto_src_trace_processor_importers_common_unittests",
+        ":perfetto_src_trace_processor_importers_ftrace_ftrace_descriptors",
+        ":perfetto_src_trace_processor_importers_ftrace_full",
+        ":perfetto_src_trace_processor_importers_ftrace_minimal",
+        ":perfetto_src_trace_processor_importers_ftrace_unittests",
+        ":perfetto_src_trace_processor_importers_fuchsia_fuchsia_record",
+        ":perfetto_src_trace_processor_importers_fuchsia_full",
+        ":perfetto_src_trace_processor_importers_fuchsia_minimal",
+        ":perfetto_src_trace_processor_importers_fuchsia_unittests",
+        ":perfetto_src_trace_processor_importers_gzip_full",
+        ":perfetto_src_trace_processor_importers_i2c_full",
+        ":perfetto_src_trace_processor_importers_json_full",
+        ":perfetto_src_trace_processor_importers_json_minimal",
         ":perfetto_src_trace_processor_importers_memory_tracker_graph_processor",
-        ":perfetto_src_trace_processor_importers_proto_storage_full",
-        ":perfetto_src_trace_processor_importers_proto_storage_minimal",
+        ":perfetto_src_trace_processor_importers_memory_tracker_unittests",
+        ":perfetto_src_trace_processor_importers_ninja_ninja",
+        ":perfetto_src_trace_processor_importers_proto_full",
+        ":perfetto_src_trace_processor_importers_proto_minimal",
+        ":perfetto_src_trace_processor_importers_proto_packet_sequence_state_generation_hdr",
+        ":perfetto_src_trace_processor_importers_proto_proto_importer_module",
         ":perfetto_src_trace_processor_importers_proto_unittests",
+        ":perfetto_src_trace_processor_importers_syscalls_full",
+        ":perfetto_src_trace_processor_importers_syscalls_unittests",
+        ":perfetto_src_trace_processor_importers_systrace_full",
+        ":perfetto_src_trace_processor_importers_systrace_systrace_line",
+        ":perfetto_src_trace_processor_importers_systrace_systrace_parser",
+        ":perfetto_src_trace_processor_importers_systrace_unittests",
         ":perfetto_src_trace_processor_lib",
         ":perfetto_src_trace_processor_metatrace",
         ":perfetto_src_trace_processor_metrics_metrics",
         ":perfetto_src_trace_processor_metrics_unittests",
+        ":perfetto_src_trace_processor_prelude_functions_functions",
+        ":perfetto_src_trace_processor_prelude_functions_unittests",
+        ":perfetto_src_trace_processor_prelude_operators_operators",
+        ":perfetto_src_trace_processor_prelude_operators_unittests",
         ":perfetto_src_trace_processor_rpc_rpc",
         ":perfetto_src_trace_processor_rpc_unittests",
+        ":perfetto_src_trace_processor_sorter_sorter",
+        ":perfetto_src_trace_processor_sorter_unittests",
         ":perfetto_src_trace_processor_sqlite_sqlite",
         ":perfetto_src_trace_processor_sqlite_sqlite_minimal",
         ":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_top_level_unittests",
         ":perfetto_src_trace_processor_types_types",
         ":perfetto_src_trace_processor_types_unittests",
         ":perfetto_src_trace_processor_unittests",
+        ":perfetto_src_trace_processor_util_bump_allocator",
         ":perfetto_src_trace_processor_util_descriptors",
+        ":perfetto_src_trace_processor_util_glob",
         ":perfetto_src_trace_processor_util_gzip",
         ":perfetto_src_trace_processor_util_interned_message_view",
+        ":perfetto_src_trace_processor_util_profile_builder",
+        ":perfetto_src_trace_processor_util_proto_profiler",
         ":perfetto_src_trace_processor_util_proto_to_args_parser",
         ":perfetto_src_trace_processor_util_protozero_to_text",
+        ":perfetto_src_trace_processor_util_sql_argument",
         ":perfetto_src_trace_processor_util_stack_traces_util",
+        ":perfetto_src_trace_processor_util_stdlib",
         ":perfetto_src_trace_processor_util_unittests",
         ":perfetto_src_trace_processor_util_util",
         ":perfetto_src_trace_processor_util_zip_reader",
@@ -10962,6 +11651,7 @@
         "libgmock",
         "libgtest",
         "perfetto_src_trace_processor_demangle",
+        "sqlite_ext_percentile",
     ],
     whole_static_libs: [
         "perfetto_gtest_logcat_printer",
@@ -11065,6 +11755,7 @@
         "perfetto_protos_perfetto_trace_translation_cpp_gen_headers",
         "perfetto_protos_perfetto_trace_translation_lite_gen_headers",
         "perfetto_protos_perfetto_trace_translation_zero_gen_headers",
+        "perfetto_protos_third_party_pprof_zero_gen_headers",
         "perfetto_protos_third_party_statsd_config_zero_gen_headers",
         "perfetto_src_base_version_gen_h",
         "perfetto_src_ipc_test_messages_cpp_gen_headers",
@@ -11075,13 +11766,17 @@
         "perfetto_src_protozero_testing_messages_lite_gen_headers",
         "perfetto_src_protozero_testing_messages_zero_gen_headers",
         "perfetto_src_trace_processor_gen_cc_test_messages_descriptor",
-        "perfetto_src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
-        "perfetto_src_trace_processor_importers_gen_cc_config_descriptor",
-        "perfetto_src_trace_processor_importers_gen_cc_statsd_atoms_descriptor",
-        "perfetto_src_trace_processor_importers_gen_cc_track_event_descriptor",
+        "perfetto_src_trace_processor_importers_proto_gen_cc_chrome_track_event_descriptor",
+        "perfetto_src_trace_processor_importers_proto_gen_cc_config_descriptor",
+        "perfetto_src_trace_processor_importers_proto_gen_cc_statsd_atoms_descriptor",
+        "perfetto_src_trace_processor_importers_proto_gen_cc_trace_descriptor",
+        "perfetto_src_trace_processor_importers_proto_gen_cc_track_event_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_sql_gen_amalgamated_sql_metrics",
+        "perfetto_src_trace_processor_stdlib_gen_amalgamated_stdlib",
+        "perfetto_src_trace_processor_tables_py_tables_unittest",
+        "perfetto_src_trace_processor_tables_tables_python",
         "perfetto_src_traced_probes_ftrace_test_messages_cpp_gen_headers",
         "perfetto_src_traced_probes_ftrace_test_messages_lite_gen_headers",
         "perfetto_src_traced_probes_ftrace_test_messages_zero_gen_headers",
@@ -11107,10 +11802,294 @@
     ],
 }
 
+// GN: //test/vts:perfetto_vts_deps
+cc_library_static {
+    name: "perfetto_vts_deps",
+    srcs: [
+        ":perfetto_base_default_platform",
+        ":perfetto_include_perfetto_base_base",
+        ":perfetto_include_perfetto_ext_base_base",
+        ":perfetto_include_perfetto_ext_base_version",
+        ":perfetto_include_perfetto_ext_ipc_ipc",
+        ":perfetto_include_perfetto_ext_traced_sys_stats_counters",
+        ":perfetto_include_perfetto_ext_traced_traced",
+        ":perfetto_include_perfetto_ext_tracing_core_core",
+        ":perfetto_include_perfetto_ext_tracing_ipc_ipc",
+        ":perfetto_include_perfetto_protozero_protozero",
+        ":perfetto_include_perfetto_public_abi_base",
+        ":perfetto_include_perfetto_public_base",
+        ":perfetto_include_perfetto_tracing_core_core",
+        ":perfetto_include_perfetto_tracing_core_forward_decls",
+        ":perfetto_include_perfetto_tracing_tracing",
+        ":perfetto_protos_perfetto_common_cpp_gen",
+        ":perfetto_protos_perfetto_common_zero_gen",
+        ":perfetto_protos_perfetto_config_android_cpp_gen",
+        ":perfetto_protos_perfetto_config_android_zero_gen",
+        ":perfetto_protos_perfetto_config_cpp_gen",
+        ":perfetto_protos_perfetto_config_ftrace_cpp_gen",
+        ":perfetto_protos_perfetto_config_ftrace_zero_gen",
+        ":perfetto_protos_perfetto_config_gpu_cpp_gen",
+        ":perfetto_protos_perfetto_config_gpu_zero_gen",
+        ":perfetto_protos_perfetto_config_inode_file_cpp_gen",
+        ":perfetto_protos_perfetto_config_inode_file_zero_gen",
+        ":perfetto_protos_perfetto_config_interceptors_cpp_gen",
+        ":perfetto_protos_perfetto_config_interceptors_zero_gen",
+        ":perfetto_protos_perfetto_config_power_cpp_gen",
+        ":perfetto_protos_perfetto_config_power_zero_gen",
+        ":perfetto_protos_perfetto_config_process_stats_cpp_gen",
+        ":perfetto_protos_perfetto_config_process_stats_zero_gen",
+        ":perfetto_protos_perfetto_config_profiling_cpp_gen",
+        ":perfetto_protos_perfetto_config_profiling_zero_gen",
+        ":perfetto_protos_perfetto_config_statsd_cpp_gen",
+        ":perfetto_protos_perfetto_config_statsd_zero_gen",
+        ":perfetto_protos_perfetto_config_sys_stats_cpp_gen",
+        ":perfetto_protos_perfetto_config_sys_stats_zero_gen",
+        ":perfetto_protos_perfetto_config_system_info_cpp_gen",
+        ":perfetto_protos_perfetto_config_system_info_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",
+        ":perfetto_protos_perfetto_ipc_wire_protocol_cpp_gen",
+        ":perfetto_protos_perfetto_trace_android_cpp_gen",
+        ":perfetto_protos_perfetto_trace_android_zero_gen",
+        ":perfetto_protos_perfetto_trace_chrome_cpp_gen",
+        ":perfetto_protos_perfetto_trace_chrome_zero_gen",
+        ":perfetto_protos_perfetto_trace_filesystem_cpp_gen",
+        ":perfetto_protos_perfetto_trace_filesystem_zero_gen",
+        ":perfetto_protos_perfetto_trace_ftrace_cpp_gen",
+        ":perfetto_protos_perfetto_trace_ftrace_zero_gen",
+        ":perfetto_protos_perfetto_trace_gpu_cpp_gen",
+        ":perfetto_protos_perfetto_trace_gpu_zero_gen",
+        ":perfetto_protos_perfetto_trace_interned_data_cpp_gen",
+        ":perfetto_protos_perfetto_trace_interned_data_zero_gen",
+        ":perfetto_protos_perfetto_trace_minimal_cpp_gen",
+        ":perfetto_protos_perfetto_trace_minimal_zero_gen",
+        ":perfetto_protos_perfetto_trace_non_minimal_cpp_gen",
+        ":perfetto_protos_perfetto_trace_non_minimal_zero_gen",
+        ":perfetto_protos_perfetto_trace_perfetto_cpp_gen",
+        ":perfetto_protos_perfetto_trace_perfetto_zero_gen",
+        ":perfetto_protos_perfetto_trace_power_cpp_gen",
+        ":perfetto_protos_perfetto_trace_power_zero_gen",
+        ":perfetto_protos_perfetto_trace_profiling_cpp_gen",
+        ":perfetto_protos_perfetto_trace_profiling_zero_gen",
+        ":perfetto_protos_perfetto_trace_ps_cpp_gen",
+        ":perfetto_protos_perfetto_trace_ps_zero_gen",
+        ":perfetto_protos_perfetto_trace_statsd_cpp_gen",
+        ":perfetto_protos_perfetto_trace_statsd_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_protos_perfetto_trace_translation_cpp_gen",
+        ":perfetto_protos_perfetto_trace_translation_zero_gen",
+        ":perfetto_protos_third_party_statsd_config_zero_gen",
+        ":perfetto_src_android_internal_headers",
+        ":perfetto_src_android_internal_lazy_library_loader",
+        ":perfetto_src_android_stats_android_stats",
+        ":perfetto_src_android_stats_perfetto_atoms",
+        ":perfetto_src_base_base",
+        ":perfetto_src_base_test_support",
+        ":perfetto_src_base_unix_socket",
+        ":perfetto_src_base_version",
+        ":perfetto_src_ipc_client",
+        ":perfetto_src_ipc_common",
+        ":perfetto_src_ipc_host",
+        ":perfetto_src_ipc_perfetto_ipc",
+        ":perfetto_src_kallsyms_kallsyms",
+        ":perfetto_src_kernel_utils_syscall_table",
+        ":perfetto_src_protozero_filtering_bytecode_common",
+        ":perfetto_src_protozero_filtering_bytecode_parser",
+        ":perfetto_src_protozero_filtering_message_filter",
+        ":perfetto_src_protozero_proto_ring_buffer",
+        ":perfetto_src_protozero_protozero",
+        ":perfetto_src_traced_probes_android_game_intervention_list_android_game_intervention_list",
+        ":perfetto_src_traced_probes_android_log_android_log",
+        ":perfetto_src_traced_probes_android_system_property_android_system_property",
+        ":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_format_parser",
+        ":perfetto_src_traced_probes_ftrace_ftrace",
+        ":perfetto_src_traced_probes_ftrace_ftrace_procfs",
+        ":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_packages_list_packages_list_parser",
+        ":perfetto_src_traced_probes_power_power",
+        ":perfetto_src_traced_probes_probes_src",
+        ":perfetto_src_traced_probes_ps_ps",
+        ":perfetto_src_traced_probes_statsd_client_statsd_client",
+        ":perfetto_src_traced_probes_sys_stats_sys_stats",
+        ":perfetto_src_traced_probes_system_info_system_info",
+        ":perfetto_src_tracing_common",
+        ":perfetto_src_tracing_core_core",
+        ":perfetto_src_tracing_core_service",
+        ":perfetto_src_tracing_ipc_common",
+        ":perfetto_src_tracing_ipc_consumer_consumer",
+        ":perfetto_src_tracing_ipc_default_socket",
+        ":perfetto_src_tracing_ipc_producer_producer",
+        ":perfetto_src_tracing_ipc_service_service",
+        ":perfetto_test_test_helper",
+    ],
+    static_libs: [
+        "libgmock",
+        "libgtest",
+    ],
+    whole_static_libs: [
+        "perfetto_gtest_logcat_printer",
+    ],
+    generated_headers: [
+        "perfetto_protos_perfetto_common_cpp_gen_headers",
+        "perfetto_protos_perfetto_common_zero_gen_headers",
+        "perfetto_protos_perfetto_config_android_cpp_gen_headers",
+        "perfetto_protos_perfetto_config_android_zero_gen_headers",
+        "perfetto_protos_perfetto_config_cpp_gen_headers",
+        "perfetto_protos_perfetto_config_ftrace_cpp_gen_headers",
+        "perfetto_protos_perfetto_config_ftrace_zero_gen_headers",
+        "perfetto_protos_perfetto_config_gpu_cpp_gen_headers",
+        "perfetto_protos_perfetto_config_gpu_zero_gen_headers",
+        "perfetto_protos_perfetto_config_inode_file_cpp_gen_headers",
+        "perfetto_protos_perfetto_config_inode_file_zero_gen_headers",
+        "perfetto_protos_perfetto_config_interceptors_cpp_gen_headers",
+        "perfetto_protos_perfetto_config_interceptors_zero_gen_headers",
+        "perfetto_protos_perfetto_config_power_cpp_gen_headers",
+        "perfetto_protos_perfetto_config_power_zero_gen_headers",
+        "perfetto_protos_perfetto_config_process_stats_cpp_gen_headers",
+        "perfetto_protos_perfetto_config_process_stats_zero_gen_headers",
+        "perfetto_protos_perfetto_config_profiling_cpp_gen_headers",
+        "perfetto_protos_perfetto_config_profiling_zero_gen_headers",
+        "perfetto_protos_perfetto_config_statsd_cpp_gen_headers",
+        "perfetto_protos_perfetto_config_statsd_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_system_info_cpp_gen_headers",
+        "perfetto_protos_perfetto_config_system_info_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",
+        "perfetto_protos_perfetto_ipc_wire_protocol_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_android_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_android_zero_gen_headers",
+        "perfetto_protos_perfetto_trace_chrome_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_chrome_zero_gen_headers",
+        "perfetto_protos_perfetto_trace_filesystem_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_filesystem_zero_gen_headers",
+        "perfetto_protos_perfetto_trace_ftrace_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_ftrace_zero_gen_headers",
+        "perfetto_protos_perfetto_trace_gpu_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_gpu_zero_gen_headers",
+        "perfetto_protos_perfetto_trace_interned_data_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers",
+        "perfetto_protos_perfetto_trace_minimal_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_minimal_zero_gen_headers",
+        "perfetto_protos_perfetto_trace_non_minimal_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_non_minimal_zero_gen_headers",
+        "perfetto_protos_perfetto_trace_perfetto_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_perfetto_zero_gen_headers",
+        "perfetto_protos_perfetto_trace_power_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_power_zero_gen_headers",
+        "perfetto_protos_perfetto_trace_profiling_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_profiling_zero_gen_headers",
+        "perfetto_protos_perfetto_trace_ps_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_ps_zero_gen_headers",
+        "perfetto_protos_perfetto_trace_statsd_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_statsd_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_protos_perfetto_trace_translation_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_translation_zero_gen_headers",
+        "perfetto_protos_third_party_statsd_config_zero_gen_headers",
+        "perfetto_src_base_version_gen_h",
+    ],
+    export_generated_headers: [
+        "perfetto_protos_perfetto_common_cpp_gen_headers",
+        "perfetto_protos_perfetto_common_zero_gen_headers",
+        "perfetto_protos_perfetto_config_android_cpp_gen_headers",
+        "perfetto_protos_perfetto_config_android_zero_gen_headers",
+        "perfetto_protos_perfetto_config_cpp_gen_headers",
+        "perfetto_protos_perfetto_config_ftrace_cpp_gen_headers",
+        "perfetto_protos_perfetto_config_ftrace_zero_gen_headers",
+        "perfetto_protos_perfetto_config_gpu_cpp_gen_headers",
+        "perfetto_protos_perfetto_config_gpu_zero_gen_headers",
+        "perfetto_protos_perfetto_config_inode_file_cpp_gen_headers",
+        "perfetto_protos_perfetto_config_inode_file_zero_gen_headers",
+        "perfetto_protos_perfetto_config_interceptors_cpp_gen_headers",
+        "perfetto_protos_perfetto_config_interceptors_zero_gen_headers",
+        "perfetto_protos_perfetto_config_power_cpp_gen_headers",
+        "perfetto_protos_perfetto_config_power_zero_gen_headers",
+        "perfetto_protos_perfetto_config_process_stats_cpp_gen_headers",
+        "perfetto_protos_perfetto_config_process_stats_zero_gen_headers",
+        "perfetto_protos_perfetto_config_profiling_cpp_gen_headers",
+        "perfetto_protos_perfetto_config_profiling_zero_gen_headers",
+        "perfetto_protos_perfetto_config_statsd_cpp_gen_headers",
+        "perfetto_protos_perfetto_config_statsd_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_system_info_cpp_gen_headers",
+        "perfetto_protos_perfetto_config_system_info_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",
+        "perfetto_protos_perfetto_ipc_wire_protocol_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_android_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_android_zero_gen_headers",
+        "perfetto_protos_perfetto_trace_chrome_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_chrome_zero_gen_headers",
+        "perfetto_protos_perfetto_trace_filesystem_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_filesystem_zero_gen_headers",
+        "perfetto_protos_perfetto_trace_ftrace_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_ftrace_zero_gen_headers",
+        "perfetto_protos_perfetto_trace_gpu_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_gpu_zero_gen_headers",
+        "perfetto_protos_perfetto_trace_interned_data_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers",
+        "perfetto_protos_perfetto_trace_minimal_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_minimal_zero_gen_headers",
+        "perfetto_protos_perfetto_trace_non_minimal_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_non_minimal_zero_gen_headers",
+        "perfetto_protos_perfetto_trace_perfetto_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_perfetto_zero_gen_headers",
+        "perfetto_protos_perfetto_trace_power_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_power_zero_gen_headers",
+        "perfetto_protos_perfetto_trace_profiling_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_profiling_zero_gen_headers",
+        "perfetto_protos_perfetto_trace_ps_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_ps_zero_gen_headers",
+        "perfetto_protos_perfetto_trace_statsd_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_statsd_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_protos_perfetto_trace_translation_cpp_gen_headers",
+        "perfetto_protos_perfetto_trace_translation_zero_gen_headers",
+        "perfetto_protos_third_party_statsd_config_zero_gen_headers",
+        "perfetto_src_base_version_gen_h",
+    ],
+    defaults: [
+        "perfetto_defaults",
+    ],
+}
+
 // GN: //src/protozero/protoc_plugin:protozero_plugin
 cc_binary_host {
     name: "protozero_plugin",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_public_abi_base",
@@ -11134,10 +12113,12 @@
 cc_binary {
     name: "trace_processor_shell",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_http_http",
         ":perfetto_include_perfetto_ext_base_version",
+        ":perfetto_include_perfetto_ext_trace_processor_demangle",
         ":perfetto_include_perfetto_ext_trace_processor_export_json",
         ":perfetto_include_perfetto_ext_trace_processor_importers_memory_tracker_memory_tracker",
         ":perfetto_include_perfetto_ext_traced_sys_stats_counters",
@@ -11180,6 +12161,7 @@
         ":perfetto_protos_perfetto_trace_system_info_zero_gen",
         ":perfetto_protos_perfetto_trace_track_event_zero_gen",
         ":perfetto_protos_perfetto_trace_translation_zero_gen",
+        ":perfetto_protos_third_party_pprof_zero_gen",
         ":perfetto_src_base_base",
         ":perfetto_src_base_http_http",
         ":perfetto_src_base_unix_socket",
@@ -11190,35 +12172,59 @@
         ":perfetto_src_profiling_symbolizer_symbolizer",
         ":perfetto_src_protozero_proto_ring_buffer",
         ":perfetto_src_protozero_protozero",
-        ":perfetto_src_trace_processor_analysis_analysis",
         ":perfetto_src_trace_processor_containers_containers",
         ":perfetto_src_trace_processor_db_db",
         ":perfetto_src_trace_processor_dynamic_dynamic",
         ":perfetto_src_trace_processor_export_json",
-        ":perfetto_src_trace_processor_ftrace_descriptors",
         ":perfetto_src_trace_processor_importers_android_bugreport_android_bugreport",
         ":perfetto_src_trace_processor_importers_common_common",
+        ":perfetto_src_trace_processor_importers_common_parser_types",
+        ":perfetto_src_trace_processor_importers_common_trace_parser_hdr",
+        ":perfetto_src_trace_processor_importers_ftrace_ftrace_descriptors",
+        ":perfetto_src_trace_processor_importers_ftrace_full",
+        ":perfetto_src_trace_processor_importers_ftrace_minimal",
+        ":perfetto_src_trace_processor_importers_fuchsia_fuchsia_record",
+        ":perfetto_src_trace_processor_importers_fuchsia_full",
+        ":perfetto_src_trace_processor_importers_fuchsia_minimal",
+        ":perfetto_src_trace_processor_importers_gzip_full",
+        ":perfetto_src_trace_processor_importers_i2c_full",
+        ":perfetto_src_trace_processor_importers_json_full",
+        ":perfetto_src_trace_processor_importers_json_minimal",
         ":perfetto_src_trace_processor_importers_memory_tracker_graph_processor",
-        ":perfetto_src_trace_processor_importers_proto_storage_full",
-        ":perfetto_src_trace_processor_importers_proto_storage_minimal",
+        ":perfetto_src_trace_processor_importers_ninja_ninja",
+        ":perfetto_src_trace_processor_importers_proto_full",
+        ":perfetto_src_trace_processor_importers_proto_minimal",
+        ":perfetto_src_trace_processor_importers_proto_packet_sequence_state_generation_hdr",
+        ":perfetto_src_trace_processor_importers_proto_proto_importer_module",
+        ":perfetto_src_trace_processor_importers_syscalls_full",
+        ":perfetto_src_trace_processor_importers_systrace_full",
+        ":perfetto_src_trace_processor_importers_systrace_systrace_line",
+        ":perfetto_src_trace_processor_importers_systrace_systrace_parser",
         ":perfetto_src_trace_processor_lib",
         ":perfetto_src_trace_processor_metatrace",
         ":perfetto_src_trace_processor_metrics_metrics",
+        ":perfetto_src_trace_processor_prelude_functions_functions",
+        ":perfetto_src_trace_processor_prelude_operators_operators",
         ":perfetto_src_trace_processor_rpc_httpd",
         ":perfetto_src_trace_processor_rpc_rpc",
+        ":perfetto_src_trace_processor_sorter_sorter",
         ":perfetto_src_trace_processor_sqlite_sqlite",
         ":perfetto_src_trace_processor_sqlite_sqlite_minimal",
-        ":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_types_types",
         ":perfetto_src_trace_processor_util_descriptors",
+        ":perfetto_src_trace_processor_util_glob",
         ":perfetto_src_trace_processor_util_gzip",
         ":perfetto_src_trace_processor_util_interned_message_view",
+        ":perfetto_src_trace_processor_util_profile_builder",
+        ":perfetto_src_trace_processor_util_proto_profiler",
         ":perfetto_src_trace_processor_util_proto_to_args_parser",
         ":perfetto_src_trace_processor_util_protozero_to_text",
+        ":perfetto_src_trace_processor_util_sql_argument",
         ":perfetto_src_trace_processor_util_stack_traces_util",
+        ":perfetto_src_trace_processor_util_stdlib",
         ":perfetto_src_trace_processor_util_util",
         ":perfetto_src_trace_processor_util_zip_reader",
         ":perfetto_src_trace_processor_views_views",
@@ -11263,14 +12269,18 @@
         "perfetto_protos_perfetto_trace_system_info_zero_gen_headers",
         "perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
         "perfetto_protos_perfetto_trace_translation_zero_gen_headers",
+        "perfetto_protos_third_party_pprof_zero_gen_headers",
         "perfetto_src_base_version_gen_h",
-        "perfetto_src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
-        "perfetto_src_trace_processor_importers_gen_cc_config_descriptor",
-        "perfetto_src_trace_processor_importers_gen_cc_statsd_atoms_descriptor",
-        "perfetto_src_trace_processor_importers_gen_cc_track_event_descriptor",
+        "perfetto_src_trace_processor_importers_proto_gen_cc_chrome_track_event_descriptor",
+        "perfetto_src_trace_processor_importers_proto_gen_cc_config_descriptor",
+        "perfetto_src_trace_processor_importers_proto_gen_cc_statsd_atoms_descriptor",
+        "perfetto_src_trace_processor_importers_proto_gen_cc_trace_descriptor",
+        "perfetto_src_trace_processor_importers_proto_gen_cc_track_event_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_sql_gen_amalgamated_sql_metrics",
+        "perfetto_src_trace_processor_stdlib_gen_amalgamated_stdlib",
+        "perfetto_src_trace_processor_tables_tables_python",
     ],
     defaults: [
         "perfetto_defaults",
@@ -11293,12 +12303,16 @@
                 "libutils",
                 "libz",
             ],
+            static_libs: [
+                "sqlite_ext_percentile",
+            ],
         },
         host: {
             static_libs: [
                 "libprotobuf-cpp-full",
                 "libsqlite",
                 "libz",
+                "sqlite_ext_percentile",
             ],
             stl: "libc++_static",
             dist: {
@@ -11314,9 +12328,11 @@
 cc_binary_host {
     name: "traceconv",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_version",
+        ":perfetto_include_perfetto_ext_trace_processor_demangle",
         ":perfetto_include_perfetto_ext_trace_processor_export_json",
         ":perfetto_include_perfetto_ext_trace_processor_importers_memory_tracker_memory_tracker",
         ":perfetto_include_perfetto_ext_traced_sys_stats_counters",
@@ -11369,33 +12385,57 @@
         ":perfetto_src_profiling_symbolizer_symbolizer",
         ":perfetto_src_protozero_proto_ring_buffer",
         ":perfetto_src_protozero_protozero",
-        ":perfetto_src_trace_processor_analysis_analysis",
         ":perfetto_src_trace_processor_containers_containers",
         ":perfetto_src_trace_processor_db_db",
         ":perfetto_src_trace_processor_dynamic_dynamic",
         ":perfetto_src_trace_processor_export_json",
-        ":perfetto_src_trace_processor_ftrace_descriptors",
         ":perfetto_src_trace_processor_importers_android_bugreport_android_bugreport",
         ":perfetto_src_trace_processor_importers_common_common",
+        ":perfetto_src_trace_processor_importers_common_parser_types",
+        ":perfetto_src_trace_processor_importers_common_trace_parser_hdr",
+        ":perfetto_src_trace_processor_importers_ftrace_ftrace_descriptors",
+        ":perfetto_src_trace_processor_importers_ftrace_full",
+        ":perfetto_src_trace_processor_importers_ftrace_minimal",
+        ":perfetto_src_trace_processor_importers_fuchsia_fuchsia_record",
+        ":perfetto_src_trace_processor_importers_fuchsia_full",
+        ":perfetto_src_trace_processor_importers_fuchsia_minimal",
+        ":perfetto_src_trace_processor_importers_gzip_full",
+        ":perfetto_src_trace_processor_importers_i2c_full",
+        ":perfetto_src_trace_processor_importers_json_full",
+        ":perfetto_src_trace_processor_importers_json_minimal",
         ":perfetto_src_trace_processor_importers_memory_tracker_graph_processor",
-        ":perfetto_src_trace_processor_importers_proto_storage_full",
-        ":perfetto_src_trace_processor_importers_proto_storage_minimal",
+        ":perfetto_src_trace_processor_importers_ninja_ninja",
+        ":perfetto_src_trace_processor_importers_proto_full",
+        ":perfetto_src_trace_processor_importers_proto_minimal",
+        ":perfetto_src_trace_processor_importers_proto_packet_sequence_state_generation_hdr",
+        ":perfetto_src_trace_processor_importers_proto_proto_importer_module",
+        ":perfetto_src_trace_processor_importers_syscalls_full",
+        ":perfetto_src_trace_processor_importers_systrace_full",
+        ":perfetto_src_trace_processor_importers_systrace_systrace_line",
+        ":perfetto_src_trace_processor_importers_systrace_systrace_parser",
         ":perfetto_src_trace_processor_lib",
         ":perfetto_src_trace_processor_metatrace",
         ":perfetto_src_trace_processor_metrics_metrics",
+        ":perfetto_src_trace_processor_prelude_functions_functions",
+        ":perfetto_src_trace_processor_prelude_operators_operators",
+        ":perfetto_src_trace_processor_sorter_sorter",
         ":perfetto_src_trace_processor_sqlite_sqlite",
         ":perfetto_src_trace_processor_sqlite_sqlite_minimal",
-        ":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_types_types",
         ":perfetto_src_trace_processor_util_descriptors",
+        ":perfetto_src_trace_processor_util_glob",
         ":perfetto_src_trace_processor_util_gzip",
         ":perfetto_src_trace_processor_util_interned_message_view",
+        ":perfetto_src_trace_processor_util_profile_builder",
+        ":perfetto_src_trace_processor_util_proto_profiler",
         ":perfetto_src_trace_processor_util_proto_to_args_parser",
         ":perfetto_src_trace_processor_util_protozero_to_text",
+        ":perfetto_src_trace_processor_util_sql_argument",
         ":perfetto_src_trace_processor_util_stack_traces_util",
+        ":perfetto_src_trace_processor_util_stdlib",
         ":perfetto_src_trace_processor_util_util",
         ":perfetto_src_trace_processor_util_zip_reader",
         ":perfetto_src_trace_processor_views_views",
@@ -11408,6 +12448,7 @@
         "libsqlite",
         "libz",
         "perfetto_src_trace_processor_demangle",
+        "sqlite_ext_percentile",
     ],
     generated_headers: [
         "perfetto_protos_perfetto_common_zero_gen_headers",
@@ -11445,13 +12486,16 @@
         "perfetto_protos_perfetto_trace_translation_zero_gen_headers",
         "perfetto_protos_third_party_pprof_zero_gen_headers",
         "perfetto_src_base_version_gen_h",
-        "perfetto_src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
-        "perfetto_src_trace_processor_importers_gen_cc_config_descriptor",
-        "perfetto_src_trace_processor_importers_gen_cc_statsd_atoms_descriptor",
-        "perfetto_src_trace_processor_importers_gen_cc_track_event_descriptor",
+        "perfetto_src_trace_processor_importers_proto_gen_cc_chrome_track_event_descriptor",
+        "perfetto_src_trace_processor_importers_proto_gen_cc_config_descriptor",
+        "perfetto_src_trace_processor_importers_proto_gen_cc_statsd_atoms_descriptor",
+        "perfetto_src_trace_processor_importers_proto_gen_cc_trace_descriptor",
+        "perfetto_src_trace_processor_importers_proto_gen_cc_track_event_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_gen_cc_metrics_descriptor",
         "perfetto_src_trace_processor_metrics_sql_gen_amalgamated_sql_metrics",
+        "perfetto_src_trace_processor_stdlib_gen_amalgamated_stdlib",
+        "perfetto_src_trace_processor_tables_tables_python",
         "perfetto_src_traceconv_gen_cc_trace_descriptor",
     ],
     defaults: [
@@ -11485,6 +12529,7 @@
 cc_binary {
     name: "traced_perf",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_version",
@@ -11679,6 +12724,7 @@
 cc_binary {
     name: "trigger_perfetto",
     srcs: [
+        ":perfetto_base_default_platform",
         ":perfetto_include_perfetto_base_base",
         ":perfetto_include_perfetto_ext_base_base",
         ":perfetto_include_perfetto_ext_base_version",
@@ -11931,14 +12977,6 @@
     data: [
         ":perfetto-trace-processor-python-data",
     ],
-    version: {
-        py2: {
-            enabled: false,
-        },
-        py3: {
-            enabled: true,
-        },
-    },
     libs: [
         "libprotobuf-python",
     ],
diff --git a/Android.bp.extras b/Android.bp.extras
index 2de83f1..f1251b4 100644
--- a/Android.bp.extras
+++ b/Android.bp.extras
@@ -107,14 +107,6 @@
     data: [
         ":perfetto-trace-processor-python-data",
     ],
-    version: {
-        py2: {
-            enabled: false,
-        },
-        py3: {
-            enabled: true,
-        },
-    },
     libs: [
         "libprotobuf-python",
     ],
diff --git a/BUILD b/BUILD
index cc6ab20..c4c9781 100644
--- a/BUILD
+++ b/BUILD
@@ -18,26 +18,28 @@
 load(
     "@perfetto//bazel:rules.bzl",
     "perfetto_build_config_cc_library",
+    "perfetto_cc_amalgamated_sql",
     "perfetto_cc_binary",
-    "perfetto_filegroup",
-    "perfetto_genrule",
     "perfetto_cc_ipc_library",
     "perfetto_cc_library",
     "perfetto_cc_proto_descriptor",
     "perfetto_cc_proto_library",
     "perfetto_cc_protocpp_library",
     "perfetto_cc_protozero_library",
+    "perfetto_cc_tp_tables",
+    "perfetto_filegroup",
+    "perfetto_genrule",
     "perfetto_go_proto_library",
-    "perfetto_java_proto_library",
     "perfetto_java_lite_proto_library",
-    "perfetto_proto_library",
+    "perfetto_java_proto_library",
     "perfetto_proto_descriptor",
+    "perfetto_proto_library",
     "perfetto_py_binary",
     "perfetto_py_library",
     "perfetto_py_proto_library",
 )
 
-package(default_visibility = ["//visibility:private"])
+package(default_visibility = [PERFETTO_CONFIG.root + ":__subpackages__"])
 
 licenses(["notice"])
 
@@ -47,6 +49,21 @@
 # Internal targets
 # ##############################################################################
 
+# GN target: //src/base:perfetto_base_default_platform
+perfetto_cc_library(
+    name = "perfetto_base_default_platform",
+    srcs = [
+        "src/base/default_platform.cc",
+    ],
+    hdrs = [
+        ":include_perfetto_base_base",
+        ":include_perfetto_ext_base_base",
+        ":include_perfetto_public_abi_base",
+        ":include_perfetto_public_base",
+    ],
+    linkstatic = True,
+)
+
 # GN target: //src/ipc/protoc_plugin:ipc_plugin
 perfetto_cc_binary(
     name = "ipc_plugin",
@@ -390,9 +407,11 @@
         "include/perfetto/ext/base/paged_memory.h",
         "include/perfetto/ext/base/periodic_task.h",
         "include/perfetto/ext/base/pipe.h",
+        "include/perfetto/ext/base/platform.h",
         "include/perfetto/ext/base/scoped_file.h",
         "include/perfetto/ext/base/small_set.h",
         "include/perfetto/ext/base/small_vector.h",
+        "include/perfetto/ext/base/status_or.h",
         "include/perfetto/ext/base/string_splitter.h",
         "include/perfetto/ext/base/string_utils.h",
         "include/perfetto/ext/base/string_view.h",
@@ -589,6 +608,7 @@
     name = "include_perfetto_trace_processor_trace_processor",
     srcs = [
         "include/perfetto/trace_processor/iterator.h",
+        "include/perfetto/trace_processor/metatrace_config.h",
         "include/perfetto/trace_processor/read_trace.h",
         "include/perfetto/trace_processor/ref_counted.h",
         "include/perfetto/trace_processor/trace_processor.h",
@@ -749,6 +769,7 @@
         "src/base/temp_file.cc",
         "src/base/thread_checker.cc",
         "src/base/thread_task_runner.cc",
+        "src/base/thread_utils.cc",
         "src/base/time.cc",
         "src/base/unix_task_runner.cc",
         "src/base/utils.cc",
@@ -763,6 +784,8 @@
         ":include_perfetto_public_abi_base",
         ":include_perfetto_public_base",
     ],
+    deps = [
+    ] + PERFETTO_CONFIG.deps.base_platform,
     linkstatic = True,
 )
 
@@ -865,6 +888,7 @@
     ],
 )
 
+# GN target: //src/perfetto_cmd:gen_cc_config_descriptor
 perfetto_cc_proto_descriptor(
     name = "src_perfetto_cmd_gen_cc_config_descriptor",
     deps = [
@@ -995,15 +1019,6 @@
     ],
 )
 
-# GN target: //src/trace_processor/analysis:analysis
-perfetto_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
 perfetto_cc_library(
     name = "src_trace_processor_containers_containers",
@@ -1062,8 +1077,6 @@
         "src/trace_processor/dynamic/connected_flow_generator.h",
         "src/trace_processor/dynamic/descendant_generator.cc",
         "src/trace_processor/dynamic/descendant_generator.h",
-        "src/trace_processor/dynamic/describe_slice_generator.cc",
-        "src/trace_processor/dynamic/describe_slice_generator.h",
         "src/trace_processor/dynamic/dynamic_table_generator.cc",
         "src/trace_processor/dynamic/dynamic_table_generator.h",
         "src/trace_processor/dynamic/experimental_annotated_stack_generator.cc",
@@ -1104,9 +1117,13 @@
         "src/trace_processor/importers/common/args_tracker.h",
         "src/trace_processor/importers/common/args_translation_table.cc",
         "src/trace_processor/importers/common/args_translation_table.h",
+        "src/trace_processor/importers/common/async_track_set_tracker.cc",
+        "src/trace_processor/importers/common/async_track_set_tracker.h",
         "src/trace_processor/importers/common/chunked_trace_reader.h",
         "src/trace_processor/importers/common/clock_tracker.cc",
         "src/trace_processor/importers/common/clock_tracker.h",
+        "src/trace_processor/importers/common/deobfuscation_mapping_table.cc",
+        "src/trace_processor/importers/common/deobfuscation_mapping_table.h",
         "src/trace_processor/importers/common/event_tracker.cc",
         "src/trace_processor/importers/common/event_tracker.h",
         "src/trace_processor/importers/common/flow_tracker.cc",
@@ -1121,12 +1138,146 @@
         "src/trace_processor/importers/common/slice_translation_table.h",
         "src/trace_processor/importers/common/system_info_tracker.cc",
         "src/trace_processor/importers/common/system_info_tracker.h",
-        "src/trace_processor/importers/common/trace_parser.h",
+        "src/trace_processor/importers/common/trace_parser.cc",
         "src/trace_processor/importers/common/track_tracker.cc",
         "src/trace_processor/importers/common/track_tracker.h",
     ],
 )
 
+# GN target: //src/trace_processor/importers/common:parser_types
+perfetto_filegroup(
+    name = "src_trace_processor_importers_common_parser_types",
+    srcs = [
+        "src/trace_processor/importers/common/parser_types.h",
+    ],
+)
+
+# GN target: //src/trace_processor/importers/common:trace_parser_hdr
+perfetto_filegroup(
+    name = "src_trace_processor_importers_common_trace_parser_hdr",
+    srcs = [
+        "src/trace_processor/importers/common/trace_parser.h",
+    ],
+)
+
+# GN target: //src/trace_processor/importers/ftrace:ftrace_descriptors
+perfetto_filegroup(
+    name = "src_trace_processor_importers_ftrace_ftrace_descriptors",
+    srcs = [
+        "src/trace_processor/importers/ftrace/ftrace_descriptors.cc",
+        "src/trace_processor/importers/ftrace/ftrace_descriptors.h",
+    ],
+)
+
+# GN target: //src/trace_processor/importers/ftrace:full
+perfetto_filegroup(
+    name = "src_trace_processor_importers_ftrace_full",
+    srcs = [
+        "src/trace_processor/importers/ftrace/binder_tracker.cc",
+        "src/trace_processor/importers/ftrace/binder_tracker.h",
+        "src/trace_processor/importers/ftrace/drm_tracker.cc",
+        "src/trace_processor/importers/ftrace/drm_tracker.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",
+        "src/trace_processor/importers/ftrace/ftrace_parser.h",
+        "src/trace_processor/importers/ftrace/ftrace_tokenizer.cc",
+        "src/trace_processor/importers/ftrace/ftrace_tokenizer.h",
+        "src/trace_processor/importers/ftrace/iostat_tracker.cc",
+        "src/trace_processor/importers/ftrace/iostat_tracker.h",
+        "src/trace_processor/importers/ftrace/mali_gpu_event_tracker.cc",
+        "src/trace_processor/importers/ftrace/mali_gpu_event_tracker.h",
+        "src/trace_processor/importers/ftrace/rss_stat_tracker.cc",
+        "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/ftrace/thread_state_tracker.cc",
+        "src/trace_processor/importers/ftrace/thread_state_tracker.h",
+        "src/trace_processor/importers/ftrace/v4l2_tracker.cc",
+        "src/trace_processor/importers/ftrace/v4l2_tracker.h",
+        "src/trace_processor/importers/ftrace/virtio_gpu_tracker.cc",
+        "src/trace_processor/importers/ftrace/virtio_gpu_tracker.h",
+        "src/trace_processor/importers/ftrace/virtio_video_tracker.cc",
+        "src/trace_processor/importers/ftrace/virtio_video_tracker.h",
+    ],
+)
+
+# GN target: //src/trace_processor/importers/ftrace:minimal
+perfetto_filegroup(
+    name = "src_trace_processor_importers_ftrace_minimal",
+    srcs = [
+        "src/trace_processor/importers/ftrace/ftrace_module.cc",
+        "src/trace_processor/importers/ftrace/ftrace_module.h",
+    ],
+)
+
+# GN target: //src/trace_processor/importers/fuchsia:fuchsia_record
+perfetto_filegroup(
+    name = "src_trace_processor_importers_fuchsia_fuchsia_record",
+    srcs = [
+        "src/trace_processor/importers/fuchsia/fuchsia_record.cc",
+        "src/trace_processor/importers/fuchsia/fuchsia_record.h",
+    ],
+)
+
+# GN target: //src/trace_processor/importers/fuchsia:full
+perfetto_filegroup(
+    name = "src_trace_processor_importers_fuchsia_full",
+    srcs = [
+        "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",
+    ],
+)
+
+# GN target: //src/trace_processor/importers/fuchsia:minimal
+perfetto_filegroup(
+    name = "src_trace_processor_importers_fuchsia_minimal",
+    srcs = [
+        "src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h",
+    ],
+)
+
+# GN target: //src/trace_processor/importers/gzip:full
+perfetto_filegroup(
+    name = "src_trace_processor_importers_gzip_full",
+    srcs = [
+        "src/trace_processor/importers/gzip/gzip_trace_parser.cc",
+        "src/trace_processor/importers/gzip/gzip_trace_parser.h",
+    ],
+)
+
+# GN target: //src/trace_processor/importers/i2c:full
+perfetto_filegroup(
+    name = "src_trace_processor_importers_i2c_full",
+    srcs = [
+        "src/trace_processor/importers/i2c/i2c_tracker.cc",
+        "src/trace_processor/importers/i2c/i2c_tracker.h",
+    ],
+)
+
+# GN target: //src/trace_processor/importers/json:full
+perfetto_filegroup(
+    name = "src_trace_processor_importers_json_full",
+    srcs = [
+        "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",
+    ],
+)
+
+# GN target: //src/trace_processor/importers/json:minimal
+perfetto_filegroup(
+    name = "src_trace_processor_importers_json_minimal",
+    srcs = [
+        "src/trace_processor/importers/json/json_utils.cc",
+        "src/trace_processor/importers/json/json_utils.h",
+    ],
+)
+
 # GN target: //src/trace_processor/importers/memory_tracker:graph_processor
 perfetto_filegroup(
     name = "src_trace_processor_importers_memory_tracker_graph_processor",
@@ -1139,70 +1290,228 @@
     ],
 )
 
-# GN target: //src/trace_processor/importers/proto:storage_full
+# GN target: //src/trace_processor/importers/ninja:ninja
 perfetto_filegroup(
-    name = "src_trace_processor_importers_proto_storage_full",
+    name = "src_trace_processor_importers_ninja_ninja",
     srcs = [
+        "src/trace_processor/importers/ninja/ninja_log_parser.cc",
+        "src/trace_processor/importers/ninja/ninja_log_parser.h",
+    ],
+)
+
+# GN target: //src/trace_processor/importers/proto:full
+perfetto_filegroup(
+    name = "src_trace_processor_importers_proto_full",
+    srcs = [
+        "src/trace_processor/importers/proto/additional_modules.cc",
+        "src/trace_processor/importers/proto/additional_modules.h",
+        "src/trace_processor/importers/proto/android_camera_event_module.cc",
+        "src/trace_processor/importers/proto/android_camera_event_module.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/content_analyzer.cc",
+        "src/trace_processor/importers/proto/content_analyzer.h",
+        "src/trace_processor/importers/proto/frame_timeline_event_parser.cc",
+        "src/trace_processor/importers/proto/frame_timeline_event_parser.h",
+        "src/trace_processor/importers/proto/gpu_event_parser.cc",
+        "src/trace_processor/importers/proto/gpu_event_parser.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_frame_event_parser.cc",
+        "src/trace_processor/importers/proto/graphics_frame_event_parser.h",
+        "src/trace_processor/importers/proto/heap_graph_module.cc",
+        "src/trace_processor/importers/proto/heap_graph_module.h",
         "src/trace_processor/importers/proto/heap_graph_tracker.cc",
         "src/trace_processor/importers/proto/heap_graph_tracker.h",
+        "src/trace_processor/importers/proto/metadata_module.cc",
+        "src/trace_processor/importers/proto/metadata_module.h",
+        "src/trace_processor/importers/proto/statsd_module.cc",
+        "src/trace_processor/importers/proto/statsd_module.h",
+        "src/trace_processor/importers/proto/system_probes_module.cc",
+        "src/trace_processor/importers/proto/system_probes_module.h",
+        "src/trace_processor/importers/proto/system_probes_parser.cc",
+        "src/trace_processor/importers/proto/system_probes_parser.h",
+        "src/trace_processor/importers/proto/translation_table_module.cc",
+        "src/trace_processor/importers/proto/translation_table_module.h",
+        "src/trace_processor/importers/proto/vulkan_memory_tracker.cc",
+        "src/trace_processor/importers/proto/vulkan_memory_tracker.h",
     ],
 )
 
-# GN target: //src/trace_processor/importers/proto:storage_minimal
-perfetto_filegroup(
-    name = "src_trace_processor_importers_proto_storage_minimal",
-    srcs = [
-        "src/trace_processor/importers/proto/heap_profile_tracker.cc",
-        "src/trace_processor/importers/proto/heap_profile_tracker.h",
-        "src/trace_processor/importers/proto/profiler_util.cc",
-        "src/trace_processor/importers/proto/profiler_util.h",
-        "src/trace_processor/importers/proto/stack_profile_tracker.cc",
-        "src/trace_processor/importers/proto/stack_profile_tracker.h",
-    ],
-)
-
+# GN target: //src/trace_processor/importers/proto:gen_cc_chrome_track_event_descriptor
 perfetto_cc_proto_descriptor(
-    name = "src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
+    name = "src_trace_processor_importers_proto_gen_cc_chrome_track_event_descriptor",
     deps = [
         ":protos_third_party_chromium_descriptor",
     ],
     outs = [
-        "src/trace_processor/importers/chrome_track_event.descriptor.h",
+        "src/trace_processor/importers/proto/chrome_track_event.descriptor.h",
     ],
 )
 
+# GN target: //src/trace_processor/importers/proto:gen_cc_config_descriptor
 perfetto_cc_proto_descriptor(
-    name = "src_trace_processor_importers_gen_cc_config_descriptor",
+    name = "src_trace_processor_importers_proto_gen_cc_config_descriptor",
     deps = [
         ":protos_perfetto_config_descriptor",
     ],
     outs = [
-        "src/trace_processor/importers/config.descriptor.h",
+        "src/trace_processor/importers/proto/config.descriptor.h",
     ],
 )
 
+# GN target: //src/trace_processor/importers/proto:gen_cc_statsd_atoms_descriptor
 perfetto_cc_proto_descriptor(
-    name = "src_trace_processor_importers_gen_cc_statsd_atoms_descriptor",
+    name = "src_trace_processor_importers_proto_gen_cc_statsd_atoms_descriptor",
     deps = [
         "src/trace_processor/importers/proto/atoms.descriptor",
     ],
     outs = [
-        "src/trace_processor/importers/atoms.descriptor.h",
+        "src/trace_processor/importers/proto/atoms.descriptor.h",
     ],
 )
 
+# GN target: //src/trace_processor/importers/proto:gen_cc_trace_descriptor
 perfetto_cc_proto_descriptor(
-    name = "src_trace_processor_importers_gen_cc_track_event_descriptor",
+    name = "src_trace_processor_importers_proto_gen_cc_trace_descriptor",
+    deps = [
+        ":protos_perfetto_trace_descriptor",
+    ],
+    outs = [
+        "src/trace_processor/importers/proto/trace.descriptor.h",
+    ],
+)
+
+# GN target: //src/trace_processor/importers/proto:gen_cc_track_event_descriptor
+perfetto_cc_proto_descriptor(
+    name = "src_trace_processor_importers_proto_gen_cc_track_event_descriptor",
     deps = [
         ":protos_perfetto_trace_track_event_descriptor",
     ],
     outs = [
-        "src/trace_processor/importers/track_event.descriptor.h",
+        "src/trace_processor/importers/proto/track_event.descriptor.h",
     ],
 )
 
-perfetto_genrule(
-    name = "src_trace_processor_metrics_sql_gen_amalgamated_sql_metrics",
+# GN target: //src/trace_processor/importers/proto:minimal
+perfetto_filegroup(
+    name = "src_trace_processor_importers_proto_minimal",
+    srcs = [
+        "src/trace_processor/importers/proto/active_chrome_processes_tracker.cc",
+        "src/trace_processor/importers/proto/active_chrome_processes_tracker.h",
+        "src/trace_processor/importers/proto/chrome_string_lookup.cc",
+        "src/trace_processor/importers/proto/chrome_string_lookup.h",
+        "src/trace_processor/importers/proto/chrome_system_probes_module.cc",
+        "src/trace_processor/importers/proto/chrome_system_probes_module.h",
+        "src/trace_processor/importers/proto/chrome_system_probes_parser.cc",
+        "src/trace_processor/importers/proto/chrome_system_probes_parser.h",
+        "src/trace_processor/importers/proto/default_modules.cc",
+        "src/trace_processor/importers/proto/default_modules.h",
+        "src/trace_processor/importers/proto/heap_profile_tracker.cc",
+        "src/trace_processor/importers/proto/heap_profile_tracker.h",
+        "src/trace_processor/importers/proto/memory_tracker_snapshot_module.cc",
+        "src/trace_processor/importers/proto/memory_tracker_snapshot_module.h",
+        "src/trace_processor/importers/proto/memory_tracker_snapshot_parser.cc",
+        "src/trace_processor/importers/proto/memory_tracker_snapshot_parser.h",
+        "src/trace_processor/importers/proto/metadata_minimal_module.cc",
+        "src/trace_processor/importers/proto/metadata_minimal_module.h",
+        "src/trace_processor/importers/proto/metadata_tracker.cc",
+        "src/trace_processor/importers/proto/metadata_tracker.h",
+        "src/trace_processor/importers/proto/packet_analyzer.cc",
+        "src/trace_processor/importers/proto/packet_analyzer.h",
+        "src/trace_processor/importers/proto/packet_sequence_state.h",
+        "src/trace_processor/importers/proto/packet_sequence_state_generation.cc",
+        "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/profiler_util.cc",
+        "src/trace_processor/importers/proto/profiler_util.h",
+        "src/trace_processor/importers/proto/proto_incremental_state.h",
+        "src/trace_processor/importers/proto/proto_trace_parser.cc",
+        "src/trace_processor/importers/proto/proto_trace_parser.h",
+        "src/trace_processor/importers/proto/proto_trace_reader.cc",
+        "src/trace_processor/importers/proto/proto_trace_reader.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/importers/proto/track_event_tracker.cc",
+        "src/trace_processor/importers/proto/track_event_tracker.h",
+    ],
+)
+
+# GN target: //src/trace_processor/importers/proto:packet_sequence_state_generation_hdr
+perfetto_filegroup(
+    name = "src_trace_processor_importers_proto_packet_sequence_state_generation_hdr",
+    srcs = [
+        "src/trace_processor/importers/proto/packet_sequence_state_generation.h",
+    ],
+)
+
+# GN target: //src/trace_processor/importers/proto:proto_importer_module
+perfetto_filegroup(
+    name = "src_trace_processor_importers_proto_proto_importer_module",
+    srcs = [
+        "src/trace_processor/importers/proto/proto_importer_module.cc",
+        "src/trace_processor/importers/proto/proto_importer_module.h",
+    ],
+)
+
+# GN target: //src/trace_processor/importers/syscalls:full
+perfetto_filegroup(
+    name = "src_trace_processor_importers_syscalls_full",
+    srcs = [
+        "src/trace_processor/importers/syscalls/syscall_tracker.cc",
+        "src/trace_processor/importers/syscalls/syscall_tracker.h",
+    ],
+)
+
+# GN target: //src/trace_processor/importers/systrace:full
+perfetto_filegroup(
+    name = "src_trace_processor_importers_systrace_full",
+    srcs = [
+        "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_trace_parser.cc",
+        "src/trace_processor/importers/systrace/systrace_trace_parser.h",
+    ],
+)
+
+# GN target: //src/trace_processor/importers/systrace:systrace_line
+perfetto_filegroup(
+    name = "src_trace_processor_importers_systrace_systrace_line",
+    srcs = [
+        "src/trace_processor/importers/systrace/systrace_line.h",
+    ],
+)
+
+# GN target: //src/trace_processor/importers/systrace:systrace_parser
+perfetto_filegroup(
+    name = "src_trace_processor_importers_systrace_systrace_parser",
+    srcs = [
+        "src/trace_processor/importers/systrace/systrace_parser.cc",
+        "src/trace_processor/importers/systrace/systrace_parser.h",
+    ],
+)
+
+# GN target: //src/trace_processor/metrics/sql/android:android
+perfetto_filegroup(
+    name = "src_trace_processor_metrics_sql_android_android",
     srcs = [
         "src/trace_processor/metrics/sql/android/android_batt.sql",
         "src/trace_processor/metrics/sql/android/android_binder.sql",
@@ -1237,9 +1546,6 @@
         "src/trace_processor/metrics/sql/android/android_simpleperf.sql",
         "src/trace_processor/metrics/sql/android/android_startup.sql",
         "src/trace_processor/metrics/sql/android/android_surfaceflinger.sql",
-        "src/trace_processor/metrics/sql/android/android_sysui_cuj.sql",
-        "src/trace_processor/metrics/sql/android/android_sysui_cuj_jank_query.sql",
-        "src/trace_processor/metrics/sql/android/android_sysui_cuj_surfaceflinger.sql",
         "src/trace_processor/metrics/sql/android/android_task_names.sql",
         "src/trace_processor/metrics/sql/android/android_trace_quality.sql",
         "src/trace_processor/metrics/sql/android/android_trusty_workqueues.sql",
@@ -1251,6 +1557,7 @@
         "src/trace_processor/metrics/sql/android/g2d.sql",
         "src/trace_processor/metrics/sql/android/g2d_duration.sql",
         "src/trace_processor/metrics/sql/android/global_counter_span_view.sql",
+        "src/trace_processor/metrics/sql/android/global_counter_span_view_merged.sql",
         "src/trace_processor/metrics/sql/android/gpu_counter_span_view.sql",
         "src/trace_processor/metrics/sql/android/jank/cujs.sql",
         "src/trace_processor/metrics/sql/android/jank/cujs_boundaries.sql",
@@ -1259,6 +1566,7 @@
         "src/trace_processor/metrics/sql/android/jank/internal/derived_events.sql",
         "src/trace_processor/metrics/sql/android/jank/internal/query_base.sql",
         "src/trace_processor/metrics/sql/android/jank/internal/query_frame_slice.sql",
+        "src/trace_processor/metrics/sql/android/jank/params.sql",
         "src/trace_processor/metrics/sql/android/jank/query_functions.sql",
         "src/trace_processor/metrics/sql/android/jank/relevant_slices.sql",
         "src/trace_processor/metrics/sql/android/jank/relevant_threads.sql",
@@ -1266,8 +1574,25 @@
         "src/trace_processor/metrics/sql/android/java_heap_histogram.sql",
         "src/trace_processor/metrics/sql/android/java_heap_stats.sql",
         "src/trace_processor/metrics/sql/android/mem_stats_priority_breakdown.sql",
+        "src/trace_processor/metrics/sql/android/p_state.sql",
         "src/trace_processor/metrics/sql/android/power_drain_in_watts.sql",
         "src/trace_processor/metrics/sql/android/power_profile_data.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/barbet.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/bluejay.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/blueline.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/bonito.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/bramble.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/coral.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/crosshatch.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/flame.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/marlin.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/oriole.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/raven.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/redfin.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/sargo.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/sunfish.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/taimen.sql",
+        "src/trace_processor/metrics/sql/android/power_profile_data/walleye.sql",
         "src/trace_processor/metrics/sql/android/process_counter_span_view.sql",
         "src/trace_processor/metrics/sql/android/process_mem.sql",
         "src/trace_processor/metrics/sql/android/process_metadata.sql",
@@ -1286,19 +1611,35 @@
         "src/trace_processor/metrics/sql/android/startup/system_state.sql",
         "src/trace_processor/metrics/sql/android/startup/thread_state_breakdown.sql",
         "src/trace_processor/metrics/sql/android/unsymbolized_frames.sql",
+    ],
+)
+
+# GN target: //src/trace_processor/metrics/sql/chrome:chrome_sql
+perfetto_filegroup(
+    name = "src_trace_processor_metrics_sql_chrome_chrome_sql",
+    srcs = [
         "src/trace_processor/metrics/sql/chrome/actual_power_by_category.sql",
         "src/trace_processor/metrics/sql/chrome/actual_power_by_rail_mode.sql",
+        "src/trace_processor/metrics/sql/chrome/chrome_args_class_names.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_event_metadata.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_histogram_hashes.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals.sql",
+        "src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals_base.sql",
+        "src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals_template.sql",
+        "src/trace_processor/metrics/sql/chrome/chrome_long_tasks.sql",
+        "src/trace_processor/metrics/sql/chrome/chrome_long_tasks_delaying_input_processing.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_performance_mark_hashes.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_processes.sql",
+        "src/trace_processor/metrics/sql/chrome/chrome_reliable_range.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_scroll_inputs_per_frame.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_scroll_jank_caused_by_scheduling.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_slice_names.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_stack_samples_for_task.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_tasks.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_tasks_delaying_input_processing.sql",
+        "src/trace_processor/metrics/sql/chrome/chrome_tasks_delaying_input_processing_base.sql",
+        "src/trace_processor/metrics/sql/chrome/chrome_tasks_delaying_input_processing_template.sql",
+        "src/trace_processor/metrics/sql/chrome/chrome_tasks_template.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_thread_slice.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_unsymbolized_args.sql",
         "src/trace_processor/metrics/sql/chrome/chrome_user_event_hashes.sql",
@@ -1306,6 +1647,10 @@
         "src/trace_processor/metrics/sql/chrome/cpu_time_by_rail_mode.sql",
         "src/trace_processor/metrics/sql/chrome/estimated_power_by_category.sql",
         "src/trace_processor/metrics/sql/chrome/estimated_power_by_rail_mode.sql",
+        "src/trace_processor/metrics/sql/chrome/event_latency_scroll_jank.sql",
+        "src/trace_processor/metrics/sql/chrome/event_latency_scroll_jank_cause.sql",
+        "src/trace_processor/metrics/sql/chrome/event_latency_to_breakdowns.sql",
+        "src/trace_processor/metrics/sql/chrome/experimental_reliable_chrome_tasks_delaying_input_processing.sql",
         "src/trace_processor/metrics/sql/chrome/gesture_flow_event.sql",
         "src/trace_processor/metrics/sql/chrome/gesture_flow_event_queuing_delay.sql",
         "src/trace_processor/metrics/sql/chrome/gesture_jank.sql",
@@ -1324,25 +1669,66 @@
         "src/trace_processor/metrics/sql/chrome/touch_flow_event.sql",
         "src/trace_processor/metrics/sql/chrome/touch_flow_event_queuing_delay.sql",
         "src/trace_processor/metrics/sql/chrome/touch_jank.sql",
+        "src/trace_processor/metrics/sql/chrome/vsync_intervals.sql",
+    ],
+)
+
+# GN target: //src/trace_processor/metrics/sql/common:common
+perfetto_filegroup(
+    name = "src_trace_processor_metrics_sql_common_common",
+    srcs = [
+        "src/trace_processor/metrics/sql/common/parent_slice.sql",
+    ],
+)
+
+# GN target: //src/trace_processor/metrics/sql/experimental:experimental
+perfetto_filegroup(
+    name = "src_trace_processor_metrics_sql_experimental_experimental",
+    srcs = [
         "src/trace_processor/metrics/sql/experimental/blink_gc_metric.sql",
         "src/trace_processor/metrics/sql/experimental/chrome_dropped_frames.sql",
         "src/trace_processor/metrics/sql/experimental/chrome_long_latency.sql",
         "src/trace_processor/metrics/sql/experimental/frame_times.sql",
         "src/trace_processor/metrics/sql/experimental/media_metric.sql",
         "src/trace_processor/metrics/sql/experimental/reported_by_page.sql",
-        "src/trace_processor/metrics/sql/trace_metadata.sql",
-        "src/trace_processor/metrics/sql/trace_stats.sql",
+    ],
+)
+
+# GN target: //src/trace_processor/metrics/sql/webview:webview
+perfetto_filegroup(
+    name = "src_trace_processor_metrics_sql_webview_webview",
+    srcs = [
         "src/trace_processor/metrics/sql/webview/webview_power_usage.sql",
     ],
+)
+
+# GN target: //src/trace_processor/metrics/sql:gen_amalgamated_sql_metrics
+perfetto_cc_amalgamated_sql(
+    name = "src_trace_processor_metrics_sql_gen_amalgamated_sql_metrics",
+    deps = [
+        ":src_trace_processor_metrics_sql_android_android",
+        ":src_trace_processor_metrics_sql_chrome_chrome_sql",
+        ":src_trace_processor_metrics_sql_common_common",
+        ":src_trace_processor_metrics_sql_experimental_experimental",
+        ":src_trace_processor_metrics_sql_misc_sql",
+        ":src_trace_processor_metrics_sql_webview_webview",
+    ],
     outs = [
         "src/trace_processor/metrics/sql/amalgamated_sql_metrics.h",
     ],
-    cmd = "$(location gen_amalgamated_sql_metrics_py) --cpp_out=$@ $(SRCS)",
-    exec_tools = [
-        ":gen_amalgamated_sql_metrics_py",
+    namespace = "sql_metrics",
+)
+
+# GN target: //src/trace_processor/metrics/sql:misc_sql
+perfetto_filegroup(
+    name = "src_trace_processor_metrics_sql_misc_sql",
+    srcs = [
+        "src/trace_processor/metrics/sql/trace_metadata.sql",
+        "src/trace_processor/metrics/sql/trace_stats.sql",
     ],
 )
 
+# GN target: //src/trace_processor/metrics:gen_cc_all_chrome_metrics_descriptor
 perfetto_cc_proto_descriptor(
     name = "src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
     deps = [
@@ -1353,6 +1739,7 @@
     ],
 )
 
+# GN target: //src/trace_processor/metrics:gen_cc_metrics_descriptor
 perfetto_cc_proto_descriptor(
     name = "src_trace_processor_metrics_gen_cc_metrics_descriptor",
     deps = [
@@ -1372,6 +1759,40 @@
     ],
 )
 
+# GN target: //src/trace_processor/prelude/functions:functions
+perfetto_filegroup(
+    name = "src_trace_processor_prelude_functions_functions",
+    srcs = [
+        "src/trace_processor/prelude/functions/create_function.cc",
+        "src/trace_processor/prelude/functions/create_function.h",
+        "src/trace_processor/prelude/functions/create_function_internal.cc",
+        "src/trace_processor/prelude/functions/create_function_internal.h",
+        "src/trace_processor/prelude/functions/create_view_function.cc",
+        "src/trace_processor/prelude/functions/create_view_function.h",
+        "src/trace_processor/prelude/functions/import.cc",
+        "src/trace_processor/prelude/functions/import.h",
+        "src/trace_processor/prelude/functions/pprof_functions.cc",
+        "src/trace_processor/prelude/functions/pprof_functions.h",
+        "src/trace_processor/prelude/functions/register_function.cc",
+        "src/trace_processor/prelude/functions/register_function.h",
+        "src/trace_processor/prelude/functions/sqlite3_str_split.cc",
+        "src/trace_processor/prelude/functions/sqlite3_str_split.h",
+        "src/trace_processor/prelude/functions/utils.h",
+        "src/trace_processor/prelude/functions/window_functions.h",
+    ],
+)
+
+# GN target: //src/trace_processor/prelude/operators:operators
+perfetto_filegroup(
+    name = "src_trace_processor_prelude_operators_operators",
+    srcs = [
+        "src/trace_processor/prelude/operators/span_join_operator.cc",
+        "src/trace_processor/prelude/operators/span_join_operator.h",
+        "src/trace_processor/prelude/operators/window_operator.cc",
+        "src/trace_processor/prelude/operators/window_operator.h",
+    ],
+)
+
 # GN target: //src/trace_processor/rpc:httpd
 perfetto_filegroup(
     name = "src_trace_processor_rpc_httpd",
@@ -1392,34 +1813,32 @@
     ],
 )
 
+# GN target: //src/trace_processor/sorter:sorter
+perfetto_filegroup(
+    name = "src_trace_processor_sorter_sorter",
+    srcs = [
+        "src/trace_processor/sorter/trace_sorter.cc",
+        "src/trace_processor/sorter/trace_sorter.h",
+        "src/trace_processor/sorter/trace_sorter_internal.h",
+        "src/trace_processor/sorter/trace_sorter_queue.h",
+    ],
+)
+
 # GN target: //src/trace_processor/sqlite:sqlite
 perfetto_filegroup(
     name = "src_trace_processor_sqlite_sqlite",
     srcs = [
-        "src/trace_processor/sqlite/create_function.cc",
-        "src/trace_processor/sqlite/create_function.h",
-        "src/trace_processor/sqlite/create_function_internal.cc",
-        "src/trace_processor/sqlite/create_function_internal.h",
-        "src/trace_processor/sqlite/create_view_function.cc",
-        "src/trace_processor/sqlite/create_view_function.h",
         "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/register_function.cc",
-        "src/trace_processor/sqlite/register_function.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_utils.cc",
         "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",
     ],
 )
 
@@ -1436,6 +1855,71 @@
     ],
 )
 
+# GN target: //src/trace_processor/stdlib/android/startup:startup
+perfetto_filegroup(
+    name = "src_trace_processor_stdlib_android_startup_startup",
+    srcs = [
+        "src/trace_processor/stdlib/android/startup/internal_startups_maxsdk28.sql",
+        "src/trace_processor/stdlib/android/startup/internal_startups_minsdk29.sql",
+        "src/trace_processor/stdlib/android/startup/internal_startups_minsdk33.sql",
+        "src/trace_processor/stdlib/android/startup/startups.sql",
+    ],
+)
+
+# GN target: //src/trace_processor/stdlib/android:android
+perfetto_filegroup(
+    name = "src_trace_processor_stdlib_android_android",
+    srcs = [
+        "src/trace_processor/stdlib/android/battery.sql",
+        "src/trace_processor/stdlib/android/binder.sql",
+        "src/trace_processor/stdlib/android/process_metadata.sql",
+    ],
+)
+
+# GN target: //src/trace_processor/stdlib/chrome:chrome_sql
+perfetto_filegroup(
+    name = "src_trace_processor_stdlib_chrome_chrome_sql",
+    srcs = [
+        "src/trace_processor/stdlib/chrome/cpu_powerups.sql",
+    ],
+)
+
+# GN target: //src/trace_processor/stdlib/common:common
+perfetto_filegroup(
+    name = "src_trace_processor_stdlib_common_common",
+    srcs = [
+        "src/trace_processor/stdlib/common/counters.sql",
+        "src/trace_processor/stdlib/common/metadata.sql",
+        "src/trace_processor/stdlib/common/percentiles.sql",
+        "src/trace_processor/stdlib/common/slices.sql",
+        "src/trace_processor/stdlib/common/timestamps.sql",
+    ],
+)
+
+# GN target: //src/trace_processor/stdlib/experimental:experimental
+perfetto_filegroup(
+    name = "src_trace_processor_stdlib_experimental_experimental",
+    srcs = [
+        "src/trace_processor/stdlib/experimental/android_broadcast.sql",
+    ],
+)
+
+# GN target: //src/trace_processor/stdlib:gen_amalgamated_stdlib
+perfetto_cc_amalgamated_sql(
+    name = "src_trace_processor_stdlib_gen_amalgamated_stdlib",
+    deps = [
+        ":src_trace_processor_stdlib_android_android",
+        ":src_trace_processor_stdlib_android_startup_startup",
+        ":src_trace_processor_stdlib_chrome_chrome_sql",
+        ":src_trace_processor_stdlib_common_common",
+        ":src_trace_processor_stdlib_experimental_experimental",
+    ],
+    outs = [
+        "src/trace_processor/stdlib/amalgamated_stdlib.h",
+    ],
+    namespace = "stdlib",
+)
+
 # GN target: //src/trace_processor/storage:storage
 perfetto_filegroup(
     name = "src_trace_processor_storage_storage",
@@ -1461,10 +1945,24 @@
         "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/trace_proto_tables.h",
         "src/trace_processor/tables/track_tables.h",
     ],
 )
 
+# GN target: //src/trace_processor/tables:tables_python
+perfetto_cc_tp_tables(
+    name = "src_trace_processor_tables_tables_python",
+    srcs = [
+        "src/trace_processor/tables/android_tables.py",
+        "src/trace_processor/tables/metadata_tables.py",
+    ],
+    outs = [
+        "src/trace_processor/tables/android_tables_py.h",
+        "src/trace_processor/tables/metadata_tables_py.h",
+    ],
+)
+
 # GN target: //src/trace_processor/types:types
 perfetto_filegroup(
     name = "src_trace_processor_types_types",
@@ -1493,6 +1991,15 @@
     ],
 )
 
+# GN target: //src/trace_processor/util:glob
+perfetto_filegroup(
+    name = "src_trace_processor_util_glob",
+    srcs = [
+        "src/trace_processor/util/glob.cc",
+        "src/trace_processor/util/glob.h",
+    ],
+)
+
 # GN target: //src/trace_processor/util:gzip
 perfetto_filegroup(
     name = "src_trace_processor_util_gzip",
@@ -1510,6 +2017,26 @@
     ],
 )
 
+# GN target: //src/trace_processor/util:profile_builder
+perfetto_filegroup(
+    name = "src_trace_processor_util_profile_builder",
+    srcs = [
+        "src/trace_processor/util/annotated_callsites.cc",
+        "src/trace_processor/util/annotated_callsites.h",
+        "src/trace_processor/util/profile_builder.cc",
+        "src/trace_processor/util/profile_builder.h",
+    ],
+)
+
+# GN target: //src/trace_processor/util:proto_profiler
+perfetto_filegroup(
+    name = "src_trace_processor_util_proto_profiler",
+    srcs = [
+        "src/trace_processor/util/proto_profiler.cc",
+        "src/trace_processor/util/proto_profiler.h",
+    ],
+)
+
 # GN target: //src/trace_processor/util:proto_to_args_parser
 perfetto_filegroup(
     name = "src_trace_processor_util_proto_to_args_parser",
@@ -1530,6 +2057,15 @@
     ],
 )
 
+# GN target: //src/trace_processor/util:sql_argument
+perfetto_filegroup(
+    name = "src_trace_processor_util_sql_argument",
+    srcs = [
+        "src/trace_processor/util/sql_argument.cc",
+        "src/trace_processor/util/sql_argument.h",
+    ],
+)
+
 # GN target: //src/trace_processor/util:stack_traces_util
 perfetto_filegroup(
     name = "src_trace_processor_util_stack_traces_util",
@@ -1539,6 +2075,14 @@
     ],
 )
 
+# GN target: //src/trace_processor/util:stdlib
+perfetto_filegroup(
+    name = "src_trace_processor_util_stdlib",
+    srcs = [
+        "src/trace_processor/util/sql_modules.h",
+    ],
+)
+
 # GN target: //src/trace_processor/util:util
 perfetto_filegroup(
     name = "src_trace_processor_util_util",
@@ -1596,15 +2140,6 @@
     ],
 )
 
-# GN target: //src/trace_processor:ftrace_descriptors
-perfetto_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
 perfetto_filegroup(
     name = "src_trace_processor_lib",
@@ -1629,156 +2164,22 @@
     ],
 )
 
-# GN target: //src/trace_processor:storage_full
-perfetto_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/drm_tracker.cc",
-        "src/trace_processor/importers/ftrace/drm_tracker.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",
-        "src/trace_processor/importers/ftrace/ftrace_parser.h",
-        "src/trace_processor/importers/ftrace/ftrace_tokenizer.cc",
-        "src/trace_processor/importers/ftrace/ftrace_tokenizer.h",
-        "src/trace_processor/importers/ftrace/iostat_tracker.cc",
-        "src/trace_processor/importers/ftrace/iostat_tracker.h",
-        "src/trace_processor/importers/ftrace/rss_stat_tracker.cc",
-        "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/ftrace/thread_state_tracker.cc",
-        "src/trace_processor/importers/ftrace/thread_state_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/i2c/i2c_tracker.cc",
-        "src/trace_processor/importers/i2c/i2c_tracker.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/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/frame_timeline_event_parser.cc",
-        "src/trace_processor/importers/proto/frame_timeline_event_parser.h",
-        "src/trace_processor/importers/proto/gpu_event_parser.cc",
-        "src/trace_processor/importers/proto/gpu_event_parser.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_frame_event_parser.cc",
-        "src/trace_processor/importers/proto/graphics_frame_event_parser.h",
-        "src/trace_processor/importers/proto/heap_graph_module.cc",
-        "src/trace_processor/importers/proto/heap_graph_module.h",
-        "src/trace_processor/importers/proto/system_probes_module.cc",
-        "src/trace_processor/importers/proto/system_probes_module.h",
-        "src/trace_processor/importers/proto/system_probes_parser.cc",
-        "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/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",
-    ],
-)
-
 # GN target: //src/trace_processor:storage_minimal
 perfetto_filegroup(
     name = "src_trace_processor_storage_minimal",
     srcs = [
         "src/trace_processor/forwarding_trace_parser.cc",
         "src/trace_processor/forwarding_trace_parser.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.h",
-        "src/trace_processor/importers/fuchsia/fuchsia_trace_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/android_camera_event_module.cc",
-        "src/trace_processor/importers/proto/android_camera_event_module.h",
-        "src/trace_processor/importers/proto/async_track_set_tracker.cc",
-        "src/trace_processor/importers/proto/async_track_set_tracker.h",
-        "src/trace_processor/importers/proto/chrome_string_lookup.cc",
-        "src/trace_processor/importers/proto/chrome_string_lookup.h",
-        "src/trace_processor/importers/proto/chrome_system_probes_module.cc",
-        "src/trace_processor/importers/proto/chrome_system_probes_module.h",
-        "src/trace_processor/importers/proto/chrome_system_probes_parser.cc",
-        "src/trace_processor/importers/proto/chrome_system_probes_parser.h",
-        "src/trace_processor/importers/proto/memory_tracker_snapshot_module.cc",
-        "src/trace_processor/importers/proto/memory_tracker_snapshot_module.h",
-        "src/trace_processor/importers/proto/memory_tracker_snapshot_parser.cc",
-        "src/trace_processor/importers/proto/memory_tracker_snapshot_parser.h",
-        "src/trace_processor/importers/proto/metadata_module.cc",
-        "src/trace_processor/importers/proto/metadata_module.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",
-        "src/trace_processor/importers/proto/proto_trace_parser.cc",
-        "src/trace_processor/importers/proto/proto_trace_parser.h",
-        "src/trace_processor/importers/proto/proto_trace_reader.cc",
-        "src/trace_processor/importers/proto/proto_trace_reader.h",
-        "src/trace_processor/importers/proto/proto_trace_tokenizer.cc",
-        "src/trace_processor/importers/proto/proto_trace_tokenizer.h",
-        "src/trace_processor/importers/proto/statsd_module.cc",
-        "src/trace_processor/importers/proto/statsd_module.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/importers/proto/track_event_tracker.cc",
-        "src/trace_processor/importers/proto/track_event_tracker.h",
-        "src/trace_processor/importers/proto/translation_table_module.cc",
-        "src/trace_processor/importers/proto/translation_table_module.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.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_processor_storage_impl.h",
-        "src/trace_processor/trace_sorter.cc",
-        "src/trace_processor/trace_sorter.h",
-        "src/trace_processor/trace_sorter_queue.h",
         "src/trace_processor/virtual_destructors.cc",
     ],
 )
 
+# GN target: //src/traceconv:gen_cc_trace_descriptor
 perfetto_cc_proto_descriptor(
     name = "src_traceconv_gen_cc_trace_descriptor",
     deps = [
@@ -1913,8 +2314,6 @@
         "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",
@@ -1928,12 +2327,16 @@
         "src/traced/probes/ftrace/ftrace_data_source.cc",
         "src/traced/probes/ftrace/ftrace_data_source.h",
         "src/traced/probes/ftrace/ftrace_metadata.h",
+        "src/traced/probes/ftrace/ftrace_print_filter.cc",
+        "src/traced/probes/ftrace/ftrace_print_filter.h",
         "src/traced/probes/ftrace/ftrace_stats.cc",
         "src/traced/probes/ftrace/ftrace_stats.h",
         "src/traced/probes/ftrace/printk_formats_parser.cc",
         "src/traced/probes/ftrace/printk_formats_parser.h",
         "src/traced/probes/ftrace/proto_translation_table.cc",
         "src/traced/probes/ftrace/proto_translation_table.h",
+        "src/traced/probes/ftrace/vendor_tracepoints.cc",
+        "src/traced/probes/ftrace/vendor_tracepoints.h",
     ],
 )
 
@@ -2552,6 +2955,7 @@
         "protos/perfetto/config/android/android_log_config.proto",
         "protos/perfetto/config/android/android_polled_state_config.proto",
         "protos/perfetto/config/android/android_system_property_config.proto",
+        "protos/perfetto/config/android/network_trace_config.proto",
         "protos/perfetto/config/android/packages_list_config.proto",
     ],
     visibility = [
@@ -3107,7 +3511,6 @@
         "protos/perfetto/metrics/android/simpleperf.proto",
         "protos/perfetto/metrics/android/startup_metric.proto",
         "protos/perfetto/metrics/android/surfaceflinger.proto",
-        "protos/perfetto/metrics/android/sysui_cuj_metrics.proto",
         "protos/perfetto/metrics/android/task_names.proto",
         "protos/perfetto/metrics/android/thread_time_in_state_metric.proto",
         "protos/perfetto/metrics/android/trace_quality.proto",
@@ -3134,6 +3537,7 @@
     name = "protos_perfetto_metrics_chrome_protos",
     srcs = [
         "protos/perfetto/metrics/chrome/all_chrome_metrics.proto",
+        "protos/perfetto/metrics/chrome/args_class_names.proto",
         "protos/perfetto/metrics/chrome/blink_gc_metric.proto",
         "protos/perfetto/metrics/chrome/dropped_frames.proto",
         "protos/perfetto/metrics/chrome/frame_times.proto",
@@ -3209,6 +3613,7 @@
         "protos/perfetto/trace/android/gpu_mem_event.proto",
         "protos/perfetto/trace/android/graphics_frame_event.proto",
         "protos/perfetto/trace/android/initial_display_state.proto",
+        "protos/perfetto/trace/android/network_trace.proto",
         "protos/perfetto/trace/android/packages_list.proto",
     ],
     visibility = [
@@ -3283,10 +3688,12 @@
 perfetto_proto_library(
     name = "protos_perfetto_trace_ftrace_protos",
     srcs = [
+        "protos/perfetto/trace/ftrace/android_fs.proto",
         "protos/perfetto/trace/ftrace/binder.proto",
         "protos/perfetto/trace/ftrace/block.proto",
         "protos/perfetto/trace/ftrace/cgroup.proto",
         "protos/perfetto/trace/ftrace/clk.proto",
+        "protos/perfetto/trace/ftrace/cma.proto",
         "protos/perfetto/trace/ftrace/compaction.proto",
         "protos/perfetto/trace/ftrace/cpuhp.proto",
         "protos/perfetto/trace/ftrace/cros_ec.proto",
@@ -3314,11 +3721,13 @@
         "protos/perfetto/trace/ftrace/kmem.proto",
         "protos/perfetto/trace/ftrace/kvm.proto",
         "protos/perfetto/trace/ftrace/lowmemorykiller.proto",
+        "protos/perfetto/trace/ftrace/lwis.proto",
         "protos/perfetto/trace/ftrace/mali.proto",
         "protos/perfetto/trace/ftrace/mdss.proto",
         "protos/perfetto/trace/ftrace/mm_event.proto",
         "protos/perfetto/trace/ftrace/net.proto",
         "protos/perfetto/trace/ftrace/oom.proto",
+        "protos/perfetto/trace/ftrace/panel.proto",
         "protos/perfetto/trace/ftrace/power.proto",
         "protos/perfetto/trace/ftrace/printk.proto",
         "protos/perfetto/trace/ftrace/raw_syscalls.proto",
@@ -3336,8 +3745,11 @@
         "protos/perfetto/trace/ftrace/tcp.proto",
         "protos/perfetto/trace/ftrace/test_bundle_wrapper.proto",
         "protos/perfetto/trace/ftrace/thermal.proto",
+        "protos/perfetto/trace/ftrace/trusty.proto",
         "protos/perfetto/trace/ftrace/ufs.proto",
         "protos/perfetto/trace/ftrace/v4l2.proto",
+        "protos/perfetto/trace/ftrace/virtio_gpu.proto",
+        "protos/perfetto/trace/ftrace/virtio_video.proto",
         "protos/perfetto/trace/ftrace/vmscan.proto",
         "protos/perfetto/trace/ftrace/workqueue.proto",
     ],
@@ -3416,6 +3828,7 @@
     srcs = [
         "protos/perfetto/trace/clock_snapshot.proto",
         "protos/perfetto/trace/system_info.proto",
+        "protos/perfetto/trace/trace_uuid.proto",
         "protos/perfetto/trace/trigger.proto",
     ],
     visibility = [
@@ -3620,6 +4033,7 @@
 perfetto_proto_library(
     name = "protos_perfetto_trace_processor_protos",
     srcs = [
+        "protos/perfetto/trace_processor/metatrace_categories.proto",
         "protos/perfetto/trace_processor/trace_processor.proto",
     ],
     visibility = [
@@ -3783,6 +4197,7 @@
 perfetto_proto_library(
     name = "protos_perfetto_trace_track_event_protos",
     srcs = [
+        "protos/perfetto/trace/track_event/chrome_active_processes.proto",
         "protos/perfetto/trace/track_event/chrome_application_state_info.proto",
         "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto",
         "protos/perfetto/trace/track_event/chrome_content_settings_event_info.proto",
@@ -3802,6 +4217,7 @@
         "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/range_of_interest.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",
@@ -4119,32 +4535,57 @@
     name = "trace_processor",
     srcs = [
         ":src_kernel_utils_syscall_table",
-        ":src_trace_processor_analysis_analysis",
         ":src_trace_processor_db_db",
         ":src_trace_processor_dynamic_dynamic",
         ":src_trace_processor_export_json",
-        ":src_trace_processor_ftrace_descriptors",
         ":src_trace_processor_importers_android_bugreport_android_bugreport",
         ":src_trace_processor_importers_common_common",
+        ":src_trace_processor_importers_common_parser_types",
+        ":src_trace_processor_importers_common_trace_parser_hdr",
+        ":src_trace_processor_importers_ftrace_ftrace_descriptors",
+        ":src_trace_processor_importers_ftrace_full",
+        ":src_trace_processor_importers_ftrace_minimal",
+        ":src_trace_processor_importers_fuchsia_fuchsia_record",
+        ":src_trace_processor_importers_fuchsia_full",
+        ":src_trace_processor_importers_fuchsia_minimal",
+        ":src_trace_processor_importers_gzip_full",
+        ":src_trace_processor_importers_i2c_full",
+        ":src_trace_processor_importers_json_full",
+        ":src_trace_processor_importers_json_minimal",
         ":src_trace_processor_importers_memory_tracker_graph_processor",
-        ":src_trace_processor_importers_proto_storage_full",
-        ":src_trace_processor_importers_proto_storage_minimal",
+        ":src_trace_processor_importers_ninja_ninja",
+        ":src_trace_processor_importers_proto_full",
+        ":src_trace_processor_importers_proto_minimal",
+        ":src_trace_processor_importers_proto_packet_sequence_state_generation_hdr",
+        ":src_trace_processor_importers_proto_proto_importer_module",
+        ":src_trace_processor_importers_syscalls_full",
+        ":src_trace_processor_importers_systrace_full",
+        ":src_trace_processor_importers_systrace_systrace_line",
+        ":src_trace_processor_importers_systrace_systrace_parser",
         ":src_trace_processor_lib",
         ":src_trace_processor_metatrace",
         ":src_trace_processor_metrics_metrics",
+        ":src_trace_processor_prelude_functions_functions",
+        ":src_trace_processor_prelude_operators_operators",
+        ":src_trace_processor_sorter_sorter",
         ":src_trace_processor_sqlite_sqlite",
         ":src_trace_processor_sqlite_sqlite_minimal",
-        ":src_trace_processor_storage_full",
         ":src_trace_processor_storage_minimal",
         ":src_trace_processor_storage_storage",
         ":src_trace_processor_tables_tables",
+        ":src_trace_processor_tables_tables_python",
         ":src_trace_processor_types_types",
         ":src_trace_processor_util_descriptors",
+        ":src_trace_processor_util_glob",
         ":src_trace_processor_util_gzip",
         ":src_trace_processor_util_interned_message_view",
+        ":src_trace_processor_util_profile_builder",
+        ":src_trace_processor_util_proto_profiler",
         ":src_trace_processor_util_proto_to_args_parser",
         ":src_trace_processor_util_protozero_to_text",
+        ":src_trace_processor_util_sql_argument",
         ":src_trace_processor_util_stack_traces_util",
+        ":src_trace_processor_util_stdlib",
         ":src_trace_processor_util_util",
         ":src_trace_processor_util_zip_reader",
         ":src_trace_processor_views_views",
@@ -4152,9 +4593,11 @@
     hdrs = [
         ":include_perfetto_base_base",
         ":include_perfetto_ext_base_base",
+        ":include_perfetto_ext_trace_processor_demangle",
         ":include_perfetto_ext_trace_processor_export_json",
         ":include_perfetto_ext_trace_processor_importers_memory_tracker_memory_tracker",
         ":include_perfetto_ext_traced_sys_stats_counters",
+        ":include_perfetto_protozero_protozero",
         ":include_perfetto_public_abi_base",
         ":include_perfetto_public_base",
         ":include_perfetto_trace_processor_basic_types",
@@ -4198,21 +4641,23 @@
                ":protos_perfetto_trace_system_info_zero",
                ":protos_perfetto_trace_track_event_zero",
                ":protos_perfetto_trace_translation_zero",
+               ":protos_third_party_pprof_zero",
                ":protozero",
                ":src_base_base",
                ":src_trace_processor_containers_containers",
-               ":src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
-               ":src_trace_processor_importers_gen_cc_config_descriptor",
-               ":src_trace_processor_importers_gen_cc_statsd_atoms_descriptor",
-               ":src_trace_processor_importers_gen_cc_track_event_descriptor",
+               ":src_trace_processor_importers_proto_gen_cc_chrome_track_event_descriptor",
+               ":src_trace_processor_importers_proto_gen_cc_config_descriptor",
+               ":src_trace_processor_importers_proto_gen_cc_statsd_atoms_descriptor",
+               ":src_trace_processor_importers_proto_gen_cc_trace_descriptor",
+               ":src_trace_processor_importers_proto_gen_cc_track_event_descriptor",
                ":src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
                ":src_trace_processor_metrics_gen_cc_metrics_descriptor",
+               ":src_trace_processor_metrics_sql_gen_amalgamated_sql_metrics",
+               ":src_trace_processor_stdlib_gen_amalgamated_stdlib",
            ] + PERFETTO_CONFIG.deps.jsoncpp +
            PERFETTO_CONFIG.deps.sqlite +
            PERFETTO_CONFIG.deps.sqlite_ext_percentile +
-           PERFETTO_CONFIG.deps.zlib + [
-        ":cc_amalgamated_sql_metrics",
-    ] +
+           PERFETTO_CONFIG.deps.zlib +
            PERFETTO_CONFIG.deps.demangle_wrapper,
     linkstatic = True,
 )
@@ -4223,6 +4668,7 @@
     srcs = [
         ":include_perfetto_base_base",
         ":include_perfetto_ext_base_base",
+        ":include_perfetto_ext_trace_processor_demangle",
         ":include_perfetto_ext_trace_processor_export_json",
         ":include_perfetto_ext_trace_processor_importers_memory_tracker_memory_tracker",
         ":include_perfetto_ext_traced_sys_stats_counters",
@@ -4237,34 +4683,59 @@
         ":src_profiling_symbolizer_symbolize_database",
         ":src_profiling_symbolizer_symbolizer",
         ":src_protozero_proto_ring_buffer",
-        ":src_trace_processor_analysis_analysis",
         ":src_trace_processor_db_db",
         ":src_trace_processor_dynamic_dynamic",
         ":src_trace_processor_export_json",
-        ":src_trace_processor_ftrace_descriptors",
         ":src_trace_processor_importers_android_bugreport_android_bugreport",
         ":src_trace_processor_importers_common_common",
+        ":src_trace_processor_importers_common_parser_types",
+        ":src_trace_processor_importers_common_trace_parser_hdr",
+        ":src_trace_processor_importers_ftrace_ftrace_descriptors",
+        ":src_trace_processor_importers_ftrace_full",
+        ":src_trace_processor_importers_ftrace_minimal",
+        ":src_trace_processor_importers_fuchsia_fuchsia_record",
+        ":src_trace_processor_importers_fuchsia_full",
+        ":src_trace_processor_importers_fuchsia_minimal",
+        ":src_trace_processor_importers_gzip_full",
+        ":src_trace_processor_importers_i2c_full",
+        ":src_trace_processor_importers_json_full",
+        ":src_trace_processor_importers_json_minimal",
         ":src_trace_processor_importers_memory_tracker_graph_processor",
-        ":src_trace_processor_importers_proto_storage_full",
-        ":src_trace_processor_importers_proto_storage_minimal",
+        ":src_trace_processor_importers_ninja_ninja",
+        ":src_trace_processor_importers_proto_full",
+        ":src_trace_processor_importers_proto_minimal",
+        ":src_trace_processor_importers_proto_packet_sequence_state_generation_hdr",
+        ":src_trace_processor_importers_proto_proto_importer_module",
+        ":src_trace_processor_importers_syscalls_full",
+        ":src_trace_processor_importers_systrace_full",
+        ":src_trace_processor_importers_systrace_systrace_line",
+        ":src_trace_processor_importers_systrace_systrace_parser",
         ":src_trace_processor_lib",
         ":src_trace_processor_metatrace",
         ":src_trace_processor_metrics_metrics",
+        ":src_trace_processor_prelude_functions_functions",
+        ":src_trace_processor_prelude_operators_operators",
         ":src_trace_processor_rpc_httpd",
         ":src_trace_processor_rpc_rpc",
+        ":src_trace_processor_sorter_sorter",
         ":src_trace_processor_sqlite_sqlite",
         ":src_trace_processor_sqlite_sqlite_minimal",
-        ":src_trace_processor_storage_full",
         ":src_trace_processor_storage_minimal",
         ":src_trace_processor_storage_storage",
         ":src_trace_processor_tables_tables",
+        ":src_trace_processor_tables_tables_python",
         ":src_trace_processor_types_types",
         ":src_trace_processor_util_descriptors",
+        ":src_trace_processor_util_glob",
         ":src_trace_processor_util_gzip",
         ":src_trace_processor_util_interned_message_view",
+        ":src_trace_processor_util_profile_builder",
+        ":src_trace_processor_util_proto_profiler",
         ":src_trace_processor_util_proto_to_args_parser",
         ":src_trace_processor_util_protozero_to_text",
+        ":src_trace_processor_util_sql_argument",
         ":src_trace_processor_util_stack_traces_util",
+        ":src_trace_processor_util_stdlib",
         ":src_trace_processor_util_util",
         ":src_trace_processor_util_zip_reader",
         ":src_trace_processor_views_views",
@@ -4309,25 +4780,27 @@
                ":protos_perfetto_trace_system_info_zero",
                ":protos_perfetto_trace_track_event_zero",
                ":protos_perfetto_trace_translation_zero",
+               ":protos_third_party_pprof_zero",
                ":protozero",
                ":src_base_base",
                ":src_base_http_http",
                ":src_base_version",
                ":src_trace_processor_containers_containers",
-               ":src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
-               ":src_trace_processor_importers_gen_cc_config_descriptor",
-               ":src_trace_processor_importers_gen_cc_statsd_atoms_descriptor",
-               ":src_trace_processor_importers_gen_cc_track_event_descriptor",
+               ":src_trace_processor_importers_proto_gen_cc_chrome_track_event_descriptor",
+               ":src_trace_processor_importers_proto_gen_cc_config_descriptor",
+               ":src_trace_processor_importers_proto_gen_cc_statsd_atoms_descriptor",
+               ":src_trace_processor_importers_proto_gen_cc_trace_descriptor",
+               ":src_trace_processor_importers_proto_gen_cc_track_event_descriptor",
                ":src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
                ":src_trace_processor_metrics_gen_cc_metrics_descriptor",
+               ":src_trace_processor_metrics_sql_gen_amalgamated_sql_metrics",
+               ":src_trace_processor_stdlib_gen_amalgamated_stdlib",
            ] + PERFETTO_CONFIG.deps.jsoncpp +
            PERFETTO_CONFIG.deps.linenoise +
            PERFETTO_CONFIG.deps.protobuf_full +
            PERFETTO_CONFIG.deps.sqlite +
            PERFETTO_CONFIG.deps.sqlite_ext_percentile +
-           PERFETTO_CONFIG.deps.zlib + [
-        ":cc_amalgamated_sql_metrics",
-    ] +
+           PERFETTO_CONFIG.deps.zlib +
            PERFETTO_CONFIG.deps.demangle_wrapper,
 )
 
@@ -4401,6 +4874,7 @@
     srcs = [
         ":include_perfetto_base_base",
         ":include_perfetto_ext_base_base",
+        ":include_perfetto_ext_trace_processor_demangle",
         ":include_perfetto_ext_trace_processor_export_json",
         ":include_perfetto_ext_trace_processor_importers_memory_tracker_memory_tracker",
         ":include_perfetto_ext_traced_sys_stats_counters",
@@ -4416,32 +4890,57 @@
         ":src_profiling_symbolizer_symbolize_database",
         ":src_profiling_symbolizer_symbolizer",
         ":src_protozero_proto_ring_buffer",
-        ":src_trace_processor_analysis_analysis",
         ":src_trace_processor_db_db",
         ":src_trace_processor_dynamic_dynamic",
         ":src_trace_processor_export_json",
-        ":src_trace_processor_ftrace_descriptors",
         ":src_trace_processor_importers_android_bugreport_android_bugreport",
         ":src_trace_processor_importers_common_common",
+        ":src_trace_processor_importers_common_parser_types",
+        ":src_trace_processor_importers_common_trace_parser_hdr",
+        ":src_trace_processor_importers_ftrace_ftrace_descriptors",
+        ":src_trace_processor_importers_ftrace_full",
+        ":src_trace_processor_importers_ftrace_minimal",
+        ":src_trace_processor_importers_fuchsia_fuchsia_record",
+        ":src_trace_processor_importers_fuchsia_full",
+        ":src_trace_processor_importers_fuchsia_minimal",
+        ":src_trace_processor_importers_gzip_full",
+        ":src_trace_processor_importers_i2c_full",
+        ":src_trace_processor_importers_json_full",
+        ":src_trace_processor_importers_json_minimal",
         ":src_trace_processor_importers_memory_tracker_graph_processor",
-        ":src_trace_processor_importers_proto_storage_full",
-        ":src_trace_processor_importers_proto_storage_minimal",
+        ":src_trace_processor_importers_ninja_ninja",
+        ":src_trace_processor_importers_proto_full",
+        ":src_trace_processor_importers_proto_minimal",
+        ":src_trace_processor_importers_proto_packet_sequence_state_generation_hdr",
+        ":src_trace_processor_importers_proto_proto_importer_module",
+        ":src_trace_processor_importers_syscalls_full",
+        ":src_trace_processor_importers_systrace_full",
+        ":src_trace_processor_importers_systrace_systrace_line",
+        ":src_trace_processor_importers_systrace_systrace_parser",
         ":src_trace_processor_lib",
         ":src_trace_processor_metatrace",
         ":src_trace_processor_metrics_metrics",
+        ":src_trace_processor_prelude_functions_functions",
+        ":src_trace_processor_prelude_operators_operators",
+        ":src_trace_processor_sorter_sorter",
         ":src_trace_processor_sqlite_sqlite",
         ":src_trace_processor_sqlite_sqlite_minimal",
-        ":src_trace_processor_storage_full",
         ":src_trace_processor_storage_minimal",
         ":src_trace_processor_storage_storage",
         ":src_trace_processor_tables_tables",
+        ":src_trace_processor_tables_tables_python",
         ":src_trace_processor_types_types",
         ":src_trace_processor_util_descriptors",
+        ":src_trace_processor_util_glob",
         ":src_trace_processor_util_gzip",
         ":src_trace_processor_util_interned_message_view",
+        ":src_trace_processor_util_profile_builder",
+        ":src_trace_processor_util_proto_profiler",
         ":src_trace_processor_util_proto_to_args_parser",
         ":src_trace_processor_util_protozero_to_text",
+        ":src_trace_processor_util_sql_argument",
         ":src_trace_processor_util_stack_traces_util",
+        ":src_trace_processor_util_stdlib",
         ":src_trace_processor_util_util",
         ":src_trace_processor_util_zip_reader",
         ":src_trace_processor_views_views",
@@ -4492,19 +4991,20 @@
                ":src_base_base",
                ":src_base_version",
                ":src_trace_processor_containers_containers",
-               ":src_trace_processor_importers_gen_cc_chrome_track_event_descriptor",
-               ":src_trace_processor_importers_gen_cc_config_descriptor",
-               ":src_trace_processor_importers_gen_cc_statsd_atoms_descriptor",
-               ":src_trace_processor_importers_gen_cc_track_event_descriptor",
+               ":src_trace_processor_importers_proto_gen_cc_chrome_track_event_descriptor",
+               ":src_trace_processor_importers_proto_gen_cc_config_descriptor",
+               ":src_trace_processor_importers_proto_gen_cc_statsd_atoms_descriptor",
+               ":src_trace_processor_importers_proto_gen_cc_trace_descriptor",
+               ":src_trace_processor_importers_proto_gen_cc_track_event_descriptor",
                ":src_trace_processor_metrics_gen_cc_all_chrome_metrics_descriptor",
                ":src_trace_processor_metrics_gen_cc_metrics_descriptor",
+               ":src_trace_processor_metrics_sql_gen_amalgamated_sql_metrics",
+               ":src_trace_processor_stdlib_gen_amalgamated_stdlib",
                ":src_traceconv_gen_cc_trace_descriptor",
            ] + PERFETTO_CONFIG.deps.jsoncpp +
            PERFETTO_CONFIG.deps.sqlite +
            PERFETTO_CONFIG.deps.sqlite_ext_percentile +
-           PERFETTO_CONFIG.deps.zlib + [
-        ":cc_amalgamated_sql_metrics",
-    ] +
+           PERFETTO_CONFIG.deps.zlib +
            PERFETTO_CONFIG.deps.demangle_wrapper,
 )
 
@@ -4550,17 +5050,12 @@
     includes = [build_config_dir_],
 )
 
-perfetto_cc_library(
-    name = "cc_amalgamated_sql_metrics",
-    hdrs = ["src/trace_processor/metrics/sql/amalgamated_sql_metrics.h"],
-)
-
 perfetto_py_binary(
-    name = "gen_amalgamated_sql_metrics_py",
+    name = "gen_amalgamated_sql_py",
     srcs = [
-        "tools/gen_amalgamated_sql_metrics.py",
+        "tools/gen_amalgamated_sql.py",
     ],
-    main = "tools/gen_amalgamated_sql_metrics.py",
+    main = "tools/gen_amalgamated_sql.py",
     python_version = "PY3",
 )
 
@@ -4589,6 +5084,22 @@
     ],
 )
 
+perfetto_go_proto_library(
+    name = "protos_perfetto_metrics_protos_go_proto",
+    visibility = PERFETTO_CONFIG.go_proto_library_visibility,
+    deps = [
+        ":protos_perfetto_metrics_protos",
+    ],
+)
+
+perfetto_go_proto_library(
+    name = "protos_perfetto_metrics_android_protos_go_proto",
+    visibility = PERFETTO_CONFIG.go_proto_library_visibility,
+    deps = [
+        ":protos_perfetto_metrics_android_protos",
+    ],
+)
+
 # This is overridden in google internal builds via
 # PERFETTO_CONFIG.deps.version_header (see perfetto_cfg.bzl).
 perfetto_cc_library(
diff --git a/BUILD.extras b/BUILD.extras
index 85c72c3..251568e 100644
--- a/BUILD.extras
+++ b/BUILD.extras
@@ -8,17 +8,12 @@
     includes = [build_config_dir_],
 )
 
-perfetto_cc_library(
-    name = "cc_amalgamated_sql_metrics",
-    hdrs = ["src/trace_processor/metrics/sql/amalgamated_sql_metrics.h"],
-)
-
 perfetto_py_binary(
-    name = "gen_amalgamated_sql_metrics_py",
+    name = "gen_amalgamated_sql_py",
     srcs = [
-        "tools/gen_amalgamated_sql_metrics.py",
+        "tools/gen_amalgamated_sql.py",
     ],
-    main = "tools/gen_amalgamated_sql_metrics.py",
+    main = "tools/gen_amalgamated_sql.py",
     python_version = "PY3",
 )
 
@@ -47,6 +42,22 @@
     ],
 )
 
+perfetto_go_proto_library(
+    name = "protos_perfetto_metrics_protos_go_proto",
+    visibility = PERFETTO_CONFIG.go_proto_library_visibility,
+    deps = [
+        ":protos_perfetto_metrics_protos",
+    ],
+)
+
+perfetto_go_proto_library(
+    name = "protos_perfetto_metrics_android_protos_go_proto",
+    visibility = PERFETTO_CONFIG.go_proto_library_visibility,
+    deps = [
+        ":protos_perfetto_metrics_android_protos",
+    ],
+)
+
 # This is overridden in google internal builds via
 # PERFETTO_CONFIG.deps.version_header (see perfetto_cfg.bzl).
 perfetto_cc_library(
diff --git a/BUILD.gn b/BUILD.gn
index dbdda00..0ad78da 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -82,10 +82,12 @@
 }
 
 if (enable_perfetto_tools) {
-  all_targets += [
-    "src/tools",
-    "src/websocket_bridge",
-  ]
+  all_targets += [ "src/tools" ]
+
+  # Windows does not have an IPC implementation.
+  if (!is_win) {
+    all_targets += [ "src/websocket_bridge" ]
+  }
 }
 
 if (enable_perfetto_unittests) {
@@ -148,6 +150,9 @@
   all_targets += [
     "test/configs",
 
+    # Used to evaluate the Python folder for Bazel script generation.
+    "python:trace_processor_py",
+
     # For syntax-checking the protos.
     "protos/perfetto/trace:merged_trace_lite",
 
@@ -171,6 +176,7 @@
 # catch refactoring breakages earlier without having to wait for treehugger.
 if (is_android && (perfetto_build_standalone || perfetto_build_with_android)) {
   all_targets += [ "test/cts:perfetto_cts_deps" ]
+  all_targets += [ "test/vts:perfetto_vts_deps" ]
 }
 
 group("all") {
@@ -200,6 +206,12 @@
   }
 }
 
+if (enable_perfetto_site) {
+  group("site") {
+    deps = [ "infra/perfetto.dev:site" ]
+  }
+}
+
 # In Android builds, we build the code of traced and traced_probes in one shared
 # library that exposes one xxx_main() for each. The executables themselves are
 # tiny shells that just invoke their own entry point into the library.
diff --git a/CHANGELOG b/CHANGELOG
index e677ff1..ef4b9fc 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,16 +1,89 @@
 Unreleased:
   Tracing service and probes:
-    *
+    * Added an explicit TraceUuid packet. The tracing service now always
+      generates a UUID, even if TraceConfig.trace_uuid_msb/lsb is empty.
   Trace Processor:
     *
   UI:
     *
   SDK:
+    * Add perfetto::Tracing::ActivateTriggers() function.
+    * Made it possible to declare track event categories in a C++ namespace
+      with PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE, allowing multiple category
+      sets to be used in one same translation unit. Correspondingly, the
+      PERFETTO_COMPONENT_EXPORT and PERFETTO_TRACK_EVENT_NAMESPACE macros have
+      been deprecated in favor of this new functionality.
+    * Deprecated the PERFETTO_COMPONENT_EXPORT macro in favor of
+      PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE_WITH_ATTRS.
+    * Added TracingInitArgs::enable_system_consumer configuration option, that
+      allows the linker to discard the consumer IPC, if not required.
+
+
+v31.0 - 2022-11-10:
+  Tracing service and probes:
+    * Added support for collecting Android Trusty ftrace events.
+    * Fixed resetting syscall filter when recording selected syscalls.
+  Trace Processor:
+    * Improved error messages on SQL syntax errors.
+    * Improved performance of queries containing GLOB. Handling of GLOB
+      constraints now happens inside trace processor instead of SQLite.
+    * Added support for parsing Android Trusty ftrace events.
+  UI:
+    * Added support for metatracing UI code and integrate with trace processor
+      metatracing.
+    * Added support for scrolling to a time region using the postMessage API.
+    * Enabled Pivot table functionality by default.
+    * Fixed downloading of Java heap profiles.
+  SDK:
+    * Switched to require C++17 by default. A time-limited opt-out exists but
+      is planned to be removed in v34. Please contact us at
+      perfetto-dev@googlegroups.com if you have thoughts or concerns on this
+      move.
+
+
+v30.0 - 2022-10-06:
+  Trace Processor:
+    * Fixed parsing of "R+" (preempted) and "I" (idle kernel thread) end states
+      of sched_switch events, collected on Linux kernels v4.14 and above.
+      Previously, preemption was not recognised, and idle was reported as
+      "x" (task dead). See commit c60a630cfe0.
+    * Add support for parsing sys_write syscalls.
+    * Remove the thread_slice table: all columns have moved to the slice table
+      and thread_slice exists as a view for backwards compatibility. This view
+      will also be removed in the future
+    * Add Base64 encode SQL function.
+    * Add support for importing function graph ftrace events.
+    * Add support for importing V4L2 ftrace events.
+    * Add support for importing virtio-video ftrace events.
+  UI:
+    * Fix downloading profiles from flamegraphs.
+    * Enable Pivot table support by default.
+  SDK:
+    * Add support for disallowing concurrent tracing sessions.
+
+
+v29.0 - 2022-09-06:
+  Tracing service and probes:
+    * Add support for only tracing selected syscalls. By selecting only syscalls
+      of interest, usage of the trace buffer and performance impact on device
+      is reduced.
+    * Add support for parsing DSI ftrace events.
+  Trace Processor:
+    * Make calling NotifyEndOfFile more than once an error: this was deprecated
+      in v28. Flush should be used instead for all by the final call.
+    * Add parsing and ingestion for V4L2 events.
+    * Upgraded SQLite to 3.39.2.
+  UI:
+    * Add support for searching Android log events.
+    * Group kernel wakelock tracks into a single track group.
+  SDK:
     * Added support for startup tracing. Tracing can be started in an app
       before it connects to the tracing service.
       The data sources which are started for startup tracing, will
       be automatically adopted to normal tracing session once we start
       normal tracing session.
+    * Added the |first_packet_on_sequence| boolean which is set the first packet
+      emitted on each TraceWriter.
 
 
 v28.0 - 2022-08-02:
diff --git a/OWNERS b/OWNERS
index 0da623c..5c57ecb 100644
--- a/OWNERS
+++ b/OWNERS
@@ -11,6 +11,7 @@
 
 # Trace Processor, metrics, infra.
 lalitm@google.com
+mayzner@google.com
 
 # Callstack / memory profilers, traced_probes & Linux internals.
 ddiproietto@google.com
@@ -21,6 +22,9 @@
 nuskos@google.com
 oysteine@google.com
 
+# UI, Chromium-related metrics and simpler trace processor changes.
+altimin@google.com
+
 # Most Android-related metrics.
 ilkos@google.com
 
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 5307e5a..6682fec 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -17,6 +17,8 @@
 import subprocess
 import time
 
+USE_PYTHON3 = True
+
 
 def RunAndReportIfLong(func, *args, **kargs):
   start = time.time()
@@ -42,7 +44,7 @@
             '.*[.]json$',
             '.*[.]sql$',
             '.*[.]out$',
-            'test/trace_processor/.*/index$',
+            'test/trace_processor/.*/tests.*$',
             '(.*/)?BUILD$',
             'WORKSPACE',
             '.*/Makefile$',
@@ -74,6 +76,7 @@
   results += RunAndReportIfLong(CheckMergedTraceConfigProto, input, output)
   results += RunAndReportIfLong(CheckProtoEventList, input, output)
   results += RunAndReportIfLong(CheckBannedCpp, input, output)
+  results += RunAndReportIfLong(CheckSqlModules, input, output)
   results += RunAndReportIfLong(CheckSqlMetrics, input, output)
   results += RunAndReportIfLong(CheckTestData, input, output)
   results += RunAndReportIfLong(CheckAmalgamatedPythonTools, input, output)
@@ -266,6 +269,20 @@
   return []
 
 
+def CheckSqlModules(input_api, output_api):
+  tool = 'tools/check_sql_modules.py'
+
+  def file_filter(x):
+    return input_api.FilterSourceFile(
+        x, files_to_check=['src/trace_processor/stdlib/.*[.]sql$', tool])
+
+  if not input_api.AffectedSourceFiles(file_filter):
+    return []
+  if subprocess.call([tool]):
+    return [output_api.PresubmitError(tool + ' failed')]
+  return []
+
+
 def CheckSqlMetrics(input_api, output_api):
   tool = 'tools/check_sql_metrics.py'
 
diff --git a/bazel/deps.bzl b/bazel/deps.bzl
index a15ecfa..0b63d8c 100644
--- a/bazel/deps.bzl
+++ b/bazel/deps.bzl
@@ -35,18 +35,18 @@
     _add_repo_if_not_existing(
         http_archive,
         name = "perfetto_dep_sqlite",
-        url = "https://storage.googleapis.com/perfetto/sqlite-amalgamation-3350400.zip",
-        sha256 = "f3bf0df69f5de0675196f4644e05d07dbc698d674dc563a12eff17d5b215cdf5",
-        strip_prefix = "sqlite-amalgamation-3350400",
+        url = "https://storage.googleapis.com/perfetto/sqlite-amalgamation-3390200.zip",
+        sha256 = "87775784f8b22d0d0f1d7811870d39feaa7896319c7c20b849a4181c5a50609b",
+        strip_prefix = "sqlite-amalgamation-3390200",
         build_file = "//bazel:sqlite.BUILD",
     )
 
     _add_repo_if_not_existing(
         http_archive,
         name = "perfetto_dep_sqlite_src",
-        url = "https://storage.googleapis.com/perfetto/sqlite-src-3250300.zip",
-        sha256 = "c7922bc840a799481050ee9a76e679462da131adba1814687f05aa5c93766421",
-        strip_prefix = "sqlite-src-3250300",
+        url = "https://storage.googleapis.com/perfetto/sqlite-src-3390200.zip",
+        sha256 = "e933d77000f45f3fbc8605f0050586a3013505a8de9b44032bd00ed72f1586f0",
+        strip_prefix = "sqlite-src-3390200",
         build_file = "//bazel:sqlite.BUILD",
     )
 
diff --git a/bazel/rules.bzl b/bazel/rules.bzl
index c2a649b..7becc96 100644
--- a/bazel/rules.bzl
+++ b/bazel/rules.bzl
@@ -22,7 +22,7 @@
 def default_cc_args():
     return {
         "deps": PERFETTO_CONFIG.deps.build_config,
-        "copts": [
+        "copts": PERFETTO_CONFIG.default_copts + [
             "-Wno-pragma-system-header-outside-header",
         ],
         "includes": ["include"],
@@ -280,6 +280,73 @@
         **kwargs
     )
 
+def perfetto_cc_amalgamated_sql(name, deps, outs, namespace, **kwargs):
+    if PERFETTO_CONFIG.root[:2] != "//":
+        fail("Expected PERFETTO_CONFIG.root to start with //")
+
+    cmd = [
+        "$(location gen_amalgamated_sql_py)",
+        "--namespace",
+        namespace,
+        "--cpp-out=$@",
+        "$(SRCS)",
+    ]
+
+    perfetto_genrule(
+        name = name + "_gen",
+        cmd = " ".join(cmd),
+        exec_tools = [
+            ":gen_amalgamated_sql_py",
+        ],
+        srcs = deps,
+        outs = outs,
+    )
+
+    perfetto_cc_library(
+        name = name,
+        hdrs = [":" + name + "_gen"],
+        **kwargs,
+    )
+
+def perfetto_cc_tp_tables(name, srcs, outs, **kwargs):
+    if PERFETTO_CONFIG.root == "//":
+      python_path = PERFETTO_CONFIG.root + "python"
+    else:
+      python_path = PERFETTO_CONFIG.root + "/python"
+
+    perfetto_py_binary(
+        name = name + "_tool",
+        deps = [
+            python_path + ":trace_processor_table_generator",
+        ],
+        srcs = srcs + [
+            "tools/gen_tp_table_headers.py",
+        ],
+        main = "tools/gen_tp_table_headers.py",
+        python_version = "PY3",
+    )
+
+    cmd = ["$(location " + name + "_tool)"]
+    cmd += ["--gen-dir", "$(RULEDIR)"]
+    cmd += ["--inputs", "$(SRCS)"]
+    cmd += ["--outputs", "$(OUTS)"]
+
+    perfetto_genrule(
+        name = name + "_gen",
+        cmd = " ".join(cmd),
+        exec_tools = [
+            ":" + name + "_tool",
+        ],
+        srcs = srcs,
+        outs = outs,
+    )
+
+    perfetto_filegroup(
+        name = name,
+        srcs = [":" + name + "_gen"],
+        **kwargs,
+    )
+
 # +----------------------------------------------------------------------------+
 # | Misc utility functions                                                     |
 # +----------------------------------------------------------------------------+
diff --git a/bazel/standalone/perfetto_cfg.bzl b/bazel/standalone/perfetto_cfg.bzl
index d1b77e4..e58b5aa 100644
--- a/bazel/standalone/perfetto_cfg.bzl
+++ b/bazel/standalone/perfetto_cfg.bzl
@@ -40,6 +40,10 @@
         # internal builds.
         version_header = ["//:cc_perfetto_version_header"],
 
+        # Target exposing platform-specific functionality for base. This is
+        # overriden in Google internal builds.
+        base_platform = ["//:perfetto_base_default_platform"],
+
         zlib = ["@perfetto_dep_zlib//:zlib"],
         jsoncpp = ["@perfetto_dep_jsoncpp//:jsoncpp"],
         linenoise = ["@perfetto_dep_linenoise//:linenoise"],
@@ -125,4 +129,9 @@
 
         go_proto_library = None,
     ),
+
+    # The default copts which we use to compile C++ code.
+    default_copts = [
+        "-std=c++17",
+    ]
 )
diff --git a/buildtools/BUILD.gn b/buildtools/BUILD.gn
index d2bc73c..5eced89 100644
--- a/buildtools/BUILD.gn
+++ b/buildtools/BUILD.gn
@@ -140,7 +140,10 @@
       "-Wno-inconsistent-missing-override",
     ]
   } else if (!is_win) {  # implies gcc
-    cflags += [ "-Wno-return-type" ]
+    cflags += [
+      "-Wno-return-type",
+      "-Wno-stringop-overread",
+    ]
   }
   if (is_win) {
     cflags += [ "/W0" ]
@@ -580,35 +583,41 @@
 }  # host_toolchain
 
 if (use_custom_libcxx) {
-  # Config applied to both libc++ and libc++abi targets below.
-  config("libc++config") {
-    visibility = _buildtools_visibility
-    defines = [
-      "LIBCXX_BUILDING_LIBCXXABI",
-      "_LIBCXXABI_NO_EXCEPTIONS",
-      "_LIBCPP_OVERRIDABLE_FUNC_VIS=__attribute__((__visibility__(\"default\")))",
+  config("libunwind_config") {
+    defines = [ "_LIBUNWIND_IS_NATIVE_ONLY" ]
+    cflags = [
+      "-fstrict-aliasing",
+      "-fPIC",
+
+      # ValueAsBitPattern in Unwind-EHABI.cpp is only used on Debug builds.
+      "-Wno-unused-function",
+
+      # libunwind expects to be compiled with unwind tables so it can
+      # unwind its own frames.
+      "-funwind-tables",
     ]
-    cflags = [ "-fstrict-aliasing" ]
   }
 
   source_set("libunwind") {
     visibility = _buildtools_visibility
-    cflags = [
-      # libunwind expects to be compiled with unwind tables so it can
-      # unwind its own frames.
-      "-funwind-tables",
-      "-fstrict-aliasing",
-    ]
     sources = [
+      # C++ sources
       "libunwind/src/Unwind-EHABI.cpp",
+      "libunwind/src/libunwind.cpp",
+
+      # C sources
       "libunwind/src/Unwind-sjlj.c",
       "libunwind/src/UnwindLevel1-gcc-ext.c",
       "libunwind/src/UnwindLevel1.c",
+
+      # ASM sources
       "libunwind/src/UnwindRegistersRestore.S",
       "libunwind/src/UnwindRegistersSave.S",
-      "libunwind/src/libunwind.cpp",
     ]
-    include_dirs = [ "libunwind/include" ]
+    include_dirs = [
+      "libunwind/include",
+      "libunwind/src",
+    ]
     configs -= [
       "//gn/standalone:extra_warnings",
       "//gn/standalone:no_exceptions",
@@ -620,24 +629,52 @@
       "//gn/standalone/sanitizers:sanitizers_cflags",
     ]
     configs += [
-      ":libc++config",
       "//gn/standalone/sanitizers:sanitizer_options_link_helper",
+
+      ":libunwind_config",
     ]
   }
 
+  # Config applied to both libc++ and libc++abi targets below.
+  config("libc++config") {
+    visibility = _buildtools_visibility
+    cflags = [ "-fstrict-aliasing" ]
+    if (is_win) {
+      cflags += [
+        # libc++ wants to redefine the macros WIN32_LEAN_AND_MEAN and
+        # _CRT_RAND_S in its implementation.
+        "-Wno-macro-redefined",
+      ]
+    } else {
+      cflags += [ "-fPIC" ]
+    }
+    configs = [ "//gn/standalone:c++20" ]
+    defines = [ "_LIBCPP_BUILDING_LIBRARY" ]
+  }
+
   source_set("libc++abi") {
     visibility = _buildtools_visibility
+
+    # Fuchsia builds don't link against any libraries that provide stack
+    # unwinding symbols, unlike Linux does with glibc (same applies for
+    # Android). Build and link against libunwind manually to get this
+    # functionality.
+    if (is_fuchsia || is_android) {
+      deps = [ ":libunwind" ]
+    }
+
     sources = [
       "libcxxabi/src/abort_message.cpp",
       "libcxxabi/src/cxa_aux_runtime.cpp",
       "libcxxabi/src/cxa_default_handlers.cpp",
-      "libcxxabi/src/cxa_demangle.cpp",
       "libcxxabi/src/cxa_exception.cpp",
       "libcxxabi/src/cxa_exception_storage.cpp",
-      "libcxxabi/src/cxa_guard.cpp",
       "libcxxabi/src/cxa_handlers.cpp",
+
+      # This file is supposed to be used in fno-exception builds of
+      # libc++abi.  We build lib++/libc++abi with exceptions enabled.
+      #"trunk/src/cxa_noexception.cpp",
       "libcxxabi/src/cxa_personality.cpp",
-      "libcxxabi/src/cxa_unexpected.cpp",
       "libcxxabi/src/cxa_vector.cpp",
       "libcxxabi/src/cxa_virtual.cpp",
       "libcxxabi/src/fallback_malloc.cpp",
@@ -647,21 +684,57 @@
       "libcxxabi/src/stdlib_typeinfo.cpp",
     ]
 
-    # On linux this seems to introduce an unwanted glibc 2.18 dependency.
-    if (is_android) {
+    if (!is_tsan) {
+      sources += [ "libcxxabi/src/cxa_guard.cpp" ]
+    }
+
+    # See the comment in cxa_demangle_stub.cc for why we don't use LLVM's
+    # demangler on android.
+    sources += [ "libcxxabi/src/cxa_demangle.cpp" ]
+
+    if (is_fuchsia || is_android || is_linux) {
       sources += [ "libcxxabi/src/cxa_thread_atexit.cpp" ]
     }
+
+    defines = [
+      "LIBCXXABI_SILENT_TERMINATE",
+
+      # TODO(crbug.com/1298070) _LIBCPP_CONSTINIT is defined in a libc++
+      # revision more recent than the one currently used in Perfetto/Chrome.
+      "_LIBCPP_CONSTINIT=constinit",
+    ]
+
     configs -= [
       "//gn/standalone:extra_warnings",
       "//gn/standalone:no_exceptions",
       "//gn/standalone:no_rtti",
-      "//gn/standalone:visibility_hidden",
     ]
+    if (perfetto_cpp11_until_q1_2023) {
+      configs -= [ "//gn/standalone:c++11" ]
+    } else {
+      configs -= [ "//gn/standalone:c++17" ]
+    }
     configs += [
       ":libc++config",
       "//gn/standalone/sanitizers:sanitizer_options_link_helper",
     ]
-    deps = [ ":libunwind" ]
+    if (!custom_libcxx_is_static) {
+      configs -= [ "//gn/standalone:visibility_hidden" ]
+    }
+
+    # libc++abi depends on libc++ internals.
+    include_dirs = [ "libcxx/src" ]
+  }
+
+  # Explicitly set version macros to Windows 7 to prevent libc++ from adding a
+  # hard dependency on GetSystemTimePreciseAsFileTime, which was introduced in
+  # Windows 8.
+  config("libc++winver") {
+    defines = [
+      "NTDDI_VERSION=NTDDI_WIN7",
+      "_WIN32_WINNT=_WIN32_WINNT_WIN7",
+      "WINVER=_WIN32_WINNT_WIN7",
+    ]
   }
 
   if (custom_libcxx_is_static) {
@@ -673,21 +746,32 @@
   target(libcxx_target_type, "libc++") {
     visibility = _buildtools_visibility
     visibility += [ "../gn/standalone/libc++:*" ]
+
+    if (is_linux && !is_clang) {
+      libs = [ "atomic" ]
+    }
+
+    inputs = [ "libcxx_config/__config_site" ]
+
     sources = [
       "libcxx/src/algorithm.cpp",
       "libcxx/src/any.cpp",
+      "libcxx/src/atomic.cpp",
+      "libcxx/src/barrier.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/format.cpp",
       "libcxx/src/functional.cpp",
       "libcxx/src/future.cpp",
       "libcxx/src/hash.cpp",
       "libcxx/src/ios.cpp",
+      "libcxx/src/ios.instantiations.cpp",
       "libcxx/src/iostream.cpp",
+      "libcxx/src/legacy_pointer_safety.cpp",
       "libcxx/src/locale.cpp",
       "libcxx/src/memory.cpp",
       "libcxx/src/mutex.cpp",
@@ -695,7 +779,11 @@
       "libcxx/src/new.cpp",
       "libcxx/src/optional.cpp",
       "libcxx/src/random.cpp",
+      "libcxx/src/random_shuffle.cpp",
       "libcxx/src/regex.cpp",
+      "libcxx/src/ryu/d2fixed.cpp",
+      "libcxx/src/ryu/d2s.cpp",
+      "libcxx/src/ryu/f2s.cpp",
       "libcxx/src/shared_mutex.cpp",
       "libcxx/src/stdexcept.cpp",
       "libcxx/src/string.cpp",
@@ -707,27 +795,81 @@
       "libcxx/src/valarray.cpp",
       "libcxx/src/variant.cpp",
       "libcxx/src/vector.cpp",
+      "libcxx/src/verbose_abort.cpp",
     ]
+
+    include_dirs = [ "libcxx/src" ]
+    if (is_win) {
+      sources += [
+        "libcxx/src/support/win32/locale_win32.cpp",
+        "libcxx/src/support/win32/support.cpp",
+        "libcxx/src/support/win32/thread_win32.cpp",
+      ]
+      configs += [ ":libc++winver" ]
+    }
     configs -= [
       "//gn/standalone:extra_warnings",
       "//gn/standalone:no_exceptions",
       "//gn/standalone:no_rtti",
-      "//gn/standalone:visibility_hidden",
     ]
+    if (perfetto_cpp11_until_q1_2023) {
+      configs -= [ "//gn/standalone:c++11" ]
+    } else {
+      configs -= [ "//gn/standalone:c++17" ]
+    }
+    if ((is_android || is_mac) && !custom_libcxx_is_static) {
+      # Use libc++_perfetto to avoid conflicting with system libc++
+      output_name = "libc++_perfettto"
+    }
     configs += [
       ":libc++config",
       "//gn/standalone/sanitizers:sanitizer_options_link_helper",
     ]
-    defines = [ "_LIBCPP_BUILDING_LIBRARY" ]
-    if ((is_linux || is_android) && using_sanitizer &&
-        (is_asan || is_tsan || is_msan)) {
+    if (!custom_libcxx_is_static) {
+      configs -= [ "//gn/standalone:visibility_hidden" ]
+    }
+
+    defines = []
+    if (custom_libcxx_is_static) {
+      if (is_mac && is_clang) {
+        # We want operator new/delete to be private on Mac, but these functions
+        # are implicitly created by the compiler for each translation unit, as
+        # specified in the C++ spec 3.7.4p2, which makes them always have
+        # default visibility.  This option is needed to force hidden visibility
+        # since -fvisibility=hidden doesn't have the desired effect.
+        cflags = [ "-fvisibility-global-new-delete-hidden" ]
+      } else {
+        defines += [
+          # This resets the visibility to default only for the various
+          # flavors of operator new and operator delete.  These symbols
+          # are weak and get overriden by Chromium-provided ones, but if
+          # these symbols had hidden visibility, this would make the
+          # Chromium symbols hidden too because elf visibility rules
+          # require that linkers use the least visible form when merging,
+          # and if this is hidden, then when we merge it with tcmalloc's
+          # operator new, hidden visibility would win. However, tcmalloc
+          # needs a visible operator new to also override operator new
+          # references from system libraries.
+          # TODO(lld): Ask lld for a --force-public-visibility flag or
+          # similar to that overrides the default elf merging rules, and
+          # make tcmalloc's gn config pass that to all its dependencies,
+          # then remove this override here.
+          "_LIBCPP_OVERRIDABLE_FUNC_VIS=__attribute__((__visibility__(\"default\")))",
+        ]
+      }
+    }
+    if (!is_mac && using_sanitizer && (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 (!is_win) {
+      defines += [ "LIBCXX_BUILDING_LIBCXXABI" ]
+      deps = [ ":libc++abi" ]
+    }
   }
 }  # if (use_custom_libcxx)
 
@@ -858,28 +1000,22 @@
 
 source_set("sqlite") {
   visibility = _buildtools_visibility
-  sources = [
-    "sqlite/sqlite3.c",
-    "sqlite/sqlite3.h",
-    "sqlite/sqlite3ext.h",
-    "sqlite_src/ext/misc/percentile.c",
-  ]
+  if (perfetto_use_system_sqlite) {
+    # Use the system sqlite library instead of the hermetic one.
+    libs = [ "sqlite3" ]
+  } else {
+    sources = [
+      "sqlite/sqlite3.c",
+      "sqlite/sqlite3.h",
+      "sqlite/sqlite3ext.h",
+      "sqlite_src/ext/misc/percentile.c",
+    ]
+  }
   configs -= [ "//gn/standalone:extra_warnings" ]
   public_configs = [ ":sqlite_config" ]
   deps = [ "//gn:default_deps" ]
 }
 
-source_set("sqlite_shell") {
-  visibility = _buildtools_visibility
-  testonly = true
-  sources = [ "sqlite/shell.c" ]
-  configs -= [ "//gn/standalone:extra_warnings" ]
-  deps = [
-    ":sqlite",
-    "//gn:default_deps",
-  ]
-}
-
 source_set("lzma") {
   visibility = _buildtools_visibility
   defines = [ "_7ZIP_ST" ]
@@ -974,6 +1110,12 @@
     perfetto_isystem_cflag,
     rebase_path("zlib", root_build_dir),
   ]
+  if (is_clang) {
+    cflags += [
+      "-Wno-unknown-warning-option",
+      "-Wno-deprecated-non-prototype",
+    ]
+  }
 }
 
 # Here be dragons. Used only by standalone profiler builds, which are
@@ -1024,6 +1166,7 @@
     "android-unwinding/libunwindstack/RegsArm64.cpp",
     "android-unwinding/libunwindstack/RegsMips.cpp",
     "android-unwinding/libunwindstack/RegsMips64.cpp",
+    "android-unwinding/libunwindstack/RegsRiscv64.cpp",
     "android-unwinding/libunwindstack/RegsX86.cpp",
     "android-unwinding/libunwindstack/RegsX86_64.cpp",
     "android-unwinding/libunwindstack/Symbols.cpp",
@@ -1049,14 +1192,18 @@
   }
   configs -= [
     "//gn/standalone:extra_warnings",
-    "//gn/standalone:c++11",
     "//gn/standalone:visibility_hidden",
   ]
   cflags = [ "-DFAKE_LOG_DEVICE=1" ]
-  public_configs = [
-    ":libunwindstack_config",
-    "//gn/standalone:c++17",
-  ]
+  if (!is_win) {
+    cflags += [ "-Wno-deprecated-declarations" ]
+  }
+  public_configs = [ ":libunwindstack_config" ]
+
+  if (perfetto_cpp11_until_q1_2023) {
+    configs -= [ "//gn/standalone:c++11" ]
+    public_configs += [ "//gn/standalone:c++17" ]
+  }
 }
 
 config("bionic_kernel_uapi_headers") {
@@ -1162,18 +1309,19 @@
   include_dirs = [ "llvm-project/llvm/include" ]
 }
 
-# NB: this is built under c++14 and linked into code that is c++11 by default.
-# We rely on the ABIs being compatible for this to be sane. At the time of
-# writing, the only c++14 specific code is behind an #ifndef NDEBUG, so we
-# could keep building as c++11 in non-debug builds, but we always use c++14 for
-# consistency.
 source_set("llvm_demangle") {
   visibility = _buildtools_visibility
-  configs -= [
-    "//gn/standalone:extra_warnings",
-    "//gn/standalone:c++11",
-  ]
-  configs += [ "//gn/standalone:c++14" ]
+  configs -= [ "//gn/standalone:extra_warnings" ]
+
+  # NB: this is built under c++14 and linked into code that is c++11 by default.
+  # We rely on the ABIs being compatible for this to be sane. At the time of
+  # writing, the only c++14 specific code is behind an #ifndef NDEBUG, so we
+  # could keep building as c++11 in non-debug builds, but we always use c++14 for
+  # consistency.
+  if (perfetto_cpp11_until_q1_2023) {
+    configs -= [ "//gn/standalone:c++11" ]
+    configs += [ "//gn/standalone:c++14" ]
+  }
   public_configs = [ ":llvm_demangle_config" ]
   sources = [
     "llvm-project/llvm/include/llvm/Demangle/Demangle.h",
diff --git a/buildtools/libcxx_config/__config_site b/buildtools/libcxx_config/__config_site
new file mode 100644
index 0000000..2df8632
--- /dev/null
+++ b/buildtools/libcxx_config/__config_site
@@ -0,0 +1,48 @@
+#ifndef _LIBCPP_CONFIG_SITE
+#define _LIBCPP_CONFIG_SITE
+
+// We set a custom _LIBCPP_ABI_NAMESPACE for the following reasons:
+//
+// 1. When libcxx_is_shared is true, symbols from libc++.so are exported for all
+//    DSOs to use.  If the system libc++ gets loaded (indirectly through a
+//    a system library), then it will conflict with our libc++.so.
+// 2. The default value of _LIBCPP_ABI_NAMESPACE is the string
+//    "_LIBCPP_ABI_NAMESPACE". This contributes to an increase in binary size;
+//    on Windows, the increase is great enough that we go above the 4GB size
+//    limit for PDBs (https://crbug.com/1327710#c5). To fix this, we set
+//    _LIBCPP_ABI_NAMESPACE to a shorter value.
+#define _LIBCPP_ABI_NAMESPACE Cr
+#define _LIBCPP_ABI_VERSION 2
+
+/* #undef _LIBCPP_ABI_FORCE_ITANIUM */
+/* #undef _LIBCPP_ABI_FORCE_MICROSOFT */
+/* #undef _LIBCPP_HAS_NO_THREADS */
+/* #undef _LIBCPP_HAS_NO_MONOTONIC_CLOCK */
+/* #undef _LIBCPP_HAS_MUSL_LIBC */
+/* #undef _LIBCPP_HAS_THREAD_API_PTHREAD */
+/* #undef _LIBCPP_HAS_THREAD_API_EXTERNAL */
+/* #undef _LIBCPP_HAS_THREAD_API_WIN32 */
+/* #undef _LIBCPP_HAS_THREAD_LIBRARY_EXTERNAL */
+/* #undef _LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS */
+#define _LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS
+/* #undef _LIBCPP_NO_VCRUNTIME */
+/* #undef _LIBCPP_TYPEINFO_COMPARISON_IMPLEMENTATION */
+/* #undef _LIBCPP_HAS_NO_FILESYSTEM_LIBRARY */
+/* #undef _LIBCPP_HAS_PARALLEL_ALGORITHMS */
+/* #undef _LIBCPP_HAS_NO_RANDOM_DEVICE */
+/* #undef _LIBCPP_HAS_NO_LOCALIZATION */
+/* #undef _LIBCPP_HAS_NO_WIDE_CHARACTERS */
+
+// Settings below aren't part of __config_site upstream.
+// We set them here since we want them to take effect everywhere,
+// unconditionally.
+
+// Prevent libc++ from embedding linker flags to try to automatically link
+// against its runtime library. This is unnecessary with our build system,
+// and can also result in build failures if libc++'s name for a library
+// does not match ours.  Only has an effect on Windows.
+#define _LIBCPP_NO_AUTO_LINK
+
+#define _LIBCPP_REMOVE_TRANSITIVE_INCLUDES
+
+#endif // _LIBCPP_CONFIG_SITE
diff --git a/docs/README.md b/docs/README.md
index 9152c0a..0dac083 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -49,7 +49,7 @@
   memory to call-stacks, based on out-of-process unwinding, configurable
   sampling, attachable to already running processes.
 
-* [Java heap profiling](/docs/data-sources/java-heap-profiler.md): an
+* Capturing [Java heap dumps](/docs/data-sources/java-heap-profiler.md) with an
   out-of-process profiler tightly integrated with the Android RunTime that
   allows to get full snapshots of the managed heap retention graph (types,
   field names, retained size and references to other objects) without, however,
@@ -82,7 +82,7 @@
 to the Linux/Android tracing daemon through a UNIX socket, allowing to combine
 app-specific instrumentation points with system-wide tracing events.
 
-The SDK is based on portable C++11 code [tested](/docs/contributing/testing.md)
+The SDK is based on portable C++17 code [tested](/docs/contributing/testing.md)
 with the major C++ sanitizers (ASan, TSan, MSan, LSan). It doesn't rely on
 run-time code modifications or compiler plugins.
 
@@ -109,7 +109,7 @@
 dedicated project for importing, parsing and querying new and legacy trace
 formats, [Trace Processor](/docs/analysis/trace-processor.md).
 
-Trace Processor is a portable C++11 library that provides column-oriented
+Trace Processor is a portable C++17 library that provides column-oriented
 table storage, designed ad-hoc for efficiently holding hours of trace data
 into memory and exposes a SQL query interface based on the popular SQLite query
 engine.
diff --git a/docs/analysis/common-queries.md b/docs/analysis/common-queries.md
index 98e8c1f..018ab94 100644
--- a/docs/analysis/common-queries.md
+++ b/docs/analysis/common-queries.md
@@ -57,3 +57,46 @@
 FROM slice_thread_state_breakdown
 GROUP BY slice_id;
 ```
+
+## Computing scheduling time by woken threads
+A given thread might cause other threads to wake up i.e. because work was
+scheduled on them. For a given thread, the amount of time threads it
+woke up ran for can be a good proxy to understand how much work is being
+spawned.
+
+To compute this, the following query can be used:
+```
+SELECT
+  SUM((
+    SELECT dur FROM sched
+    WHERE
+      sched.ts > wakee_runnable.ts AND
+      wakee_runnable.utid = wakee_runnable.utid
+    ORDER BY ts
+    LIMIT 1
+  )) AS scheduled_dur
+FROM thread AS waker
+JOIN thread_state AS wakee_runnable ON waker.utid = wakee_runnable.waker_utid
+WHERE waker.name = <your waker thread name here>
+```
+
+To do this for all the threads in the trace simultaenously:
+```
+SELECT
+  waker_process.name AS process_name,
+  waker.name AS thread_name,
+  SUM((
+    SELECT dur FROM sched
+    WHERE
+      sched.ts > wakee_runnable.ts AND
+      sched.utid = wakee_runnable.utid
+    ORDER BY ts
+    LIMIT 1
+  )) AS scheduled_dur
+FROM thread AS waker
+JOIN process AS waker_process USING (upid)
+JOIN thread_state AS wakee_runnable ON waker.utid = wakee_runnable.waker_utid
+WHERE waker.utid != 0
+GROUP BY 1, 2
+ORDER BY 3 desc
+```
diff --git a/docs/analysis/metrics.md b/docs/analysis/metrics.md
index 6d2213c..7aaba2d5 100644
--- a/docs/analysis/metrics.md
+++ b/docs/analysis/metrics.md
@@ -84,8 +84,8 @@
 can run the following commands from a Perfetto checkout:
 ```python
 > ./tools/trace_processor --interactive \
-  --run_metrics android_startup \
-  --metric-extension src/trace_processor/metrics@/
+  --run-metrics android_startup \
+  --metric-extension src/trace_processor/metrics@/ \
   --dev \
   <trace>
 android_startup {
@@ -109,7 +109,7 @@
 
 This also works for custom metrics specified on the command line:
 ```python
-> ./tools/trace_processor -i --run_metrics /tmp/my_custom_metric.sql <trace>
+> ./tools/trace_processor -i --run-metrics /tmp/my_custom_metric.sql <trace>
 my_custom_metric {
   <contents of my_custom_metric>
 }
@@ -139,7 +139,7 @@
 For example, from inside a Perfetto checkout:
 ```python
 > ./tools/trace_processor \
-  --run_metrics android_cpu \
+  --run-metrics android_cpu \
   --metric-extension src/trace_processor/metrics@/
   --dev
   <trace>
diff --git a/docs/analysis/pivot-tables.md b/docs/analysis/pivot-tables.md
new file mode 100644
index 0000000..4add1ba
--- /dev/null
+++ b/docs/analysis/pivot-tables.md
@@ -0,0 +1,38 @@
+# Pivot Tables
+
+Pivot Tables are a way to summarize and aggregate information about selected
+slices in a configurable way. It is available in canary and autopush channels by
+default, and can be enabled in stable by toggling a corresponding flag
+(Support > Flags > Pivot tables V2).
+
+To create a pivot table, you need to select a timeline area that contains
+slices. The table will then show an aggregation of the selected slices.
+
+## Conceptual model
+
+Pivot tables have two types of columns: pivots and aggregations. Conceptually,
+the UI requests all the data from columns of both types and then computes
+aggregate values for aggregation columns for every distinct tuple of pivot
+column values.
+
+Pivots are hierarchical, and the aggregate values are also computed for all the
+prefixes of pivot column values. For example, if you select process name,
+category, and event name (in that order) as pivots and have duration sum as the
+only aggregation columns, the following aggregate values will be computed:
+
+*   Total duration for each process
+*   Total duration for each process and category
+*   Total duration for each process, category, and event name
+
+The table rows are appropriately nested in the UI, from more general to more
+specific. Portions of the table can be collapsed and expanded.
+
+## Working with pivot table
+
+Pivot tables can be configured using dropdown menus in the table header cells.
+These can be used to:
+
+*   Add and remove pivots
+*   Add and remove aggregations
+*   Change aggregation functions
+*   Sort by aggregation columns
diff --git a/docs/analysis/trace-processor.md b/docs/analysis/trace-processor.md
index 85dd43f..2a8351d 100644
--- a/docs/analysis/trace-processor.md
+++ b/docs/analysis/trace-processor.md
@@ -289,9 +289,11 @@
 
 ### Span join
 Span join is a custom operator table which computes the intersection of
-spans of time from two tables or views. A column (called the *partition*)
-can optionally be specified which divides the rows from each table into
-partitions before computing the intersection.
+spans of time from two tables or views. A span in this concept is a row in a
+table/view which contains a "ts" (timestamp) and "dur" (duration) columns.
+
+A column (called the *partition*) can optionally be specified which divides the
+rows from each table into partitions before computing the intersection.
 
 ![Span join block diagram](/docs/images/span-join.png)
 
@@ -783,10 +785,26 @@
 difference is highlighted.
 
 All diff tests are organized under [test/trace_processor](/test/trace_processor)
+in `tests{_category name}.py` files as methods of a class in each file
 and are run by the script
 [`tools/diff_test_trace_processor.py`](/tools/diff_test_trace_processor.py).
-New tests can be added with the helper script
-[`tools/add_tp_diff_test.py`](/tools/add_tp_diff_test.py).
+To add a new test its enough to add a new method starting with `test_` in suitable
+python tests file.
+
+Methods can't take arguments and have to return `DiffTestBlueprint`:
+```python
+class DiffTestBlueprint:
+  trace: Union[Path, Json, Systrace, TextProto]
+  query: Union[str, Path, Metric]
+  out: Union[Path, Json, Csv, TextProto]
+```
+*Trace* and *Out*: For every type apart from `Path`, contents of the object will be treated as
+file contents so it has to follow the same rules.
+
+*Query*: For metric tests it is enough to provide the metric name. For query tests there
+can be a raw SQL statement, for example `"SELECT * FROM SLICE"` or path to an `.sql` file.
+
+
 
 NOTE: `trace_processor_shell` and associated proto descriptors needs to be
 built before running `tools/diff_test_trace_processor.py`. The easiest way
@@ -794,8 +812,7 @@
 every change to trace processor code or builtin metrics.
 
 #### Choosing where to add diff tests
-When adding a new test with `tools/add_tp_diff_test.py`, the user is
-prompted for a folder to add the new test to. Often this can be confusing
+Choosing a folder with a diff tests often can be confusing
 as a test can fall into more than one category. This section is a guide
 to decide which folder to choose.
 
diff --git a/docs/case-studies/memory.md b/docs/case-studies/memory.md
index de4e401..b55bdce 100644
--- a/docs/case-studies/memory.md
+++ b/docs/case-studies/memory.md
@@ -307,7 +307,7 @@
 **Native Heap Profiles require Android 10.**
 
 NOTE: For detailed instructions about the native heap profiler and
-      troubleshooting see the [Data sources > Native heap profiler](
+      troubleshooting see the [Data sources > Heap profiler](
       /docs/data-sources/native-heap-profiler.md) page.
 
 Applications usually get memory through `malloc` or C++'s `new` rather than
@@ -353,42 +353,40 @@
 
 The tabs that are available are
 
-* **Unreleased size**: how many bytes were allocated but not freed at this
-  callstack the moment the dump was created.
-* **Total size**: how many bytes were allocated (including ones freed at the
-  moment of the dump) at this callstack
-* **Unreleased count**: how many allocations without matching frees were done at
-  this callstack.
-* **Total count**: how many allocations (including ones with matching frees)
-  were done at this callstack.
+* **Unreleased malloc size**: how many bytes were allocated but not freed at
+  this callstack the moment the dump was created.
+* **Total malloc size**: how many bytes were allocated (including ones freed at
+  the moment of the dump) at this callstack.
+* **Unreleased malloc count**: how many allocations without matching frees were
+  done at this callstack.
+* **Total malloc count**: how many allocations (including ones with matching
+  frees) were done at this callstack.
 
 The default view will show you all allocations that were done while the
 profile was running but that weren't freed (the **space** tab).
 
-![Native Flamegraph](/docs/images/syssrv-apk-assets-two.png)
+![Native Flamegraph](/docs/images/native-heap-prof.png)
 
 We can see that a lot of memory gets allocated in paths through
-`ResourceManager.loadApkAssets`. To get the total memory that was allocated
-this way, we can enter "loadApkAssets" into the Focus textbox. This will only
-show callstacks where some frame matches "loadApkAssets".
+`AssetManager.applyStyle`. To get the total memory that was allocated
+this way, we can enter "applyStyle" into the Focus textbox. This will only
+show callstacks where some frame matches "applyStyle".
 
-![Native Flamegraph with Focus](/docs/images/syssrv-apk-assets-focus.png)
+![Native Flamegraph with Focus](/docs/images/native-heap-prof-focus.png)
 
 From this we have a clear idea where in the code we have to look. From the
 code we can see how that memory is being used and if we actually need all of
-it. In this case the key is the `_CompressedAsset` that requires decompressing
-into RAM rather than being able to (_cleanly_) memory-map. By not compressing
-these data, we can save RAM.
+it.
 
 ## {#java-hprof} Analyzing the Java Heap
 
 **Java Heap Dumps require Android 11.**
 
-NOTE: For detailed instructions about the Java heap profiler and
-      troubleshooting see the [Data sources > Java heap profiler](
+NOTE: For detailed instructions about capturing Java heap dumps and
+      troubleshooting see the [Data sources > Java heap dumps](
       /docs/data-sources/java-heap-profiler.md) page.
 
-### {#capture-profile-java} Capturing the profile
+### {#capture-profile-java} Dumping the java heap
 We can get a snapshot of the graph of all the Java objects that constitute the
 Java heap. We use the `tools/java_heap_dump` script. If you are having trouble
 make sure you are using the [latest version](
@@ -415,12 +413,12 @@
 and is generally the highest-signal. The rightmost `[merged]` stacks is the
 sum of all objects that are too small to be displayed.
 
-![Java Flamegraph](/docs/images/java-flamegraph.png)
+![Java Flamegraph](/docs/images/java-heap-graph.png)
 
 The tabs that are available are
 
-* **space**: how many bytes are retained via this path to the GC root.
-* **objects**: how many objects are retained via this path to the GC root.
+* **Size**: how many bytes are retained via this path to the GC root.
+* **Objects**: how many objects are retained via this path to the GC root.
 
 If we want to only see callstacks that have a frame that contains some string,
 we can use the Focus feature. If we want to know all allocations that have to
@@ -430,7 +428,7 @@
 graph, we can filter by the names of the classes. If we wanted to see everything
 that could be caused by notifications, we can put "notification" in the Focus box.
 
-![Java Flamegraph with Focus](/docs/images/java-flamegraph-focus.png)
+![Java Flamegraph with Focus](/docs/images/java-heap-graph-focus.png)
 
 We aggregate the paths per class name, so if there are multiple objects of the
 same type retained by a `java.lang.Object[]`, we will show one element as its
diff --git a/docs/concepts/config.md b/docs/concepts/config.md
index 33e94b7..c4326e6 100644
--- a/docs/concepts/config.md
+++ b/docs/concepts/config.md
@@ -94,13 +94,13 @@
 owned by the tracing service. It looks as follows:
 
 ```protobuf
-// Buffer #0
+# Buffer #0
 buffers {
   size_kb: 4096
   fill_policy: RING_BUFFER
 }
 
-// Buffer #1
+# Buffer #1
 buffers {
   size_kb: 8192
   fill_policy: DISCARD
@@ -160,7 +160,7 @@
 data_sources {
   config {
     name: "linux.ftrace"
-    target_buffer: 0       // <-- This goes into buffer 0.
+    target_buffer: 0       # <-- This goes into buffer 0.
     ftrace_config { ... }
   }
 }
@@ -168,7 +168,7 @@
 data_sources: {
   config {
       name: "linux.sys_stats"
-      target_buffer: 1     // <-- This goes into buffer 1.
+      target_buffer: 1     # <-- This goes into buffer 1.
       sys_stats_config { ... }
   }
 }
@@ -176,7 +176,7 @@
 data_sources: {
   config {
     name: "android.heapprofd"
-    target_buffer: 1       // <-- This goes into buffer 1 as well.
+    target_buffer: 1       # <-- This goes into buffer 1 as well.
     heapprofd_config { ... }
   }
 }
diff --git a/docs/contributing/build-instructions.md b/docs/contributing/build-instructions.md
index 878a003..e0374796 100644
--- a/docs/contributing/build-instructions.md
+++ b/docs/contributing/build-instructions.md
@@ -290,7 +290,7 @@
 
 ## Build configurations
 
-TIP: `tools/build_all_configs.py` can be used to generate out/XXX folders for
+TIP: `tools/setup_all_configs.py` can be used to generate out/XXX folders for
 most of the supported configurations.
 
 The following [GN args][gn-quickstart] are supported:
@@ -326,6 +326,12 @@
 
 Use bundled toolchain from `buildtools/` rather than system-wide one.
 
+`non_hermetic_clang_stdlib = libc++ | libstdc++`
+
+If `is_hermetic_clang` is `false`, sets the `-stdlib` flag for clang
+invocations. `libstdc++` is default on Linux hosts and `libc++` is
+default everywhere else.
+
 `cc = "gcc" / cxx = "g++"`
 
 Uses a different compiler binary (default: autodetected depending on is_clang).
diff --git a/docs/contributing/common-tasks.md b/docs/contributing/common-tasks.md
index 5ef5dae..61df4a1 100644
--- a/docs/contributing/common-tasks.md
+++ b/docs/contributing/common-tasks.md
@@ -14,21 +14,107 @@
 
 Here is an [example change](https://android-review.googlesource.com/c/platform/external/perfetto/+/1290645) which added the `ion/ion_stat` event.
 
+## Contribute to SQL standard library
+
+1. Add or edit an SQL file inside `perfetto/src/trace_processor/stdlib/`.
+2. For a new file inside an existing module add the file to the corresponding `BUILD.gn`.
+3. For a new module (subdirectory of `/stdlib/`), module name (directory name) has to be added to the list in `/stdlib/BUILD.gn`.
+
+Files inside the standard library have to be formatted in a very specific way, as its structure is used to generate documentation. There are presubmit checks, but they are not infallible.
+
+- Running the file cannot generate any data. There can be only `CREATE_FUNCTION`, `CREATE TABLE/VIEW` or `CREATE_VIEW_FUNCTION` inside.
+- The name of each table/view/function needs to start with `{module_name}_` or `{internal_}`. Views/tables are must be `[a-z_]`, while functions are `[A-Z_]`. When a module is imported (using the `IMPORT` function), objects prefixed with internal should not be used.
+  - The only exception is the `common` module. The name of functions/views/tables inside should not be prefixed with `common_`, as they are supposed to be module agnostic and widely used.
+- Every non internal object has be prefixed with an SQL comment following a particular documentation schema e.g. similar to javadoc. The schema is a comment directly over the SQL which creates it, without empty lines. Any text is going to be parsed as markdown, so usage of markdown functionality (code, links, lists) is encouraged. Whitespaces in anything apart from descriptions are ignored, so comments can be formatted neatly. If the line with description exceeds 80 chars, description can be continued in following lines.
+  - **Table/view**: each has to have object description and list of columns.
+    - Description is any text above column comments.
+    - For each column there has to be a comment line `-- @column {col name} {col description}`.
+  - **Functions**: each has to have a function description, list of arguments (names, types, description) and description of return value in this order.
+    - Function description is any text above argument comments.
+    - For each argument there has to be a comment line `-- @arg {arg name} {arg type} {arg description}`. Arg name should follow `[a-z_]*`, arg type has to be exactly the same as specified in the function, so `[A-Z]*`.
+    - Return comment is `-- @ret {return type} {return description}`. Return type should be exactly the same as specified in the function, so `[A-Z]*`.
+  - **View functions**: each has to have a function description, list of arguments (names, types, description) and list of columns.
+    - Function description is any text above argument comments.
+    - For each argument there has to be a comment line `-- @arg {arg name} {arg type} {arg description}`. Arg name should follow `[a-z_]*`, arg type has to be exactly the same as specified in the function, so `[A-Z]*`.
+    - For each column there has to be a comment line `-- @column {col name} {col description}`.
+
+NOTE: Break lines outside of import description will be ignored.
+
+Example of properly formatted view in module `android`:
+```sql
+-- Count Binder transactions per process.
+--
+-- @column process_name  Name of the process that started the binder transaction.
+-- @column pid           PID of the process that started the binder transaction.
+-- @column slice_name    Name of the slice with binder transaction.
+-- @column event_count   Number of binder transactions in process in slice.
+CREATE VIEW android_binder_metrics_by_process AS
+SELECT
+  process.name AS process_name,
+  process.pid AS pid,
+  slice.name AS slice_name,
+  COUNT(*) AS event_count
+FROM slice
+INNER JOIN thread_track ON slice.track_id = thread_track.id
+INNER JOIN thread ON thread.utid = thread_track.utid
+INNER JOIN process ON thread.upid = process.upid
+WHERE
+  slice.name GLOB 'binder*'
+GROUP BY
+  process_name,
+  slice_name;
+```
+
+Example of function in module `common`:
+```sql
+-- Extracts an int value with the given name from the metadata table.
+--
+-- @arg name STRING The name of the metadata entry.
+-- @ret LONG int_value for the given name. NULL if there's no such entry.
+SELECT CREATE_FUNCTION(
+    'EXTRACT_INT_METADATA(name STRING)',
+    'LONG',
+    'SELECT int_value FROM metadata WHERE name = ($name)');
+```
+
+Example of view function in module `android`:
+```sql
+-- Given a launch id and GLOB for a slice name, returns columns for matching slices.
+--
+-- @arg launch_id INT         Id of launch.
+-- @arg slice_name STRING     Name of slice with launch.
+-- @column slice_name         Name of slice with launch.
+-- @column slice_ts INT       Timestamp of slice start.
+-- @column slice_dur INT      Duration of slice.
+-- @column thread_name STRING Name of thread with slice
+-- @column arg_set_id INT     Arg set id.
+SELECT CREATE_VIEW_FUNCTION(
+  'ANDROID_SLICES_FOR_LAUNCH_AND_SLICE_NAME(launch_id INT, slice_name STRING)',
+  'slice_name STRING, slice_ts INT, slice_dur INT, thread_name STRING, arg_set_id INT',
+  '
+    SELECT slice_name, slice_ts, slice_dur, thread_name, arg_set_id
+    FROM thread_slices_for_all_launches
+    WHERE launch_id = $launch_id AND slice_name GLOB $slice_name
+  '
+);
+```
+
+
 ## {#new-metric} Add a new trace-based metric
 
 1. Create the proto file containing the metric in the [protos/perfetto/metrics](/protos/perfetto/metrics) folder. The appropriate` BUILD.gn` file should be updated as well.
 2. Import the proto in [protos/perfetto/metrics/metrics.proto](/protos/perfetto/metrics/metrics.proto) and add a field for the new message.
 3. Run `tools/gen_all out/YOUR_BUILD_DIRECTORY`. This will update the generated headers containing the descriptors for the proto.
   * *Note: this step has to be performed any time any metric-related proto is modified.*
+  * If you don't see anything inside the `out/` directory you might have to
+  rerun `tools/setup_all_configs.py`.
 4. Add a new SQL file for the metric to [src/trace_processor/metrics](/src/trace_processor/metrics). The appropriate `BUILD.gn` file should be updated as well.
   * To learn how to write new metrics, see the [trace-based metrics documentation](/docs/analysis/metrics.md).
 5. Build all targets in your out directory with `tools/ninja -C out/YOUR_BUILD_DIRECTORY`.
 6. Add a new diff test for the metric. This can be done by adding files to
-the [test/trace_processor](/test/trace_processor) folder and modifying one
-of the index files listed in
-[/test/trace_processor/include_index](/test/trace_processor/include_index).
-7. Run the newly added test with `tools/diff_test_trace_processor.py <path to trace processor binary>`.
-8. Upload and land your change as normal.
+the `tests.*.py` files in a proper [test/trace_processor](/test/trace_processor) subfolder.
+1. Run the newly added test with `tools/diff_test_trace_processor.py <path to trace processor binary>`.
+2. Upload and land your change as normal.
 
 Here is an [example change](https://android-review.googlesource.com/c/platform/external/perfetto/+/1290643) which added the `time_in_state` metric.
 
@@ -44,15 +130,6 @@
   3. Run the newly added test with `tools/diff_test_trace_processor.py <path to trace processor binary>`.
 4. Upload and land your change as normal.
 
-## {#new-annotation} Add a new annotation
-
-NOTE: all currently implemented annotations are based only on the name of the slice. It is straightforward to extend this to also consider ancestors and other similar properties; we plan on doing this in the future.
-
-1. Change the [`DescribeSlice`](/src/trace_processor/analysis/describe_slice.h) function as appropriate.
-  * 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>`.
-2. Upload and land your change as normal.
-
 ## Adding new derived events
 
 As derived events depend on metrics, the initial steps are same as that of developing a metric (see above).
@@ -70,14 +147,14 @@
 
 The schema of the `<metric name>_event` 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. Also the track identifier i.e. all events with same `track_name` appear on the same track. |
-| `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                                     |
+| 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. Also the track identifier i.e. all events with same `track_name` appear on the same track.                                                                                                          |
+| `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                                                                                                                                                                                                                    |
 | `group_name` | `string` | Optional                              | Name of the track group under which the track appears. All tracks with the same `group_name` are placed under the same group by that name. Tracks that lack this field or have NULL value in this field are displayed without any grouping. |
 
 #### Known issues:
diff --git a/docs/contributing/embedding.md b/docs/contributing/embedding.md
index dbf6c56..0d080c9 100644
--- a/docs/contributing/embedding.md
+++ b/docs/contributing/embedding.md
@@ -44,32 +44,6 @@
 
 WARNING: embedders should ensure that the path of any registered metric is consistent with the name used to execute the metric and output view in the SQL.
 
-### Annotations
-
-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 needs to be set equal to the id of the slice for which to get the description. For example, to get the description and doc link for slice with id `5`:
-
-```sql
-select description, doc_link
-from describe_slice
-where slice_id = 5
-```
-
-The `describe_slice` table can also be _joined_ with the slice table to obtain descriptions for more than one slice. For example, to get the `ts`, `dur` and `description` for all `measure` slices:
-
-```sql
-select ts, dur, description
-from slice s
-join desribe_slice d on s.id = d.slice_id
-where name = 'measure'
-```
-
 ### Creating derived events
 
 As creating derived events is 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>_event` table/view which can then be queried using the `ExectueQuery` function.
diff --git a/docs/contributing/getting-started.md b/docs/contributing/getting-started.md
index 9696e7e..bbac4c2 100644
--- a/docs/contributing/getting-started.md
+++ b/docs/contributing/getting-started.md
@@ -1,9 +1,74 @@
 # Contributing to Perfetto
 
+## Quickstart
+
+NOTE: Perfetto can be built on Windows, Mac or Linux. However, setting up the environment on Windows is complicated so is not covered by this quickstart.
+
+Prerequisites: git and python3.
+
+Setup:
+```sh
+git clone https://android.googlesource.com/platform/external/perfetto/
+cd perfetto
+tools/install-build-deps
+tools/setup_all_configs.py
+```
+
+### Building
+
+#### On Linux
+
+For production:
+```sh
+tools/ninja -C out/linux_clang_release
+```
+
+For debug:
+```sh
+tools/ninja -C out/linux_clang_debug
+```
+
+#### On Mac
+
+For production:
+```sh
+tools/ninja -C out/mac_release
+```
+
+For debug:
+```sh
+tools/ninja -C out/mac_debug
+```
+
+### Contributing
+
+1. Create an account at [android.googlesource.com](https://android.googlesource.com/).
+2. Download `depot_tools`, a collection of helper scripts which make uploading changes to Android gerrit easier.
+```sh
+cd perfetto
+git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
+```
+3. Add `depot_tools` to your path:
+```sh
+depot_path="$(realpath depot_tools)"
+export PATH=$depot_path:$PATH
+```
+4. Create a branch with the change:
+```sh
+git new-branch first-contribution
+```
+5. Make change in the repo.
+5. Add and commit the change:
+```sh
+git add .
+git commit -m "My first contribution"
+git cl upload
+```
+
 ## Repository
 
 This project uses [Android AOSP Gerrit][perfetto-gerrit] for code reviews,
-follows the [Google C++ style][google-cpp-style], and targets `-std=c++11`.
+follows the [Google C++ style][google-cpp-style], and targets `-std=c++17`.
 
 Development happens in the AOSP repository:
 https://android.googlesource.com/platform/external/perfetto/
@@ -44,6 +109,9 @@
 exceptions are UI-only, docs-only or GN-only changes, for which the Android CI
 can be bypassed, as those are not built as part of the Android tree.
 
+You can also
+[test a pending Perfetto CL against Chrome's TryBots](testing.md#chromium).
+
 ## Community
 
 You can reach us on our [Discord channel](https://discord.gg/35ShE3A).
diff --git a/docs/contributing/perfetto-in-the-press.md b/docs/contributing/perfetto-in-the-press.md
new file mode 100644
index 0000000..40a7578
--- /dev/null
+++ b/docs/contributing/perfetto-in-the-press.md
@@ -0,0 +1,18 @@
+# Perfetto in the press
+
+This a partial collection of the talks, blogposts, presentations, and articles that mention Perfetto.
+
+- [Performance: Perfetto Traceviewer - MAD Skills](https://www.youtube.com/watch?v=phhLFicMacY)
+"On this episode of the MAD Skills series on Performance, Android Performance Engineer Carmen Jackson discusses the Perfetto traceviewer, an alternative to Android Studio for viewing system traces."
+- [Performance and optimisation on the Meta Quest Platform](https://m.facebook.com/RealityLabs/videos/performance-and-optimization-on-meta-quest-platform/488126049869673/)
+- [Performance testing through proportional traces
+](https://www.jviotti.com/2022/09/07/performance-testing-through-proportional-traces.html)
+- [Performance](https://www.twoscomplement.org/podcast/performance.mp3) An episode of the [Twoscomplement podcast](https://www.twoscomplement.org/#podcast) "Our most efficient podcast ever. Ben and Matt talk performance testing and optimization in fewer than 30 minutes."
+- [Collabora: Profiling virtualized GPU acceleration with Perfetto](https://www.collabora.com/news-and-blog/blog/2021/04/22/profiling-virtualized-gpu-acceleration-with-perfetto/)
+- [Snap: Client Tracing at Scale](https://www.droidcon.com/2022/06/28/client-tracing-at-scale/) "With the wide range of Android devices, it can be
+difficult to find the root cause of performance problems. By leveraging traces,
+we can begin to understand the exact circumstances that led to a poor user
+experience. We will discuss how we instrument our Snapchat app such that we can
+have the necessary signals for explainability. Additionally, we will describe
+how we incorporate tracing into our development process from local debugging,
+to performance tests and finally in production."
\ No newline at end of file
diff --git a/docs/contributing/testing.md b/docs/contributing/testing.md
index f45d665..e09514b 100644
--- a/docs/contributing/testing.md
+++ b/docs/contributing/testing.md
@@ -149,9 +149,9 @@
 use your new screenshots.
 
 NOTE: If you see a failing diff test you can see the pixel differences on the CI
-by using the link to the UI and replace `/ui/index.html` with
-`/ui-test-artifacts/<name_of_failing_png_test_from_logs>.png`. This allows you
-to tell where in the picture the change was introduced.
+by using a link ending with `ui-test-artifacts/index.html`. Report located on
+that page contains changed screenshots as well as a command to accept the
+changes if these are desirable.
 
 Android CTS tests
 -----------------
@@ -177,7 +177,7 @@
 adb shell /data/local/tmp/CtsPerfettoTestCases64
 ```
 
-Chromium waterfall
+{#chromium} Chromium waterfall
 ------------------
 Perfetto is constantly rolled into chromium's //third_party/perfetto via
 [this autoroller](https://autoroll.skia.org/r/perfetto-chromium-autoroll).
@@ -185,4 +185,66 @@
 The [Chromium CI](https://build.chromium.org) runs the `perfetto_unittests`
 target, as defined in the [buildbot config][chromium_buildbot].
 
+You can also test a pending Perfetto CL against Chromium's CI / TryBots
+before submitting it. This can be useful when making trickier API changes or to
+test on platforms that the Perfetto CI doesn't cover (e.g. Windows, MacOS),
+allowing you to verify the patch before you submit it (and it then eventually
+auto-rolls into Chromium).
+
+To do this, first make sure you have uploaded your Perfetto patch to the
+Android Gerrit. Next, create a new Chromium CL that modifies Chromium's
+`//src/DEPS` file.
+
+If you recently uploaded your change, it may be enough to modify the git commit
+hash in the `DEPS` entry for `src/third_party/perfetto`:
+
+```
+  'src/third_party/perfetto':
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '8fe19f55468ee227e99c1a682bd8c0e8f7e5bcdb',
+```
+
+Replace the git hash with the commit hash of your most recent patch set, which
+you can find in gerrit next to the active patch set number.
+
+Alternatively, you can add `hooks` to patch in the pending CL on top of
+Chromium's current third_party/perfetto revision. For this, add the following
+entries to the `hooks` array in Chromium's `//src/DEPS` file, modifying the
+`refs/changes/XX/YYYYYYY/ZZ` to the appropriate values for your gerrit change.
+You can see these values when pressing the "Download" button in gerrit. You can
+also use this method to patch in multiple Perfetto changes at once by
+adding additional `hooks` entries. [Here][chromium_cl]'s an example CL.
+
+```
+  {
+    'name': 'fetch_custom_patch',
+    'pattern': '.',
+    'action': [ 'git', '-C', 'src/third_party/perfetto/',
+                'fetch', 'https://android.googlesource.com/platform/external/perfetto',
+                'refs/changes/XX/YYYYYYY/ZZ',
+    ],
+  },
+  {
+    'name': 'apply_custom_patch',
+    'pattern': '.',
+    'action': ['git', '-C', 'src/third_party/perfetto/',
+               '-c', 'user.name=Custom Patch', '-c', 'user.email=custompatch@example.com',
+               'cherry-pick', 'FETCH_HEAD',
+    ],
+  },
+```
+
+If you'd like to test your change against the SDK build of Chrome, you
+can add `Cq-Include-Trybots:` lines for perfetto SDK trybots to the change
+description in gerrit (this won't be needed once Chrome's migration to the
+SDK is complete, see [tracking bug][sdk_migration_bug]):
+
+```
+Cq-Include-Trybots: luci.chromium.try:linux-perfetto-rel
+Cq-Include-Trybots: luci.chromium.try:android-perfetto-rel
+Cq-Include-Trybots: luci.chromium.try:mac-perfetto-rel
+Cq-Include-Trybots: luci.chromium.try:win-perfetto-rel
+```
+
 [chromium_buildbot]: https://cs.chromium.org/search/?q=perfetto_.*tests+f:%5Esrc/testing.*json$&sq=package:chromium&type=cs
+[chromium_cl]: https://chromium-review.googlesource.com/c/chromium/src/+/2030528
+[sdk_migration_bug]: https://crbug.com/1006541
diff --git a/docs/contributing/ui-development.md b/docs/contributing/ui-development.md
new file mode 100644
index 0000000..692ab66
--- /dev/null
+++ b/docs/contributing/ui-development.md
@@ -0,0 +1,48 @@
+# UI development
+
+Some tips to get started with the UI development:
+
+## Development environment
+
+If you're looking for an IDE to write the TypeScript code, Visual Studio Code
+works well out of the box. WebStorm or IntelliJ Idea Ultimate (Community does
+not have JavaScript/TypeScript support) also work really well. The code is
+located in the `ui` folder.
+
+## Working with devserver
+
+See [Build Instructions](build-instructions.md) page for the details about
+starting the local development server.
+
+The devserver has a live reload functionality: once you make a change in
+TypeScript files, the resulting code will be recompiled and the page is going to
+reload automatically. By default, this logic uses a timeout in order to prevent
+successive reloads on rapid changes. This logic can be disabled via
+development-only "Rapid live reload" flag in the UI. Disabling it will reload
+the page earlier, at the cost of sometimes making multiple reloads in a row.
+
+## Mithril components
+
+Perfetto UI uses the [Mithril](https://mithril.js.org/) library for rendering
+the interface. The majority of the components in the codebase use
+[class components](https://mithril.js.org/components.html#classes). When Mithril
+is imported via `m` alias (as it is usually done in the codebase), the class
+component should extend `m.ClassComponent`, which has an optional generic
+parameter allowing the component to take inputs. The entry point of class
+components is a `view` method, returning a tree of virtual DOM elements to be
+rendered when the component is present on the page.
+
+## Component state
+
+Local state of components can reside in class members and accessed directly in
+methods via accessing `this`. State that is shared across different components
+is stored in the `State` class definition, and should be modified via
+implementing a new action in `src/common/actions.ts`. A new field added to
+`State` should be initialized in `src/common/empty_state.ts`.
+
+There are restrictions on whan can be used in the global state: plain JS objects
+are OK, but class instances are not (this limitation is due to state
+serialization: the state should be a valid JSON object). If storing class
+instances (like `Map` and `Set` data structures) is necessary, these can be
+stored in the `NonSerializableState` portion of the state, that is omitted from
+saving into JSON objects.
diff --git a/docs/data-sources/android-game-intervention-list.md b/docs/data-sources/android-game-intervention-list.md
new file mode 100644
index 0000000..f8cb833
--- /dev/null
+++ b/docs/data-sources/android-game-intervention-list.md
@@ -0,0 +1,50 @@
+# Android Game Intervention List
+
+_This data source is supported only on Android userdebug builds._
+
+The "android.game_interventions" data source gathers  the list of available game modes and game interventions of each game.
+
+This allows you to better compare between or document traces of the same game but under different game mode or with different game intervention.
+
+### UI
+
+At the UI level, game interventions are shown as a table in trace info page.
+
+![](/docs/images/android_game_interventions.png "Android game intervention list in the UI")
+
+### SQL
+
+At the SQL level, game interventions data is written in the following table:
+
+* [`android_game_intervention_list`](docs/analysis/sql-tables.autogen#android_game_intervention_list)
+
+Below is an example of querying what modes are supported (with interventions) and the current game mode of each game.
+
+```sql
+select package_name, current_mode, standard_mode_supported, performance_mode_supported, battery_mode_supported
+from android_game_intervention_list
+order by package_name
+```
+package_name | current_mode | standard_mode_supported | performance_mode_supported | battery_mode_supported
+-------------|--------------|-------------------------|---------------------------|-----------------------
+com.supercell.clashofclans | 1 | 1 | 0 | 1
+com.mobile.legends | 3 | 1 | 0 | 1
+com.riot.league.wildrift | 1 | 1 | 0 | 1
+
+### TraceConfig
+
+Android game intervention list is configured through [AndroidGameInterventionListConfig](/docs/reference/trace-config-proto.autogen#AndroidGameInterventionListConfig) section of trace config.
+
+Sample config:
+
+```protobuf
+data_sources: {
+    config {
+        name: "android.game_interventions"
+        android_game_intervention_list_config {
+            package_name_filter: "com.my.game1"
+            package_name_filter: "com.my.game2"
+        }
+    }
+}
+```
diff --git a/docs/data-sources/battery-counters.md b/docs/data-sources/battery-counters.md
index 5cc837c..0628263 100644
--- a/docs/data-sources/battery-counters.md
+++ b/docs/data-sources/battery-counters.md
@@ -111,14 +111,15 @@
 }
 ```
 
-## Power rails
+## {#odpm} On-Device Power Rails Monitor (ODPM)
 
 _This data source has been introduced in Android 10 (Q) and requires the
 dedicated hardware on the device. This hardware is not yet available on
 most production phones._
 
 Recent version of Android introduced the support for more advanced power
-monitoring at the hardware subsystem level, known as "Power rail counters".
+monitoring at the hardware subsystem level, known as
+"On-Device Power Rail Monitors" (ODPMs).
 These counters measure the energy drained by (groups of) hardware units.
 
 Unlike the battery counters, they are not affected by the charging/discharging
@@ -128,11 +129,14 @@
 manufacturer. At the platform level this data is obtained polling the
 Android [IPowerStats HAL][power-hal].
 
+Googlers: See [go/power-rails-internal-doc](http://go/power-rails-internal-doc)
+for instructions on how to change the refault rail selection on Pixel devices.
+
 [power-hal]: https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/power/stats/1.0/IPowerStats.hal
 
 Simplified block diagram:
 
-![](/docs/images/power-rails.png "Block diagram of power rail counters")
+![](/docs/images/power-rails.png "Block diagram of ODPMs")
 
 ### TraceConfig
 
diff --git a/docs/data-sources/cpu-freq.md b/docs/data-sources/cpu-freq.md
index 5f67b24..7185c74 100644
--- a/docs/data-sources/cpu-freq.md
+++ b/docs/data-sources/cpu-freq.md
@@ -121,7 +121,7 @@
 ### TraceConfig
 
 ```protobuf
-// Event-driven recording of frequency and idle state changes.
+# Event-driven recording of frequency and idle state changes.
 data_sources: {
     config {
         name: "linux.ftrace"
@@ -133,7 +133,7 @@
     }
 }
 
-// Polling the current cpu frequency.
+# Polling the current cpu frequency.
 data_sources: {
     config {
         name: "linux.sys_stats"
@@ -143,7 +143,7 @@
     }
 }
 
-// Reporting the list of available frequency for each CPU.
+# Reporting the list of available frequency for each CPU.
 data_sources {
     config {
         name: "linux.system_info"
diff --git a/docs/data-sources/cpu-scheduling.md b/docs/data-sources/cpu-scheduling.md
index 29a2144..062d9b9 100644
--- a/docs/data-sources/cpu-scheduling.md
+++ b/docs/data-sources/cpu-scheduling.md
@@ -6,7 +6,7 @@
 
 This allows to get fine grained scheduling events such as:
 
-* Which threads were scheduling on which CPU cores at any point in time, with
+* Which threads were scheduled on which CPU core at any point in time, with
   nanosecond accuracy.
 * The reason why a running thread got descheduled (e.g. pre-emption, blocked on
   a mutex, blocking syscall or any other wait queue).
@@ -35,19 +35,6 @@
 
 ![](/docs/images/thread-states.png "States of individual threads")
 
-
-```protobuf
-data_sources {
-  config {
-    name: "linux.ftrace"
-    ftrace_config {
-      ftrace_events: "sched/sched_switch"
-      ftrace_events: "sched/sched_waking"
-    }
-  }
-}
-```
-
 ## SQL
 
 At the SQL level, the scheduling data is exposed in the
@@ -73,25 +60,32 @@
 
 ## TraceConfig
 
+To collect this data, include the following data sources:
+
 ```protobuf
+# Scheduling data from the kernel.
 data_sources: {
-    config {
-        name: "linux.ftrace"
-        ftrace_config {
-            ftrace_events: "sched/sched_switch"
-            ftrace_events: "sched/sched_process_exit"
-            ftrace_events: "sched/sched_process_free"
-            ftrace_events: "task/task_newtask"
-            ftrace_events: "task/task_rename"
-        }
+  config {
+    name: "linux.ftrace"
+    ftrace_config {
+      compact_sched: {
+        enabled: true
+      }
+      ftrace_events: "sched/sched_switch"
+      # optional: precise thread lifetime tracking:
+      ftrace_events: "sched/sched_process_exit"
+      ftrace_events: "sched/sched_process_free"
+      ftrace_events: "task/task_newtask"
+      ftrace_events: "task/task_rename"
     }
+  }
 }
 
-# This is to get full process name and thread<>process relationships.
+# Adds full process names and thread<>process relationships:
 data_sources: {
-    config {
-        name: "linux.process_stats"
-    }
+  config {
+    name: "linux.process_stats"
+  }
 }
 ```
 
@@ -169,19 +163,19 @@
 | end_state  | Translation            |
 |------------|------------------------|
 | R          | Runnable               |
+| R+         | Runnable (Preempted)   |
 | S          | Sleeping               |
-| D          | Uninterruptible Sleep |
+| D          | Uninterruptible Sleep  |
 | T          | Stopped                |
 | t          | Traced                 |
 | X          | Exit (Dead)            |
 | Z          | Exit (Zombie)          |
 | x          | Task Dead              |
-| I          | Task Dead              |
+| I          | Idle                   |
 | K          | Wake Kill              |
 | W          | Waking                 |
 | P          | Parked                 |
 | N          | No Load                |
-| +          | (Preempted)            |
 
 Not all combinations of characters are meaningful.
 
diff --git a/docs/data-sources/java-heap-profiler.md b/docs/data-sources/java-heap-profiler.md
index f67cf03..e3d5921 100644
--- a/docs/data-sources/java-heap-profiler.md
+++ b/docs/data-sources/java-heap-profiler.md
@@ -1,15 +1,17 @@
-# Memory: Java heap profiler
+# Memory: Java heap dumps
 
-NOTE: The Java heap profiler requires Android 11 or higher
+NOTE: Capturing Java heap dumps requires Android 11 or higher
 
 See the [Memory Guide](/docs/case-studies/memory.md#java-hprof) for getting
-started with Java heap profiling.
+started with Java heap dumps.
 
-Conversely from the [Native heap profiler](native-heap-profiler.md), the Java
-heap profiler reports full retention graphs of managed objects but not
-call-stacks. The information recorded by the Java heap profiler is of the form:
-_Object X retains object Y, which is N bytes large, through its class member
-named Z_.
+Conversely from [Native heap profiles](native-heap-profiler.md), Java heap dumps
+report full retention graphs of managed objects but not call-stacks. The
+information recorded in a Java heap dump is of the form: _Object X retains
+object Y, which is N bytes large, through its class member named Z_.
+
+Java heap dumps are not to be confused with profiles taken by the
+[Java heap sampler](native-heap-profiler.md#java-heap-sampling)
 
 ## UI
 
@@ -17,13 +19,13 @@
 diamond in the _"Heap Profile"_ track of a process. Each diamond corresponds to
 a heap dump.
 
-![Java heap profiles in the process tracks](/docs/images/profile-diamond.png)
+![Java heap dumps in the process tracks](/docs/images/profile-diamond.png)
 
-![Flamegraph of a Java heap profiler](/docs/images/java-flamegraph.png)
+![Flamegraph of a Java heap dump](/docs/images/java-heap-graph.png)
 
 The native size of certain objects is represented as an extra child node in the
 flamegraph, prefixed with "[native]". The extra node counts as an extra object.
-This is available only on Android T+.
+This is available only on Android 13 or higher.
 
 ## SQL
 
@@ -93,7 +95,7 @@
 
 ## TraceConfig
 
-The Java heap profiler is configured through the
+The Java heap dump data source is configured through the
 [JavaHprofConfig](/docs/reference/trace-config-proto.autogen#JavaHprofConfig)
 section of the trace config.
 
diff --git a/docs/data-sources/native-heap-profiler.md b/docs/data-sources/native-heap-profiler.md
index 720b806..9cc9d8d 100644
--- a/docs/data-sources/native-heap-profiler.md
+++ b/docs/data-sources/native-heap-profiler.md
@@ -1,13 +1,17 @@
-# Native heap profiler
+# Heap profiler
 
 NOTE: **heapprofd requires Android 10 or higher**
 
-Heapprofd is a tool that tracks native heap allocations & deallocations of an
-Android process within a given time period. The resulting profile can be used to
+Heapprofd is a tool that tracks heap allocations & deallocations of an Android
+process within a given time period. The resulting profile can be used to
 attribute memory usage to particular call-stacks, supporting a mix of both
 native and java code. The tool can be used by Android platform and app
 developers to investigate memory issues.
 
+By default, the tool records native allocations and deallocations done with
+malloc/free (or new/delete). It can be configured to record java heap memory
+allocations instead: see [Java heap sampling](#java-heap-sampling) below.
+
 On debug Android builds, you can profile all apps and most system services.
 On "user" builds, you can only use it on apps with the debuggable or
 profileable manifest flag.
@@ -25,7 +29,7 @@
 
 ![heapprofd snapshots in the UI tracks](/docs/images/profile-diamond.png)
 
-![heapprofd flamegraph](/docs/images/native-flamegraph.png)
+![heapprofd flamegraph](/docs/images/native-heap-prof.png)
 
 ## SQL
 
@@ -68,6 +72,13 @@
 For the full arguments list see the
 [heap_profile cmdline reference page](/docs/reference/heap_profile-cli).
 
+You can use the [Perfetto UI](https://ui.perfetto.dev) to visualize heap dumps.
+Upload the `raw-trace` file in your output directory. You will see all heap
+dumps as diamonds on the timeline, click any of them to get a flamegraph.
+
+Alternatively [Speedscope](https://speedscope.app) can be used to visualize
+the gzipped protos, but will only show the "Unreleased malloc size" view.
+
 #### Using the Recording page of Perfetto UI
 
 You can also use the [Perfetto UI](https://ui.perfetto.dev/#!/record/memory)
@@ -78,30 +89,56 @@
 
 ## Viewing the data
 
-The resulting profile proto contains four views on the data
+![Profile Diamond](/docs/images/profile-diamond.png)
 
-* **Unreleased size**: how many bytes were allocated but not freed at this
-  callstack the moment the dump was created.
-* **Total size**: how many bytes were allocated (including ones freed at the
-  moment of the dump) at this callstack
-* **Unreleased count**: how many allocations without matching frees were done at
-  this callstack.
-* **Total count**: how many allocations (including ones with matching frees)
-  were done at this callstack.
+The resulting profile proto contains four views on the data, for each diamond.
+
+* **Unreleased malloc size**: how many bytes were allocated but not freed at
+  this callstack, from the moment the recording was started until the timestamp
+  of the diamond.
+* **Total malloc size**: how many bytes were allocated (including ones freed at
+  the moment of the dump) at this callstack, from the moment the recording was
+  started until the timestamp of the diamond.
+* **Unreleased malloc count**: how many allocations without matching frees were
+  done at this callstack, from the moment the recording was started until the
+  timestamp of the diamond.
+* **Total malloc count**: how many allocations (including ones with matching
+  frees) were done at this callstack, from the moment the recording was started
+  started until the timestamp of the diamond.
 
 _(Googlers: You can also open the gzipped protos using http://pprof/)_
 
 TIP: you might want to put `libart.so` as a "Hide regex" when profiling apps.
 
-You can use the [Perfetto UI](https://ui.perfetto.dev) to visualize heap dumps.
-Upload the `raw-trace` file in your output directory. You will see all heap
-dumps as diamonds on the timeline, click any of them to get a flamegraph.
-
-Alternatively [Speedscope](https://speedscope.app) can be used to visualize
-the gzipped protos, but will only show the space view.
-
 TIP: Click Left Heavy on the top left for a good visualization.
 
+## Continuous dumps
+
+By default, the heap profiler captures all the allocations from the beginning of
+the recording and stores a single snapshot, shown as a single diamond in the UI,
+which summarizes all allocations/frees.
+
+It is possible to configure the heap profiler to periodically (not just at the
+end of the trace) store snapshots (continuous dumps), for example every 5000ms
+
+* By setting "Continuous dumps interval" in the UI to 5000.
+* By adding
+  ```
+  continuous_dump_config {
+    dump_interval_ms: 5000
+  }
+  ```
+  in the
+  [HeapprofdConfig](/docs/reference/trace-config-proto.autogen#HeapprofdConfig).
+* By adding `-c 5000` to the invocation of
+  [`tools/heap_profile`](/docs/reference/heap_profile-cli).
+
+![Continuous dump flamegraph](/docs/images/heap_prof_continuous.png)
+
+The resulting visualization shows multiple diamonds. Clicking on each diamond
+shows a summary of the allocations/frees from the beginning of the trace until
+that point (i.e. the summary is cumulative).
+
 ## Sampling interval
 
 Heapprofd samples heap allocations by hooking calls to malloc/free and C++'s
@@ -217,6 +254,41 @@
 </manifest>
 ```
 
+## {#java-heap-sampling} Java heap sampling
+
+NOTE: **Java heap sampling is available on Android 12 or higher**
+
+NOTE: **Java heap sampling is not to be confused with [Java heap
+dumps](/docs/data-sources/java-heap-profiler.md)**
+
+Heapprofd can be configured to track Java allocations instead of native ones.
+* By setting adding `heaps: "com.android.art"` in
+  [HeapprofdConfig](/docs/reference/trace-config-proto.autogen#HeapprofdConfig).
+* By adding `--heaps com.android.art` to the invocation of
+  [`tools/heap_profile`](/docs/reference/heap_profile-cli).
+
+Unlike java heap dumps (which show the retention graph of a snapshot of the live
+objects) but like native heap profiles, java heap samples show callstacks of
+allocations over time of the entire profile.
+
+Java heap samples only show callstacks of when objects are created, not when
+they're deleted or garbage collected.
+
+![javaheapsamples](/docs/images/java-heap-samples.png)
+
+The resulting profile proto contains two views on the data:
+
+* **Total allocation size**: how many bytes were allocated at this callstack
+  over time of the profile until this point. The bytes might have been freed or
+  not, the tool does not keep track of that.
+* **Total allocation count**: how many object were allocated at this callstack
+  over time of the profile until this point. The objects might have been freed
+  or not, the tool does not keep track of that.
+
+Java heap samples are useful to understand memory churn showing the call stack
+of which parts of the code large allocations are attributed to as well as the
+allocation type from the ART runtime.
+
 ## DEDUPED frames
 
 If the name of a Java method includes `[DEDUPED]`, this means that multiple
@@ -298,7 +370,7 @@
 If your profile contains obfuscated Java methods (like `fsd.a`), you can
 provide a deobfuscation map to turn them back into human readable.
 To do so, use the `PERFETTO_PROGUARD_MAP` environment variable, using the
-format `packagename=filename[:packagename=filename...]`, e.g.
+format `packagename=map_filename[:packagename=map_filename...]`, e.g.
 `PERFETTO_PROGUARD_MAP=com.example.pkg1=foo.txt:com.example.pkg2=bar.txt`.
 All tools
 (traceconv, trace_processor_shell, the heap_profile script) support specifying
@@ -306,13 +378,17 @@
 
 You can get a deobfuscation map for your trace using
 `tools/traceconv deobfuscate`. Then concatenate the resulting file to your
-trace to get a deobfuscated version of it.
+trace to get a deobfuscated version of it (the input trace should be in the
+perfetto format, otherwise concatenation will not produce a reasonable output).
 
 ```
-PERFETTO_PROGUARD_MAP=com.example.pkg tools/traceconv deobfuscate ${TRACE} > deobfuscation_map
+PERFETTO_PROGUARD_MAP=com.example.pkg=proguard_map.txt tools/traceconv deobfuscate ${TRACE} > deobfuscation_map
 cat ${TRACE} deobfuscation_map > deobfuscated_trace
 ```
 
+`deobfuscated_trace` can be viewed in the
+[Perfetto UI](https://ui.perfetto.dev).
+
 ## Troubleshooting
 
 ### Buffer overrun
@@ -390,7 +466,7 @@
 to do this once.
 
 ```
-tools/build_all_configs.py
+tools/setup_all_configs.py
 ninja -C out/linux_clang_release
 ```
 
@@ -427,6 +503,18 @@
 
 ## Known Issues
 
+### {#known-issues-android13} Android 13
+
+* Unwinding java frames might not work properly, depending on the ART module
+  version in use. The UI reports a single "unknown" frame at the top of the
+  stack in this case. The problem is fixed in Android 13 QPR1.
+
+### {#known-issues-android12} Android 12
+
+* Unwinding java frames might not work properly, depending on the ART module
+  version in use. The UI reports a single "unknown" frame at the top of the
+  stack in this case.
+
 ### {#known-issues-android11} Android 11
 
 * 32-bit programs cannot be targeted on 64-bit devices.
@@ -532,7 +620,7 @@
 Trace Processor. For each frame, we get one row for the number of allocated
 bytes, where `count` and `size` is positive, and, if any of them were already
 freed, another line with negative `count` and `size`. The sum of those gets us
-the `space` view.
+the `Unreleased malloc size` view.
 
 ```sql
 select a.callsite_id, a.ts, a.upid, f.name, f.rel_pc, m.build_id, m.name as mapping_name,
diff --git a/docs/design-docs/api-and-abi.md b/docs/design-docs/api-and-abi.md
index c930c95..e93a4b0 100644
--- a/docs/design-docs/api-and-abi.md
+++ b/docs/design-docs/api-and-abi.md
@@ -254,7 +254,7 @@
 any doesn't use any unix socket. Instead it uses the functionally equivalent
 Mojo endpoints [`Producer{Client,Host}` and `Consumer{Client,Host}`][mojom].
 
-### Shared memory
+### {#shmem-abi} Shared memory
 
 This section describes the binary interface of the memory buffer shared between
 a producer process and the tracing service (SMB).
@@ -396,7 +396,7 @@
 IPC (the Producer ID is not spoofable and is tied to the IPC socket file
 descriptor).
 
-### Proto definitions
+### {#protos} Proto definitions
 
 The following protobuf messages are part of the overall trace protocol ABI and
 are updated maintaining backward-compatibility, unless marked as experimental
diff --git a/docs/design-docs/batch-trace-processor.md b/docs/design-docs/batch-trace-processor.md
new file mode 100644
index 0000000..1b3199a
--- /dev/null
+++ b/docs/design-docs/batch-trace-processor.md
@@ -0,0 +1,193 @@
+# Batch Trace Processor
+This document describes the overall design of Batch Trace Processor and
+aids in integrating it into other systems.
+
+![BTP Overview](/docs/images/perfetto-btp-overview.svg)
+
+## Motivation
+The Perfetto trace processor is the de-facto way to perform analysis on a
+single trace. Using the
+[trace processor Python API](/docs/analysis/trace-processor#python-api),
+traces can be queried interactively, plots made from those results etc.
+
+While queries on a single trace are useful when debugging a specific problem
+in that trace or in the very early stages of understanding a domain, it soon
+becomes limiting. One trace is unlikely to be representative
+of the entire population and it's easy to overfit queries i.e. spend a
+lot of effort on breaking down a problem in that trace while neglecting
+other, more common issues in the population.
+
+Because of this, what we actually want is to be able to query many traces
+(usually on the order of 250-10000+) and identify the patterns which show
+up in a significant fraction of them. This ensures that time is being spent
+on issues which are affecting user experience instead of just a random
+problem which happened to show up in the trace.
+
+One low-effort option for solving this problem is simply to ask people to use
+utilities like [Executors](https://docs.python.org/3/library/concurrent.futures.html#executor-objects)
+with the Python API to load multiple traces and query them in parallel.
+Unfortunately, there are several downsides to this approach:
+* Every user has to reinvent the wheel every time they want to query multiple
+  traces. Over time, there would likely be a proliferation of slightly modified
+  code which is copied from each place.
+* While the basics of parallelising queries on multiple traces on a single
+  machine is straightforward, one day, we may want to shard trace processing
+  across multiple machines. Once this happens, the complexity of the code would
+  rise significantly to the point where a central implementation becomes a
+  necessity. Because of this, it's better to have the API first before engineers
+  start building their own custom solutions.
+* A big aim for the Perfetto team these days is to make trace analysis more
+  accessible to reduce the number of places where we need to be in the loop.
+  Having a well supported API for an important usecase like bulk trace analysis
+  directly helps with this.
+
+While we've discussed querying traces so far, the experience for loading traces
+from different traces should be just as good. This has historically been a big
+reason why the Python API has not gained as much adoption as we would have
+liked.
+
+Especially internally in Google, we should not be relying on engineers
+knowing where traces live on the network filesystem and the directory layout.
+Instead, they should be able to simply be able to specify the data source (i.e.
+lab, testing population) and some parameters (e.g. build id, date, kernel
+version) that traces should match should match and traces meeting these criteria
+should found and loaded.
+
+Putting all this together, we want to build a library which can:
+* Interactively query ~1000+ traces in O(s) (for simple queries)
+* Expose full SQL expressiveness from trace processor
+* Load traces from many sources with minimal ceremony. This should  include
+  Google-internal sources: e.g. lab runs and internal testing populations
+* Integrate with data analysis libraries for easy charting and visulazation
+
+## Design Highlights
+In this section, we briefly discuss some of the most impactful design decisions
+taken when building batch trace processor and the reasons behind them.
+
+### Language
+The choice of langugage is pretty straightforward. Python is already the go-to
+langugage for data analysis in a wide variety of domains and our problem
+is not unique enough to warrant making a different decision. Moreover, another
+point in favour is the existence of the Python API for trace processor. This
+further eases the implementation as we do not have to start from scratch.
+
+The main downside of choosing Python is performance but given that that all
+the data crunching happens in C++ inside TP,  this is not a big factor.
+
+### Trace URIs and Resolvers
+[Trace URIs](/docs/analysis/batch-trace-processor#trace-uris)
+are an elegant solution to the problem of loading traces from a diverse range
+of public and internal sources. As with web URIs, the idea with trace URIs is
+to describe both the protocol (i.e. the source) from which traces should be
+fetched and the arguments (i.e. query parameters) which the traces should match.
+
+Batch trace processor should integrate tightly with trace URIs and their
+resolvers. Users should be able to pass either just the URI (whcih is really
+just a string for maximum flexibility) or a resolver object which can yield a
+list of trace file paths.
+
+To handle URI strings, there should be some mecahinsm of "registering" resolvers
+to make them eligible to resolve a certain "protocol". By default, we should
+provide a resolver to handle filesystem. We should ensure that the resolver
+design is such that resolvers can be closed soruce while the rest of batch trace
+processor is open.
+
+Along with the job of yielding a list of traces, resolvers should also be
+responsible for creating metadata for each trace these are different pieces
+of information about the trace that the user might be interested in e.g. OS
+version, device name, collected date etc. The metadata can then be used when
+"flattening" results across many traces as discussed below.
+
+### Persisting loaded traces
+Optimizing the loading of traces is critical for the O(s) query performance
+we want out of batch trace processor. Traces are often accessed
+over the network meaning fetching their contents has a high latency.
+Traces also take at least a few seconds to parse, eating up the budget for
+O(s) before even getting the running time of queries.
+
+To address this issue, we take the decision to keep all traces fully loaded in
+memory in trace processor instances. That way, instead of loading them on every
+query/set of queries, we can issue queries directly.
+
+For the moment, we restrict the loading and querying of traces to a
+single machine. While querying n traces is "embarassngly parallel" and shards
+perfectly across multiple machines, introducing distributed systems to any
+solution simply makes everything more complicated. The move to multiple
+machines is explored further in the "Future plans" section.
+
+### Flattening query results
+The naive way to return the result of querying n traces is a list
+of n elements, with each element being result for a single trace. However,
+after performing several case-study performance investigations using BTP, it
+became obvious that this obvious answer was not the most convienent for the end
+user.
+
+Instead, a pattern which proved very useful was to "flatten" the results into
+a single table, containing the results from all the traces. However,
+simply flattening causes us to lose the information about which trace a row
+originated from. We can deal with this by allowing resolvers to silently add
+columns with the metadata for each trace.
+
+
+So suppose we query three traces with:
+
+```SELECT ts, dur FROM slice```
+
+Then in the flattening operation might do something like this behind the scenes:
+![BTP Flattening](/docs/images/perfetto-btp-flattening.svg)
+
+
+## Integration points
+Batch trace processor needs to be both open source yet allow deep integration
+with Google internal tooling. Because of this, there are various integration
+points built design to allow closed compoentns to be slotted in place of the
+default, open source ones.
+
+The first point is the formalization of the idea "platform" code. Even since the
+begining of the Python API, there was always a need for code internally to be
+run slightly different to open source code. For example, Google internal Python
+distrubution does not use Pip, instead packaging dependencies into a single
+binary. The notion of a "platform" loosely existed to abstract this sort of
+differences but this was very ad-hoc. As part of batch trace processor
+implementation, this has been retroactively formalized.
+
+Resolvers are another big point of pluggability. By allowing registration of
+a "protocol" for each internal trace source (e.g. lab, testing population), we
+allow for trace loading to be neatly abstracted.
+
+Finally, for batch trace processor specifically, we abstract the creation of
+thread pools for loading traces and running queries. The parallelism and memory
+available to programs internally is often does not 1:1 correspond with the
+available CPUs/memory on the system: internal APIs need to be accessed to find
+out this information.
+
+## Future plans
+One common problem when running batch trace processor is that we are
+constrained by a single machine and so can only load O(1000) traces.
+For rare problems, there might only be a handful of traces matching a given
+pattern even in such a large sample.
+
+A way around this would be to build a "no trace limit" mode. The idea here
+is that you would develop queries like usual with batch trace processor
+operating on a O(1000) traces with O(s) performance. Once the queries are
+relatively finalized, we could then "switch" the mode of batch trace processor
+to opeate closer to a "MapReduce" style pipeline which operates over O(10000)+
+traces loading O(n cpus) traces at any one time.
+
+This allows us to retain both the quick iteration speed while developing queries
+while also allowing for large scale analysis without needing to move code
+to a pipeline model. However, this approach does not really resolve the root
+cause of the problem which is that we are restricted to a single machine.
+
+The "ideal" solution here is to, as mentioned above, shard batch trace processor
+across >1 machine. When querying traces, each trace is entirely independent of
+any other so paralleising across multiple machines yields very close to perfect
+gains in performance at little cost.
+
+This is would be however quite a complex undertaking. We would need to design
+the API in such a way that allows for pluggable integration with various compute
+platforms (e.g. GCP, Google internal, your custom infra). Even restricting to
+just Google infra and leaving others as open for contribution, internal infra's
+ideal workload does not match the approach of "have a bunch of machines tied to
+one user waiting for their input". There would need to be significiant research
+and design work before going here but it would likely be wortwhile.
diff --git a/docs/design-docs/heapprofd-sampling.md b/docs/design-docs/heapprofd-sampling.md
index 3ebfb3d..9f238b5 100644
--- a/docs/design-docs/heapprofd-sampling.md
+++ b/docs/design-docs/heapprofd-sampling.md
@@ -1,6 +1,6 @@
 # heapprofd: Sampling for Memory Profiles
 
-_tomlewin, fmayer **·** 2021-04-14_  
+_tomlewin, fmayer **·** 2021-04-14_
 
 ## Background
 
@@ -173,7 +173,7 @@
 
 [algorithm]:
   https://cs.android.com/android/platform/superproject/+/master:external/perfetto/src/profiling/memory/sampler.h
-[example visualization]: /docs/images/syssrv-apk-assets-two.png
+[example visualization]: /docs/images/native-heap-prof.png
 [Android Heap Profiler]: /docs/design-docs/heapprofd-design
 [mean absolute percentage error]: https://en.wikipedia.org/wiki/Mean_absolute_percentage_error
-[full results]: https://gist.github.com/fmayer/3aafcaf58f8db09714ba09aa4ac2b1ac
\ No newline at end of file
+[full results]: https://gist.github.com/fmayer/3aafcaf58f8db09714ba09aa4ac2b1ac
diff --git a/docs/design-docs/pivot-tables.md b/docs/design-docs/pivot-tables.md
deleted file mode 100644
index ddc340c..0000000
--- a/docs/design-docs/pivot-tables.md
+++ /dev/null
@@ -1,206 +0,0 @@
-# Pivot Tables
-
-_**Project Plan**: [Perfetto: Pivot tables for slices](https://docs.google.com/document/d/1RuEGQKLgOA8YWjZJHD6CTA3ghRRg6o5Phg3_rFCJEDE/)_  
-_**How to Use**: [Pivot Table Usage](/docs/visualization/perfetto-ui#pivot-tables)_  
-_**For Googlers**: [Perfetto: Pivot Table Use Cases](https://docs.google.com/document/d/1_iR-JjD7m19Q9GQtMk1_5NLSYXFicB_gg4S9D-6Q8lU/)_  
-
-## Objective
-Pivot tables give a simplified aggregated view of more complex data. They are
-made up of a number of pivots and aggregations that are grouped around these
-pivots. You can add more columns/aggregations and drag and drop the columns to
-explore the underlying data.
-
-## Motivation
-Pivot tables are useful in debugging hangs, stalls, and digging into traces
-which usually have too much data to clearly see the problems.
-The pivot table allows users to create custom tables to view specific
-information about traces in a summarized and less complex way.
-
-## Main Components
-
-![Pivot table design](/docs/images/pivot-tables/pivot-table-design.png)
-
-### Details Panel (Frontend)
-The [DetailsPanel](https://cs.android.com/android/_/android/platform/external/perfetto/+/0ae7c36fd528824ee9fdea6cfd4494e9f05183b5:ui/src/frontend/details_panel.ts)
-searches for active PivotTables to display on screen. It also syncs the
-PivotTableHelper with data from the State. (PivotTableHelper only syncs when the
-PivotTableEditor modal is not open).
-
-
-### Pivot Table (Frontend)
-The [PivotTable](https://cs.android.com/android/_/android/platform/external/perfetto/+/0ae7c36fd528824ee9fdea6cfd4494e9f05183b5:ui/src/frontend/pivot_table.ts) builds
-the pivot table tab and the table. It also handles user requests (like opening
-the pivot table editor, drag and drop columns, expand, etc) by calling the
-PivotTableHelper and updating the table.
-
-
-### PivotTableEditor (Frontend)
-The [PivotTableEditor](https://cs.android.com/android/_/android/platform/external/perfetto/+/0ae7c36fd528824ee9fdea6cfd4494e9f05183b5:ui/src/frontend/pivot_table_editor.ts)
-consists of ColumnPicker and ColumnDisplay classes.
-ColumnPicker allows the user to select column type, name and aggregation. Edits
-made through the ColumnPicker are saved temporarily in the PivotTableHelper
-without updating the state.
-ColumnDisplay displays the selected column from the ColumnPicker, it also allows
-users to manipulate the columns after selection (like delete, reorder, change
-the default sorting, etc...).
-In this stage the user is able to query the selected columns and update the
-table or discard the changes made and the PivotTableHelper will resync with the
-data in state.
-
-
-### PivotTableHelper (Frontend)
-The [PivotTableHelper](https://cs.android.com/android/_/android/platform/external/perfetto/+/0ae7c36fd528824ee9fdea6cfd4494e9f05183b5:ui/src/frontend/pivot_table_helper.ts)
-is created by every PivotTableController for every PivotTableId. It stores a
-copy of the selectedPivots and selectedAggregations from state. It also holds
-the logic for manipulating the data locally, which are used by the PivotTable
-and PivotTableEditor.
-It also replaces the data in the State with the changes upon request.
-The PivotTableHelper also checks for special “stack” columns, called stackPivots
-(`name (stack)` for [slice table](/docs/analysis/sql-tables.autogen#slice) is
-currently the only special column), as it sets the column attributes which are
-then used to identify them by other components.
-
-
-### State (Common)
-[PivotTableState](https://cs.android.com/android/_/android/platform/external/perfetto/+/0ae7c36fd528824ee9fdea6cfd4494e9f05183b5:ui/src/common/state.ts;l=303) holds the
-information that needs to be transferred to and from the frontend and the
-controller for each pivot table instance (PivotTableId). It also includes the
-global PivotTableConfigs (like the availableColumns and availableAggregations).
-
-
-### PivotTableController (Controller)
-A new [PivotTableController](https://cs.android.com/android/_/android/platform/external/perfetto/+/0ae7c36fd528824ee9fdea6cfd4494e9f05183b5:ui/src/controller/pivot_table_controller.ts)
-is created for every PivotTableId.
-The PivotTableController handles the setup of the pivot table once added, it
-queries for the columns for all tables and sets the PivotTableConfig. It also
-creates and initializes a PivotTableHelper for every PivotTableId and publishes
-it to the frontend.
-Additionally, the PivotTableController handles the collection and the
-computation of all data needed by the PivotTableQueryGenerator.
-It constantly checks if a request has been set in the PivotTableState and acts
-on it if so.
-It decides what columns to query, what whereFilters and tables to include and
-how to reformat the query result into a PivotTableQueryResponse based on the
-request type.
-
-There are four types of requests implemented in the controller:
-
-**_QUERY:_**
-Queries the first pivot of the selectedPivots and all the aggregations,
-including any global or table-wide whereFilters (Like the start and end
-timestamp and selected track_ids that are set by the pivot table generated
-through area selection).
-It also adds a whereFilter (Filter in the where clause of the query) if the
-pivot is a stackPivot to restrict the result to the top level slices only, since
-descendants can be generated by expanding the cell and issuing the DESCENDANTS
-request, and returns the result as a PivotTableQueryResponse.
-
-![Pivot table query](/docs/images/pivot-tables/pivot-table-query.png)
-
-Returned PivotTableQueryResponse:
-
-```typescript
-pivotTableQueryResponse = {
-  columns: ['slice type', 'slice category', 'slice name'];
-  rows: [
-    {
-      row: 'internal_slice',
-      expandableColumns: ['slice type'],
-      expandedRows = [],
-    }, {
-      row: 'thread_slice',
-      expandableColumns: ['slice type'],
-      expandedRows = [],
-    };
-  ]
-}
-```
-
-**_EXPAND:_**
-The [PivotTableBody](https://cs.android.com/android/_/android/platform/external/perfetto/+/a9118d769009349da7f264abb392f4207e66602b:ui/src/frontend/pivot_table.ts;l=235;drc=0bc8ff07f372a58ca4d0399d88567a66ef5b591b) generates the nested structure by
-recursively displaying the rows and checking if the row contains any expanded
-rows with the isExpanded flag set to true. As it goes through the nested rows,
-it passes the row index that it's about to expand, along with the column it's
-expanding for till it reaches a [PivotTableRow](https://cs.android.com/android/_/android/platform/external/perfetto/+/0ae7c36fd528824ee9fdea6cfd4494e9f05183b5:ui/src/frontend/pivot_table.ts;l=192).
-The PivotTableRow creates a cell for each column. If the cell is at a column
-that can be expanded, it is created as an [ExpandableCell](https://cs.android.com/android/_/android/platform/external/perfetto/+/0ae7c36fd528824ee9fdea6cfd4494e9f05183b5:ui/src/frontend/pivot_table.ts;l=121).
-When an 'EXPAND' request is issued on an ExpandableCell, it sets the
-requestedAction in the PivotTableState and provides it with the SubQueryAttrs.
-
-Given a columnIdx, value, and an array of rowIndicies (SubQueryAttrs) from
-the requestedAction in the PivotTableState, it finds the exact row that called
-this request from the main PivotTableQueryResponse, and finds the next pivot
-to query. It then generates the query similarly to the ‘QUERY’ request, but
-includes the whereFilter of the previous column (column name = column value).
-The rows of the query result are then nested into the caller row’s expandedRows,
-to build a tree view structure while expanding.
-
-![Pivot table expanded cell](/docs/images/pivot-tables/pivot-table-expanded-cell.png)
-
-Passed value:
-
-```typescript
-subQueryAttrs = {
-  rowIndices: [0, 3],
-  columnIdx: 1,
-  value: 'blink,benchmark',
-  expandedRowColumns: ['slice category'],
-}
-```
-
-Returned expanded rows:
-
-```typescript
-rows = [
-  {
-    row: 'LocalFrameView::RunAccessibilityLifecyclePhase',
-    expandableColumns: [],
-    expandedRows: []
-  },
-  {
-    row: 'LocalFrameView::RunCompositingInputsLifecyclePhase',
-    expandableColumns: [],
-    expandedRows: []
-  },
-  {
-    row: 'LocalFrameView::RunStyleAndLayoutLifecyclePhases',
-    expandableColumns: [],
-    expandedRows: []
-  },
-  ...
-]
-```
-
-The returned rows are saved inside the caller row expandedRows map.
-
-```typescript
-rows = {
-  row: 'blink,benchmark',
-  expandableColumns: ['slice category'],
-  expandedRows: [
-    'slice name' => {
-        isExpanded: true,
-        rows
-      }
-  ]
-}
-```
-
-**_UNEXPAND:_**
-Sets the caller row’s isExpanded flag to false, to hide it from the display but
-also keeping its expandedRows saved so as to not have to query them again if
-requested.
-
-**_DESCENDANTS:_**
-Should only be called for stackPivots, generates a query containing the
-stackPivot and the next pivot, if it exists, and all the aggregations. It also
-requests the PivotTableQueryGenerator to order by depth first, which is then
-used to refactor the resulting rows into the PivotTableQueryResponse tree view
-structure.
-The returned format is similar to the EXPAND request.
-
-### PivotTableQueryGenerator (Common)
-[PivotTableQueryGenerator](https://cs.android.com/android/_/android/platform/external/perfetto/+/0ae7c36fd528824ee9fdea6cfd4494e9f05183b5:ui/src/common/pivot_table_query_generator.ts)
-generates an sql query based on the given data, along with any hidden columns
-that may need to be added. It also creates an alias for each pivot and
-aggregation that is used to identify the resulting cells in the rows.
diff --git a/docs/faq.md b/docs/faq.md
new file mode 100644
index 0000000..29ba71b
--- /dev/null
+++ b/docs/faq.md
@@ -0,0 +1,23 @@
+# Frequently Asked Questions
+This page contains some common questions that the Perfetto team is asked
+and their answers.
+
+- [Frequently Asked Questions](#frequently-asked-questions)
+  - [How do I open trace in UI from command line?](#how-do-i-open-trace-in-ui-from-command-line)
+
+## How do I open trace in UI from command line?
+When collecting traces from the command line, a convenient way to open traces
+is to use the [open_trace_in_ui script](/tools/open_trace_in_ui).
+
+This can be used as follows:
+```sh
+curl -OL https://github.com/google/perfetto/raw/master/tools/open_trace_in_ui
+chmod +x open_trace_in_ui
+./open_trace_in_ui -i /path/to/trace
+```
+
+If you already have a Perfetto checkout, the first steps can be skipped.
+From the Perfetto root, run:
+```sh
+tools/open_trace_in_ui -i /path/to/trace
+```
\ No newline at end of file
diff --git a/docs/images/android_game_interventions.png b/docs/images/android_game_interventions.png
new file mode 100644
index 0000000..48575fe
--- /dev/null
+++ b/docs/images/android_game_interventions.png
Binary files differ
diff --git a/docs/images/heap_prof_continuous.png b/docs/images/heap_prof_continuous.png
new file mode 100644
index 0000000..0d31e68
--- /dev/null
+++ b/docs/images/heap_prof_continuous.png
Binary files differ
diff --git a/docs/images/java-flamegraph-focus.png b/docs/images/java-flamegraph-focus.png
deleted file mode 100644
index d75648f..0000000
--- a/docs/images/java-flamegraph-focus.png
+++ /dev/null
Binary files differ
diff --git a/docs/images/java-flamegraph.png b/docs/images/java-flamegraph.png
deleted file mode 100644
index fa58843..0000000
--- a/docs/images/java-flamegraph.png
+++ /dev/null
Binary files differ
diff --git a/docs/images/java-heap-graph-focus.png b/docs/images/java-heap-graph-focus.png
new file mode 100644
index 0000000..cd5d232
--- /dev/null
+++ b/docs/images/java-heap-graph-focus.png
Binary files differ
diff --git a/docs/images/java-heap-graph.png b/docs/images/java-heap-graph.png
new file mode 100644
index 0000000..829c7fe
--- /dev/null
+++ b/docs/images/java-heap-graph.png
Binary files differ
diff --git a/docs/images/java-heap-samples.png b/docs/images/java-heap-samples.png
new file mode 100644
index 0000000..7bd4307
--- /dev/null
+++ b/docs/images/java-heap-samples.png
Binary files differ
diff --git a/docs/images/native-flamegraph.png b/docs/images/native-flamegraph.png
deleted file mode 100644
index 88b92e3..0000000
--- a/docs/images/native-flamegraph.png
+++ /dev/null
Binary files differ
diff --git a/docs/images/native-heap-prof-focus.png b/docs/images/native-heap-prof-focus.png
new file mode 100644
index 0000000..acb3da6
--- /dev/null
+++ b/docs/images/native-heap-prof-focus.png
Binary files differ
diff --git a/docs/images/native-heap-prof.png b/docs/images/native-heap-prof.png
new file mode 100644
index 0000000..5b7f9fa
--- /dev/null
+++ b/docs/images/native-heap-prof.png
Binary files differ
diff --git a/docs/images/perfetto-btp-flattening.svg b/docs/images/perfetto-btp-flattening.svg
new file mode 100644
index 0000000..583889e
--- /dev/null
+++ b/docs/images/perfetto-btp-flattening.svg
@@ -0,0 +1 @@
+<svg version="1.1" viewBox="0.0 0.0 1311.0892388451443 459.750656167979" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><clipPath id="p.0"><path d="m0 0l1311.0892 0l0 459.75067l-1311.0892 0l0 -459.75067z" clip-rule="nonzero"/></clipPath><g clip-path="url(#p.0)"><path fill="#000000" fill-opacity="0.0" d="m0 0l1311.0892 0l0 459.75067l-1311.0892 0z" fill-rule="evenodd"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m127.83465 71.8294l0 101.99737" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m227.17586 71.8294l0 101.99737" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m351.17847 71.8294l0 101.99737" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m127.33596 72.32809l224.34119 0" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m127.33596 107.32809l224.34119 0" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m127.33596 140.32808l224.34119 0" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m127.33596 173.32808l224.34119 0" fill-rule="nonzero"/><path fill="#4a4a4a" d="m171.3649 86.62746l4.828125 0l0 1.5625l-4.828125 0l0 -1.5625zm1.25 4.859375l0 -6.875l1.828125 0l0 6.53125q0 0.5 0.203125 0.765625q0.21875 0.25 0.703125 0.25q0.28125 0 0.46875 -0.078125q0.203125 -0.09375 0.4375 -0.234375l0 1.796875q-0.296875 0.109375 -0.609375 0.171875q-0.3125 0.0625 -0.671875 0.0625q-1.109375 0 -1.734375 -0.625q-0.625 -0.640625 -0.625 -1.765625zm7.960724 2.5q-0.921875 0 -1.578125 -0.265625q-0.640625 -0.265625 -1.046875 -0.6875q-0.40625 -0.4375 -0.609375 -0.953125l1.640625 -0.71875q0.234375 0.5 0.640625 0.765625q0.40625 0.25 0.953125 0.25q0.453125 0 0.8125 -0.140625q0.359375 -0.15625 0.359375 -0.546875q0 -0.28125 -0.1875 -0.4375q-0.1875 -0.15625 -0.46875 -0.25q-0.28125 -0.09375 -0.5625 -0.140625l-0.84375 -0.1875q-0.515625 -0.109375 -0.984375 -0.390625q-0.453125 -0.296875 -0.75 -0.734375q-0.28125 -0.4375 -0.28125 -1.015625q0 -0.640625 0.359375 -1.109375q0.375 -0.484375 1.015625 -0.75q0.640625 -0.265625 1.40625 -0.265625q0.71875 0 1.28125 0.171875q0.578125 0.15625 1.0 0.5q0.421875 0.34375 0.671875 0.875l-1.5625 0.6875q-0.234375 -0.421875 -0.59375 -0.578125q-0.34375 -0.171875 -0.75 -0.171875q-0.484375 0 -0.765625 0.1875q-0.28125 0.171875 -0.28125 0.421875q0 0.265625 0.28125 0.46875q0.28125 0.1875 0.671875 0.28125l1.0625 0.265625q1.0625 0.25 1.59375 0.8125q0.546875 0.546875 0.546875 1.34375q0 0.6875 -0.40625 1.21875q-0.40625 0.53125 -1.09375 0.8125q-0.6875 0.28125 -1.53125 0.28125z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m281.54697 93.98683q-0.984375 0 -1.78125 -0.46875q-0.78125 -0.484375 -1.234375 -1.34375q-0.453125 -0.859375 -0.453125 -1.96875q0 -1.125 0.453125 -1.96875q0.453125 -0.859375 1.234375 -1.34375q0.796875 -0.484375 1.78125 -0.484375q0.515625 0 0.921875 0.15625q0.421875 0.140625 0.734375 0.40625q0.3125 0.25 0.484375 0.53125l0.109375 0l-0.109375 -1.015625l0 -2.75l1.8125 0l0 10.03125l-1.703125 0l0 -0.859375l-0.109375 0q-0.171875 0.28125 -0.484375 0.53125q-0.3125 0.25 -0.734375 0.390625q-0.40625 0.15625 -0.921875 0.15625zm0.296875 -1.6875q0.515625 0 0.953125 -0.265625q0.453125 -0.265625 0.71875 -0.734375q0.28125 -0.46875 0.28125 -1.09375q0 -0.640625 -0.28125 -1.109375q-0.265625 -0.46875 -0.71875 -0.71875q-0.4375 -0.265625 -0.953125 -0.265625q-0.5 0 -0.9375 0.265625q-0.4375 0.25 -0.71875 0.71875q-0.265625 0.453125 -0.265625 1.109375q0 0.625 0.265625 1.109375q0.28125 0.46875 0.71875 0.734375q0.4375 0.25 0.9375 0.25zm8.037079 1.6875q-1.3125 0 -1.96875 -0.8125q-0.65625 -0.8125 -0.65625 -2.203125l0 -4.34375l1.828125 0l0 4.109375q0 0.734375 0.359375 1.15625q0.359375 0.40625 0.921875 0.40625q0.546875 0 0.921875 -0.265625q0.375 -0.28125 0.578125 -0.734375q0.203125 -0.46875 0.203125 -1.03125l0 -3.640625l1.828125 0l0 7.140625l-1.71875 0l0 -0.890625l-0.109375 0q-0.203125 0.328125 -0.546875 0.578125q-0.328125 0.25 -0.75 0.390625q-0.40625 0.140625 -0.890625 0.140625zm5.8078613 -0.21875l0 -7.140625l1.734375 0l0 0.953125l0.109375 0q0.171875 -0.34375 0.46875 -0.59375q0.3125 -0.265625 0.71875 -0.40625q0.40625 -0.15625 0.859375 -0.15625q0.28125 0 0.5 0.046875q0.234375 0.03125 0.40625 0.09375l0 1.84375q-0.296875 -0.109375 -0.59375 -0.1875q-0.28125 -0.078125 -0.59375 -0.078125q-0.546875 0 -0.953125 0.28125q-0.390625 0.265625 -0.609375 0.734375q-0.203125 0.46875 -0.203125 1.046875l0 3.5625l-1.84375 0z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m138.75652 128.76808l0 -7.90625l-1.671875 1.203125l-0.625 -0.90625l2.734375 -1.96875l0.75 0l0 9.578125l-1.1875 0zm6.913727 0.21875q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m238.09773 128.76808l0 -7.90625l-1.671875 1.203125l-0.625 -0.90625l2.734375 -1.96875l0.75 0l0 9.578125l-1.1875 0zm6.913727 0.21875q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625zm9.029968 1.125q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.70310974 0.375 1.2031097 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.2031097 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59373474 -0.53125 0.90623474 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90623474 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m136.45964 161.76808l0 -1.1875q0.046875 -0.046875 0.34375 -0.328125q0.296875 -0.296875 0.71875 -0.734375q0.4375 -0.453125 0.90625 -0.9375q0.484375 -0.484375 0.890625 -0.90625q0.421875 -0.421875 0.65625 -0.6875q0.4375 -0.484375 0.703125 -0.828125q0.265625 -0.34375 0.390625 -0.671875q0.125 -0.328125 0.125 -0.765625q0 -0.40625 -0.21875 -0.765625q-0.203125 -0.375 -0.609375 -0.609375q-0.390625 -0.25 -0.984375 -0.25q-0.5625 0 -0.953125 0.25q-0.375 0.234375 -0.59375 0.5625q-0.203125 0.328125 -0.28125 0.59375l-1.078125 -0.4375q0.09375 -0.296875 0.296875 -0.640625q0.21875 -0.359375 0.578125 -0.6875q0.359375 -0.328125 0.859375 -0.546875q0.515625 -0.21875 1.1875 -0.21875q0.921875 0 1.59375 0.390625q0.671875 0.375 1.03125 1.0q0.375 0.625 0.375 1.34375q0 0.640625 -0.234375 1.203125q-0.234375 0.5625 -0.59375 1.03125q-0.34375 0.453125 -0.703125 0.8125q-0.1875 0.171875 -0.515625 0.5q-0.3125 0.328125 -0.6875 0.703125q-0.359375 0.359375 -0.71875 0.71875q-0.34375 0.34375 -0.609375 0.609375q-0.25 0.265625 -0.34375 0.34375l0 0l4.515625 0l0 1.140625l-6.046875 0zm11.254593 0.21875q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m235.80086 161.76808l0 -1.1875q0.046875 -0.046875 0.34375 -0.328125q0.296875 -0.296875 0.71875 -0.734375q0.4375 -0.453125 0.90625 -0.9375q0.484375 -0.484375 0.890625 -0.90625q0.421875 -0.421875 0.65625 -0.6875q0.4375 -0.484375 0.703125 -0.828125q0.265625 -0.34375 0.390625 -0.671875q0.125 -0.328125 0.125 -0.765625q0 -0.40625 -0.21875 -0.765625q-0.203125 -0.375 -0.609375 -0.609375q-0.390625 -0.25 -0.984375 -0.25q-0.5625 0 -0.953125 0.25q-0.375 0.234375 -0.59375 0.5625q-0.203125 0.328125 -0.28125 0.59375l-1.078125 -0.4375q0.09375 -0.296875 0.296875 -0.640625q0.21875 -0.359375 0.578125 -0.6875q0.359375 -0.328125 0.859375 -0.546875q0.515625 -0.21875 1.1875 -0.21875q0.921875 0 1.59375 0.390625q0.671875 0.375 1.03125 1.0q0.375 0.625 0.375 1.34375q0 0.640625 -0.234375 1.203125q-0.234375 0.5625 -0.59375 1.03125q-0.34375 0.453125 -0.703125 0.8125q-0.1875 0.171875 -0.515625 0.5q-0.3125 0.328125 -0.6875 0.703125q-0.359375 0.359375 -0.71875 0.71875q-0.34375 0.34375 -0.609375 0.609375q-0.25 0.265625 -0.34375 0.34375l0 0l4.515625 0l0 1.140625l-6.046875 0zm11.254593 0.21875q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625zm9.029968 1.125q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m0 101.82021l119.1811 0l0 42.01574l-119.1811 0z" fill-rule="evenodd"/><path fill="#000000" d="m35.141422 128.7402l0 -12.406242l1.59375 0l0 12.406242l-1.59375 0zm-3.75 -11.843742l0 -1.515625l9.0625 0l0 1.515625l-9.0625 0zm9.6045685 11.843742l0 -9.515617l1.515625 0l0 1.53125l0.078125 0q0.1875 -0.546875 0.625 -0.9375q0.4375 -0.40625 1.0 -0.640625q0.578125 -0.234375 1.125 -0.234375q0.4375 0 0.671875 0.046875q0.25 0.046875 0.453125 0.140625l0 1.71875q-0.296875 -0.15625 -0.640625 -0.21875q-0.34375 -0.078125 -0.703125 -0.078125q-0.6875 0 -1.265625 0.390625q-0.578125 0.390625 -0.921875 1.046875q-0.34375 0.65625 -0.34375 1.4375l0 5.3124924l-1.59375 0zm9.669632 0.296875q-1.0625 0 -1.875 -0.40625q-0.796875 -0.40625 -1.25 -1.1249924q-0.453125 -0.71875 -0.453125 -1.640625q0 -1.046875 0.53125 -1.765625q0.546875 -0.71875 1.453125 -1.078125q0.921875 -0.359375 2.015625 -0.359375q0.640625 0 1.171875 0.109375q0.546875 0.09375 0.9375 0.234375q0.40625 0.140625 0.625 0.265625l0 -0.578125q0 -1.078125 -0.765625 -1.703125q-0.765625 -0.640625 -1.875 -0.640625q-0.78125 0 -1.46875 0.34375q-0.671875 0.34375 -1.0625 0.953125l-1.203125 -0.890625q0.375 -0.5625 0.9375 -0.96875q0.5625 -0.40625 1.28125 -0.625q0.71875 -0.234375 1.515625 -0.234375q1.9375 0 3.03125 1.03125q1.109375 1.015625 1.109375 2.75l0 6.0312424l-1.5 0l0 -1.3593674l-0.078125 0q-0.25 0.40625 -0.703125 0.7968674q-0.4375 0.375 -1.046875 0.609375q-0.609375 0.25 -1.328125 0.25zm0.140625 -1.3906174q0.828125 0 1.5 -0.40625q0.6875 -0.421875 1.09375 -1.109375q0.421875 -0.6875 0.421875 -1.515625q-0.4375 -0.296875 -1.078125 -0.484375q-0.640625 -0.1875 -1.40625 -0.1875q-1.359375 0 -2.0 0.5625q-0.640625 0.5625 -0.640625 1.375q0 0.78125 0.59375 1.28125q0.609375 0.484375 1.515625 0.484375zm10.938126 1.3906174q-1.40625 0 -2.5 -0.65625q-1.078125 -0.6718674 -1.703125 -1.8124924q-0.609375 -1.15625 -0.609375 -2.578125q0 -1.46875 0.609375 -2.59375q0.625 -1.140625 1.703125 -1.796875q1.09375 -0.671875 2.5 -0.671875q1.609375 0 2.6406212 0.734375q1.03125 0.734375 1.46875 1.890625l-1.4375 0.609375q-0.359375 -0.890625 -1.0624962 -1.34375q-0.6875 -0.453125 -1.6875 -0.453125q-0.828125 0 -1.546875 0.453125q-0.71875 0.4375 -1.171875 1.25q-0.453125 0.8125 -0.453125 1.921875q0 1.078125 0.453125 1.90625q0.453125 0.8125 1.171875 1.265625q0.71875 0.4375 1.546875 0.4375q1.015625 0 1.734375 -0.46875q0.7343712 -0.46875 1.0937462 -1.3125l1.40625 0.59375q-0.46875 1.09375 -1.515625 1.8593674q-1.0312462 0.765625 -2.6406212 0.765625zm10.014843 0q-1.375 0 -2.453125 -0.640625q-1.0625 -0.6562424 -1.671875 -1.7968674q-0.609375 -1.140625 -0.609375 -2.59375q0 -1.359375 0.5625 -2.515625q0.578125 -1.15625 1.609375 -1.859375q1.03125 -0.703125 2.4375 -0.703125q1.421875 0 2.4375 0.625q1.015625 0.625 1.5625 1.734375q0.546875 1.09375 0.546875 2.515625q0 0.125 -0.015625 0.265625q0 0.125 -0.015625 0.21875l-8.1875 0l0 -1.3125l6.546875 0q-0.015625 -0.390625 -0.1875 -0.84375q-0.15625 -0.46875 -0.5 -0.859375q-0.34375 -0.40625 -0.875 -0.65625q-0.53125 -0.25 -1.3125 -0.25q-0.9375 0 -1.625 0.484375q-0.671875 0.46875 -1.046875 1.296875q-0.359375 0.8125 -0.359375 1.859375q0 1.203125 0.46875 2.015625q0.46875 0.796875 1.203125 1.1875q0.75 0.390625 1.546875 0.390625q1.046875 0 1.71875 -0.484375q0.6875 -0.5 1.09375 -1.234375l1.34375 0.65625q-0.5625 1.078125 -1.609375 1.7968674q-1.03125 0.703125 -2.609375 0.703125zm13.331711 -0.296875l0 -10.546867l-2.25 1.609375l-0.8125 -1.21875l3.640625 -2.609375l1.0 0l0 12.765617l-1.578125 0z" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m127.83465 191.82939l0 101.99739" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m227.17586 191.82939l0 101.99739" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m351.17847 191.82939l0 101.99739" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m127.33596 192.32808l224.34119 0" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m127.33596 227.32808l224.34119 0" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m127.33596 260.3281l224.34119 0" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m127.33596 293.3281l224.34119 0" fill-rule="nonzero"/><path fill="#4a4a4a" d="m171.3649 206.62746l4.828125 0l0 1.5625l-4.828125 0l0 -1.5625zm1.25 4.859375l0 -6.875l1.828125 0l0 6.53125q0 0.5 0.203125 0.765625q0.21875 0.25 0.703125 0.25q0.28125 0 0.46875 -0.078125q0.203125 -0.09375 0.4375 -0.234375l0 1.796875q-0.296875 0.109375 -0.609375 0.171875q-0.3125 0.0625 -0.671875 0.0625q-1.109375 0 -1.734375 -0.625q-0.625 -0.640625 -0.625 -1.765625zm7.960724 2.5q-0.921875 0 -1.578125 -0.265625q-0.640625 -0.265625 -1.046875 -0.6875q-0.40625 -0.4375 -0.609375 -0.953125l1.640625 -0.71875q0.234375 0.5 0.640625 0.765625q0.40625 0.25 0.953125 0.25q0.453125 0 0.8125 -0.140625q0.359375 -0.15625 0.359375 -0.546875q0 -0.28125 -0.1875 -0.4375q-0.1875 -0.15625 -0.46875 -0.25q-0.28125 -0.09375 -0.5625 -0.140625l-0.84375 -0.1875q-0.515625 -0.109375 -0.984375 -0.390625q-0.453125 -0.296875 -0.75 -0.734375q-0.28125 -0.4375 -0.28125 -1.015625q0 -0.640625 0.359375 -1.109375q0.375 -0.484375 1.015625 -0.75q0.640625 -0.265625 1.40625 -0.265625q0.71875 0 1.28125 0.171875q0.578125 0.15625 1.0 0.5q0.421875 0.34375 0.671875 0.875l-1.5625 0.6875q-0.234375 -0.421875 -0.59375 -0.578125q-0.34375 -0.171875 -0.75 -0.171875q-0.484375 0 -0.765625 0.1875q-0.28125 0.171875 -0.28125 0.421875q0 0.265625 0.28125 0.46875q0.28125 0.1875 0.671875 0.28125l1.0625 0.265625q1.0625 0.25 1.59375 0.8125q0.546875 0.546875 0.546875 1.34375q0 0.6875 -0.40625 1.21875q-0.40625 0.53125 -1.09375 0.8125q-0.6875 0.28125 -1.53125 0.28125z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m281.54697 213.98683q-0.984375 0 -1.78125 -0.46875q-0.78125 -0.484375 -1.234375 -1.34375q-0.453125 -0.859375 -0.453125 -1.96875q0 -1.125 0.453125 -1.96875q0.453125 -0.859375 1.234375 -1.34375q0.796875 -0.484375 1.78125 -0.484375q0.515625 0 0.921875 0.15625q0.421875 0.140625 0.734375 0.40625q0.3125 0.25 0.484375 0.53125l0.109375 0l-0.109375 -1.015625l0 -2.75l1.8125 0l0 10.03125l-1.703125 0l0 -0.859375l-0.109375 0q-0.171875 0.28125 -0.484375 0.53125q-0.3125 0.25 -0.734375 0.390625q-0.40625 0.15625 -0.921875 0.15625zm0.296875 -1.6875q0.515625 0 0.953125 -0.265625q0.453125 -0.265625 0.71875 -0.734375q0.28125 -0.46875 0.28125 -1.09375q0 -0.640625 -0.28125 -1.109375q-0.265625 -0.46875 -0.71875 -0.71875q-0.4375 -0.265625 -0.953125 -0.265625q-0.5 0 -0.9375 0.265625q-0.4375 0.25 -0.71875 0.71875q-0.265625 0.453125 -0.265625 1.109375q0 0.625 0.265625 1.109375q0.28125 0.46875 0.71875 0.734375q0.4375 0.25 0.9375 0.25zm8.037079 1.6875q-1.3125 0 -1.96875 -0.8125q-0.65625 -0.8125 -0.65625 -2.203125l0 -4.34375l1.828125 0l0 4.109375q0 0.734375 0.359375 1.15625q0.359375 0.40625 0.921875 0.40625q0.546875 0 0.921875 -0.265625q0.375 -0.28125 0.578125 -0.734375q0.203125 -0.46875 0.203125 -1.03125l0 -3.640625l1.828125 0l0 7.140625l-1.71875 0l0 -0.890625l-0.109375 0q-0.203125 0.328125 -0.546875 0.578125q-0.328125 0.25 -0.75 0.390625q-0.40625 0.140625 -0.890625 0.140625zm5.8078613 -0.21875l0 -7.140625l1.734375 0l0 0.953125l0.109375 0q0.171875 -0.34375 0.46875 -0.59375q0.3125 -0.265625 0.71875 -0.40625q0.40625 -0.15625 0.859375 -0.15625q0.28125 0 0.5 0.046875q0.234375 0.03125 0.40625 0.09375l0 1.84375q-0.296875 -0.109375 -0.59375 -0.1875q-0.28125 -0.078125 -0.59375 -0.078125q-0.546875 0 -0.953125 0.28125q-0.390625 0.265625 -0.609375 0.734375q-0.203125 0.46875 -0.203125 1.046875l0 3.5625l-1.84375 0z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m138.75652 248.76808l0 -7.90625l-1.671875 1.203125l-0.625 -0.90625l2.734375 -1.96875l0.75 0l0 9.578125l-1.1875 0zm6.913727 0.21875q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625zm9.029968 1.125q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m238.09773 248.76808l0 -7.90625l-1.671875 1.203125l-0.625 -0.90625l2.734375 -1.96875l0.75 0l0 9.578125l-1.1875 0zm6.913727 0.21875q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m136.45964 281.7681l0 -1.1875q0.046875 -0.046875 0.34375 -0.328125q0.296875 -0.296875 0.71875 -0.734375q0.4375 -0.453125 0.90625 -0.9375q0.484375 -0.484375 0.890625 -0.90625q0.421875 -0.421875 0.65625 -0.6875q0.4375 -0.484375 0.703125 -0.828125q0.265625 -0.34375 0.390625 -0.671875q0.125 -0.328125 0.125 -0.765625q0 -0.40625 -0.21875 -0.765625q-0.203125 -0.375 -0.609375 -0.609375q-0.390625 -0.25 -0.984375 -0.25q-0.5625 0 -0.953125 0.25q-0.375 0.234375 -0.59375 0.5625q-0.203125 0.328125 -0.28125 0.59375l-1.078125 -0.4375q0.09375 -0.296875 0.296875 -0.640625q0.21875 -0.359375 0.578125 -0.6875q0.359375 -0.328125 0.859375 -0.546875q0.515625 -0.21875 1.1875 -0.21875q0.921875 0 1.59375 0.390625q0.671875 0.375 1.03125 1.0q0.375 0.625 0.375 1.34375q0 0.640625 -0.234375 1.203125q-0.234375 0.5625 -0.59375 1.03125q-0.34375 0.453125 -0.703125 0.8125q-0.1875 0.171875 -0.515625 0.5q-0.3125 0.328125 -0.6875 0.703125q-0.359375 0.359375 -0.71875 0.71875q-0.34375 0.34375 -0.609375 0.609375q-0.25 0.265625 -0.34375 0.34375l0 0l4.515625 0l0 1.140625l-6.046875 0zm11.254593 0.21875q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625zm9.029968 1.125q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m235.80086 281.7681l0 -1.1875q0.046875 -0.046875 0.34375 -0.328125q0.296875 -0.296875 0.71875 -0.734375q0.4375 -0.453125 0.90625 -0.9375q0.484375 -0.484375 0.890625 -0.90625q0.421875 -0.421875 0.65625 -0.6875q0.4375 -0.484375 0.703125 -0.828125q0.265625 -0.34375 0.390625 -0.671875q0.125 -0.328125 0.125 -0.765625q0 -0.40625 -0.21875 -0.765625q-0.203125 -0.375 -0.609375 -0.609375q-0.390625 -0.25 -0.984375 -0.25q-0.5625 0 -0.953125 0.25q-0.375 0.234375 -0.59375 0.5625q-0.203125 0.328125 -0.28125 0.59375l-1.078125 -0.4375q0.09375 -0.296875 0.296875 -0.640625q0.21875 -0.359375 0.578125 -0.6875q0.359375 -0.328125 0.859375 -0.546875q0.515625 -0.21875 1.1875 -0.21875q0.921875 0 1.59375 0.390625q0.671875 0.375 1.03125 1.0q0.375 0.625 0.375 1.34375q0 0.640625 -0.234375 1.203125q-0.234375 0.5625 -0.59375 1.03125q-0.34375 0.453125 -0.703125 0.8125q-0.1875 0.171875 -0.515625 0.5q-0.3125 0.328125 -0.6875 0.703125q-0.359375 0.359375 -0.71875 0.71875q-0.34375 0.34375 -0.609375 0.609375q-0.25 0.265625 -0.34375 0.34375l0 0l4.515625 0l0 1.140625l-6.046875 0zm11.254593 0.21875q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m0 221.8202l119.1811 0l0 42.015762l-119.1811 0z" fill-rule="evenodd"/><path fill="#000000" d="m33.779514 248.7402l0 -12.40625l1.59375 0l0 12.40625l-1.59375 0zm-3.75 -11.84375l0 -1.515625l9.0625 0l0 1.515625l-9.0625 0zm9.6045685 11.84375l0 -9.515625l1.515625 0l0 1.53125l0.078125 0q0.1875 -0.546875 0.625 -0.9375q0.4375 -0.40625 1.0 -0.640625q0.578125 -0.234375 1.125 -0.234375q0.4375 0 0.671875 0.046875q0.25 0.046875 0.453125 0.140625l0 1.71875q-0.296875 -0.15625 -0.640625 -0.21875q-0.34375 -0.078125 -0.703125 -0.078125q-0.6875 0 -1.265625 0.390625q-0.578125 0.390625 -0.921875 1.046875q-0.34375 0.65625 -0.34375 1.4375l0 5.3125l-1.59375 0zm9.669632 0.296875q-1.0625 0 -1.875 -0.40625q-0.796875 -0.40625 -1.25 -1.125q-0.453125 -0.71875 -0.453125 -1.640625q0 -1.046875 0.53125 -1.765625q0.546875 -0.71875 1.453125 -1.078125q0.921875 -0.359375 2.015625 -0.359375q0.640625 0 1.171875 0.109375q0.546875 0.09375 0.9375 0.234375q0.40625 0.140625 0.625 0.265625l0 -0.578125q0 -1.078125 -0.765625 -1.703125q-0.765625 -0.640625 -1.875 -0.640625q-0.78125 0 -1.46875 0.34375q-0.671875 0.34375 -1.0625 0.953125l-1.203125 -0.890625q0.375 -0.5625 0.9375 -0.96875q0.5625 -0.40625 1.28125 -0.625q0.71875 -0.234375 1.515625 -0.234375q1.9375 0 3.03125 1.03125q1.109375 1.015625 1.109375 2.75l0 6.03125l-1.5 0l0 -1.359375l-0.078125 0q-0.25 0.40625 -0.703125 0.796875q-0.4375 0.375 -1.046875 0.609375q-0.609375 0.25 -1.328125 0.25zm0.140625 -1.390625q0.828125 0 1.5 -0.40625q0.6875 -0.421875 1.09375 -1.109375q0.421875 -0.6875 0.421875 -1.515625q-0.4375 -0.296875 -1.078125 -0.484375q-0.640625 -0.1875 -1.40625 -0.1875q-1.359375 0 -2.0 0.5625q-0.640625 0.5625 -0.640625 1.375q0 0.78125 0.59375 1.28125q0.609375 0.484375 1.515625 0.484375zm10.938126 1.390625q-1.40625 0 -2.5 -0.65625q-1.078125 -0.671875 -1.703125 -1.8125q-0.609375 -1.15625 -0.609375 -2.578125q0 -1.46875 0.609375 -2.59375q0.625 -1.140625 1.703125 -1.796875q1.09375 -0.671875 2.5 -0.671875q1.609375 0 2.640625 0.734375q1.0312462 0.734375 1.4687462 1.890625l-1.4374962 0.609375q-0.359375 -0.890625 -1.0625 -1.34375q-0.6875 -0.453125 -1.6875 -0.453125q-0.828125 0 -1.546875 0.453125q-0.71875 0.4375 -1.171875 1.25q-0.453125 0.8125 -0.453125 1.921875q0 1.078125 0.453125 1.90625q0.453125 0.8125 1.171875 1.265625q0.71875 0.4375 1.546875 0.4375q1.015625 0 1.734375 -0.46875q0.734375 -0.46875 1.09375 -1.3125l1.4062462 0.59375q-0.46875 1.09375 -1.5156212 1.859375q-1.03125 0.765625 -2.640625 0.765625zm10.014843 0q-1.375 0 -2.453125 -0.640625q-1.0625 -0.65625 -1.671875 -1.796875q-0.609375 -1.140625 -0.609375 -2.59375q0 -1.359375 0.5625 -2.515625q0.578125 -1.15625 1.609375 -1.859375q1.03125 -0.703125 2.4375 -0.703125q1.421875 0 2.4375 0.625q1.015625 0.625 1.5625 1.734375q0.546875 1.09375 0.546875 2.515625q0 0.125 -0.015625 0.265625q0 0.125 -0.015625 0.21875l-8.1875 0l0 -1.3125l6.546875 0q-0.015625 -0.390625 -0.1875 -0.84375q-0.15625 -0.46875 -0.5 -0.859375q-0.34375 -0.40625 -0.875 -0.65625q-0.53125 -0.25 -1.3125 -0.25q-0.9375 0 -1.625 0.484375q-0.671875 0.46875 -1.046875 1.296875q-0.359375 0.8125 -0.359375 1.859375q0 1.203125 0.46875 2.015625q0.46875 0.796875 1.203125 1.1875q0.75 0.390625 1.546875 0.390625q1.046875 0 1.71875 -0.484375q0.6875 -0.5 1.09375 -1.234375l1.34375 0.65625q-0.5625 1.078125 -1.609375 1.796875q-1.03125 0.703125 -2.609375 0.703125zm10.269211 -0.296875l0 -1.578125q0.046875 -0.0625 0.4375 -0.453125q0.390625 -0.390625 0.96875 -0.96875q0.578125 -0.59375 1.21875 -1.234375q0.640625 -0.65625 1.1875 -1.21875q0.546875 -0.578125 0.859375 -0.921875q0.578125 -0.640625 0.9375 -1.09375q0.359375 -0.453125 0.515625 -0.890625q0.171875 -0.453125 0.171875 -1.03125q0 -0.53125 -0.28125 -1.015625q-0.28125 -0.5 -0.8125 -0.8125q-0.53125 -0.328125 -1.328125 -0.328125q-0.75 0 -1.265625 0.328125q-0.5 0.3125 -0.78125 0.75q-0.28125 0.4375 -0.375 0.78125l-1.4375 -0.578125q0.109375 -0.390625 0.390625 -0.859375q0.296875 -0.484375 0.765625 -0.921875q0.484375 -0.4375 1.15625 -0.71875q0.6875 -0.296875 1.578125 -0.296875q1.21875 0 2.109375 0.515625q0.90625 0.515625 1.390625 1.34375q0.5 0.828125 0.5 1.796875q0 0.84375 -0.328125 1.609375q-0.3125 0.75 -0.78125 1.359375q-0.453125 0.609375 -0.9375 1.078125q-0.25 0.25 -0.6875 0.6875q-0.421875 0.421875 -0.90625 0.921875q-0.484375 0.484375 -0.953125 0.953125q-0.46875 0.46875 -0.8125 0.828125q-0.34375 0.34375 -0.46875 0.453125l0 0l6.03125 0l0 1.515625l-8.0625 0z" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m127.83465 311.8294l0 101.997375" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m227.17586 311.8294l0 101.997375" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m351.17847 311.8294l0 101.997375" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m127.33596 312.3281l224.34119 0" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m127.33596 347.3281l224.34119 0" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m127.33596 380.3281l224.34119 0" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m127.33596 413.3281l224.34119 0" fill-rule="nonzero"/><path fill="#4a4a4a" d="m171.3649 326.62747l4.828125 0l0 1.5625l-4.828125 0l0 -1.5625zm1.25 4.859375l0 -6.875l1.828125 0l0 6.53125q0 0.5 0.203125 0.765625q0.21875 0.25 0.703125 0.25q0.28125 0 0.46875 -0.078125q0.203125 -0.09375 0.4375 -0.234375l0 1.796875q-0.296875 0.109375 -0.609375 0.171875q-0.3125 0.0625 -0.671875 0.0625q-1.109375 0 -1.734375 -0.625q-0.625 -0.640625 -0.625 -1.765625zm7.960724 2.5q-0.921875 0 -1.578125 -0.265625q-0.640625 -0.265625 -1.046875 -0.6875q-0.40625 -0.4375 -0.609375 -0.953125l1.640625 -0.71875q0.234375 0.5 0.640625 0.765625q0.40625 0.25 0.953125 0.25q0.453125 0 0.8125 -0.140625q0.359375 -0.15625 0.359375 -0.546875q0 -0.28125 -0.1875 -0.4375q-0.1875 -0.15625 -0.46875 -0.25q-0.28125 -0.09375 -0.5625 -0.140625l-0.84375 -0.1875q-0.515625 -0.109375 -0.984375 -0.390625q-0.453125 -0.296875 -0.75 -0.734375q-0.28125 -0.4375 -0.28125 -1.015625q0 -0.640625 0.359375 -1.109375q0.375 -0.484375 1.015625 -0.75q0.640625 -0.265625 1.40625 -0.265625q0.71875 0 1.28125 0.171875q0.578125 0.15625 1.0 0.5q0.421875 0.34375 0.671875 0.875l-1.5625 0.6875q-0.234375 -0.421875 -0.59375 -0.578125q-0.34375 -0.171875 -0.75 -0.171875q-0.484375 0 -0.765625 0.1875q-0.28125 0.171875 -0.28125 0.421875q0 0.265625 0.28125 0.46875q0.28125 0.1875 0.671875 0.28125l1.0625 0.265625q1.0625 0.25 1.59375 0.8125q0.546875 0.546875 0.546875 1.34375q0 0.6875 -0.40625 1.21875q-0.40625 0.53125 -1.09375 0.8125q-0.6875 0.28125 -1.53125 0.28125z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m281.54697 333.98685q-0.984375 0 -1.78125 -0.46875q-0.78125 -0.484375 -1.234375 -1.34375q-0.453125 -0.859375 -0.453125 -1.96875q0 -1.125 0.453125 -1.96875q0.453125 -0.859375 1.234375 -1.34375q0.796875 -0.484375 1.78125 -0.484375q0.515625 0 0.921875 0.15625q0.421875 0.140625 0.734375 0.40625q0.3125 0.25 0.484375 0.53125l0.109375 0l-0.109375 -1.015625l0 -2.75l1.8125 0l0 10.03125l-1.703125 0l0 -0.859375l-0.109375 0q-0.171875 0.28125 -0.484375 0.53125q-0.3125 0.25 -0.734375 0.390625q-0.40625 0.15625 -0.921875 0.15625zm0.296875 -1.6875q0.515625 0 0.953125 -0.265625q0.453125 -0.265625 0.71875 -0.734375q0.28125 -0.46875 0.28125 -1.09375q0 -0.640625 -0.28125 -1.109375q-0.265625 -0.46875 -0.71875 -0.71875q-0.4375 -0.265625 -0.953125 -0.265625q-0.5 0 -0.9375 0.265625q-0.4375 0.25 -0.71875 0.71875q-0.265625 0.453125 -0.265625 1.109375q0 0.625 0.265625 1.109375q0.28125 0.46875 0.71875 0.734375q0.4375 0.25 0.9375 0.25zm8.037079 1.6875q-1.3125 0 -1.96875 -0.8125q-0.65625 -0.8125 -0.65625 -2.203125l0 -4.34375l1.828125 0l0 4.109375q0 0.734375 0.359375 1.15625q0.359375 0.40625 0.921875 0.40625q0.546875 0 0.921875 -0.265625q0.375 -0.28125 0.578125 -0.734375q0.203125 -0.46875 0.203125 -1.03125l0 -3.640625l1.828125 0l0 7.140625l-1.71875 0l0 -0.890625l-0.109375 0q-0.203125 0.328125 -0.546875 0.578125q-0.328125 0.25 -0.75 0.390625q-0.40625 0.140625 -0.890625 0.140625zm5.8078613 -0.21875l0 -7.140625l1.734375 0l0 0.953125l0.109375 0q0.171875 -0.34375 0.46875 -0.59375q0.3125 -0.265625 0.71875 -0.40625q0.40625 -0.15625 0.859375 -0.15625q0.28125 0 0.5 0.046875q0.234375 0.03125 0.40625 0.09375l0 1.84375q-0.296875 -0.109375 -0.59375 -0.1875q-0.28125 -0.078125 -0.59375 -0.078125q-0.546875 0 -0.953125 0.28125q-0.390625 0.265625 -0.609375 0.734375q-0.203125 0.46875 -0.203125 1.046875l0 3.5625l-1.84375 0z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m139.49089 368.98685q-0.5625 0 -1.203125 -0.234375q-0.625 -0.234375 -1.140625 -0.75q-0.515625 -0.53125 -0.765625 -1.40625l1.078125 -0.453125q0.234375 0.8125 0.765625 1.265625q0.53125 0.453125 1.265625 0.453125q0.546875 0 1.0 -0.234375q0.453125 -0.234375 0.71875 -0.640625q0.28125 -0.40625 0.28125 -0.921875q0 -0.5 -0.28125 -0.890625q-0.28125 -0.40625 -0.75 -0.640625q-0.453125 -0.234375 -1.015625 -0.234375l-0.828125 0l0 -1.109375l0.75 0q0.5 0 0.90625 -0.1875q0.421875 -0.203125 0.65625 -0.5625q0.234375 -0.359375 0.234375 -0.859375q0 -0.6875 -0.515625 -1.078125q-0.5 -0.40625 -1.234375 -0.40625q-0.53125 0 -0.875 0.203125q-0.34375 0.203125 -0.546875 0.515625q-0.203125 0.296875 -0.3125 0.59375l-1.09375 -0.46875q0.140625 -0.421875 0.484375 -0.875q0.359375 -0.453125 0.9375 -0.765625q0.59375 -0.328125 1.40625 -0.328125q0.859375 0 1.515625 0.34375q0.671875 0.328125 1.046875 0.921875q0.390625 0.59375 0.390625 1.34375q0 0.53125 -0.1875 0.9375q-0.1875 0.40625 -0.484375 0.6875q-0.296875 0.28125 -0.640625 0.453125l0 0.0625q0.453125 0.171875 0.8125 0.5q0.375 0.328125 0.59375 0.796875q0.234375 0.453125 0.234375 1.03125q0 0.859375 -0.4375 1.53125q-0.4375 0.65625 -1.171875 1.03125q-0.71875 0.375 -1.59375 0.375zm8.34935 0q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m238.09773 368.7681l0 -7.90625l-1.671875 1.203125l-0.625 -0.90625l2.734375 -1.96875l0.75 0l0 9.578125l-1.1875 0zm6.913727 0.21875q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625zm9.029968 1.125q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.70310974 0.375 1.2031097 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.2031097 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59373474 -0.53125 0.90623474 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90623474 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625zm9.029953 1.125q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m136.45964 399.7056l0 -0.890625l4.484375 -6.625l1.28125 0l0 9.578125l-1.171875 0l0 -7.84375l-0.046875 0l-3.125 4.671875l5.578125 0l0 1.109375l-7.0 0zm12.192596 2.28125q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m235.80086 401.7681l0 -1.1875q0.046875 -0.046875 0.34375 -0.328125q0.296875 -0.296875 0.71875 -0.734375q0.4375 -0.453125 0.90625 -0.9375q0.484375 -0.484375 0.890625 -0.90625q0.421875 -0.421875 0.65625 -0.6875q0.4375 -0.484375 0.703125 -0.828125q0.265625 -0.34375 0.390625 -0.671875q0.125 -0.328125 0.125 -0.765625q0 -0.40625 -0.21875 -0.765625q-0.203125 -0.375 -0.609375 -0.609375q-0.390625 -0.25 -0.984375 -0.25q-0.5625 0 -0.953125 0.25q-0.375 0.234375 -0.59375 0.5625q-0.203125 0.328125 -0.28125 0.59375l-1.078125 -0.4375q0.09375 -0.296875 0.296875 -0.640625q0.21875 -0.359375 0.578125 -0.6875q0.359375 -0.328125 0.859375 -0.546875q0.515625 -0.21875 1.1875 -0.21875q0.921875 0 1.59375 0.390625q0.671875 0.375 1.03125 1.0q0.375 0.625 0.375 1.34375q0 0.640625 -0.234375 1.203125q-0.234375 0.5625 -0.59375 1.03125q-0.34375 0.453125 -0.703125 0.8125q-0.1875 0.171875 -0.515625 0.5q-0.3125 0.328125 -0.6875 0.703125q-0.359375 0.359375 -0.71875 0.71875q-0.34375 0.34375 -0.609375 0.609375q-0.25 0.265625 -0.34375 0.34375l0 0l4.515625 0l0 1.140625l-6.046875 0zm11.254593 0.21875q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625zm9.029968 1.125q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625zm9.029968 1.125q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m0 341.82022l119.1811 0l0 42.015747l-119.1811 0z" fill-rule="evenodd"/><path fill="#000000" d="m33.69556 368.7402l0 -12.40625l1.59375 0l0 12.40625l-1.59375 0zm-3.75 -11.84375l0 -1.515625l9.0625 0l0 1.515625l-9.0625 0zm9.6045685 11.84375l0 -9.515625l1.515625 0l0 1.53125l0.078125 0q0.1875 -0.546875 0.625 -0.9375q0.4375 -0.40625 1.0 -0.640625q0.578125 -0.234375 1.125 -0.234375q0.4375 0 0.671875 0.046875q0.25 0.046875 0.453125 0.140625l0 1.71875q-0.296875 -0.15625 -0.640625 -0.21875q-0.34375 -0.078125 -0.703125 -0.078125q-0.6875 0 -1.265625 0.390625q-0.578125 0.390625 -0.921875 1.046875q-0.34375 0.65625 -0.34375 1.4375l0 5.3125l-1.59375 0zm9.669632 0.296875q-1.0625 0 -1.875 -0.40625q-0.796875 -0.40625 -1.25 -1.125q-0.453125 -0.71875 -0.453125 -1.640625q0 -1.046875 0.53125 -1.765625q0.546875 -0.71875 1.453125 -1.078125q0.921875 -0.359375 2.015625 -0.359375q0.640625 0 1.171875 0.109375q0.546875 0.09375 0.9375 0.234375q0.40625 0.140625 0.625 0.265625l0 -0.578125q0 -1.078125 -0.765625 -1.703125q-0.765625 -0.640625 -1.875 -0.640625q-0.78125 0 -1.46875 0.34375q-0.671875 0.34375 -1.0625 0.953125l-1.203125 -0.890625q0.375 -0.5625 0.9375 -0.96875q0.5625 -0.40625 1.28125 -0.625q0.71875 -0.234375 1.515625 -0.234375q1.9375 0 3.03125 1.03125q1.109375 1.015625 1.109375 2.75l0 6.03125l-1.5 0l0 -1.359375l-0.078125 0q-0.25 0.40625 -0.703125 0.796875q-0.4375 0.375 -1.046875 0.609375q-0.609375 0.25 -1.328125 0.25zm0.140625 -1.390625q0.828125 0 1.5 -0.40625q0.6875 -0.421875 1.09375 -1.109375q0.421875 -0.6875 0.421875 -1.515625q-0.4375 -0.296875 -1.078125 -0.484375q-0.640625 -0.1875 -1.40625 -0.1875q-1.359375 0 -2.0 0.5625q-0.640625 0.5625 -0.640625 1.375q0 0.78125 0.59375 1.28125q0.609375 0.484375 1.515625 0.484375zm10.938126 1.390625q-1.40625 0 -2.5 -0.65625q-1.078125 -0.671875 -1.703125 -1.8125q-0.609375 -1.15625 -0.609375 -2.578125q0 -1.46875 0.609375 -2.59375q0.625 -1.140625 1.703125 -1.796875q1.09375 -0.671875 2.5 -0.671875q1.609375 0 2.640625 0.734375q1.03125 0.734375 1.4687462 1.890625l-1.4374962 0.609375q-0.359375 -0.890625 -1.0625 -1.34375q-0.6875 -0.453125 -1.6875 -0.453125q-0.828125 0 -1.546875 0.453125q-0.71875 0.4375 -1.171875 1.25q-0.453125 0.8125 -0.453125 1.921875q0 1.078125 0.453125 1.90625q0.453125 0.8125 1.171875 1.265625q0.71875 0.4375 1.546875 0.4375q1.015625 0 1.734375 -0.46875q0.734375 -0.46875 1.09375 -1.3125l1.4062462 0.59375q-0.4687462 1.09375 -1.5156212 1.859375q-1.03125 0.765625 -2.640625 0.765625zm10.014843 0q-1.375 0 -2.453125 -0.640625q-1.0625 -0.65625 -1.671875 -1.796875q-0.609375 -1.140625 -0.609375 -2.59375q0 -1.359375 0.5625 -2.515625q0.578125 -1.15625 1.609375 -1.859375q1.03125 -0.703125 2.4375 -0.703125q1.421875 0 2.4375 0.625q1.015625 0.625 1.5625 1.734375q0.546875 1.09375 0.546875 2.515625q0 0.125 -0.015625 0.265625q0 0.125 -0.015625 0.21875l-8.1875 0l0 -1.3125l6.546875 0q-0.015625 -0.390625 -0.1875 -0.84375q-0.15625 -0.46875 -0.5 -0.859375q-0.34375 -0.40625 -0.875 -0.65625q-0.53125 -0.25 -1.3125 -0.25q-0.9375 0 -1.625 0.484375q-0.671875 0.46875 -1.046875 1.296875q-0.359375 0.8125 -0.359375 1.859375q0 1.203125 0.46875 2.015625q0.46875 0.796875 1.203125 1.1875q0.75 0.390625 1.546875 0.390625q1.046875 0 1.71875 -0.484375q0.6875 -0.5 1.09375 -1.234375l1.34375 0.65625q-0.5625 1.078125 -1.609375 1.796875q-1.03125 0.703125 -2.609375 0.703125zm14.300461 0q-0.75 0 -1.59375 -0.3125q-0.84375 -0.3125 -1.53125 -1.015625q-0.6875 -0.703125 -1.015625 -1.859375l1.4375 -0.59375q0.3125 1.078125 1.015625 1.671875q0.71875 0.59375 1.6875 0.59375q0.71875 0 1.328125 -0.296875q0.609375 -0.3125 0.96875 -0.84375q0.359375 -0.546875 0.359375 -1.234375q0 -0.671875 -0.375 -1.203125q-0.359375 -0.53125 -0.984375 -0.84375q-0.609375 -0.3125 -1.359375 -0.3125l-1.09375 0l0 -1.46875l0.984375 0q0.671875 0 1.21875 -0.265625q0.546875 -0.265625 0.859375 -0.75q0.328125 -0.484375 0.328125 -1.140625q0 -0.90625 -0.6875 -1.4375q-0.671875 -0.53125 -1.640625 -0.53125q-0.71875 0 -1.1875 0.28125q-0.453125 0.265625 -0.734375 0.671875q-0.265625 0.390625 -0.390625 0.78125l-1.46875 -0.609375q0.1875 -0.5625 0.65625 -1.171875q0.46875 -0.609375 1.234375 -1.03125q0.78125 -0.4375 1.890625 -0.4375q1.125 0 2.015625 0.453125q0.890625 0.453125 1.40625 1.25q0.515625 0.78125 0.515625 1.78125q0 0.71875 -0.265625 1.265625q-0.25 0.53125 -0.65625 0.90625q-0.390625 0.375 -0.84375 0.59375l0 0.078125q0.609375 0.234375 1.09375 0.6875q0.5 0.4375 0.796875 1.046875q0.296875 0.59375 0.296875 1.375q0 1.140625 -0.578125 2.03125q-0.578125 0.890625 -1.546875 1.390625q-0.96875 0.5 -2.140625 0.5z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m359.83203 242.82677l179.49603 0" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m359.83203 242.82677l173.49603 0" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m533.32806 244.4785l4.538147 -1.6517334l-4.538147 -1.6517334z" fill-rule="evenodd"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m539.32806 109.87664l0 239.99738" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m639.6168 109.87664l0 239.99738" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m759.6877 109.87664l0 239.99738" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m538.8294 110.37533l221.35693 0" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m538.8294 145.37534l221.35693 0" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m538.8294 179.37534l221.35693 0" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m538.8294 213.37534l221.35693 0" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m538.8294 247.37534l221.35693 0" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m538.8294 281.37534l221.35693 0" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m538.8294 315.37534l221.35693 0" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m538.8294 349.37534l221.35693 0" fill-rule="nonzero"/><path fill="#4a4a4a" d="m583.3321 124.674706l4.828125 0l0 1.5625l-4.828125 0l0 -1.5625zm1.25 4.8593674l0 -6.8749924l1.828125 0l0 6.5312424q0 0.5 0.203125 0.765625q0.21875 0.25 0.703125 0.25q0.28125 0 0.46875 -0.078125q0.203125 -0.09375 0.4375 -0.234375l0 1.796875q-0.296875 0.109375 -0.609375 0.171875q-0.3125 0.0625 -0.671875 0.0625q-1.109375 0 -1.734375 -0.625q-0.625 -0.640625 -0.625 -1.765625zm7.9606934 2.5q-0.921875 0 -1.578125 -0.265625q-0.640625 -0.265625 -1.046875 -0.6875q-0.40625 -0.4375 -0.609375 -0.953125l1.640625 -0.71875q0.234375 0.5 0.640625 0.765625q0.40625 0.25 0.953125 0.25q0.453125 0 0.8125 -0.140625q0.359375 -0.15625 0.359375 -0.546875q0 -0.28125 -0.1875 -0.4375q-0.1875 -0.15625 -0.46875 -0.25q-0.28125 -0.09375 -0.5625 -0.140625l-0.84375 -0.1875q-0.515625 -0.109375 -0.984375 -0.390625q-0.453125 -0.296875 -0.75 -0.7343674q-0.28125 -0.4375 -0.28125 -1.015625q0 -0.640625 0.359375 -1.109375q0.375 -0.484375 1.015625 -0.75q0.640625 -0.265625 1.40625 -0.265625q0.71875 0 1.28125 0.171875q0.578125 0.15625 1.0 0.5q0.421875 0.34375 0.671875 0.875l-1.5625 0.6875q-0.234375 -0.421875 -0.59375 -0.578125q-0.34375 -0.171875 -0.75 -0.171875q-0.484375 0 -0.765625 0.1875q-0.28125 0.171875 -0.28125 0.421875q0 0.265625 0.28125 0.46875q0.28125 0.1875 0.671875 0.28125l1.0625 0.265625q1.0625 0.25 1.59375 0.8124924q0.546875 0.546875 0.546875 1.34375q0 0.6875 -0.40625 1.21875q-0.40625 0.53125 -1.09375 0.8125q-0.6875 0.28125 -1.53125 0.28125z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m692.02203 132.03407q-0.984375 0 -1.78125 -0.46875q-0.78125 -0.484375 -1.234375 -1.34375q-0.453125 -0.859375 -0.453125 -1.96875q0 -1.1249924 0.453125 -1.9687424q0.453125 -0.859375 1.234375 -1.34375q0.796875 -0.484375 1.78125 -0.484375q0.515625 0 0.921875 0.15625q0.421875 0.140625 0.734375 0.40625q0.3125 0.25 0.484375 0.53125l0.109375 0l-0.109375 -1.015625l0 -2.75l1.8125 0l0 10.031242l-1.703125 0l0 -0.859375l-0.109375 0q-0.171875 0.28125 -0.484375 0.53125q-0.3125 0.25 -0.734375 0.390625q-0.40625 0.15625 -0.921875 0.15625zm0.296875 -1.6875q0.515625 0 0.953125 -0.265625q0.453125 -0.265625 0.71875 -0.734375q0.28125 -0.46875 0.28125 -1.09375q0 -0.6406174 -0.28125 -1.1093674q-0.265625 -0.46875 -0.71875 -0.71875q-0.4375 -0.265625 -0.953125 -0.265625q-0.5 0 -0.9375 0.265625q-0.4375 0.25 -0.71875 0.71875q-0.265625 0.453125 -0.265625 1.1093674q0 0.625 0.265625 1.109375q0.28125 0.46875 0.71875 0.734375q0.4375 0.25 0.9375 0.25zm8.037109 1.6875q-1.3125 0 -1.96875 -0.8125q-0.65625 -0.8125 -0.65625 -2.203125l0 -4.3437424l1.828125 0l0 4.1093674q0 0.734375 0.359375 1.15625q0.359375 0.40625 0.921875 0.40625q0.546875 0 0.921875 -0.265625q0.375 -0.28125 0.578125 -0.734375q0.203125 -0.46875 0.203125 -1.03125l0 -3.6406174l1.828125 0l0 7.1406174l-1.71875 0l0 -0.890625l-0.109375 0q-0.203125 0.328125 -0.546875 0.578125q-0.328125 0.25 -0.75 0.390625q-0.40625 0.140625 -0.890625 0.140625zm5.8078003 -0.21875l0 -7.1406174l1.734375 0l0 0.953125l0.109375 0q0.171875 -0.34375 0.46875 -0.59375q0.3125 -0.265625 0.71875 -0.40625q0.40625 -0.15625 0.859375 -0.15625q0.28125 0 0.5 0.046875q0.234375 0.03125 0.40625 0.09375l0 1.84375q-0.296875 -0.109375 -0.59375 -0.1875q-0.28125 -0.078125 -0.59375 -0.078125q-0.546875 0 -0.953125 0.28125q-0.390625 0.265625 -0.609375 0.734375q-0.203125 0.46875 -0.203125 1.0468674l0 3.5625l-1.84375 0z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m550.24994 166.81532l0 -7.90625l-1.671875 1.203125l-0.625 -0.90625l2.734375 -1.96875l0.75 0l0 9.578125l-1.1875 0zm6.9137573 0.21875q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m650.5387 166.81532l0 -7.90625l-1.671875 1.203125l-0.625 -0.90625l2.734375 -1.96875l0.75 0l0 9.578125l-1.1875 0zm6.9136963 0.21875q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625zm9.029968 1.125q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m547.95306 200.81532l0 -1.1875q0.046875 -0.046875 0.34375 -0.328125q0.296875 -0.296875 0.71875 -0.734375q0.4375 -0.453125 0.90625 -0.9375q0.484375 -0.484375 0.890625 -0.90625q0.421875 -0.421875 0.65625 -0.6875q0.4375 -0.484375 0.703125 -0.828125q0.265625 -0.34375 0.390625 -0.671875q0.125 -0.328125 0.125 -0.765625q0 -0.40625 -0.21875 -0.765625q-0.203125 -0.375 -0.609375 -0.609375q-0.390625 -0.25 -0.984375 -0.25q-0.5625 0 -0.953125 0.25q-0.375 0.234375 -0.59375 0.5625q-0.203125 0.328125 -0.28125 0.59375l-1.078125 -0.4375q0.09375 -0.296875 0.296875 -0.640625q0.21875 -0.359375 0.578125 -0.6875q0.359375 -0.328125 0.859375 -0.546875q0.515625 -0.21875 1.1875 -0.21875q0.921875 0 1.59375 0.390625q0.671875 0.375 1.03125 1.0q0.375 0.625 0.375 1.34375q0 0.640625 -0.234375 1.203125q-0.234375 0.5625 -0.59375 1.03125q-0.34375 0.453125 -0.703125 0.8125q-0.1875 0.171875 -0.515625 0.5q-0.3125 0.328125 -0.6875 0.703125q-0.359375 0.359375 -0.71875 0.71875q-0.34375 0.34375 -0.609375 0.609375q-0.25 0.265625 -0.34375 0.34375l0 0l4.515625 0l0 1.140625l-6.046875 0zm11.254639 0.21875q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m648.2418 200.81532l0 -1.1875q0.046875 -0.046875 0.34375 -0.328125q0.296875 -0.296875 0.71875 -0.734375q0.4375 -0.453125 0.90625 -0.9375q0.484375 -0.484375 0.890625 -0.90625q0.421875 -0.421875 0.65625 -0.6875q0.4375 -0.484375 0.703125 -0.828125q0.265625 -0.34375 0.390625 -0.671875q0.125 -0.328125 0.125 -0.765625q0 -0.40625 -0.21875 -0.765625q-0.203125 -0.375 -0.609375 -0.609375q-0.390625 -0.25 -0.984375 -0.25q-0.5625 0 -0.953125 0.25q-0.375 0.234375 -0.59375 0.5625q-0.203125 0.328125 -0.28125 0.59375l-1.078125 -0.4375q0.09375 -0.296875 0.296875 -0.640625q0.21875 -0.359375 0.578125 -0.6875q0.359375 -0.328125 0.859375 -0.546875q0.515625 -0.21875 1.1875 -0.21875q0.921875 0 1.59375 0.390625q0.671875 0.375 1.03125 1.0q0.375 0.625 0.375 1.34375q0 0.640625 -0.234375 1.203125q-0.234375 0.5625 -0.59375 1.03125q-0.34375 0.453125 -0.703125 0.8125q-0.1875 0.171875 -0.515625 0.5q-0.3125 0.328125 -0.6875 0.703125q-0.359375 0.359375 -0.71875 0.71875q-0.34375 0.34375 -0.609375 0.609375q-0.25 0.265625 -0.34375 0.34375l0 0l4.515625 0l0 1.140625l-6.046875 0zm11.254578 0.21875q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625zm9.029968 1.125q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m550.24994 234.81532l0 -7.90625l-1.671875 1.203125l-0.625 -0.90625l2.734375 -1.96875l0.75 0l0 9.578125l-1.1875 0zm6.9137573 0.21875q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625zm9.029968 1.125q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m650.5387 234.81532l0 -7.90625l-1.671875 1.203125l-0.625 -0.90625l2.734375 -1.96875l0.75 0l0 9.578125l-1.1875 0zm6.9136963 0.21875q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m547.95306 268.81534l0 -1.1875q0.046875 -0.046875 0.34375 -0.328125q0.296875 -0.296875 0.71875 -0.734375q0.4375 -0.453125 0.90625 -0.9375q0.484375 -0.484375 0.890625 -0.90625q0.421875 -0.421875 0.65625 -0.6875q0.4375 -0.484375 0.703125 -0.828125q0.265625 -0.34375 0.390625 -0.671875q0.125 -0.328125 0.125 -0.765625q0 -0.40625 -0.21875 -0.765625q-0.203125 -0.375 -0.609375 -0.609375q-0.390625 -0.25 -0.984375 -0.25q-0.5625 0 -0.953125 0.25q-0.375 0.234375 -0.59375 0.5625q-0.203125 0.328125 -0.28125 0.59375l-1.078125 -0.4375q0.09375 -0.296875 0.296875 -0.640625q0.21875 -0.359375 0.578125 -0.6875q0.359375 -0.328125 0.859375 -0.546875q0.515625 -0.21875 1.1875 -0.21875q0.921875 0 1.59375 0.390625q0.671875 0.375 1.03125 1.0q0.375 0.625 0.375 1.34375q0 0.640625 -0.234375 1.203125q-0.234375 0.5625 -0.59375 1.03125q-0.34375 0.453125 -0.703125 0.8125q-0.1875 0.171875 -0.515625 0.5q-0.3125 0.328125 -0.6875 0.703125q-0.359375 0.359375 -0.71875 0.71875q-0.34375 0.34375 -0.609375 0.609375q-0.25 0.265625 -0.34375 0.34375l0 0l4.515625 0l0 1.140625l-6.046875 0zm11.254639 0.21875q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625zm9.029968 1.125q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m648.2418 268.81534l0 -1.1875q0.046875 -0.046875 0.34375 -0.328125q0.296875 -0.296875 0.71875 -0.734375q0.4375 -0.453125 0.90625 -0.9375q0.484375 -0.484375 0.890625 -0.90625q0.421875 -0.421875 0.65625 -0.6875q0.4375 -0.484375 0.703125 -0.828125q0.265625 -0.34375 0.390625 -0.671875q0.125 -0.328125 0.125 -0.765625q0 -0.40625 -0.21875 -0.765625q-0.203125 -0.375 -0.609375 -0.609375q-0.390625 -0.25 -0.984375 -0.25q-0.5625 0 -0.953125 0.25q-0.375 0.234375 -0.59375 0.5625q-0.203125 0.328125 -0.28125 0.59375l-1.078125 -0.4375q0.09375 -0.296875 0.296875 -0.640625q0.21875 -0.359375 0.578125 -0.6875q0.359375 -0.328125 0.859375 -0.546875q0.515625 -0.21875 1.1875 -0.21875q0.921875 0 1.59375 0.390625q0.671875 0.375 1.03125 1.0q0.375 0.625 0.375 1.34375q0 0.640625 -0.234375 1.203125q-0.234375 0.5625 -0.59375 1.03125q-0.34375 0.453125 -0.703125 0.8125q-0.1875 0.171875 -0.515625 0.5q-0.3125 0.328125 -0.6875 0.703125q-0.359375 0.359375 -0.71875 0.71875q-0.34375 0.34375 -0.609375 0.609375q-0.25 0.265625 -0.34375 0.34375l0 0l4.515625 0l0 1.140625l-6.046875 0zm11.254578 0.21875q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m550.9843 303.0341q-0.5625 0 -1.203125 -0.234375q-0.625 -0.234375 -1.140625 -0.75q-0.515625 -0.53125 -0.765625 -1.40625l1.078125 -0.453125q0.234375 0.8125 0.765625 1.265625q0.53125 0.453125 1.265625 0.453125q0.546875 0 1.0 -0.234375q0.453125 -0.234375 0.71875 -0.640625q0.28125 -0.40625 0.28125 -0.921875q0 -0.5 -0.28125 -0.890625q-0.28125 -0.40625 -0.75 -0.640625q-0.453125 -0.234375 -1.015625 -0.234375l-0.828125 0l0 -1.109375l0.75 0q0.5 0 0.90625 -0.1875q0.421875 -0.203125 0.65625 -0.5625q0.234375 -0.359375 0.234375 -0.859375q0 -0.6875 -0.515625 -1.078125q-0.5 -0.40625 -1.234375 -0.40625q-0.53125 0 -0.875 0.203125q-0.34375 0.203125 -0.546875 0.515625q-0.203125 0.296875 -0.3125 0.59375l-1.09375 -0.46875q0.140625 -0.421875 0.484375 -0.875q0.359375 -0.453125 0.9375 -0.765625q0.59375 -0.328125 1.40625 -0.328125q0.859375 0 1.515625 0.34375q0.671875 0.328125 1.046875 0.921875q0.390625 0.59375 0.390625 1.34375q0 0.53125 -0.1875 0.9375q-0.1875 0.40625 -0.484375 0.6875q-0.296875 0.28125 -0.640625 0.453125l0 0.0625q0.453125 0.171875 0.8125 0.5q0.375 0.328125 0.59375 0.796875q0.234375 0.453125 0.234375 1.03125q0 0.859375 -0.4375 1.53125q-0.4375 0.65625 -1.171875 1.03125q-0.71875 0.375 -1.59375 0.375zm8.349365 0q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m650.5387 302.81534l0 -7.90625l-1.671875 1.203125l-0.625 -0.90625l2.734375 -1.96875l0.75 0l0 9.578125l-1.1875 0zm6.9136963 0.21875q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625zm9.029968 1.125q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625zm9.029968 1.125q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m547.95306 334.75284l0 -0.890625l4.484375 -6.625l1.28125 0l0 9.578125l-1.171875 0l0 -7.84375l-0.046875 0l-3.125 4.671875l5.578125 0l0 1.109375l-7.0 0zm12.192627 2.28125q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m648.2418 336.81534l0 -1.1875q0.046875 -0.046875 0.34375 -0.328125q0.296875 -0.296875 0.71875 -0.734375q0.4375 -0.453125 0.90625 -0.9375q0.484375 -0.484375 0.890625 -0.90625q0.421875 -0.421875 0.65625 -0.6875q0.4375 -0.484375 0.703125 -0.828125q0.265625 -0.34375 0.390625 -0.671875q0.125 -0.328125 0.125 -0.765625q0 -0.40625 -0.21875 -0.765625q-0.203125 -0.375 -0.609375 -0.609375q-0.390625 -0.25 -0.984375 -0.25q-0.5625 0 -0.953125 0.25q-0.375 0.234375 -0.59375 0.5625q-0.203125 0.328125 -0.28125 0.59375l-1.078125 -0.4375q0.09375 -0.296875 0.296875 -0.640625q0.21875 -0.359375 0.578125 -0.6875q0.359375 -0.328125 0.859375 -0.546875q0.515625 -0.21875 1.1875 -0.21875q0.921875 0 1.59375 0.390625q0.671875 0.375 1.03125 1.0q0.375 0.625 0.375 1.34375q0 0.640625 -0.234375 1.203125q-0.234375 0.5625 -0.59375 1.03125q-0.34375 0.453125 -0.703125 0.8125q-0.1875 0.171875 -0.515625 0.5q-0.3125 0.328125 -0.6875 0.703125q-0.359375 0.359375 -0.71875 0.71875q-0.34375 0.34375 -0.609375 0.609375q-0.25 0.265625 -0.34375 0.34375l0 0l4.515625 0l0 1.140625l-6.046875 0zm11.254578 0.21875q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625zm9.029968 1.125q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625zm9.029968 1.125q-0.890625 0 -1.59375 -0.375q-0.703125 -0.390625 -1.203125 -1.078125q-0.5 -0.6875 -0.765625 -1.59375q-0.265625 -0.90625 -0.265625 -1.953125q0 -1.0625 0.265625 -1.96875q0.265625 -0.90625 0.765625 -1.59375q0.5 -0.6875 1.203125 -1.0625q0.703125 -0.390625 1.59375 -0.390625q0.875 0 1.578125 0.390625q0.703125 0.375 1.203125 1.0625q0.515625 0.6875 0.78125 1.59375q0.265625 0.90625 0.265625 1.96875q0 1.046875 -0.265625 1.953125q-0.265625 0.90625 -0.78125 1.59375q-0.5 0.6875 -1.203125 1.078125q-0.703125 0.375 -1.578125 0.375zm0 -1.125q0.796875 0 1.390625 -0.515625q0.59375 -0.53125 0.90625 -1.40625q0.3125 -0.875 0.3125 -1.953125q0 -1.09375 -0.3125 -1.96875q-0.3125 -0.890625 -0.90625 -1.40625q-0.59375 -0.515625 -1.390625 -0.515625q-0.8125 0 -1.40625 0.515625q-0.578125 0.515625 -0.890625 1.40625q-0.3125 0.875 -0.3125 1.96875q0 1.078125 0.3125 1.953125q0.3125 0.875 0.890625 1.40625q0.59375 0.515625 1.40625 0.515625z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m389.9895 180.6483l119.18109 0l0 58.173218l-119.18109 0z" fill-rule="evenodd"/><path fill="#000000" d="m411.07724 205.0083l0 -11.453125l6.625 0l0 1.296875l-5.265625 0l0 10.15625l-1.359375 0zm0.71875 -4.953125l0 -1.28125l5.390625 0l0 1.28125l-5.390625 0zm7.511627 4.953125l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm5.8897705 0.25q-0.90625 0 -1.59375 -0.34375q-0.6875 -0.359375 -1.078125 -0.96875q-0.390625 -0.625 -0.390625 -1.40625q0 -0.890625 0.453125 -1.5q0.46875 -0.625 1.25 -0.9375q0.796875 -0.3125 1.734375 -0.3125q0.546875 0 1.0 0.09375q0.46875 0.078125 0.8125 0.203125q0.34375 0.125 0.515625 0.234375l0 -0.5q0 -0.921875 -0.65625 -1.46875q-0.640625 -0.546875 -1.59375 -0.546875q-0.671875 0 -1.265625 0.296875q-0.578125 0.296875 -0.90625 0.828125l-1.03125 -0.765625q0.328125 -0.484375 0.796875 -0.828125q0.484375 -0.359375 1.09375 -0.546875q0.625 -0.203125 1.3125 -0.203125q1.671875 0 2.609375 0.890625q0.9375 0.875 0.9375 2.359375l0 5.171875l-1.296875 0l0 -1.171875l-0.0625 0q-0.203125 0.359375 -0.59375 0.6875q-0.375 0.328125 -0.90625 0.53125q-0.515625 0.203125 -1.140625 0.203125zm0.140625 -1.1875q0.703125 0 1.28125 -0.34375q0.578125 -0.359375 0.921875 -0.953125q0.359375 -0.59375 0.359375 -1.296875q-0.359375 -0.265625 -0.921875 -0.421875q-0.546875 -0.15625 -1.203125 -0.15625q-1.15625 0 -1.703125 0.484375q-0.546875 0.46875 -0.546875 1.171875q0 0.671875 0.5 1.09375q0.515625 0.421875 1.3125 0.421875zm5.0391235 -5.984375l0 -1.234375l6.578125 0l0 1.234375l-6.578125 0zm1.421875 4.78125l0 -8.328125l1.359375 0l0 7.984375q0 0.640625 0.265625 1.0q0.265625 0.34375 0.875 0.34375q0.265625 0 0.484375 -0.078125q0.234375 -0.078125 0.40625 -0.1875l0 1.328125q-0.203125 0.09375 -0.453125 0.140625q-0.25 0.0625 -0.671875 0.0625q-1.015625 0 -1.640625 -0.59375q-0.625 -0.609375 -0.625 -1.671875zm3.609375 -6.015625l4.78125 0l0 1.234375l-4.78125 0l0 -1.234375zm1.421875 6.015625l0 -8.328125l1.359375 0l0 7.984375q0 0.640625 0.265625 1.0q0.265625 0.34375 0.875 0.34375q0.265625 0 0.484375 -0.078125q0.234375 -0.078125 0.40625 -0.1875l0 1.328125q-0.203125 0.09375 -0.453125 0.140625q-0.25 0.0625 -0.671875 0.0625q-1.015625 0 -1.640625 -0.59375q-0.625 -0.609375 -0.625 -1.671875zm8.26239 2.390625q-1.1875 0 -2.109375 -0.546875q-0.921875 -0.5625 -1.4375 -1.53125q-0.515625 -0.984375 -0.515625 -2.234375q0 -1.171875 0.484375 -2.15625q0.484375 -1.0 1.375 -1.59375q0.890625 -0.609375 2.09375 -0.609375q1.21875 0 2.078125 0.546875q0.875 0.53125 1.34375 1.484375q0.484375 0.9375 0.484375 2.15625q0 0.109375 -0.015625 0.21875q0 0.109375 -0.015625 0.1875l-7.03125 0l0 -1.109375l5.609375 0q-0.015625 -0.34375 -0.15625 -0.734375q-0.125 -0.390625 -0.421875 -0.734375q-0.296875 -0.34375 -0.75 -0.5625q-0.453125 -0.21875 -1.125 -0.21875q-0.796875 0 -1.390625 0.40625q-0.578125 0.40625 -0.890625 1.125q-0.3125 0.703125 -0.3125 1.59375q0 1.03125 0.390625 1.734375q0.40625 0.6875 1.03125 1.03125q0.640625 0.328125 1.328125 0.328125q0.890625 0 1.46875 -0.421875q0.59375 -0.4375 0.953125 -1.0625l1.140625 0.5625q-0.46875 0.9375 -1.375 1.546875q-0.890625 0.59375 -2.234375 0.59375zm5.383026 -0.25l0 -8.15625l1.28125 0l0 1.203125l0.078125 0q0.3125 -0.59375 1.03125 -1.03125q0.734375 -0.4375 1.6093445 -0.4375q1.5 0 2.25 0.875q0.765625 0.875 0.765625 2.3125l0 5.234375l-1.359375 0l0 -5.03125q0 -1.171875 -0.578125 -1.65625q-0.5625 -0.5 -1.453125 -0.5q-0.6718445 0 -1.1874695 0.375q-0.515625 0.375 -0.796875 0.96875q-0.28125 0.59375 -0.28125 1.25l0 4.59375l-1.359375 0zm12.812988 0l0 -8.15625l1.359375 0l0 8.15625l-1.359375 0zm0.671875 -9.65625q-0.40625 0 -0.703125 -0.28125q-0.28125 -0.296875 -0.28125 -0.703125q0 -0.421875 0.28125 -0.6875q0.296875 -0.28125 0.703125 -0.28125q0.40625 0 0.6875 0.28125q0.28125 0.265625 0.28125 0.6875q0 0.40625 -0.28125 0.703125q-0.28125 0.28125 -0.6875 0.28125zm2.8191528 9.65625l0 -8.15625l1.28125 0l0 1.203125l0.078125 0q0.3125 -0.59375 1.03125 -1.03125q0.734375 -0.4375 1.609375 -0.4375q1.5 0 2.25 0.875q0.765625 0.875 0.765625 2.3125l0 5.234375l-1.359375 0l0 -5.03125q0 -1.171875 -0.578125 -1.65625q-0.5625 -0.5 -1.453125 -0.5q-0.671875 0 -1.1875 0.375q-0.515625 0.375 -0.796875 0.96875q-0.28125 0.59375 -0.28125 1.25l0 4.59375l-1.359375 0zm8.013519 -8.15625l4.78125 0l0 1.234375l-4.78125 0l0 -1.234375zm1.421875 6.015625l0 -8.328125l1.359375 0l0 7.984375q0 0.640625 0.265625 1.0q0.265625 0.34375 0.875 0.34375q0.265625 0 0.484375 -0.078125q0.234375 -0.078125 0.40625 -0.1875l0 1.328125q-0.203125 0.09375 -0.453125 0.140625q-0.25 0.0625 -0.671875 0.0625q-1.015625 0 -1.640625 -0.59375q-0.625 -0.609375 -0.625 -1.671875zm8.378632 2.390625q-1.234375 0 -2.1875 -0.5625q-0.9375 -0.578125 -1.46875 -1.5625q-0.53125 -0.984375 -0.53125 -2.203125q0 -1.21875 0.53125 -2.203125q0.53125 -0.984375 1.46875 -1.5625q0.953125 -0.578125 2.1875 -0.578125q1.234375 0 2.171875 0.59375q0.953125 0.578125 1.484375 1.5625q0.53125 0.984375 0.53125 2.1875q0 1.21875 -0.53125 2.203125q-0.53125 0.984375 -1.484375 1.5625q-0.9375 0.5625 -2.171875 0.5625zm0 -1.21875q0.734375 0 1.375 -0.375q0.65625 -0.375 1.046875 -1.0625q0.40625 -0.703125 0.40625 -1.671875q0 -0.984375 -0.40625 -1.671875q-0.390625 -0.703125 -1.046875 -1.0625q-0.640625 -0.375 -1.375 -0.375q-0.734375 0 -1.390625 0.375q-0.65625 0.359375 -1.0625 1.0625q-0.390625 0.6875 -0.390625 1.671875q0 0.96875 0.390625 1.671875q0.40625 0.6875 1.0625 1.0625q0.65625 0.375 1.390625 0.375z" fill-rule="nonzero"/><path fill="#000000" d="m412.63937 224.4583q-0.921875 0 -1.625 -0.296875q-0.6875 -0.296875 -1.140625 -0.796875q-0.453125 -0.5 -0.671875 -1.09375l1.203125 -0.546875q0.328125 0.734375 0.9375 1.140625q0.609375 0.40625 1.390625 0.40625q0.75 0 1.25 -0.296875q0.515625 -0.3125 0.515625 -0.90625q0 -0.375 -0.21875 -0.625q-0.203125 -0.25 -0.609375 -0.421875q-0.390625 -0.171875 -0.96875 -0.3125l-1.0 -0.265625q-0.5625 -0.15625 -1.078125 -0.4375q-0.515625 -0.296875 -0.828125 -0.75q-0.3125 -0.453125 -0.3125 -1.109375q0 -0.734375 0.421875 -1.265625q0.4375 -0.53125 1.140625 -0.8125q0.703125 -0.28125 1.515625 -0.28125q0.703125 0 1.3125 0.203125q0.625 0.203125 1.078125 0.59375q0.46875 0.390625 0.703125 0.96875l-1.171875 0.546875q-0.3125 -0.609375 -0.828125 -0.84375q-0.5 -0.25 -1.125 -0.25q-0.671875 0 -1.171875 0.296875q-0.5 0.296875 -0.5 0.8125q0 0.515625 0.40625 0.765625q0.40625 0.25 1.0 0.421875l1.1875 0.296875q1.203125 0.3125 1.8125 0.90625q0.609375 0.59375 0.609375 1.46875q0 0.765625 -0.4375 1.328125q-0.4375 0.546875 -1.171875 0.859375q-0.734375 0.296875 -1.625 0.296875zm4.7692566 -0.25l0 -8.15625l1.359375 0l0 8.15625l-1.359375 0zm0.671875 -9.65625q-0.40625 0 -0.703125 -0.28125q-0.28125 -0.296875 -0.28125 -0.703125q0 -0.421875 0.28125 -0.6875q0.296875 -0.28125 0.703125 -0.28125q0.40625 0 0.6875 0.28125q0.28125 0.265625 0.28125 0.6875q0 0.40625 -0.28125 0.703125q-0.28125 0.28125 -0.6875 0.28125zm2.8191223 9.65625l0 -8.15625l1.28125 0l0 1.203125l0.078125 0q0.3125 -0.59375 1.03125 -1.03125q0.734375 -0.4375 1.609375 -0.4375q1.5 0 2.25 0.875q0.765625 0.875 0.765625 2.3125l0 5.234375l-1.359375 0l0 -5.03125q0 -1.171875 -0.578125 -1.65625q-0.5625 -0.5 -1.453125 -0.5q-0.671875 0 -1.1875 0.375q-0.515625 0.375 -0.796875 0.96875q-0.28125 0.59375 -0.28125 1.25l0 4.59375l-1.359375 0zm12.474152 3.71875q-1.046875 0 -1.796875 -0.34375q-0.734375 -0.34375 -1.1875 -0.875q-0.453125 -0.53125 -0.65625 -1.078125l1.25 -0.53125q0.265625 0.671875 0.875 1.125q0.625 0.46875 1.515625 0.46875q1.28125 0 1.96875 -0.75q0.6875 -0.75 0.6875 -2.078125l0 -0.90625l-0.0625 0q-0.390625 0.59375 -1.109375 1.0q-0.71875 0.40625 -1.671875 0.40625q-1.0625 0 -1.9375 -0.53125q-0.875 -0.546875 -1.390625 -1.515625q-0.5 -0.96875 -0.5 -2.234375q0 -1.265625 0.5 -2.234375q0.515625 -0.96875 1.390625 -1.515625q0.875 -0.546875 1.9375 -0.546875q0.953125 0 1.671875 0.421875q0.71875 0.40625 1.109375 1.015625l0.0625 0l0 -1.171875l1.296875 0l0 7.84375q0 1.359375 -0.53125 2.25q-0.515625 0.890625 -1.40625 1.328125q-0.875 0.453125 -2.015625 0.453125zm0 -4.796875q0.71875 0 1.328125 -0.359375q0.609375 -0.359375 0.96875 -1.046875q0.359375 -0.6875 0.359375 -1.640625q0 -1.0 -0.359375 -1.671875q-0.359375 -0.6875 -0.96875 -1.03125q-0.609375 -0.359375 -1.328125 -0.359375q-0.71875 0 -1.328125 0.359375q-0.609375 0.359375 -0.984375 1.046875q-0.359375 0.671875 -0.359375 1.65625q0 0.96875 0.359375 1.65625q0.375 0.671875 0.984375 1.03125q0.609375 0.359375 1.328125 0.359375zm5.973877 1.078125l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm6.9210205 0.25q-1.1875 0 -2.109375 -0.546875q-0.921875 -0.5625 -1.4375 -1.53125q-0.515625 -0.984375 -0.515625 -2.234375q0 -1.171875 0.484375 -2.15625q0.484375 -1.0 1.375 -1.59375q0.890625 -0.609375 2.09375 -0.609375q1.21875 0 2.078125 0.546875q0.875 0.53125 1.34375 1.484375q0.484375 0.9375 0.484375 2.15625q0 0.109375 -0.015625 0.21875q0 0.109375 -0.015625 0.1875l-7.03125 0l0 -1.109375l5.609375 0q-0.015625 -0.34375 -0.15625 -0.734375q-0.125 -0.390625 -0.421875 -0.734375q-0.296875 -0.34375 -0.75 -0.5625q-0.453125 -0.21875 -1.125 -0.21875q-0.796875 0 -1.390625 0.40625q-0.578125 0.40625 -0.890625 1.125q-0.3125 0.703125 -0.3125 1.59375q0 1.03125 0.390625 1.734375q0.40625 0.6875 1.03125 1.03125q0.640625 0.328125 1.328125 0.328125q0.890625 0 1.46875 -0.421875q0.59375 -0.4375 0.953125 -1.0625l1.140625 0.5625q-0.46875 0.9375 -1.375 1.546875q-0.890625 0.59375 -2.234375 0.59375zm8.532501 -8.40625l4.78125 0l0 1.234375l-4.78125 0l0 -1.234375zm1.421875 6.015625l0 -8.328125l1.359375 0l0 7.984375q0 0.640625 0.265625 1.0q0.265625 0.34375 0.875 0.34375q0.265625 0 0.484375 -0.078125q0.234375 -0.078125 0.40625 -0.1875l0 1.328125q-0.203125 0.09375 -0.453125 0.140625q-0.25 0.0625 -0.671875 0.0625q-1.015625 0 -1.640625 -0.59375q-0.625 -0.609375 -0.625 -1.671875zm7.4143677 2.390625q-0.90625 0 -1.59375 -0.34375q-0.6875 -0.359375 -1.078125 -0.96875q-0.390625 -0.625 -0.390625 -1.40625q0 -0.890625 0.453125 -1.5q0.46875 -0.625 1.25 -0.9375q0.796875 -0.3125 1.734375 -0.3125q0.546875 0 1.0 0.09375q0.46875 0.078125 0.8125 0.203125q0.34375 0.125 0.515625 0.234375l0 -0.5q0 -0.921875 -0.65625 -1.46875q-0.640625 -0.546875 -1.59375 -0.546875q-0.671875 0 -1.265625 0.296875q-0.578125 0.296875 -0.90625 0.828125l-1.03125 -0.765625q0.328125 -0.484375 0.796875 -0.828125q0.484375 -0.359375 1.09375 -0.546875q0.625 -0.203125 1.3125 -0.203125q1.671875 0 2.609375 0.890625q0.9375 0.875 0.9375 2.359375l0 5.171875l-1.296875 0l0 -1.171875l-0.0625 0q-0.203125 0.359375 -0.59375 0.6875q-0.375 0.328125 -0.90625 0.53125q-0.515625 0.203125 -1.140625 0.203125zm0.140625 -1.1875q0.703125 0 1.28125 -0.34375q0.578125 -0.359375 0.921875 -0.953125q0.359375 -0.59375 0.359375 -1.296875q-0.359375 -0.265625 -0.921875 -0.421875q-0.546875 -0.15625 -1.203125 -0.15625q-1.15625 0 -1.703125 0.484375q-0.546875 0.46875 -0.546875 1.171875q0 0.671875 0.5 1.09375q0.515625 0.421875 1.3125 0.421875zm9.902283 1.1875q-0.65625 0 -1.203125 -0.203125q-0.546875 -0.203125 -0.953125 -0.53125q-0.40625 -0.34375 -0.625 -0.71875l-0.078125 0l0 1.203125l-1.28125 0l0 -11.453125l1.359375 0l0 3.375l-0.078125 1.140625l0.078125 0q0.21875 -0.390625 0.625 -0.71875q0.40625 -0.34375 0.953125 -0.546875q0.546875 -0.21875 1.203125 -0.21875q1.109375 0 1.984375 0.5625q0.890625 0.5625 1.40625 1.546875q0.515625 0.96875 0.515625 2.234375q0 1.265625 -0.515625 2.25q-0.515625 0.96875 -1.40625 1.53125q-0.875 0.546875 -1.984375 0.546875zm-0.140625 -1.21875q0.703125 0 1.3125 -0.375q0.609375 -0.390625 0.984375 -1.09375q0.390625 -0.703125 0.390625 -1.640625q0 -0.96875 -0.390625 -1.65625q-0.375 -0.703125 -0.984375 -1.078125q-0.609375 -0.375 -1.3125 -0.375q-0.71875 0 -1.328125 0.375q-0.609375 0.375 -1.0 1.078125q-0.390625 0.6875 -0.390625 1.65625q0 0.953125 0.390625 1.65625q0.390625 0.6875 1.0 1.078125q0.609375 0.375 1.328125 0.375zm5.552002 0.96875l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm6.9210205 0.25q-1.1875 0 -2.109375 -0.546875q-0.921875 -0.5625 -1.4375 -1.53125q-0.515625 -0.984375 -0.515625 -2.234375q0 -1.171875 0.484375 -2.15625q0.484375 -1.0 1.375 -1.59375q0.890625 -0.609375 2.09375 -0.609375q1.21875 0 2.078125 0.546875q0.875 0.53125 1.34375 1.484375q0.484375 0.9375 0.484375 2.15625q0 0.109375 -0.015625 0.21875q0 0.109375 -0.015625 0.1875l-7.03125 0l0 -1.109375l5.609375 0q-0.015625 -0.34375 -0.15625 -0.734375q-0.125 -0.390625 -0.421875 -0.734375q-0.296875 -0.34375 -0.75 -0.5625q-0.453125 -0.21875 -1.125 -0.21875q-0.796875 0 -1.390625 0.40625q-0.578125 0.40625 -0.890625 1.125q-0.3125 0.703125 -0.3125 1.59375q0 1.03125 0.390625 1.734375q0.40625 0.6875 1.03125 1.03125q0.640625 0.328125 1.328125 0.328125q0.890625 0 1.46875 -0.421875q0.59375 -0.4375 0.953125 -1.0625l1.140625 0.5625q-0.46875 0.9375 -1.375 1.546875q-0.890625 0.59375 -2.234375 0.59375z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m759.6877 242.82677l179.49603 0" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m759.6877 242.82677l173.49603 0" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m933.1837 244.4785l4.538086 -1.6517334l-4.538086 -1.6517334z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m789.84515 140.32808l119.18109 0l0 96.944885l-119.18109 0z" fill-rule="evenodd"/><path fill="#000000" d="m814.654 164.68808l4.359375 -11.453125l1.53125 0l4.359375 11.453125l-1.5 0l-3.140625 -8.5625l-0.453125 -1.234375l-0.0625 0l-0.453125 1.234375l-3.140625 8.5625l-1.5 0zm4.96875 -3.15625l0 -1.28125l2.640625 0l0.453125 1.28125l-3.09375 0zm-3.109375 0l0.46875 -1.28125l2.640625 0l0 1.28125l-3.109375 0zm12.8706665 3.40625q-1.109375 0 -2.0 -0.546875q-0.890625 -0.5625 -1.40625 -1.53125q-0.5 -0.984375 -0.5 -2.25q0 -1.265625 0.5 -2.234375q0.515625 -0.984375 1.40625 -1.546875q0.890625 -0.5625 2.0 -0.5625q0.65625 0 1.1875 0.21875q0.546875 0.203125 0.953125 0.546875q0.421875 0.328125 0.640625 0.71875l0.0625 0l-0.0625 -1.140625l0 -3.375l1.359375 0l0 11.453125l-1.296875 0l0 -1.203125l-0.0625 0q-0.21875 0.375 -0.640625 0.71875q-0.40625 0.328125 -0.953125 0.53125q-0.53125 0.203125 -1.1875 0.203125zm0.140625 -1.21875q0.703125 0 1.3125 -0.375q0.625 -0.390625 1.0 -1.078125q0.390625 -0.703125 0.390625 -1.65625q0 -0.96875 -0.390625 -1.65625q-0.375 -0.703125 -1.0 -1.078125q-0.609375 -0.375 -1.3125 -0.375q-0.703125 0 -1.328125 0.375q-0.609375 0.375 -1.0 1.078125q-0.375 0.6875 -0.375 1.65625q0 0.9375 0.375 1.640625q0.390625 0.703125 1.0 1.09375q0.625 0.375 1.328125 0.375zm9.411377 1.21875q-1.109375 0 -2.0 -0.546875q-0.890625 -0.5625 -1.40625 -1.53125q-0.5 -0.984375 -0.5 -2.25q0 -1.265625 0.5 -2.234375q0.515625 -0.984375 1.40625 -1.546875q0.890625 -0.5625 2.0 -0.5625q0.65625 0 1.1875 0.21875q0.546875 0.203125 0.953125 0.546875q0.421875 0.328125 0.640625 0.71875l0.0625 0l-0.0625 -1.140625l0 -3.375l1.359375 0l0 11.453125l-1.296875 0l0 -1.203125l-0.0625 0q-0.21875 0.375 -0.640625 0.71875q-0.40625 0.328125 -0.953125 0.53125q-0.53125 0.203125 -1.1875 0.203125zm0.140625 -1.21875q0.703125 0 1.3125 -0.375q0.625 -0.390625 1.0 -1.078125q0.390625 -0.703125 0.390625 -1.65625q0 -0.96875 -0.390625 -1.65625q-0.375 -0.703125 -1.0 -1.078125q-0.609375 -0.375 -1.3125 -0.375q-0.703125 0 -1.328125 0.375q-0.609375 0.375 -1.0 1.078125q-0.375 0.6875 -0.375 1.65625q0 0.9375 0.375 1.640625q0.390625 0.703125 1.0 1.09375q0.625 0.375 1.328125 0.375zm13.35968 1.21875q-1.1875 0 -2.109375 -0.546875q-0.921875 -0.5625 -1.4375 -1.53125q-0.515625 -0.984375 -0.515625 -2.234375q0 -1.171875 0.484375 -2.15625q0.484375 -1.0 1.375 -1.59375q0.890625 -0.609375 2.09375 -0.609375q1.21875 0 2.078125 0.546875q0.875 0.53125 1.34375 1.484375q0.484375 0.9375 0.484375 2.15625q0 0.109375 -0.015625 0.21875q0 0.109375 -0.015625 0.1875l-7.03125 0l0 -1.109375l5.609375 0q-0.015625 -0.34375 -0.15625 -0.734375q-0.125 -0.390625 -0.421875 -0.734375q-0.296875 -0.34375 -0.75 -0.5625q-0.453125 -0.21875 -1.125 -0.21875q-0.796875 0 -1.390625 0.40625q-0.578125 0.40625 -0.890625 1.125q-0.3125 0.703125 -0.3125 1.59375q0 1.03125 0.390625 1.734375q0.40625 0.6875 1.03125 1.03125q0.640625 0.328125 1.328125 0.328125q0.890625 0 1.46875 -0.421875q0.59375 -0.4375 0.953125 -1.0625l1.140625 0.5625q-0.46875 0.9375 -1.375 1.546875q-0.890625 0.59375 -2.234375 0.59375zm4.4104614 -0.25l3.265625 -4.6875l0.140625 0l2.328125 -3.46875l1.59375 0l-3.1875 4.5l-0.109375 0l-2.4375 3.65625l-1.59375 0zm0.0625 -8.15625l1.546875 0l2.53125 3.625l0.0625 0l3.171875 4.53125l-1.578125 0l-2.4375 -3.609375l-0.109375 0l-3.1875 -4.546875zm7.979553 0l4.78125 0l0 1.234375l-4.78125 0l0 -1.234375zm1.421875 6.015625l0 -8.328125l1.359375 0l0 7.984375q0 0.640625 0.265625 1.0q0.265625 0.34375 0.875 0.34375q0.265625 0 0.484375 -0.078125q0.234375 -0.078125 0.40625 -0.1875l0 1.328125q-0.203125 0.09375 -0.453125 0.140625q-0.25 0.0625 -0.671875 0.0625q-1.015625 0 -1.640625 -0.59375q-0.625 -0.609375 -0.625 -1.671875zm4.980652 2.140625l0 -8.15625l1.28125 0l0 1.3125l0.078125 0q0.15625 -0.46875 0.53125 -0.8125q0.375 -0.34375 0.859375 -0.546875q0.484375 -0.203125 0.96875 -0.203125q0.375 0 0.578125 0.046875q0.203125 0.046875 0.390625 0.125l0 1.46875q-0.265625 -0.125 -0.5625 -0.1875q-0.296875 -0.078125 -0.59375 -0.078125q-0.59375 0 -1.09375 0.34375q-0.5 0.328125 -0.796875 0.890625q-0.28125 0.5625 -0.28125 1.234375l0 4.5625l-1.359375 0zm8.273743 0.25q-0.90625 0 -1.59375 -0.34375q-0.6875 -0.359375 -1.078125 -0.96875q-0.390625 -0.625 -0.390625 -1.40625q0 -0.890625 0.453125 -1.5q0.46875 -0.625 1.25 -0.9375q0.796875 -0.3125 1.734375 -0.3125q0.546875 0 1.0 0.09375q0.46875 0.078125 0.8125 0.203125q0.34375 0.125 0.515625 0.234375l0 -0.5q0 -0.921875 -0.65625 -1.46875q-0.640625 -0.546875 -1.59375 -0.546875q-0.671875 0 -1.265625 0.296875q-0.578125 0.296875 -0.90625 0.828125l-1.03125 -0.765625q0.328125 -0.484375 0.796875 -0.828125q0.484375 -0.359375 1.09375 -0.546875q0.625 -0.203125 1.3125 -0.203125q1.671875 0 2.609375 0.890625q0.9375 0.875 0.9375 2.359375l0 5.171875l-1.296875 0l0 -1.171875l-0.0625 0q-0.203125 0.359375 -0.59375 0.6875q-0.375 0.328125 -0.90625 0.53125q-0.515625 0.203125 -1.140625 0.203125zm0.140625 -1.1875q0.703125 0 1.28125 -0.34375q0.578125 -0.359375 0.921875 -0.953125q0.359375 -0.59375 0.359375 -1.296875q-0.359375 -0.265625 -0.921875 -0.421875q-0.546875 -0.15625 -1.203125 -0.15625q-1.15625 0 -1.703125 0.484375q-0.546875 0.46875 -0.546875 1.171875q0 0.671875 0.5 1.09375q0.515625 0.421875 1.3125 0.421875z" fill-rule="nonzero"/><path fill="#000000" d="m823.67474 184.13809q-1.203125 0 -2.140625 -0.5625q-0.9375 -0.5625 -1.46875 -1.546875q-0.515625 -0.984375 -0.515625 -2.21875q0 -1.25 0.515625 -2.21875q0.53125 -0.984375 1.46875 -1.546875q0.9375 -0.578125 2.140625 -0.578125q1.375 0 2.265625 0.640625q0.890625 0.625 1.25 1.625l-1.234375 0.515625q-0.296875 -0.765625 -0.90625 -1.15625q-0.59375 -0.390625 -1.4375 -0.390625q-0.71875 0 -1.34375 0.390625q-0.609375 0.375 -1.0 1.078125q-0.375 0.6875 -0.375 1.640625q0 0.921875 0.375 1.625q0.390625 0.703125 1.0 1.09375q0.625 0.390625 1.34375 0.390625q0.859375 0 1.484375 -0.40625q0.625 -0.40625 0.9375 -1.140625l1.203125 0.515625q-0.390625 0.9375 -1.296875 1.59375q-0.890625 0.65625 -2.265625 0.65625zm8.718567 0q-1.234375 0 -2.1875 -0.5625q-0.9375 -0.578125 -1.46875 -1.5625q-0.53125 -0.984375 -0.53125 -2.203125q0 -1.21875 0.53125 -2.203125q0.53125 -0.984375 1.46875 -1.5625q0.953125 -0.578125 2.1875 -0.578125q1.234375 0 2.171875 0.59375q0.953125 0.578125 1.484375 1.5625q0.53125 0.984375 0.53125 2.1875q0 1.21875 -0.53125 2.203125q-0.53125 0.984375 -1.484375 1.5625q-0.9375 0.5625 -2.171875 0.5625zm0 -1.21875q0.734375 0 1.375 -0.375q0.65625 -0.375 1.046875 -1.0625q0.40625 -0.703125 0.40625 -1.671875q0 -0.984375 -0.40625 -1.671875q-0.390625 -0.703125 -1.046875 -1.0625q-0.640625 -0.375 -1.375 -0.375q-0.734375 0 -1.390625 0.375q-0.65625 0.359375 -1.0625 1.0625q-0.390625 0.6875 -0.390625 1.671875q0 0.96875 0.390625 1.671875q0.40625 0.6875 1.0625 1.0625q0.65625 0.375 1.390625 0.375zm5.7059937 0.96875l0 -11.453125l1.359375 0l0 11.453125l-1.359375 0zm6.3291626 0.25q-1.5 0 -2.265625 -0.875q-0.765625 -0.875 -0.765625 -2.40625l0 -5.125l1.359375 0l0 4.921875q0 1.21875 0.5625 1.75q0.5625 0.515625 1.390625 0.515625q0.71875 0 1.25 -0.375q0.53125 -0.390625 0.8125 -0.96875q0.296875 -0.59375 0.296875 -1.234375l0 -4.609375l1.359375 0l0 8.15625l-1.296875 0l0 -1.1875l-0.0625 0q-0.203125 0.390625 -0.625 0.71875q-0.40625 0.328125 -0.921875 0.515625q-0.515625 0.203125 -1.09375 0.203125zm6.022888 -0.25l0 -8.15625l1.28125 0l0 1.203125l0.078125 0q0.21875 -0.40625 0.609375 -0.734375q0.390625 -0.328125 0.890625 -0.53125q0.515625 -0.203125 1.03125 -0.203125q0.921875 0 1.578125 0.453125q0.65625 0.4375 0.9375 1.140625q0.421875 -0.6875 1.125 -1.140625q0.703125 -0.453125 1.671875 -0.453125q1.46875 0 2.15625 0.890625q0.6875 0.875 0.6875 2.296875l0 5.234375l-1.34375 0l0 -5.03125q0 -1.171875 -0.484375 -1.65625q-0.484375 -0.5 -1.359375 -0.5q-0.625 0 -1.125 0.359375q-0.484375 0.359375 -0.765625 0.953125q-0.265625 0.578125 -0.265625 1.265625l0 4.609375l-1.359375 0l0 -5.015625q0 -1.171875 -0.484375 -1.671875q-0.484375 -0.5 -1.34375 -0.5q-0.625 0 -1.125 0.375q-0.484375 0.359375 -0.765625 0.953125q-0.265625 0.59375 -0.265625 1.28125l0 4.578125l-1.359375 0zm14.0 0l0 -8.15625l1.28125 0l0 1.203125l0.078125 0q0.3125 -0.59375 1.03125 -1.03125q0.734375 -0.4375 1.609375 -0.4375q1.5 0 2.25 0.875q0.765625 0.875 0.765625 2.3125l0 5.234375l-1.359375 0l0 -5.03125q0 -1.171875 -0.578125 -1.65625q-0.5625 -0.5 -1.453125 -0.5q-0.671875 0 -1.1875 0.375q-0.515625 0.375 -0.796875 0.96875q-0.28125 0.59375 -0.28125 1.25l0 4.59375l-1.359375 0zm11.739746 0.25q-0.921875 0 -1.625 -0.296875q-0.6875 -0.296875 -1.140625 -0.796875q-0.453125 -0.5 -0.671875 -1.09375l1.203125 -0.546875q0.328125 0.734375 0.9375 1.140625q0.609375 0.40625 1.390625 0.40625q0.75 0 1.25 -0.296875q0.515625 -0.3125 0.515625 -0.90625q0 -0.375 -0.21875 -0.625q-0.203125 -0.25 -0.609375 -0.421875q-0.390625 -0.171875 -0.96875 -0.3125l-1.0 -0.265625q-0.5625 -0.15625 -1.078125 -0.4375q-0.515625 -0.296875 -0.828125 -0.75q-0.3125 -0.453125 -0.3125 -1.109375q0 -0.734375 0.421875 -1.265625q0.4375 -0.53125 1.140625 -0.8125q0.703125 -0.28125 1.515625 -0.28125q0.703125 0 1.3125 0.203125q0.625 0.203125 1.078125 0.59375q0.46875 0.390625 0.703125 0.96875l-1.171875 0.546875q-0.3125 -0.609375 -0.828125 -0.84375q-0.5 -0.25 -1.125 -0.25q-0.671875 0 -1.171875 0.296875q-0.5 0.296875 -0.5 0.8125q0 0.515625 0.40625 0.765625q0.40625 0.25 1.0 0.421875l1.1875 0.296875q1.203125 0.3125 1.8125 0.90625q0.609375 0.59375 0.609375 1.46875q0 0.765625 -0.4375 1.328125q-0.4375 0.546875 -1.171875 0.859375q-0.734375 0.296875 -1.625 0.296875z" fill-rule="nonzero"/><path fill="#000000" d="m815.20825 203.08809l0 -8.15625l1.359375 0l0 8.15625l-1.359375 0zm0.671875 -9.65625q-0.40625 0 -0.703125 -0.28125q-0.28125 -0.296875 -0.28125 -0.703125q0 -0.421875 0.28125 -0.6875q0.296875 -0.28125 0.703125 -0.28125q0.40625 0 0.6875 0.28125q0.28125 0.265625 0.28125 0.6875q0 0.40625 -0.28125 0.703125q-0.28125 0.28125 -0.6875 0.28125zm2.8191528 9.65625l0 -8.15625l1.28125 0l0 1.203125l0.078125 0q0.3125 -0.59375 1.03125 -1.03125q0.734375 -0.4375 1.609375 -0.4375q1.5 0 2.25 0.875q0.765625 0.875 0.765625 2.3125l0 5.234375l-1.359375 0l0 -5.03125q0 -1.171875 -0.578125 -1.65625q-0.5625 -0.5 -1.453125 -0.5q-0.671875 0 -1.1875 0.375q-0.515625 0.375 -0.796875 0.96875q-0.28125 0.59375 -0.28125 1.25l0 4.59375l-1.359375 0zm12.444763 0.25q-1.109375 0 -2.0 -0.546875q-0.890625 -0.5625 -1.40625 -1.53125q-0.5 -0.984375 -0.5 -2.25q0 -1.265625 0.5 -2.234375q0.515625 -0.984375 1.40625 -1.546875q0.890625 -0.5625 2.0 -0.5625q0.65625 0 1.1875 0.21875q0.546875 0.203125 0.953125 0.546875q0.421875 0.328125 0.640625 0.71875l0.0625 0l-0.0625 -1.140625l0 -3.375l1.359375 0l0 11.453125l-1.296875 0l0 -1.203125l-0.0625 0q-0.21875 0.375 -0.640625 0.71875q-0.40625 0.328125 -0.953125 0.53125q-0.53125 0.203125 -1.1875 0.203125zm0.140625 -1.21875q0.703125 0 1.3125 -0.375q0.625 -0.390625 1.0 -1.078125q0.390625 -0.703125 0.390625 -1.65625q0 -0.96875 -0.390625 -1.65625q-0.375 -0.703125 -1.0 -1.078125q-0.609375 -0.375 -1.3125 -0.375q-0.703125 0 -1.328125 0.375q-0.609375 0.375 -1.0 1.078125q-0.375 0.6875 -0.375 1.65625q0 0.9375 0.375 1.640625q0.390625 0.703125 1.0 1.09375q0.625 0.375 1.328125 0.375zm6.147644 0.96875l0 -8.15625l1.359375 0l0 8.15625l-1.359375 0zm0.671875 -9.65625q-0.40625 0 -0.703125 -0.28125q-0.28125 -0.296875 -0.28125 -0.703125q0 -0.421875 0.28125 -0.6875q0.296875 -0.28125 0.703125 -0.28125q0.40625 0 0.6875 0.28125q0.28125 0.265625 0.28125 0.6875q0 0.40625 -0.28125 0.703125q-0.28125 0.28125 -0.6875 0.28125zm6.4266357 9.90625q-1.203125 0 -2.140625 -0.5625q-0.9375 -0.5625 -1.46875 -1.546875q-0.515625 -0.984375 -0.515625 -2.21875q0 -1.25 0.515625 -2.21875q0.53125 -0.984375 1.46875 -1.546875q0.9375 -0.578125 2.140625 -0.578125q1.375 0 2.265625 0.640625q0.890625 0.625 1.25 1.625l-1.234375 0.515625q-0.296875 -0.765625 -0.90625 -1.15625q-0.59375 -0.390625 -1.4375 -0.390625q-0.71875 0 -1.34375 0.390625q-0.609375 0.375 -1.0 1.078125q-0.375 0.6875 -0.375 1.640625q0 0.921875 0.375 1.625q0.390625 0.703125 1.0 1.09375q0.625 0.390625 1.34375 0.390625q0.859375 0 1.484375 -0.40625q0.625 -0.40625 0.9375 -1.140625l1.203125 0.515625q-0.390625 0.9375 -1.296875 1.59375q-0.890625 0.65625 -2.265625 0.65625zm7.562256 0q-0.90625 0 -1.59375 -0.34375q-0.6875 -0.359375 -1.078125 -0.96875q-0.390625 -0.625 -0.390625 -1.40625q0 -0.890625 0.453125 -1.5q0.46875 -0.625 1.25 -0.9375q0.796875 -0.3125 1.734375 -0.3125q0.546875 0 1.0 0.09375q0.46875 0.078125 0.8125 0.203125q0.34375 0.125 0.515625 0.234375l0 -0.5q0 -0.921875 -0.65625 -1.46875q-0.640625 -0.546875 -1.59375 -0.546875q-0.671875 0 -1.265625 0.296875q-0.578125 0.296875 -0.90625 0.828125l-1.03125 -0.765625q0.328125 -0.484375 0.796875 -0.828125q0.484375 -0.359375 1.09375 -0.546875q0.625 -0.203125 1.3125 -0.203125q1.671875 0 2.609375 0.890625q0.9375 0.875 0.9375 2.359375l0 5.171875l-1.296875 0l0 -1.171875l-0.0625 0q-0.203125 0.359375 -0.59375 0.6875q-0.375 0.328125 -0.90625 0.53125q-0.515625 0.203125 -1.140625 0.203125zm0.140625 -1.1875q0.703125 0 1.28125 -0.34375q0.578125 -0.359375 0.921875 -0.953125q0.359375 -0.59375 0.359375 -1.296875q-0.359375 -0.265625 -0.921875 -0.421875q-0.546875 -0.15625 -1.203125 -0.15625q-1.15625 0 -1.703125 0.484375q-0.546875 0.46875 -0.546875 1.171875q0 0.671875 0.5 1.09375q0.515625 0.421875 1.3125 0.421875zm5.0391235 -7.21875l4.78125 0l0 1.234375l-4.78125 0l0 -1.234375zm1.421875 6.015625l0 -8.328125l1.359375 0l0 7.984375q0 0.640625 0.265625 1.0q0.265625 0.34375 0.875 0.34375q0.265625 0 0.484375 -0.078125q0.234375 -0.078125 0.40625 -0.1875l0 1.328125q-0.203125 0.09375 -0.453125 0.140625q-0.25 0.0625 -0.671875 0.0625q-1.015625 0 -1.640625 -0.59375q-0.625 -0.609375 -0.625 -1.671875zm5.185669 2.140625l0 -8.15625l1.359375 0l0 8.15625l-1.359375 0zm0.671875 -9.65625q-0.40625 0 -0.703125 -0.28125q-0.28125 -0.296875 -0.28125 -0.703125q0 -0.421875 0.28125 -0.6875q0.296875 -0.28125 0.703125 -0.28125q0.40625 0 0.6875 0.28125q0.28125 0.265625 0.28125 0.6875q0 0.40625 -0.28125 0.703125q-0.28125 0.28125 -0.6875 0.28125zm2.8190918 9.65625l0 -8.15625l1.28125 0l0 1.203125l0.078125 0q0.3125 -0.59375 1.03125 -1.03125q0.734375 -0.4375 1.609375 -0.4375q1.5 0 2.25 0.875q0.765625 0.875 0.765625 2.3125l0 5.234375l-1.359375 0l0 -5.03125q0 -1.171875 -0.578125 -1.65625q-0.5625 -0.5 -1.453125 -0.5q-0.671875 0 -1.1875 0.375q-0.515625 0.375 -0.796875 0.96875q-0.28125 0.59375 -0.28125 1.25l0 4.59375l-1.359375 0zm12.474182 3.71875q-1.046875 0 -1.796875 -0.34375q-0.734375 -0.34375 -1.1875 -0.875q-0.453125 -0.53125 -0.65625 -1.078125l1.25 -0.53125q0.265625 0.671875 0.875 1.125q0.625 0.46875 1.515625 0.46875q1.28125 0 1.96875 -0.75q0.6875 -0.75 0.6875 -2.078125l0 -0.90625l-0.0625 0q-0.390625 0.59375 -1.109375 1.0q-0.71875 0.40625 -1.671875 0.40625q-1.0625 0 -1.9375 -0.53125q-0.875 -0.546875 -1.390625 -1.515625q-0.5 -0.96875 -0.5 -2.234375q0 -1.265625 0.5 -2.234375q0.515625 -0.96875 1.390625 -1.515625q0.875 -0.546875 1.9375 -0.546875q0.953125 0 1.671875 0.421875q0.71875 0.40625 1.109375 1.015625l0.0625 0l0 -1.171875l1.296875 0l0 7.84375q0 1.359375 -0.53125 2.25q-0.515625 0.890625 -1.40625 1.328125q-0.875 0.453125 -2.015625 0.453125zm0 -4.796875q0.71875 0 1.328125 -0.359375q0.609375 -0.359375 0.96875 -1.046875q0.359375 -0.6875 0.359375 -1.640625q0 -1.0 -0.359375 -1.671875q-0.359375 -0.6875 -0.96875 -1.03125q-0.609375 -0.359375 -1.328125 -0.359375q-0.71875 0 -1.328125 0.359375q-0.609375 0.359375 -0.984375 1.046875q-0.359375 0.671875 -0.359375 1.65625q0 0.96875 0.359375 1.65625q0.375 0.671875 0.984375 1.03125q0.609375 0.359375 1.328125 0.359375z" fill-rule="nonzero"/><path fill="#000000" d="m808.00696 222.53809q-0.921875 0 -1.625 -0.296875q-0.6875 -0.296875 -1.140625 -0.796875q-0.453125 -0.5 -0.671875 -1.09375l1.203125 -0.546875q0.328125 0.734375 0.9375 1.140625q0.609375 0.40625 1.390625 0.40625q0.75 0 1.25 -0.296875q0.515625 -0.3125 0.515625 -0.90625q0 -0.375 -0.21875 -0.625q-0.203125 -0.25 -0.609375 -0.421875q-0.390625 -0.171875 -0.96875 -0.3125l-1.0 -0.265625q-0.5625 -0.15625 -1.078125 -0.4375q-0.515625 -0.296875 -0.828125 -0.75q-0.3125 -0.453125 -0.3125 -1.109375q0 -0.734375 0.421875 -1.265625q0.4375 -0.53125 1.140625 -0.8125q0.703125 -0.28125 1.515625 -0.28125q0.703125 0 1.3125 0.203125q0.625 0.203125 1.078125 0.59375q0.46875 0.390625 0.703125 0.96875l-1.171875 0.546875q-0.3125 -0.609375 -0.828125 -0.84375q-0.5 -0.25 -1.125 -0.25q-0.671875 0 -1.171875 0.296875q-0.5 0.296875 -0.5 0.8125q0 0.515625 0.40625 0.765625q0.40625 0.25 1.0 0.421875l1.1875 0.296875q1.203125 0.3125 1.8125 0.90625q0.609375 0.59375 0.609375 1.46875q0 0.765625 -0.4375 1.328125q-0.4375 0.546875 -1.171875 0.859375q-0.734375 0.296875 -1.625 0.296875zm8.55426 0q-1.234375 0 -2.1875 -0.5625q-0.9375 -0.578125 -1.46875 -1.5625q-0.53125 -0.984375 -0.53125 -2.203125q0 -1.21875 0.53125 -2.203125q0.53125 -0.984375 1.46875 -1.5625q0.953125 -0.578125 2.1875 -0.578125q1.234375 0 2.171875 0.59375q0.953125 0.578125 1.484375 1.5625q0.53125 0.984375 0.53125 2.1875q0 1.21875 -0.53125 2.203125q-0.53125 0.984375 -1.484375 1.5625q-0.9375 0.5625 -2.171875 0.5625zm0 -1.21875q0.734375 0 1.375 -0.375q0.65625 -0.375 1.046875 -1.0625q0.40625 -0.703125 0.40625 -1.671875q0 -0.984375 -0.40625 -1.671875q-0.390625 -0.703125 -1.046875 -1.0625q-0.640625 -0.375 -1.375 -0.375q-0.734375 0 -1.390625 0.375q-0.65625 0.359375 -1.0625 1.0625q-0.390625 0.6875 -0.390625 1.671875q0 0.96875 0.390625 1.671875q0.40625 0.6875 1.0625 1.0625q0.65625 0.375 1.390625 0.375zm8.65918 1.21875q-1.5 0 -2.265625 -0.875q-0.765625 -0.875 -0.765625 -2.40625l0 -5.125l1.359375 0l0 4.921875q0 1.21875 0.5625 1.75q0.5625 0.515625 1.390625 0.515625q0.71875 0 1.25 -0.375q0.53125 -0.390625 0.8125 -0.96875q0.296875 -0.59375 0.296875 -1.234375l0 -4.609375l1.359375 0l0 8.15625l-1.296875 0l0 -1.1875l-0.0625 0q-0.203125 0.390625 -0.625 0.71875q-0.40625 0.328125 -0.921875 0.515625q-0.515625 0.203125 -1.09375 0.203125zm6.022888 -0.25l0 -8.15625l1.28125 0l0 1.3125l0.078125 0q0.15625 -0.46875 0.53125 -0.8125q0.375 -0.34375 0.859375 -0.546875q0.484375 -0.203125 0.96875 -0.203125q0.375 0 0.578125 0.046875q0.203125 0.046875 0.390625 0.125l0 1.46875q-0.265625 -0.125 -0.5625 -0.1875q-0.296875 -0.078125 -0.59375 -0.078125q-0.59375 0 -1.09375 0.34375q-0.5 0.328125 -0.796875 0.890625q-0.28125 0.5625 -0.28125 1.234375l0 4.5625l-1.359375 0zm9.20752 0.25q-1.203125 0 -2.140625 -0.5625q-0.9375 -0.5625 -1.46875 -1.546875q-0.515625 -0.984375 -0.515625 -2.21875q0 -1.25 0.515625 -2.21875q0.53125 -0.984375 1.46875 -1.546875q0.9375 -0.578125 2.140625 -0.578125q1.375 0 2.265625 0.640625q0.890625 0.625 1.25 1.625l-1.234375 0.515625q-0.296875 -0.765625 -0.90625 -1.15625q-0.59375 -0.390625 -1.4375 -0.390625q-0.71875 0 -1.34375 0.390625q-0.609375 0.375 -1.0 1.078125q-0.375 0.6875 -0.375 1.640625q0 0.921875 0.375 1.625q0.390625 0.703125 1.0 1.09375q0.625 0.390625 1.34375 0.390625q0.859375 0 1.484375 -0.40625q0.625 -0.40625 0.9375 -1.140625l1.203125 0.515625q-0.390625 0.9375 -1.296875 1.59375q-0.890625 0.65625 -2.265625 0.65625zm8.593506 0q-1.1875 0 -2.109375 -0.546875q-0.921875 -0.5625 -1.4375 -1.53125q-0.515625 -0.984375 -0.515625 -2.234375q0 -1.171875 0.484375 -2.15625q0.484375 -1.0 1.375 -1.59375q0.890625 -0.609375 2.09375 -0.609375q1.21875 0 2.078125 0.546875q0.875 0.53125 1.34375 1.484375q0.484375 0.9375 0.484375 2.15625q0 0.109375 -0.015625 0.21875q0 0.109375 -0.015625 0.1875l-7.03125 0l0 -1.109375l5.609375 0q-0.015625 -0.34375 -0.15625 -0.734375q-0.125 -0.390625 -0.421875 -0.734375q-0.296875 -0.34375 -0.75 -0.5625q-0.453125 -0.21875 -1.125 -0.21875q-0.796875 0 -1.390625 0.40625q-0.578125 0.40625 -0.890625 1.125q-0.3125 0.703125 -0.3125 1.59375q0 1.03125 0.390625 1.734375q0.40625 0.6875 1.03125 1.03125q0.640625 0.328125 1.328125 0.328125q0.890625 0 1.46875 -0.421875q0.59375 -0.4375 0.953125 -1.0625l1.140625 0.5625q-0.46875 0.9375 -1.375 1.546875q-0.890625 0.59375 -2.234375 0.59375zm8.532532 -8.40625l4.78125 0l0 1.234375l-4.78125 0l0 -1.234375zm1.421875 6.015625l0 -8.328125l1.359375 0l0 7.984375q0 0.640625 0.265625 1.0q0.265625 0.34375 0.875 0.34375q0.265625 0 0.484375 -0.078125q0.234375 -0.078125 0.40625 -0.1875l0 1.328125q-0.203125 0.09375 -0.453125 0.140625q-0.25 0.0625 -0.671875 0.0625q-1.015625 0 -1.640625 -0.59375q-0.625 -0.609375 -0.625 -1.671875zm4.980652 2.140625l0 -8.15625l1.28125 0l0 1.3125l0.078125 0q0.15625 -0.46875 0.53125 -0.8125q0.375 -0.34375 0.859375 -0.546875q0.484375 -0.203125 0.96875 -0.203125q0.375 0 0.578125 0.046875q0.203125 0.046875 0.390625 0.125l0 1.46875q-0.265625 -0.125 -0.5625 -0.1875q-0.296875 -0.078125 -0.59375 -0.078125q-0.59375 0 -1.09375 0.34375q-0.5 0.328125 -0.796875 0.890625q-0.28125 0.5625 -0.28125 1.234375l0 4.5625l-1.359375 0zm8.273743 0.25q-0.90625 0 -1.59375 -0.34375q-0.6875 -0.359375 -1.078125 -0.96875q-0.390625 -0.625 -0.390625 -1.40625q0 -0.890625 0.453125 -1.5q0.46875 -0.625 1.25 -0.9375q0.796875 -0.3125 1.734375 -0.3125q0.546875 0 1.0 0.09375q0.46875 0.078125 0.8125 0.203125q0.34375 0.125 0.515625 0.234375l0 -0.5q0 -0.921875 -0.65625 -1.46875q-0.640625 -0.546875 -1.59375 -0.546875q-0.671875 0 -1.265625 0.296875q-0.578125 0.296875 -0.90625 0.828125l-1.03125 -0.765625q0.328125 -0.484375 0.796875 -0.828125q0.484375 -0.359375 1.09375 -0.546875q0.625 -0.203125 1.3125 -0.203125q1.671875 0 2.609375 0.890625q0.9375 0.875 0.9375 2.359375l0 5.171875l-1.296875 0l0 -1.171875l-0.0625 0q-0.203125 0.359375 -0.59375 0.6875q-0.375 0.328125 -0.90625 0.53125q-0.515625 0.203125 -1.140625 0.203125zm0.140625 -1.1875q0.703125 0 1.28125 -0.34375q0.578125 -0.359375 0.921875 -0.953125q0.359375 -0.59375 0.359375 -1.296875q-0.359375 -0.265625 -0.921875 -0.421875q-0.546875 -0.15625 -1.203125 -0.15625q-1.15625 0 -1.703125 0.484375q-0.546875 0.46875 -0.546875 1.171875q0 0.671875 0.5 1.09375q0.515625 0.421875 1.3125 0.421875zm9.369141 1.1875q-1.203125 0 -2.140625 -0.5625q-0.9375 -0.5625 -1.46875 -1.546875q-0.515625 -0.984375 -0.515625 -2.21875q0 -1.25 0.515625 -2.21875q0.53125 -0.984375 1.46875 -1.546875q0.9375 -0.578125 2.140625 -0.578125q1.375 0 2.265625 0.640625q0.890625 0.625 1.25 1.625l-1.234375 0.515625q-0.296875 -0.765625 -0.90625 -1.15625q-0.59375 -0.390625 -1.4375 -0.390625q-0.71875 0 -1.34375 0.390625q-0.609375 0.375 -1.0 1.078125q-0.375 0.6875 -0.375 1.640625q0 0.921875 0.375 1.625q0.390625 0.703125 1.0 1.09375q0.625 0.390625 1.34375 0.390625q0.859375 0 1.484375 -0.40625q0.625 -0.40625 0.9375 -1.140625l1.203125 0.515625q-0.390625 0.9375 -1.296875 1.59375q-0.890625 0.65625 -2.265625 0.65625zm8.593506 0q-1.1875 0 -2.109375 -0.546875q-0.921875 -0.5625 -1.4375 -1.53125q-0.515625 -0.984375 -0.515625 -2.234375q0 -1.171875 0.484375 -2.15625q0.484375 -1.0 1.375 -1.59375q0.890625 -0.609375 2.09375 -0.609375q1.21875 0 2.078125 0.546875q0.875 0.53125 1.34375 1.484375q0.484375 0.9375 0.484375 2.15625q0 0.109375 -0.015625 0.21875q0 0.109375 -0.015625 0.1875l-7.03125 0l0 -1.109375l5.609375 0q-0.015625 -0.34375 -0.15625 -0.734375q-0.125 -0.390625 -0.421875 -0.734375q-0.296875 -0.34375 -0.75 -0.5625q-0.453125 -0.21875 -1.125 -0.21875q-0.796875 0 -1.390625 0.40625q-0.578125 0.40625 -0.890625 1.125q-0.3125 0.703125 -0.3125 1.59375q0 1.03125 0.390625 1.734375q0.40625 0.6875 1.03125 1.03125q0.640625 0.328125 1.328125 0.328125q0.890625 0 1.46875 -0.421875q0.59375 -0.4375 0.953125 -1.0625l1.140625 0.5625q-0.46875 0.9375 -1.375 1.546875q-0.890625 0.59375 -2.234375 0.59375z" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m939.1837 101.32284l0 239.99738" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m1007.08923 101.32284l0 239.99738" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m1088.3649 101.32284l0 239.99738" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m1237.1942 101.32284l0 239.99738" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m1306.8818 101.32284l0 239.99738" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m938.68506 101.821526l368.69556 0" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m938.68506 136.82152l368.69556 0" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m938.68506 170.82152l368.69556 0" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m938.68506 204.82152l368.69556 0" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m938.68506 238.82152l368.69556 0" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m938.68506 272.82153l368.69556 0" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m938.68506 306.82153l368.69556 0" fill-rule="nonzero"/><path stroke="#434343" stroke-width="1.0" stroke-linecap="butt" d="m938.68506 340.82153l368.69556 0" fill-rule="nonzero"/><path fill="#4a4a4a" d="m970.2971 114.04277l0 1.828125l1.265625 0l0 1.4375l-1.265625 0l0 3.703125q0 0.40625 0.15625 0.59375q0.15625 0.171875 0.609375 0.171875q0.328125 0 0.578125 -0.046875l0 1.484375q-0.578125 0.1875 -1.203125 0.1875q-2.078125 0 -2.109375 -2.109375l0 -3.984375l-1.09375 0l0 -1.4375l1.09375 0l0 -1.828125l1.96875 0zm6.3710938 7.171875q0 -0.359375 -0.359375 -0.5625q-0.359375 -0.21875 -1.15625 -0.375q-2.640625 -0.5625 -2.640625 -2.25q0 -0.984375 0.8125 -1.640625q0.828125 -0.65625 2.140625 -0.65625q1.421875 0 2.265625 0.671875q0.84375 0.65625 0.84375 1.71875l-1.984375 0q0 -0.4375 -0.28125 -0.703125q-0.265625 -0.28125 -0.84375 -0.28125q-0.5 0 -0.78125 0.234375q-0.265625 0.21875 -0.265625 0.5625q0 0.328125 0.3125 0.53125q0.3125 0.203125 1.046875 0.359375q0.734375 0.140625 1.25 0.328125q1.5625 0.5625 1.5625 1.984375q0 1.015625 -0.875 1.640625q-0.859375 0.625 -2.234375 0.625q-0.9375 0 -1.65625 -0.328125q-0.71875 -0.34375 -1.125 -0.921875q-0.40625 -0.578125 -0.40625 -1.25l1.859375 0q0.03125 0.53125 0.390625 0.8125q0.375 0.28125 0.96875 0.28125q0.578125 0 0.859375 -0.21875q0.296875 -0.21875 0.296875 -0.5625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m1037.7656 119.51152q0 -1.734375 0.765625 -2.75q0.78125 -1.03125 2.125 -1.03125q1.09375 0 1.796875 0.796875l0 -3.765625l1.96875 0l0 10.5l-1.78125 0l-0.09375 -0.78125q-0.734375 0.921875 -1.890625 0.921875q-1.3125 0 -2.109375 -1.03125q-0.78125 -1.03125 -0.78125 -2.859375zm1.96875 0.140625q0 1.03125 0.359375 1.59375q0.375 0.546875 1.0625 0.546875q0.921875 0 1.296875 -0.765625l0 -2.921875q-0.375 -0.765625 -1.28125 -0.765625q-1.4375 0 -1.4375 2.3125zm10.654297 2.859375q-0.734375 0.890625 -2.015625 0.890625q-1.203125 0 -1.828125 -0.6875q-0.625 -0.6875 -0.625 -2.0l0 -4.84375l1.96875 0l0 4.765625q0 1.15625 1.046875 1.15625q1.015625 0 1.390625 -0.6875l0 -5.234375l1.984375 0l0 7.390625l-1.859375 0l-0.0625 -0.75zm7.5683594 -4.796875q-0.40625 -0.046875 -0.71875 -0.046875q-1.125 0 -1.46875 0.75l0 4.84375l-1.96875 0l0 -7.390625l1.859375 0l0.0625 0.875q0.59375 -1.015625 1.640625 -1.015625q0.328125 0 0.609375 0.09375l-0.015625 1.890625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m1149.3865 119.51152q0 -1.734375 0.765625 -2.75q0.78125 -1.03125 2.125 -1.03125q1.09375 0 1.796875 0.796875l0 -3.765625l1.96875 0l0 10.5l-1.78125 0l-0.09375 -0.78125q-0.734375 0.921875 -1.890625 0.921875q-1.3125 0 -2.109375 -1.03125q-0.78125 -1.03125 -0.78125 -2.859375zm1.96875 0.140625q0 1.03125 0.359375 1.59375q0.375 0.546875 1.0625 0.546875q0.921875 0 1.296875 -0.765625l0 -2.921875q-0.375 -0.765625 -1.28125 -0.765625q-1.4375 0 -1.4375 2.3125zm10.513672 3.609375q-0.140625 -0.265625 -0.203125 -0.65625q-0.71875 0.796875 -1.859375 0.796875q-1.09375 0 -1.8125 -0.625q-0.703125 -0.640625 -0.703125 -1.59375q0 -1.171875 0.859375 -1.796875q0.875 -0.640625 2.53125 -0.640625l0.90625 0l0 -0.421875q0 -0.515625 -0.265625 -0.828125q-0.265625 -0.3125 -0.828125 -0.3125q-0.5 0 -0.78125 0.25q-0.28125 0.234375 -0.28125 0.65625l-1.984375 0q0 -0.640625 0.390625 -1.1875q0.40625 -0.546875 1.125 -0.859375q0.734375 -0.3125 1.640625 -0.3125q1.359375 0 2.15625 0.6875q0.8125 0.6875 0.8125 1.921875l0 3.21875q0 1.046875 0.296875 1.59375l0 0.109375l-2.0 0zm-1.640625 -1.375q0.4375 0 0.8125 -0.1875q0.375 -0.203125 0.546875 -0.53125l0 -1.265625l-0.734375 0q-1.484375 0 -1.578125 1.015625l-0.015625 0.125q0 0.359375 0.25 0.609375q0.265625 0.234375 0.71875 0.234375zm7.2246094 -7.84375l0 1.828125l1.265625 0l0 1.4375l-1.265625 0l0 3.703125q0 0.40625 0.15625 0.59375q0.15625 0.171875 0.609375 0.171875q0.328125 0 0.578125 -0.046875l0 1.484375q-0.578125 0.1875 -1.203125 0.1875q-2.078125 0 -2.109375 -2.109375l0 -3.984375l-1.09375 0l0 -1.4375l1.09375 0l0 -1.828125l1.96875 0zm5.7617188 9.359375q-1.625 0 -2.640625 -1.0q-1.015625 -1.0 -1.015625 -2.65625l0 -0.203125q0 -1.109375 0.421875 -1.984375q0.4375 -0.875 1.21875 -1.34375q0.796875 -0.484375 1.796875 -0.484375q1.53125 0 2.390625 0.953125q0.875 0.953125 0.875 2.71875l0 0.796875l-4.703125 0q0.09375 0.734375 0.578125 1.171875q0.484375 0.4375 1.21875 0.4375q1.140625 0 1.78125 -0.828125l0.96875 1.078125q-0.4375 0.640625 -1.203125 1.0q-0.75 0.34375 -1.6875 0.34375zm-0.21875 -6.078125q-0.59375 0 -0.953125 0.40625q-0.359375 0.390625 -0.46875 1.125l2.75 0l0 -0.15625q-0.015625 -0.65625 -0.359375 -1.015625q-0.34375 -0.359375 -0.96875 -0.359375z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m1251.98 119.51152q0 -1.734375 0.765625 -2.75q0.78125 -1.03125 2.125 -1.03125q1.09375 0 1.796875 0.796875l0 -3.765625l1.96875 0l0 10.5l-1.78125 0l-0.09375 -0.78125q-0.734375 0.921875 -1.890625 0.921875q-1.3125 0 -2.109375 -1.03125q-0.78125 -1.03125 -0.78125 -2.859375zm1.96875 0.140625q0 1.03125 0.359375 1.59375q0.375 0.546875 1.0625 0.546875q0.921875 0 1.296875 -0.765625l0 -2.921875q-0.375 -0.765625 -1.28125 -0.765625q-1.4375 0 -1.4375 2.3125zm9.623047 3.75q-1.625 0 -2.640625 -1.0q-1.015625 -1.0 -1.015625 -2.65625l0 -0.203125q0 -1.109375 0.421875 -1.984375q0.4375 -0.875 1.21875 -1.34375q0.796875 -0.484375 1.796875 -0.484375q1.53125 0 2.390625 0.953125q0.875 0.953125 0.875 2.71875l0 0.796875l-4.703125 0q0.09375 0.734375 0.578125 1.171875q0.484375 0.4375 1.21875 0.4375q1.140625 0 1.78125 -0.828125l0.96875 1.078125q-0.4375 0.640625 -1.203125 1.0q-0.75 0.34375 -1.6875 0.34375zm-0.21875 -6.078125q-0.59375 0 -0.953125 0.40625q-0.359375 0.390625 -0.46875 1.125l2.75 0l0 -0.15625q-0.015625 -0.65625 -0.359375 -1.015625q-0.34375 -0.359375 -0.96875 -0.359375zm7.0722656 3.53125l1.375 -4.984375l2.0625 0l-2.5 7.390625l-1.890625 0l-2.484375 -7.390625l2.0625 0l1.375 4.984375zm6.3876953 2.40625l-1.984375 0l0 -7.390625l1.984375 0l0 7.390625zm-2.09375 -9.3125q0 -0.4375 0.296875 -0.71875q0.296875 -0.296875 0.8125 -0.296875q0.5 0 0.796875 0.296875q0.296875 0.28125 0.296875 0.71875q0 0.453125 -0.296875 0.75q-0.296875 0.28125 -0.796875 0.28125q-0.5 0 -0.8125 -0.28125q-0.296875 -0.296875 -0.296875 -0.75zm6.790039 7.859375q0.546875 0 0.890625 -0.296875q0.34375 -0.3125 0.359375 -0.8125l1.859375 0q-0.015625 0.75 -0.421875 1.390625q-0.390625 0.625 -1.09375 0.96875q-0.703125 0.34375 -1.546875 0.34375q-1.59375 0 -2.515625 -1.0q-0.90625 -1.015625 -0.90625 -2.796875l0 -0.125q0 -1.71875 0.90625 -2.734375q0.90625 -1.015625 2.5 -1.015625q1.390625 0 2.21875 0.796875q0.84375 0.78125 0.859375 2.09375l-1.859375 0q-0.015625 -0.578125 -0.359375 -0.9375q-0.34375 -0.359375 -0.890625 -0.359375q-0.703125 0 -1.0625 0.515625q-0.34375 0.5 -0.34375 1.625l0 0.203125q0 1.140625 0.34375 1.640625q0.359375 0.5 1.0625 0.5zm7.6289062 1.59375q-1.625 0 -2.640625 -1.0q-1.015625 -1.0 -1.015625 -2.65625l0 -0.203125q0 -1.109375 0.421875 -1.984375q0.4375 -0.875 1.21875 -1.34375q0.796875 -0.484375 1.796875 -0.484375q1.53125 0 2.390625 0.953125q0.875 0.953125 0.875 2.71875l0 0.796875l-4.703125 0q0.09375 0.734375 0.578125 1.171875q0.484375 0.4375 1.21875 0.4375q1.140625 0 1.78125 -0.828125l0.96875 1.078125q-0.4375 0.640625 -1.203125 1.0q-0.75 0.34375 -1.6875 0.34375zm-0.21875 -6.078125q-0.59375 0 -0.953125 0.40625q-0.359375 0.390625 -0.46875 1.125l2.75 0l0 -0.15625q-0.015625 -0.65625 -0.359375 -1.015625q-0.34375 -0.359375 -0.96875 -0.359375z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m952.1681 158.26152l-1.265625 0l0 -8.421875l-2.5625 0.921875l0 -1.140625l3.625 -1.359375l0.203125 0l0 10.0zm9.939453 -4.25q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m1020.0736 158.26152l-1.265625 0l0 -8.421875l-2.5625 0.921875l0 -1.140625l3.625 -1.359375l0.203125 0l0 10.0zm9.939453 -4.25q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625zm9.126953 1.71875q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m1103.7086 158.26152l-6.515625 0l0 -0.90625l3.4375 -3.828125q0.765625 -0.875 1.0625 -1.40625q0.296875 -0.546875 0.296875 -1.140625q0 -0.765625 -0.484375 -1.265625q-0.46875 -0.5 -1.25 -0.5q-0.9375 0 -1.46875 0.53125q-0.515625 0.53125 -0.515625 1.5l-1.265625 0q0 -1.375 0.875 -2.21875q0.890625 -0.859375 2.375 -0.859375q1.390625 0 2.1875 0.734375q0.8125 0.71875 0.8125 1.9375q0 1.46875 -1.875 3.5l-2.65625 2.890625l4.984375 0l0 1.03125zm7.580078 -4.25q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625zm9.408203 5.96875l-6.515625 0l0 -0.90625l3.4375 -3.828125q0.765625 -0.875 1.0625 -1.40625q0.296875 -0.546875 0.296875 -1.140625q0 -0.765625 -0.484375 -1.265625q-0.46875 -0.5 -1.25 -0.5q-0.9375 0 -1.46875 0.53125q-0.515625 0.53125 -0.515625 1.5l-1.265625 0q0 -1.375 0.875 -2.21875q0.890625 -0.859375 2.375 -0.859375q1.390625 0 2.1875 0.734375q0.8125 0.71875 0.8125 1.9375q0 1.46875 -1.875 3.5l-2.65625 2.890625l4.984375 0l0 1.03125zm7.861328 0l-6.515625 0l0 -0.90625l3.4375 -3.828125q0.765625 -0.875 1.0625 -1.40625q0.296875 -0.546875 0.296875 -1.140625q0 -0.765625 -0.484375 -1.265625q-0.46875 -0.5 -1.25 -0.5q-0.9375 0 -1.46875 0.53125q-0.515625 0.53125 -0.515625 1.5l-1.265625 0q0 -1.375 0.875 -2.21875q0.890625 -0.859375 2.375 -0.859375q1.390625 0 2.1875 0.734375q0.8125 0.71875 0.8125 1.9375q0 1.46875 -1.875 3.5l-2.65625 2.890625l4.984375 0l0 1.03125zm4.111328 -3.71875l-3.34375 0l0 -1.03125l3.34375 0l0 1.03125zm7.3310547 -0.53125q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625zm9.408203 5.96875l-6.515625 0l0 -0.90625l3.4375 -3.828125q0.765625 -0.875 1.0625 -1.40625q0.296875 -0.546875 0.296875 -1.140625q0 -0.765625 -0.484375 -1.265625q-0.46875 -0.5 -1.25 -0.5q-0.9375 0 -1.46875 0.53125q-0.515625 0.53125 -0.515625 1.5l-1.265625 0q0 -1.375 0.875 -2.21875q0.890625 -0.859375 2.375 -0.859375q1.390625 0 2.1875 0.734375q0.8125 0.71875 0.8125 1.9375q0 1.46875 -1.875 3.5l-2.65625 2.890625l4.984375 0l0 1.03125zm4.111328 -3.71875l-3.34375 0l0 -1.03125l3.34375 0l0 1.03125zm7.3310547 -0.53125q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625zm7.048828 5.96875l-1.265625 0l0 -8.421875l-2.5625 0.921875l0 -1.140625l3.625 -1.359375l0.203125 0l0 10.0z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m1245.8192 154.4959q0 -1.09375 0.421875 -1.953125q0.4375 -0.875 1.1875 -1.34375q0.765625 -0.46875 1.75 -0.46875q1.5 0 2.4375 1.046875q0.9375 1.046875 0.9375 2.78125l0 0.09375q0 1.078125 -0.421875 1.9375q-0.40625 0.84375 -1.1875 1.328125q-0.765625 0.484375 -1.75 0.484375q-1.515625 0 -2.453125 -1.046875q-0.921875 -1.046875 -0.921875 -2.765625l0 -0.09375zm1.265625 0.15625q0 1.21875 0.5625 1.96875q0.578125 0.75 1.546875 0.75q0.953125 0 1.515625 -0.75q0.578125 -0.765625 0.578125 -2.125q0 -1.21875 -0.578125 -1.96875q-0.578125 -0.765625 -1.53125 -0.765625q-0.9375 0 -1.515625 0.75q-0.578125 0.75 -0.578125 2.140625zm10.625 -2.65625q-0.28125 -0.046875 -0.625 -0.046875q-1.234375 0 -1.6875 1.0625l0 5.25l-1.265625 0l0 -7.390625l1.234375 0l0.015625 0.84375q0.625 -0.984375 1.765625 -0.984375q0.375 0 0.5625 0.09375l0 1.171875zm2.5341797 6.265625l-1.265625 0l0 -7.390625l1.265625 0l0 7.390625zm-1.359375 -9.359375q0 -0.3125 0.1875 -0.515625q0.1875 -0.21875 0.546875 -0.21875q0.375 0 0.5625 0.21875q0.203125 0.203125 0.203125 0.515625q0 0.3125 -0.203125 0.515625q-0.1875 0.203125 -0.5625 0.203125q-0.359375 0 -0.546875 -0.203125q-0.1875 -0.203125 -0.1875 -0.515625zm3.053711 5.59375q0 -1.09375 0.421875 -1.953125q0.4375 -0.875 1.1875 -1.34375q0.765625 -0.46875 1.75 -0.46875q1.5 0 2.4375 1.046875q0.9375 1.046875 0.9375 2.78125l0 0.09375q0 1.078125 -0.421875 1.9375q-0.40625 0.84375 -1.1875 1.328125q-0.765625 0.484375 -1.75 0.484375q-1.515625 0 -2.453125 -1.046875q-0.921875 -1.046875 -0.921875 -2.765625l0 -0.09375zm1.265625 0.15625q0 1.21875 0.5625 1.96875q0.578125 0.75 1.546875 0.75q0.953125 0 1.515625 -0.75q0.578125 -0.765625 0.578125 -2.125q0 -1.21875 -0.578125 -1.96875q-0.578125 -0.765625 -1.53125 -0.765625q-0.9375 0 -1.515625 0.75q-0.578125 0.75 -0.578125 2.140625zm8.421875 3.609375l-1.265625 0l0 -10.5l1.265625 0l0 10.5zm5.100586 0.140625q-1.515625 0 -2.453125 -0.984375q-0.9375 -1.0 -0.9375 -2.65625l0 -0.21875q0 -1.109375 0.421875 -1.96875q0.421875 -0.875 1.171875 -1.359375q0.75 -0.484375 1.640625 -0.484375q1.4375 0 2.234375 0.953125q0.796875 0.9375 0.796875 2.71875l0 0.515625l-5.0 0q0.015625 1.09375 0.625 1.78125q0.625 0.671875 1.5625 0.671875q0.671875 0 1.125 -0.265625q0.46875 -0.28125 0.828125 -0.734375l0.765625 0.59375q-0.921875 1.4375 -2.78125 1.4375zm-0.15625 -6.640625q-0.765625 0 -1.296875 0.5625q-0.515625 0.5625 -0.640625 1.5625l3.703125 0l0 -0.09375q-0.046875 -0.96875 -0.515625 -1.5q-0.46875 -0.53125 -1.25 -0.53125z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m954.52747 192.26152l-6.515625 0l0 -0.90625l3.4375 -3.828125q0.765625 -0.875 1.0625 -1.40625q0.296875 -0.546875 0.296875 -1.140625q0 -0.765625 -0.484375 -1.265625q-0.46875 -0.5 -1.25 -0.5q-0.9375 0 -1.46875 0.53125q-0.515625 0.53125 -0.515625 1.5l-1.265625 0q0 -1.375 0.875 -2.21875q0.890625 -0.859375 2.375 -0.859375q1.390625 0 2.1875 0.734375q0.8125 0.71875 0.8125 1.9375q0 1.46875 -1.875 3.5l-2.65625 2.890625l4.984375 0l0 1.03125zm7.580078 -4.25q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m1022.433 192.26152l-6.515625 0l0 -0.90625l3.4375 -3.828125q0.765625 -0.875 1.0625 -1.40625q0.296875 -0.546875 0.296875 -1.140625q0 -0.765625 -0.484375 -1.265625q-0.46875 -0.5 -1.25 -0.5q-0.9375 0 -1.46875 0.53125q-0.515625 0.53125 -0.515625 1.5l-1.265625 0q0 -1.375 0.875 -2.21875q0.890625 -0.859375 2.375 -0.859375q1.390625 0 2.1875 0.734375q0.8125 0.71875 0.8125 1.9375q0 1.46875 -1.875 3.5l-2.65625 2.890625l4.984375 0l0 1.03125zm7.580078 -4.25q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625zm9.126953 1.71875q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m1103.7086 192.26152l-6.515625 0l0 -0.90625l3.4375 -3.828125q0.765625 -0.875 1.0625 -1.40625q0.296875 -0.546875 0.296875 -1.140625q0 -0.765625 -0.484375 -1.265625q-0.46875 -0.5 -1.25 -0.5q-0.9375 0 -1.46875 0.53125q-0.515625 0.53125 -0.515625 1.5l-1.265625 0q0 -1.375 0.875 -2.21875q0.890625 -0.859375 2.375 -0.859375q1.390625 0 2.1875 0.734375q0.8125 0.71875 0.8125 1.9375q0 1.46875 -1.875 3.5l-2.65625 2.890625l4.984375 0l0 1.03125zm7.580078 -4.25q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625zm9.408203 5.96875l-6.515625 0l0 -0.90625l3.4375 -3.828125q0.765625 -0.875 1.0625 -1.40625q0.296875 -0.546875 0.296875 -1.140625q0 -0.765625 -0.484375 -1.265625q-0.46875 -0.5 -1.25 -0.5q-0.9375 0 -1.46875 0.53125q-0.515625 0.53125 -0.515625 1.5l-1.265625 0q0 -1.375 0.875 -2.21875q0.890625 -0.859375 2.375 -0.859375q1.390625 0 2.1875 0.734375q0.8125 0.71875 0.8125 1.9375q0 1.46875 -1.875 3.5l-2.65625 2.890625l4.984375 0l0 1.03125zm7.861328 0l-6.515625 0l0 -0.90625l3.4375 -3.828125q0.765625 -0.875 1.0625 -1.40625q0.296875 -0.546875 0.296875 -1.140625q0 -0.765625 -0.484375 -1.265625q-0.46875 -0.5 -1.25 -0.5q-0.9375 0 -1.46875 0.53125q-0.515625 0.53125 -0.515625 1.5l-1.265625 0q0 -1.375 0.875 -2.21875q0.890625 -0.859375 2.375 -0.859375q1.390625 0 2.1875 0.734375q0.8125 0.71875 0.8125 1.9375q0 1.46875 -1.875 3.5l-2.65625 2.890625l4.984375 0l0 1.03125zm4.111328 -3.71875l-3.34375 0l0 -1.03125l3.34375 0l0 1.03125zm7.3310547 -0.53125q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625zm9.408203 5.96875l-6.515625 0l0 -0.90625l3.4375 -3.828125q0.765625 -0.875 1.0625 -1.40625q0.296875 -0.546875 0.296875 -1.140625q0 -0.765625 -0.484375 -1.265625q-0.46875 -0.5 -1.25 -0.5q-0.9375 0 -1.46875 0.53125q-0.515625 0.53125 -0.515625 1.5l-1.265625 0q0 -1.375 0.875 -2.21875q0.890625 -0.859375 2.375 -0.859375q1.390625 0 2.1875 0.734375q0.8125 0.71875 0.8125 1.9375q0 1.46875 -1.875 3.5l-2.65625 2.890625l4.984375 0l0 1.03125zm4.111328 -3.71875l-3.34375 0l0 -1.03125l3.34375 0l0 1.03125zm7.3310547 -0.53125q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625zm7.048828 5.96875l-1.265625 0l0 -8.421875l-2.5625 0.921875l0 -1.140625l3.625 -1.359375l0.203125 0l0 10.0z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m1245.8192 188.4959q0 -1.09375 0.421875 -1.953125q0.4375 -0.875 1.1875 -1.34375q0.765625 -0.46875 1.75 -0.46875q1.5 0 2.4375 1.046875q0.9375 1.046875 0.9375 2.78125l0 0.09375q0 1.078125 -0.421875 1.9375q-0.40625 0.84375 -1.1875 1.328125q-0.765625 0.484375 -1.75 0.484375q-1.515625 0 -2.453125 -1.046875q-0.921875 -1.046875 -0.921875 -2.765625l0 -0.09375zm1.265625 0.15625q0 1.21875 0.5625 1.96875q0.578125 0.75 1.546875 0.75q0.953125 0 1.515625 -0.75q0.578125 -0.765625 0.578125 -2.125q0 -1.21875 -0.578125 -1.96875q-0.578125 -0.765625 -1.53125 -0.765625q-0.9375 0 -1.515625 0.75q-0.578125 0.75 -0.578125 2.140625zm10.625 -2.65625q-0.28125 -0.046875 -0.625 -0.046875q-1.234375 0 -1.6875 1.0625l0 5.25l-1.265625 0l0 -7.390625l1.234375 0l0.015625 0.84375q0.625 -0.984375 1.765625 -0.984375q0.375 0 0.5625 0.09375l0 1.171875zm2.5341797 6.265625l-1.265625 0l0 -7.390625l1.265625 0l0 7.390625zm-1.359375 -9.359375q0 -0.3125 0.1875 -0.515625q0.1875 -0.21875 0.546875 -0.21875q0.375 0 0.5625 0.21875q0.203125 0.203125 0.203125 0.515625q0 0.3125 -0.203125 0.515625q-0.1875 0.203125 -0.5625 0.203125q-0.359375 0 -0.546875 -0.203125q-0.1875 -0.203125 -0.1875 -0.515625zm3.053711 5.59375q0 -1.09375 0.421875 -1.953125q0.4375 -0.875 1.1875 -1.34375q0.765625 -0.46875 1.75 -0.46875q1.5 0 2.4375 1.046875q0.9375 1.046875 0.9375 2.78125l0 0.09375q0 1.078125 -0.421875 1.9375q-0.40625 0.84375 -1.1875 1.328125q-0.765625 0.484375 -1.75 0.484375q-1.515625 0 -2.453125 -1.046875q-0.921875 -1.046875 -0.921875 -2.765625l0 -0.09375zm1.265625 0.15625q0 1.21875 0.5625 1.96875q0.578125 0.75 1.546875 0.75q0.953125 0 1.515625 -0.75q0.578125 -0.765625 0.578125 -2.125q0 -1.21875 -0.578125 -1.96875q-0.578125 -0.765625 -1.53125 -0.765625q-0.9375 0 -1.515625 0.75q-0.578125 0.75 -0.578125 2.140625zm8.421875 3.609375l-1.265625 0l0 -10.5l1.265625 0l0 10.5zm5.100586 0.140625q-1.515625 0 -2.453125 -0.984375q-0.9375 -1.0 -0.9375 -2.65625l0 -0.21875q0 -1.109375 0.421875 -1.96875q0.421875 -0.875 1.171875 -1.359375q0.75 -0.484375 1.640625 -0.484375q1.4375 0 2.234375 0.953125q0.796875 0.9375 0.796875 2.71875l0 0.515625l-5.0 0q0.015625 1.09375 0.625 1.78125q0.625 0.671875 1.5625 0.671875q0.671875 0 1.125 -0.265625q0.46875 -0.28125 0.828125 -0.734375l0.765625 0.59375q-0.921875 1.4375 -2.78125 1.4375zm-0.15625 -6.640625q-0.765625 0 -1.296875 0.5625q-0.515625 0.5625 -0.640625 1.5625l3.703125 0l0 -0.09375q-0.046875 -0.96875 -0.515625 -1.5q-0.46875 -0.53125 -1.25 -0.53125z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m952.1681 226.26152l-1.265625 0l0 -8.421875l-2.5625 0.921875l0 -1.140625l3.625 -1.359375l0.203125 0l0 10.0zm9.939453 -4.25q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625zm9.126953 1.71875q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m1020.0736 226.26152l-1.265625 0l0 -8.421875l-2.5625 0.921875l0 -1.140625l3.625 -1.359375l0.203125 0l0 10.0zm9.939453 -4.25q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m1103.7086 226.26152l-6.515625 0l0 -0.90625l3.4375 -3.828125q0.765625 -0.875 1.0625 -1.40625q0.296875 -0.546875 0.296875 -1.140625q0 -0.765625 -0.484375 -1.265625q-0.46875 -0.5 -1.25 -0.5q-0.9375 0 -1.46875 0.53125q-0.515625 0.53125 -0.515625 1.5l-1.265625 0q0 -1.375 0.875 -2.21875q0.890625 -0.859375 2.375 -0.859375q1.390625 0 2.1875 0.734375q0.8125 0.71875 0.8125 1.9375q0 1.46875 -1.875 3.5l-2.65625 2.890625l4.984375 0l0 1.03125zm7.580078 -4.25q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625zm9.408203 5.96875l-6.515625 0l0 -0.90625l3.4375 -3.828125q0.765625 -0.875 1.0625 -1.40625q0.296875 -0.546875 0.296875 -1.140625q0 -0.765625 -0.484375 -1.265625q-0.46875 -0.5 -1.25 -0.5q-0.9375 0 -1.46875 0.53125q-0.515625 0.53125 -0.515625 1.5l-1.265625 0q0 -1.375 0.875 -2.21875q0.890625 -0.859375 2.375 -0.859375q1.390625 0 2.1875 0.734375q0.8125 0.71875 0.8125 1.9375q0 1.46875 -1.875 3.5l-2.65625 2.890625l4.984375 0l0 1.03125zm7.861328 0l-6.515625 0l0 -0.90625l3.4375 -3.828125q0.765625 -0.875 1.0625 -1.40625q0.296875 -0.546875 0.296875 -1.140625q0 -0.765625 -0.484375 -1.265625q-0.46875 -0.5 -1.25 -0.5q-0.9375 0 -1.46875 0.53125q-0.515625 0.53125 -0.515625 1.5l-1.265625 0q0 -1.375 0.875 -2.21875q0.890625 -0.859375 2.375 -0.859375q1.390625 0 2.1875 0.734375q0.8125 0.71875 0.8125 1.9375q0 1.46875 -1.875 3.5l-2.65625 2.890625l4.984375 0l0 1.03125zm4.111328 -3.71875l-3.34375 0l0 -1.03125l3.34375 0l0 1.03125zm7.3310547 -0.53125q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625zm4.736328 0.375l0.9375 0q0.90625 -0.015625 1.40625 -0.46875q0.515625 -0.453125 0.515625 -1.234375q0 -1.75 -1.734375 -1.75q-0.828125 0 -1.3125 0.46875q-0.484375 0.46875 -0.484375 1.234375l-1.265625 0q0 -1.1875 0.859375 -1.96875q0.875 -0.78125 2.203125 -0.78125q1.40625 0 2.203125 0.75q0.796875 0.75 0.796875 2.0625q0 0.65625 -0.421875 1.265625q-0.40625 0.609375 -1.140625 0.90625q0.828125 0.265625 1.265625 0.875q0.453125 0.59375 0.453125 1.453125q0 1.34375 -0.875 2.140625q-0.875 0.78125 -2.28125 0.78125q-1.390625 0 -2.28125 -0.75q-0.875 -0.765625 -0.875 -2.015625l1.28125 0q0 0.78125 0.5 1.265625q0.515625 0.46875 1.375 0.46875q0.921875 0 1.40625 -0.484375q0.484375 -0.484375 0.484375 -1.375q0 -0.859375 -0.53125 -1.328125q-0.53125 -0.46875 -1.546875 -0.484375l-0.9375 0l0 -1.03125zm8.783203 1.875l-3.34375 0l0 -1.03125l3.34375 0l0 1.03125zm5.2529297 3.71875l-1.265625 0l0 -8.421875l-2.5625 0.921875l0 -1.140625l3.625 -1.359375l0.203125 0l0 10.0zm9.939453 -4.25q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m1247.3348 218.8709l0.046875 0.8125q0.8125 -0.953125 2.1875 -0.953125q1.546875 0 2.109375 1.1875q0.375 -0.53125 0.96875 -0.859375q0.59375 -0.328125 1.390625 -0.328125q2.4375 0 2.484375 2.578125l0 4.953125l-1.265625 0l0 -4.875q0 -0.796875 -0.375 -1.1875q-0.359375 -0.390625 -1.203125 -0.390625q-0.71875 0 -1.1875 0.421875q-0.453125 0.40625 -0.53125 1.125l0 4.90625l-1.265625 0l0 -4.84375q0 -1.609375 -1.578125 -1.609375q-1.25 0 -1.703125 1.046875l0 5.40625l-1.265625 0l0 -7.390625l1.1875 0zm15.661133 7.390625q-0.109375 -0.21875 -0.1875 -0.78125q-0.875 0.921875 -2.109375 0.921875q-1.09375 0 -1.796875 -0.609375q-0.6875 -0.625 -0.6875 -1.578125q0 -1.15625 0.875 -1.796875q0.875 -0.640625 2.46875 -0.640625l1.234375 0l0 -0.578125q0 -0.671875 -0.40625 -1.0625q-0.390625 -0.390625 -1.15625 -0.390625q-0.6875 0 -1.140625 0.34375q-0.453125 0.34375 -0.453125 0.828125l-1.28125 0q0 -0.5625 0.390625 -1.078125q0.40625 -0.515625 1.078125 -0.8125q0.671875 -0.296875 1.46875 -0.296875q1.28125 0 2.0 0.640625q0.734375 0.640625 0.765625 1.75l0 3.40625q0 1.015625 0.25 1.625l0 0.109375l-1.3125 0zm-2.109375 -0.96875q0.59375 0 1.125 -0.296875q0.546875 -0.3125 0.78125 -0.8125l0 -1.515625l-1.0 0q-2.3125 0 -2.3125 1.359375q0 0.59375 0.390625 0.9375q0.390625 0.328125 1.015625 0.328125zm8.724609 -5.296875q-0.28125 -0.046875 -0.625 -0.046875q-1.234375 0 -1.6875 1.0625l0 5.25l-1.265625 0l0 -7.390625l1.234375 0l0.015625 0.84375q0.625 -0.984375 1.765625 -0.984375q0.375 0 0.5625 0.09375l0 1.171875zm2.5341797 6.265625l-1.265625 0l0 -10.5l1.265625 0l0 10.5zm3.397461 0l-1.265625 0l0 -7.390625l1.265625 0l0 7.390625zm-1.359375 -9.359375q0 -0.3125 0.1875 -0.515625q0.1875 -0.21875 0.546875 -0.21875q0.375 0 0.5625 0.21875q0.203125 0.203125 0.203125 0.515625q0 0.3125 -0.203125 0.515625q-0.1875 0.203125 -0.5625 0.203125q-0.359375 0 -0.546875 -0.203125q-0.1875 -0.203125 -0.1875 -0.515625zm4.584961 1.96875l0.03125 0.921875q0.859375 -1.0625 2.21875 -1.0625q2.34375 0 2.375 2.640625l0 4.890625l-1.265625 0l0 -4.890625q-0.015625 -0.796875 -0.375 -1.171875q-0.359375 -0.390625 -1.109375 -0.390625q-0.625 0 -1.09375 0.328125q-0.453125 0.328125 -0.71875 0.859375l0 5.265625l-1.265625 0l0 -7.390625l1.203125 0z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m954.52747 260.26154l-6.515625 0l0 -0.90625l3.4375 -3.8281403q0.765625 -0.875 1.0625 -1.40625q0.296875 -0.546875 0.296875 -1.140625q0 -0.765625 -0.484375 -1.265625q-0.46875 -0.5 -1.25 -0.5q-0.9375 0 -1.46875 0.53125q-0.515625 0.53125 -0.515625 1.5l-1.265625 0q0 -1.375 0.875 -2.21875q0.890625 -0.859375 2.375 -0.859375q1.390625 0 2.1875 0.734375q0.8125 0.71875 0.8125 1.9375q0 1.46875 -1.875 3.5000153l-2.65625 2.890625l4.984375 0l0 1.03125zm7.580078 -4.25q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875153q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.6406403zm-1.265625 -1.7187653q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.0312653q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625153zm9.126953 1.7187653q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875153q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.6406403zm-1.265625 -1.7187653q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.0312653q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625153z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m1022.433 260.26154l-6.515625 0l0 -0.90625l3.4375 -3.8281403q0.765625 -0.875 1.0625 -1.40625q0.296875 -0.546875 0.296875 -1.140625q0 -0.765625 -0.484375 -1.265625q-0.46875 -0.5 -1.25 -0.5q-0.9375 0 -1.46875 0.53125q-0.515625 0.53125 -0.515625 1.5l-1.265625 0q0 -1.375 0.875 -2.21875q0.890625 -0.859375 2.375 -0.859375q1.390625 0 2.1875 0.734375q0.8125 0.71875 0.8125 1.9375q0 1.46875 -1.875 3.5000153l-2.65625 2.890625l4.984375 0l0 1.03125zm7.580078 -4.25q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875153q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.6406403zm-1.265625 -1.7187653q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.0312653q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625153z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m1103.7086 260.26154l-6.515625 0l0 -0.90625l3.4375 -3.8281403q0.765625 -0.875 1.0625 -1.40625q0.296875 -0.546875 0.296875 -1.140625q0 -0.765625 -0.484375 -1.265625q-0.46875 -0.5 -1.25 -0.5q-0.9375 0 -1.46875 0.53125q-0.515625 0.53125 -0.515625 1.5l-1.265625 0q0 -1.375 0.875 -2.21875q0.890625 -0.859375 2.375 -0.859375q1.390625 0 2.1875 0.734375q0.8125 0.71875 0.8125 1.9375q0 1.46875 -1.875 3.5000153l-2.65625 2.890625l4.984375 0l0 1.03125zm7.580078 -4.25q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875153q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.6406403zm-1.265625 -1.7187653q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.0312653q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625153zm9.408203 5.9687653l-6.515625 0l0 -0.90625l3.4375 -3.8281403q0.765625 -0.875 1.0625 -1.40625q0.296875 -0.546875 0.296875 -1.140625q0 -0.765625 -0.484375 -1.265625q-0.46875 -0.5 -1.25 -0.5q-0.9375 0 -1.46875 0.53125q-0.515625 0.53125 -0.515625 1.5l-1.265625 0q0 -1.375 0.875 -2.21875q0.890625 -0.859375 2.375 -0.859375q1.390625 0 2.1875 0.734375q0.8125 0.71875 0.8125 1.9375q0 1.46875 -1.875 3.5000153l-2.65625 2.890625l4.984375 0l0 1.03125zm7.861328 0l-6.515625 0l0 -0.90625l3.4375 -3.8281403q0.765625 -0.875 1.0625 -1.40625q0.296875 -0.546875 0.296875 -1.140625q0 -0.765625 -0.484375 -1.265625q-0.46875 -0.5 -1.25 -0.5q-0.9375 0 -1.46875 0.53125q-0.515625 0.53125 -0.515625 1.5l-1.265625 0q0 -1.375 0.875 -2.21875q0.890625 -0.859375 2.375 -0.859375q1.390625 0 2.1875 0.734375q0.8125 0.71875 0.8125 1.9375q0 1.46875 -1.875 3.5000153l-2.65625 2.890625l4.984375 0l0 1.03125zm4.111328 -3.71875l-3.34375 0l0 -1.0312653l3.34375 0l0 1.0312653zm7.3310547 -0.53125q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875153q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.6406403zm-1.265625 -1.7187653q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.0312653q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625153zm4.736328 0.375l0.9375 0q0.90625 -0.015625 1.40625 -0.46875q0.515625 -0.453125 0.515625 -1.234375q0 -1.75 -1.734375 -1.75q-0.828125 0 -1.3125 0.46875q-0.484375 0.46875 -0.484375 1.234375l-1.265625 0q0 -1.1875 0.859375 -1.96875q0.875 -0.78125 2.203125 -0.78125q1.40625 0 2.203125 0.75q0.796875 0.75 0.796875 2.0625q0 0.65625 -0.421875 1.265625q-0.40625 0.609375 -1.140625 0.90625q0.828125 0.265625 1.265625 0.87501526q0.453125 0.59375 0.453125 1.453125q0 1.34375 -0.875 2.140625q-0.875 0.78125 -2.28125 0.78125q-1.390625 0 -2.28125 -0.75q-0.875 -0.765625 -0.875 -2.015625l1.28125 0q0 0.78125 0.5 1.265625q0.515625 0.46875 1.375 0.46875q0.921875 0 1.40625 -0.484375q0.484375 -0.484375 0.484375 -1.375q0 -0.859375 -0.53125 -1.328125q-0.53125 -0.46876526 -1.546875 -0.48439026l-0.9375 0l0 -1.03125zm8.783203 1.8750153l-3.34375 0l0 -1.0312653l3.34375 0l0 1.0312653zm5.2529297 3.71875l-1.265625 0l0 -8.42189l-2.5625 0.921875l0 -1.140625l3.625 -1.359375l0.203125 0l0 10.000015zm9.939453 -4.25q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875153q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.6406403zm-1.265625 -1.7187653q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.0312653q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625153z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m1247.3348 252.8709l0.046875 0.8125q0.8125 -0.953125 2.1875 -0.953125q1.546875 0 2.109375 1.1875q0.375 -0.53125 0.96875 -0.859375q0.59375 -0.328125 1.390625 -0.328125q2.4375 0 2.484375 2.578125l0 4.9531403l-1.265625 0l0 -4.8750153q0 -0.796875 -0.375 -1.1875q-0.359375 -0.390625 -1.203125 -0.390625q-0.71875 0 -1.1875 0.421875q-0.453125 0.40625 -0.53125 1.125l0 4.9062653l-1.265625 0l0 -4.8437653q0 -1.609375 -1.578125 -1.609375q-1.25 0 -1.703125 1.046875l0 5.4062653l-1.265625 0l0 -7.3906403l1.1875 0zm15.661133 7.3906403q-0.109375 -0.21875 -0.1875 -0.78125q-0.875 0.921875 -2.109375 0.921875q-1.09375 0 -1.796875 -0.609375q-0.6875 -0.625 -0.6875 -1.578125q0 -1.15625 0.875 -1.796875q0.875 -0.64064026 2.46875 -0.64064026l1.234375 0l0 -0.578125q0 -0.671875 -0.40625 -1.0625q-0.390625 -0.390625 -1.15625 -0.390625q-0.6875 0 -1.140625 0.34375q-0.453125 0.34375 -0.453125 0.828125l-1.28125 0q0 -0.5625 0.390625 -1.078125q0.40625 -0.515625 1.078125 -0.8125q0.671875 -0.296875 1.46875 -0.296875q1.28125 0 2.0 0.640625q0.734375 0.640625 0.765625 1.75l0 3.4062653q0 1.015625 0.25 1.625l0 0.109375l-1.3125 0zm-2.109375 -0.96875q0.59375 0 1.125 -0.296875q0.546875 -0.3125 0.78125 -0.8125l0 -1.515625l-1.0 0q-2.3125 0 -2.3125 1.359375q0 0.59375 0.390625 0.9375q0.390625 0.328125 1.015625 0.328125zm8.724609 -5.2968903q-0.28125 -0.046875 -0.625 -0.046875q-1.234375 0 -1.6875 1.0625l0 5.2500153l-1.265625 0l0 -7.3906403l1.234375 0l0.015625 0.84375q0.625 -0.984375 1.765625 -0.984375q0.375 0 0.5625 0.09375l0 1.171875zm2.5341797 6.2656403l-1.265625 0l0 -10.500015l1.265625 0l0 10.500015zm3.397461 0l-1.265625 0l0 -7.3906403l1.265625 0l0 7.3906403zm-1.359375 -9.35939q0 -0.3125 0.1875 -0.515625q0.1875 -0.21875 0.546875 -0.21875q0.375 0 0.5625 0.21875q0.203125 0.203125 0.203125 0.515625q0 0.3125 -0.203125 0.515625q-0.1875 0.203125 -0.5625 0.203125q-0.359375 0 -0.546875 -0.203125q-0.1875 -0.203125 -0.1875 -0.515625zm4.584961 1.96875l0.03125 0.921875q0.859375 -1.0625 2.21875 -1.0625q2.34375 0 2.375 2.640625l0 4.8906403l-1.265625 0l0 -4.8906403q-0.015625 -0.796875 -0.375 -1.171875q-0.359375 -0.390625 -1.109375 -0.390625q-0.625 0 -1.09375 0.328125q-0.453125 0.328125 -0.71875 0.859375l0 5.2656403l-1.265625 0l0 -7.3906403l1.203125 0z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m949.8556 288.6678l0.9375 0q0.90625 -0.015625 1.40625 -0.46875q0.515625 -0.453125 0.515625 -1.234375q0 -1.75 -1.734375 -1.75q-0.828125 0 -1.3125 0.46875q-0.484375 0.46875 -0.484375 1.234375l-1.265625 0q0 -1.1875 0.859375 -1.96875q0.875 -0.78125 2.203125 -0.78125q1.40625 0 2.203125 0.75q0.796875 0.75 0.796875 2.0625q0 0.65625 -0.421875 1.265625q-0.40625 0.609375 -1.140625 0.90625q0.828125 0.265625 1.265625 0.875q0.453125 0.59375 0.453125 1.453125q0 1.34375 -0.875 2.140625q-0.875 0.78125 -2.28125 0.78125q-1.390625 0 -2.28125 -0.75q-0.875 -0.765625 -0.875 -2.015625l1.28125 0q0 0.78125 0.5 1.265625q0.515625 0.46875 1.375 0.46875q0.921875 0 1.40625 -0.484375q0.484375 -0.484375 0.484375 -1.375q0 -0.859375 -0.53125 -1.328125q-0.53125 -0.46875 -1.546875 -0.484375l-0.9375 0l0 -1.03125zm12.251953 1.34375q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m1020.0736 294.26154l-1.265625 0l0 -8.421875l-2.5625 0.921875l0 -1.140625l3.625 -1.359375l0.203125 0l0 10.0zm9.939453 -4.25q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625zm9.126953 1.71875q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625zm9.126953 1.71875q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m1103.7086 294.26154l-6.515625 0l0 -0.90625l3.4375 -3.828125q0.765625 -0.875 1.0625 -1.40625q0.296875 -0.546875 0.296875 -1.140625q0 -0.765625 -0.484375 -1.265625q-0.46875 -0.5 -1.25 -0.5q-0.9375 0 -1.46875 0.53125q-0.515625 0.53125 -0.515625 1.5l-1.265625 0q0 -1.375 0.875 -2.21875q0.890625 -0.859375 2.375 -0.859375q1.390625 0 2.1875 0.734375q0.8125 0.71875 0.8125 1.9375q0 1.46875 -1.875 3.5l-2.65625 2.890625l4.984375 0l0 1.03125zm7.580078 -4.25q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625zm9.408203 5.96875l-6.515625 0l0 -0.90625l3.4375 -3.828125q0.765625 -0.875 1.0625 -1.40625q0.296875 -0.546875 0.296875 -1.140625q0 -0.765625 -0.484375 -1.265625q-0.46875 -0.5 -1.25 -0.5q-0.9375 0 -1.46875 0.53125q-0.515625 0.53125 -0.515625 1.5l-1.265625 0q0 -1.375 0.875 -2.21875q0.890625 -0.859375 2.375 -0.859375q1.390625 0 2.1875 0.734375q0.8125 0.71875 0.8125 1.9375q0 1.46875 -1.875 3.5l-2.65625 2.890625l4.984375 0l0 1.03125zm5.501953 0l-1.265625 0l0 -8.421875l-2.5625 0.921875l0 -1.140625l3.625 -1.359375l0.203125 0l0 10.0zm6.470703 -3.71875l-3.34375 0l0 -1.03125l3.34375 0l0 1.03125zm5.2529297 3.71875l-1.265625 0l0 -8.421875l-2.5625 0.921875l0 -1.140625l3.625 -1.359375l0.203125 0l0 10.0zm9.939453 -4.25q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625zm5.658203 2.25l-3.34375 0l0 -1.03125l3.34375 0l0 1.03125zm7.6123047 3.71875l-6.515625 0l0 -0.90625l3.4375 -3.828125q0.765625 -0.875 1.0625 -1.40625q0.296875 -0.546875 0.296875 -1.140625q0 -0.765625 -0.484375 -1.265625q-0.46875 -0.5 -1.25 -0.5q-0.9375 0 -1.46875 0.53125q-0.515625 0.53125 -0.515625 1.5l-1.265625 0q0 -1.375 0.875 -2.21875q0.890625 -0.859375 2.375 -0.859375q1.390625 0 2.1875 0.734375q0.8125 0.71875 0.8125 1.9375q0 1.46875 -1.875 3.5l-2.65625 2.890625l4.984375 0l0 1.03125zm6.689453 -3.34375l1.375 0l0 1.03125l-1.375 0l0 2.3125l-1.28125 0l0 -2.3125l-4.53125 0l0 -0.75l4.453125 -6.890625l1.359375 0l0 6.609375zm-4.375 0l3.09375 0l0 -4.875l-0.140625 0.265625l-2.953125 4.609375z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m1249.7255 287.9959q-0.28125 -0.046875 -0.625 -0.046875q-1.234375 0 -1.6875 1.0625l0 5.25l-1.265625 0l0 -7.390625l1.234375 0l0.015625 0.84375q0.625 -0.984375 1.765625 -0.984375q0.375 0 0.5625 0.09375l0 1.171875zm5.463867 6.265625q-0.109375 -0.21875 -0.1875 -0.78125q-0.875 0.921875 -2.109375 0.921875q-1.09375 0 -1.796875 -0.609375q-0.6875 -0.625 -0.6875 -1.578125q0 -1.15625 0.875 -1.796875q0.875 -0.640625 2.46875 -0.640625l1.234375 0l0 -0.578125q0 -0.671875 -0.40625 -1.0625q-0.390625 -0.390625 -1.15625 -0.390625q-0.6875 0 -1.140625 0.34375q-0.453125 0.34375 -0.453125 0.828125l-1.28125 0q0 -0.5625 0.390625 -1.078125q0.40625 -0.515625 1.078125 -0.8125q0.671875 -0.296875 1.46875 -0.296875q1.28125 0 2.0 0.640625q0.734375 0.640625 0.765625 1.75l0 3.40625q0 1.015625 0.25 1.625l0 0.109375l-1.3125 0zm-2.109375 -0.96875q0.59375 0 1.125 -0.296875q0.546875 -0.3125 0.78125 -0.8125l0 -1.515625l-1.0 0q-2.3125 0 -2.3125 1.359375q0 0.59375 0.390625 0.9375q0.390625 0.328125 1.015625 0.328125zm7.4814453 -0.75l1.84375 -5.671875l1.28125 0l-2.640625 7.390625l-0.96875 0l-2.6875 -7.390625l1.296875 0l1.875 5.671875zm7.333008 1.859375q-1.515625 0 -2.453125 -0.984375q-0.9375 -1.0 -0.9375 -2.65625l0 -0.21875q0 -1.109375 0.421875 -1.96875q0.421875 -0.875 1.171875 -1.359375q0.75 -0.484375 1.640625 -0.484375q1.4375 0 2.234375 0.953125q0.796875 0.9375 0.796875 2.71875l0 0.515625l-5.0 0q0.015625 1.09375 0.625 1.78125q0.625 0.671875 1.5625 0.671875q0.671875 0 1.125 -0.265625q0.46875 -0.28125 0.828125 -0.734375l0.765625 0.59375q-0.921875 1.4375 -2.78125 1.4375zm-0.15625 -6.640625q-0.765625 0 -1.296875 0.5625q-0.515625 0.5625 -0.640625 1.5625l3.703125 0l0 -0.09375q-0.046875 -0.96875 -0.515625 -1.5q-0.46875 -0.53125 -1.25 -0.53125zm5.698242 -0.890625l0.03125 0.921875q0.859375 -1.0625 2.21875 -1.0625q2.34375 0 2.375 2.640625l0 4.890625l-1.265625 0l0 -4.890625q-0.015625 -0.796875 -0.375 -1.171875q-0.359375 -0.390625 -1.109375 -0.390625q-0.625 0 -1.09375 0.328125q-0.453125 0.328125 -0.71875 0.859375l0 5.265625l-1.265625 0l0 -7.390625l1.203125 0z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m953.3556 324.9178l1.375 0l0 1.03125l-1.375 0l0 2.3125l-1.28125 0l0 -2.3125l-4.53125 0l0 -0.75l4.453125 -6.890625l1.359375 0l0 6.609375zm-4.375 0l3.09375 0l0 -4.875l-0.140625 0.265625l-2.953125 4.609375zm13.126953 -0.90625q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m1022.433 328.26154l-6.515625 0l0 -0.90625l3.4375 -3.828125q0.765625 -0.875 1.0625 -1.40625q0.296875 -0.546875 0.296875 -1.140625q0 -0.765625 -0.484375 -1.265625q-0.46875 -0.5 -1.25 -0.5q-0.9375 0 -1.46875 0.53125q-0.515625 0.53125 -0.515625 1.5l-1.265625 0q0 -1.375 0.875 -2.21875q0.890625 -0.859375 2.375 -0.859375q1.390625 0 2.1875 0.734375q0.8125 0.71875 0.8125 1.9375q0 1.46875 -1.875 3.5l-2.65625 2.890625l4.984375 0l0 1.03125zm7.580078 -4.25q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625zm9.126953 1.71875q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625zm9.126953 1.71875q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m1103.7086 328.26154l-6.515625 0l0 -0.90625l3.4375 -3.828125q0.765625 -0.875 1.0625 -1.40625q0.296875 -0.546875 0.296875 -1.140625q0 -0.765625 -0.484375 -1.265625q-0.46875 -0.5 -1.25 -0.5q-0.9375 0 -1.46875 0.53125q-0.515625 0.53125 -0.515625 1.5l-1.265625 0q0 -1.375 0.875 -2.21875q0.890625 -0.859375 2.375 -0.859375q1.390625 0 2.1875 0.734375q0.8125 0.71875 0.8125 1.9375q0 1.46875 -1.875 3.5l-2.65625 2.890625l4.984375 0l0 1.03125zm7.580078 -4.25q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625zm9.408203 5.96875l-6.515625 0l0 -0.90625l3.4375 -3.828125q0.765625 -0.875 1.0625 -1.40625q0.296875 -0.546875 0.296875 -1.140625q0 -0.765625 -0.484375 -1.265625q-0.46875 -0.5 -1.25 -0.5q-0.9375 0 -1.46875 0.53125q-0.515625 0.53125 -0.515625 1.5l-1.265625 0q0 -1.375 0.875 -2.21875q0.890625 -0.859375 2.375 -0.859375q1.390625 0 2.1875 0.734375q0.8125 0.71875 0.8125 1.9375q0 1.46875 -1.875 3.5l-2.65625 2.890625l4.984375 0l0 1.03125zm5.501953 0l-1.265625 0l0 -8.421875l-2.5625 0.921875l0 -1.140625l3.625 -1.359375l0.203125 0l0 10.0zm6.470703 -3.71875l-3.34375 0l0 -1.03125l3.34375 0l0 1.03125zm5.2529297 3.71875l-1.265625 0l0 -8.421875l-2.5625 0.921875l0 -1.140625l3.625 -1.359375l0.203125 0l0 10.0zm9.939453 -4.25q0 2.21875 -0.765625 3.3125q-0.75 1.078125 -2.359375 1.078125q-1.59375 0 -2.359375 -1.046875q-0.765625 -1.0625 -0.796875 -3.171875l0 -1.6875q0 -2.1875 0.75 -3.25q0.765625 -1.078125 2.390625 -1.078125q1.609375 0 2.359375 1.03125q0.765625 1.03125 0.78125 3.171875l0 1.640625zm-1.265625 -1.71875q0 -1.609375 -0.453125 -2.34375q-0.4375 -0.734375 -1.421875 -0.734375q-0.96875 0 -1.421875 0.734375q-0.4375 0.71875 -0.453125 2.234375l0 2.03125q0 1.609375 0.46875 2.390625q0.46875 0.765625 1.421875 0.765625q0.9375 0 1.390625 -0.71875q0.453125 -0.734375 0.46875 -2.296875l0 -2.0625zm5.658203 2.25l-3.34375 0l0 -1.03125l3.34375 0l0 1.03125zm7.6123047 3.71875l-6.515625 0l0 -0.90625l3.4375 -3.828125q0.765625 -0.875 1.0625 -1.40625q0.296875 -0.546875 0.296875 -1.140625q0 -0.765625 -0.484375 -1.265625q-0.46875 -0.5 -1.25 -0.5q-0.9375 0 -1.46875 0.53125q-0.515625 0.53125 -0.515625 1.5l-1.265625 0q0 -1.375 0.875 -2.21875q0.890625 -0.859375 2.375 -0.859375q1.390625 0 2.1875 0.734375q0.8125 0.71875 0.8125 1.9375q0 1.46875 -1.875 3.5l-2.65625 2.890625l4.984375 0l0 1.03125zm6.689453 -3.34375l1.375 0l0 1.03125l-1.375 0l0 2.3125l-1.28125 0l0 -2.3125l-4.53125 0l0 -0.75l4.453125 -6.890625l1.359375 0l0 6.609375zm-4.375 0l3.09375 0l0 -4.875l-0.140625 0.265625l-2.953125 4.609375z" fill-rule="nonzero"/><path fill="#4a4a4a" d="m1249.7255 321.9959q-0.28125 -0.046875 -0.625 -0.046875q-1.234375 0 -1.6875 1.0625l0 5.25l-1.265625 0l0 -7.390625l1.234375 0l0.015625 0.84375q0.625 -0.984375 1.765625 -0.984375q0.375 0 0.5625 0.09375l0 1.171875zm5.463867 6.265625q-0.109375 -0.21875 -0.1875 -0.78125q-0.875 0.921875 -2.109375 0.921875q-1.09375 0 -1.796875 -0.609375q-0.6875 -0.625 -0.6875 -1.578125q0 -1.15625 0.875 -1.796875q0.875 -0.640625 2.46875 -0.640625l1.234375 0l0 -0.578125q0 -0.671875 -0.40625 -1.0625q-0.390625 -0.390625 -1.15625 -0.390625q-0.6875 0 -1.140625 0.34375q-0.453125 0.34375 -0.453125 0.828125l-1.28125 0q0 -0.5625 0.390625 -1.078125q0.40625 -0.515625 1.078125 -0.8125q0.671875 -0.296875 1.46875 -0.296875q1.28125 0 2.0 0.640625q0.734375 0.640625 0.765625 1.75l0 3.40625q0 1.015625 0.25 1.625l0 0.109375l-1.3125 0zm-2.109375 -0.96875q0.59375 0 1.125 -0.296875q0.546875 -0.3125 0.78125 -0.8125l0 -1.515625l-1.0 0q-2.3125 0 -2.3125 1.359375q0 0.59375 0.390625 0.9375q0.390625 0.328125 1.015625 0.328125zm7.4814453 -0.75l1.84375 -5.671875l1.28125 0l-2.640625 7.390625l-0.96875 0l-2.6875 -7.390625l1.296875 0l1.875 5.671875zm7.333008 1.859375q-1.515625 0 -2.453125 -0.984375q-0.9375 -1.0 -0.9375 -2.65625l0 -0.21875q0 -1.109375 0.421875 -1.96875q0.421875 -0.875 1.171875 -1.359375q0.75 -0.484375 1.640625 -0.484375q1.4375 0 2.234375 0.953125q0.796875 0.9375 0.796875 2.71875l0 0.515625l-5.0 0q0.015625 1.09375 0.625 1.78125q0.625 0.671875 1.5625 0.671875q0.671875 0 1.125 -0.265625q0.46875 -0.28125 0.828125 -0.734375l0.765625 0.59375q-0.921875 1.4375 -2.78125 1.4375zm-0.15625 -6.640625q-0.765625 0 -1.296875 0.5625q-0.515625 0.5625 -0.640625 1.5625l3.703125 0l0 -0.09375q-0.046875 -0.96875 -0.515625 -1.5q-0.46875 -0.53125 -1.25 -0.53125zm5.698242 -0.890625l0.03125 0.921875q0.859375 -1.0625 2.21875 -1.0625q2.34375 0 2.375 2.640625l0 4.890625l-1.265625 0l0 -4.890625q-0.015625 -0.796875 -0.375 -1.171875q-0.359375 -0.390625 -1.109375 -0.390625q-0.625 0 -1.09375 0.328125q-0.453125 0.328125 -0.71875 0.859375l0 5.265625l-1.265625 0l0 -7.390625l1.203125 0z" fill-rule="nonzero"/></g></svg>
\ No newline at end of file
diff --git a/docs/images/perfetto-btp-overview.svg b/docs/images/perfetto-btp-overview.svg
new file mode 100644
index 0000000..d715a9f
--- /dev/null
+++ b/docs/images/perfetto-btp-overview.svg
@@ -0,0 +1 @@
+<svg version="1.1" viewBox="0.0 0.0 1923.8398950131234 817.9212598425197" fill="none" stroke="none" stroke-linecap="square" stroke-miterlimit="10" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg"><clipPath id="p.0"><path d="m0 0l1923.8398 0l0 817.92126l-1923.8398 0l0 -817.92126z" clip-rule="nonzero"/></clipPath><g clip-path="url(#p.0)"><path fill="#000000" fill-opacity="0.0" d="m0 0l1923.8398 0l0 817.92126l-1923.8398 0z" fill-rule="evenodd"/><path fill="#38761d" d="m868.98914 109.214195l150.86615 0l0 121.76379l-150.86615 0z" fill-rule="evenodd"/><path fill="#ffffff" d="m924.31714 147.69609l0 -9.890625l2.265625 0l0 9.890625l-2.265625 0zm-6.4375 -19.078125l2.625 0l4.890625 7.984375l0.109375 0l4.796875 -7.984375l2.625 0l-6.34375 10.25l-2.265625 0l-6.4375 -10.25zm20.724976 19.5q-2.046875 0 -3.625 -0.953125q-1.5625 -0.96875 -2.46875 -2.59375q-0.890625 -1.640625 -0.890625 -3.671875q0 -2.03125 0.890625 -3.671875q0.90625 -1.640625 2.46875 -2.59375q1.578125 -0.953125 3.625 -0.953125q2.0625 0 3.625 0.96875q1.578125 0.96875 2.46875 2.609375q0.890625 1.640625 0.890625 3.640625q0 2.03125 -0.890625 3.671875q-0.890625 1.625 -2.46875 2.59375q-1.5625 0.953125 -3.625 0.953125zm0 -2.046875q1.234375 0 2.3125 -0.609375q1.078125 -0.625 1.734375 -1.78125q0.671875 -1.15625 0.671875 -2.78125q0 -1.625 -0.671875 -2.78125q-0.65625 -1.171875 -1.734375 -1.78125q-1.078125 -0.609375 -2.3125 -0.609375q-1.21875 0 -2.3125 0.609375q-1.09375 0.609375 -1.765625 1.78125q-0.65625 1.15625 -0.65625 2.78125q0 1.625 0.65625 2.78125q0.671875 1.15625 1.765625 1.78125q1.09375 0.609375 2.3125 0.609375zm14.425659 2.046875q-2.5 0 -3.765625 -1.453125q-1.265625 -1.46875 -1.265625 -4.0l0 -8.5625l2.265625 0l0 8.203125q0 2.03125 0.921875 2.90625q0.9375 0.859375 2.328125 0.859375q1.203125 0 2.078125 -0.625q0.875 -0.625 1.359375 -1.609375q0.484375 -0.984375 0.484375 -2.0625l0 -7.671875l2.265625 0l0 13.59375l-2.171875 0l0 -1.96875l-0.09375 0q-0.359375 0.640625 -1.046875 1.1875q-0.671875 0.546875 -1.53125 0.875q-0.859375 0.328125 -1.828125 0.328125zm10.016602 -0.421875l0 -13.59375l2.171875 0l0 2.1875l0.09375 0q0.28125 -0.78125 0.90625 -1.34375q0.625 -0.578125 1.4375 -0.90625q0.8125 -0.34375 1.609375 -0.34375q0.609375 0 0.953125 0.078125q0.359375 0.0625 0.640625 0.1875l0 2.453125q-0.421875 -0.203125 -0.921875 -0.3125q-0.484375 -0.109375 -0.984375 -0.109375q-1.0 0 -1.828125 0.5625q-0.8125 0.5625 -1.3125 1.5q-0.5 0.921875 -0.5 2.046875l0 7.59375l-2.265625 0z" fill-rule="nonzero"/><path fill="#ffffff" d="m907.29724 180.11797q-2.0 0 -3.5625 -0.9375q-1.5625 -0.953125 -2.453125 -2.59375q-0.875 -1.640625 -0.875 -3.6875q0 -2.078125 0.875 -3.703125q0.890625 -1.625 2.453125 -2.5625q1.5625 -0.953125 3.5625 -0.953125q2.28125 0 3.765625 1.046875q1.484375 1.046875 2.09375 2.703125l-2.046875 0.859375q-0.515625 -1.25 -1.515625 -1.90625q-1.0 -0.65625 -2.40625 -0.65625q-1.203125 0 -2.234375 0.640625q-1.015625 0.640625 -1.65625 1.796875q-0.640625 1.15625 -0.640625 2.734375q0 1.546875 0.640625 2.71875q0.640625 1.171875 1.65625 1.8125q1.03125 0.640625 2.234375 0.640625q1.4375 0 2.46875 -0.65625q1.046875 -0.671875 1.546875 -1.90625l2.03125 0.859375q-0.671875 1.5625 -2.171875 2.65625q-1.484375 1.09375 -3.765625 1.09375zm13.319885 0q-2.5 0 -3.765625 -1.453125q-1.265625 -1.46875 -1.265625 -4.0l0 -8.5625l2.265625 0l0 8.203125q0 2.03125 0.921875 2.90625q0.9375 0.859375 2.328125 0.859375q1.203125 0 2.078125 -0.625q0.875 -0.625 1.359375 -1.609375q0.484375 -0.984375 0.484375 -2.0625l0 -7.671875l2.265625 0l0 13.59375l-2.171875 0l0 -1.96875l-0.09375 0q-0.359375 0.640625 -1.046875 1.1875q-0.671875 0.546875 -1.53125 0.875q-0.859375 0.328125 -1.828125 0.328125zm14.633301 0q-1.515625 0 -2.6875 -0.484375q-1.15625 -0.5 -1.921875 -1.328125q-0.75 -0.84375 -1.125 -1.828125l2.03125 -0.90625q0.53125 1.21875 1.546875 1.890625q1.015625 0.65625 2.3125 0.65625q1.265625 0 2.09375 -0.5q0.84375 -0.5 0.84375 -1.5q0 -0.609375 -0.34375 -1.015625q-0.34375 -0.421875 -1.015625 -0.703125q-0.671875 -0.296875 -1.625 -0.546875l-1.65625 -0.421875q-0.953125 -0.265625 -1.8125 -0.734375q-0.84375 -0.484375 -1.375 -1.25q-0.515625 -0.765625 -0.515625 -1.859375q0 -1.21875 0.71875 -2.09375q0.71875 -0.890625 1.890625 -1.34375q1.171875 -0.46875 2.515625 -0.46875q1.171875 0 2.1875 0.328125q1.03125 0.328125 1.796875 0.984375q0.78125 0.65625 1.1875 1.609375l-1.984375 0.90625q-0.5 -1.015625 -1.359375 -1.40625q-0.84375 -0.40625 -1.890625 -0.40625q-1.109375 0 -1.9375 0.5q-0.828125 0.484375 -0.828125 1.34375q0 0.859375 0.671875 1.28125q0.6875 0.421875 1.671875 0.6875l1.96875 0.515625q2.0 0.5 3.015625 1.5q1.015625 1.0 1.015625 2.4375q0 1.28125 -0.71875 2.21875q-0.71875 0.921875 -1.953125 1.4375q-1.21875 0.5 -2.71875 0.5zm6.8031616 -14.015625l7.96875 0l0 2.046875l-7.96875 0l0 -2.046875zm2.375 10.015625l0 -13.859375l2.265625 0l0 13.3125q0 1.0625 0.4375 1.65625q0.4375 0.578125 1.453125 0.578125q0.453125 0 0.828125 -0.125q0.375 -0.140625 0.65625 -0.328125l0 2.203125q-0.34375 0.171875 -0.765625 0.265625q-0.40625 0.09375 -1.09375 0.09375q-1.703125 0 -2.75 -1.0q-1.03125 -1.0 -1.03125 -2.796875zm13.955322 4.0q-2.046875 0 -3.625 -0.953125q-1.5625 -0.96875 -2.46875 -2.59375q-0.890625 -1.640625 -0.890625 -3.671875q0 -2.03125 0.890625 -3.671875q0.90625 -1.640625 2.46875 -2.59375q1.578125 -0.953125 3.625 -0.953125q2.0625 0 3.625 0.96875q1.578125 0.96875 2.46875 2.609375q0.890625 1.640625 0.890625 3.640625q0 2.03125 -0.890625 3.671875q-0.890625 1.625 -2.46875 2.59375q-1.5625 0.953125 -3.625 0.953125zm0 -2.046875q1.234375 0 2.3125 -0.609375q1.078125 -0.625 1.734375 -1.78125q0.671875 -1.15625 0.671875 -2.78125q0 -1.625 -0.671875 -2.78125q-0.65625 -1.171875 -1.734375 -1.78125q-1.078125 -0.609375 -2.3125 -0.609375q-1.21875 0 -2.3125 0.609375q-1.09375 0.609375 -1.765625 1.78125q-0.65625 1.15625 -0.65625 2.78125q0 1.625 0.65625 2.78125q0.671875 1.15625 1.765625 1.78125q1.09375 0.609375 2.3125 0.609375zm9.354736 1.625l0 -13.59375l2.171875 0l0 2.0l0.09375 0q0.375 -0.671875 1.03125 -1.21875q0.65625 -0.546875 1.484375 -0.875q0.84375 -0.328125 1.734375 -0.328125q1.515625 0 2.609375 0.734375q1.09375 0.71875 1.578125 1.90625q0.6875 -1.15625 1.859375 -1.890625q1.171875 -0.75 2.796875 -0.75q2.421875 0 3.5625 1.46875q1.15625 1.453125 1.15625 3.828125l0 8.71875l-2.234375 0l0 -8.375q0 -1.96875 -0.8125 -2.78125q-0.796875 -0.8125 -2.25 -0.8125q-1.046875 0 -1.859375 0.609375q-0.8125 0.59375 -1.28125 1.5625q-0.46875 0.96875 -0.46875 2.125l0 7.671875l-2.265625 0l0 -8.34375q0 -1.96875 -0.796875 -2.796875q-0.796875 -0.828125 -2.234375 -0.828125q-1.046875 0 -1.859375 0.609375q-0.8125 0.609375 -1.28125 1.609375q-0.46875 0.984375 -0.46875 2.125l0 7.625l-2.265625 0z" fill-rule="nonzero"/><path fill="#ffffff" d="m894.5413 211.69609l0 -13.59375l2.171875 0l0 2.1875l0.09375 0q0.28125 -0.78125 0.90625 -1.34375q0.625 -0.578125 1.4375 -0.90625q0.8125 -0.34375 1.609375 -0.34375q0.609375 0 0.953125 0.078125q0.359375 0.0625 0.640625 0.1875l0 2.453125q-0.421875 -0.203125 -0.921875 -0.3125q-0.484375 -0.109375 -0.984375 -0.109375q-1.0 0 -1.828125 0.5625q-0.8125 0.5625 -1.3125 1.5q-0.5 0.921875 -0.5 2.046875l0 7.59375l-2.265625 0zm15.258545 0.421875q-1.984375 0 -3.515625 -0.921875q-1.53125 -0.9375 -2.40625 -2.5625q-0.859375 -1.625 -0.859375 -3.703125q0 -1.953125 0.8125 -3.59375q0.8125 -1.65625 2.296875 -2.65625q1.484375 -1.0 3.484375 -1.0q2.015625 0 3.46875 0.890625q1.453125 0.890625 2.234375 2.46875q0.796875 1.5625 0.796875 3.59375q0 0.1875 -0.015625 0.375q-0.015625 0.1875 -0.03125 0.3125l-11.703125 0l0 -1.859375l9.359375 0q-0.03125 -0.5625 -0.265625 -1.21875q-0.21875 -0.65625 -0.71875 -1.21875q-0.484375 -0.578125 -1.25 -0.9375q-0.765625 -0.359375 -1.875 -0.359375q-1.34375 0 -2.3125 0.6875q-0.96875 0.671875 -1.5 1.84375q-0.515625 1.171875 -0.515625 2.671875q0 1.734375 0.671875 2.875q0.671875 1.140625 1.71875 1.703125q1.046875 0.5625 2.203125 0.5625q1.484375 0 2.453125 -0.703125q0.984375 -0.703125 1.5625 -1.75l1.921875 0.9375q-0.796875 1.546875 -2.296875 2.5625q-1.484375 1.0 -3.71875 1.0zm13.828247 0q-1.515625 0 -2.6875 -0.484375q-1.15625 -0.5 -1.921875 -1.328125q-0.75 -0.84375 -1.125 -1.828125l2.03125 -0.90625q0.53125 1.21875 1.546875 1.890625q1.015625 0.65625 2.3125 0.65625q1.265625 0 2.09375 -0.5q0.84375 -0.5 0.84375 -1.5q0 -0.609375 -0.34375 -1.015625q-0.34375 -0.421875 -1.015625 -0.703125q-0.671875 -0.296875 -1.625 -0.546875l-1.65625 -0.421875q-0.953125 -0.265625 -1.8125 -0.734375q-0.84375 -0.484375 -1.375 -1.25q-0.515625 -0.765625 -0.515625 -1.859375q0 -1.21875 0.71875 -2.09375q0.71875 -0.890625 1.890625 -1.34375q1.171875 -0.46875 2.515625 -0.46875q1.171875 0 2.1875 0.328125q1.03125 0.328125 1.796875 0.984375q0.78125 0.65625 1.1875 1.609375l-1.984375 0.90625q-0.5 -1.015625 -1.359375 -1.40625q-0.84375 -0.40625 -1.890625 -0.40625q-1.109375 0 -1.9375 0.5q-0.828125 0.484375 -0.828125 1.34375q0 0.859375 0.671875 1.28125q0.6875 0.421875 1.671875 0.6875l1.96875 0.515625q2.0 0.5 3.015625 1.5q1.015625 1.0 1.015625 2.4375q0 1.28125 -0.71875 2.21875q-0.71875 0.921875 -1.953125 1.4375q-1.21875 0.5 -2.71875 0.5zm14.257324 0q-2.046875 0 -3.625 -0.953125q-1.5625 -0.96875 -2.46875 -2.59375q-0.890625 -1.640625 -0.890625 -3.671875q0 -2.03125 0.890625 -3.671875q0.90625 -1.640625 2.46875 -2.59375q1.578125 -0.953125 3.625 -0.953125q2.0625 0 3.625 0.96875q1.578125 0.96875 2.46875 2.609375q0.890625 1.640625 0.890625 3.640625q0 2.03125 -0.890625 3.671875q-0.890625 1.625 -2.46875 2.59375q-1.5625 0.953125 -3.625 0.953125zm0 -2.046875q1.234375 0 2.3125 -0.609375q1.078125 -0.625 1.734375 -1.78125q0.671875 -1.15625 0.671875 -2.78125q0 -1.625 -0.671875 -2.78125q-0.65625 -1.171875 -1.734375 -1.78125q-1.078125 -0.609375 -2.3125 -0.609375q-1.21875 0 -2.3125 0.609375q-1.09375 0.609375 -1.765625 1.78125q-0.65625 1.15625 -0.65625 2.78125q0 1.625 0.65625 2.78125q0.671875 1.15625 1.765625 1.78125q1.09375 0.609375 2.3125 0.609375zm9.488098 1.625l0 -19.078125l2.265625 0l0 19.078125l-2.265625 0zm9.624451 0l-5.484375 -13.59375l2.421875 0l4.21875 10.921875l0.046875 0l4.265625 -10.921875l2.375 0l-5.546875 13.59375l-2.296875 0zm15.36377 0.421875q-1.984375 0 -3.515625 -0.921875q-1.53125 -0.9375 -2.40625 -2.5625q-0.859375 -1.625 -0.859375 -3.703125q0 -1.953125 0.8125 -3.59375q0.8125 -1.65625 2.296875 -2.65625q1.484375 -1.0 3.484375 -1.0q2.015625 0 3.46875 0.890625q1.453125 0.890625 2.234375 2.46875q0.796875 1.5625 0.796875 3.59375q0 0.1875 -0.015625 0.375q-0.015625 0.1875 -0.03125 0.3125l-11.703125 0l0 -1.859375l9.359375 0q-0.03125 -0.5625 -0.265625 -1.21875q-0.21875 -0.65625 -0.71875 -1.21875q-0.484375 -0.578125 -1.25 -0.9375q-0.765625 -0.359375 -1.875 -0.359375q-1.34375 0 -2.3125 0.6875q-0.96875 0.671875 -1.5 1.84375q-0.515625 1.171875 -0.515625 2.671875q0 1.734375 0.671875 2.875q0.671875 1.140625 1.71875 1.703125q1.046875 0.5625 2.203125 0.5625q1.484375 0 2.453125 -0.703125q0.984375 -0.703125 1.5625 -1.75l1.921875 0.9375q-0.796875 1.546875 -2.296875 2.5625q-1.484375 1.0 -3.71875 1.0zm8.944885 -0.421875l0 -13.59375l2.171875 0l0 2.1875l0.09375 0q0.28125 -0.78125 0.90625 -1.34375q0.625 -0.578125 1.4375 -0.90625q0.8125 -0.34375 1.609375 -0.34375q0.609375 0 0.953125 0.078125q0.359375 0.0625 0.640625 0.1875l0 2.453125q-0.421875 -0.203125 -0.921875 -0.3125q-0.484375 -0.109375 -0.984375 -0.109375q-1.0 0 -1.828125 0.5625q-0.8125 0.5625 -1.3125 1.5q-0.5 0.921875 -0.5 2.046875l0 7.59375l-2.265625 0zm10.621094 -5.625l-0.25 -6.984375l0 -6.46875l2.25 0l0 6.46875l-0.265625 6.984375l-1.734375 0zm0.875 5.78125q-0.71875 0 -1.21875 -0.484375q-0.484375 -0.5 -0.484375 -1.21875q0 -0.6875 0.484375 -1.171875q0.5 -0.5 1.21875 -0.5q0.71875 0 1.203125 0.5q0.5 0.484375 0.5 1.171875q0 0.71875 -0.5 1.21875q-0.484375 0.484375 -1.203125 0.484375z" fill-rule="nonzero"/><path fill="#45818e" d="m272.9465 382.9236l762.7401 0l0 199.874l-762.7401 0z" fill-rule="evenodd"/><path fill="#ffffff" d="m457.4199 466.99026l0 -28.640625l10.28125 0q2.203125 0 4.0625 0.984375q1.859375 0.96875 3.0 2.71875q1.140625 1.734375 1.140625 4.015625q0 2.125 -1.09375 3.6875q-1.078125 1.546875 -2.828125 2.359375l0 0.15625q2.109375 0.671875 3.484375 2.4375q1.390625 1.765625 1.390625 4.15625q0 2.40625 -1.203125 4.234375q-1.203125 1.8125 -3.171875 2.859375q-1.953125 1.03125 -4.21875 1.03125l-10.84375 0zm1.796875 -13.046875l0 -3.234375l8.484375 0q1.515625 0 2.59375 -0.703125q1.078125 -0.703125 1.65625 -1.734375q0.578125 -1.046875 0.578125 -2.125q0 -1.078125 -0.5625 -2.109375q-0.546875 -1.046875 -1.59375 -1.75q-1.03125 -0.703125 -2.515625 -0.703125l-7.046875 0l0 22.171875l7.640625 0q1.53125 0 2.640625 -0.734375q1.125 -0.75 1.71875 -1.890625q0.609375 -1.140625 0.609375 -2.296875q0 -1.203125 -0.625 -2.3125q-0.625 -1.125 -1.765625 -1.84375q-1.125 -0.734375 -2.734375 -0.734375l-9.078125 0zm28.286835 13.6875q-2.28125 0 -4.015625 -0.875q-1.71875 -0.890625 -2.703125 -2.421875q-0.96875 -1.546875 -0.96875 -3.5q0 -2.25 1.15625 -3.78125q1.171875 -1.546875 3.125 -2.328125q1.953125 -0.78125 4.3125 -0.78125q1.359375 0 2.515625 0.234375q1.171875 0.21875 2.03125 0.515625q0.859375 0.296875 1.296875 0.578125l0 -1.234375q0 -2.328125 -1.640625 -3.6875q-1.640625 -1.359375 -4.0 -1.359375q-1.671875 0 -3.140625 0.75q-1.453125 0.734375 -2.296875 2.046875l-2.5625 -1.921875q0.796875 -1.1875 2.0 -2.0625q1.203125 -0.890625 2.734375 -1.375q1.546875 -0.484375 3.265625 -0.484375q4.15625 0 6.515625 2.203125q2.375 2.203125 2.375 5.921875l0 12.921875l-3.25 0l0 -2.921875l-0.15625 0q-0.515625 0.875 -1.484375 1.703125q-0.953125 0.8125 -2.265625 1.328125q-1.296875 0.53125 -2.84375 0.53125zm0.3125 -3.0q1.765625 0 3.21875 -0.875q1.46875 -0.890625 2.34375 -2.359375q0.875 -1.484375 0.875 -3.25q-0.921875 -0.640625 -2.296875 -1.03125q-1.375 -0.40625 -3.015625 -0.40625q-2.921875 0 -4.28125 1.203125q-1.359375 1.203125 -1.359375 2.953125q0 1.6875 1.28125 2.734375q1.28125 1.03125 3.234375 1.03125zm12.605621 -18.046875l11.9530945 0l0 3.09375l-11.9530945 0l0 -3.09375zm3.5625 15.046875l0 -20.796875l3.390625 0l0 19.953125q0 1.609375 0.65625 2.484375q0.671875 0.875 2.1875 0.875q0.671875 0 1.234375 -0.1875q0.5624695 -0.203125 0.9999695 -0.484375l0 3.3125q-0.5155945 0.25 -1.1405945 0.375q-0.609375 0.140625 -1.65625 0.140625q-2.5625 0 -4.125 -1.5q-1.546875 -1.5 -1.546875 -4.171875zm20.798126 6.0q-3.0 0 -5.34375 -1.421875q-2.34375 -1.421875 -3.671875 -3.875q-1.3125 -2.46875 -1.3125 -5.546875q0 -3.109375 1.3125 -5.546875q1.328125 -2.453125 3.671875 -3.875q2.34375 -1.421875 5.34375 -1.421875q3.4375 0 5.656189 1.59375q2.21875 1.578125 3.140625 4.046875l-3.078125 1.28125q-0.765625 -1.875 -2.265564 -2.84375q-1.5 -0.984375 -3.625 -0.984375q-1.796875 0 -3.34375 0.953125q-1.53125 0.953125 -2.5 2.703125q-0.953125 1.734375 -0.953125 4.09375q0 2.328125 0.953125 4.09375q0.96875 1.75 2.5 2.71875q1.546875 0.953125 3.34375 0.953125q2.171875 0 3.71875 -1.0q1.562439 -1.0 2.328064 -2.84375l3.046875 1.28125q-1.0 2.359375 -3.25 4.0q-2.234314 1.640625 -5.671814 1.640625zm12.589966 -0.640625l0 -28.640625l3.40625 0l0 8.4375l-0.15625 2.796875l0.15625 0q0.796875 -1.515625 2.609375 -2.578125q1.828125 -1.0625 4.03125 -1.0625q2.59375 0 4.265625 1.046875q1.6875 1.046875 2.53125 2.828125q0.84375 1.765625 0.84375 4.09375l0 13.078125l-3.40625 0l0 -12.5625q0 -1.953125 -0.703125 -3.125q-0.6875 -1.1875 -1.828125 -1.71875q-1.140625 -0.546875 -2.5 -0.546875q-1.6875 0 -3.015625 0.953125q-1.3125 0.953125 -2.078125 2.484375q-0.75 1.515625 -0.75 3.15625l0 11.359375l-3.40625 0zm36.80243 0l0 -26.59375l3.40625 0l0 26.59375l-3.40625 0zm-8.03125 -25.40625l0 -3.234375l19.4375 0l0 3.234375l-19.4375 0zm20.588745 25.40625l0 -20.40625l3.25 0l0 3.28125l0.15625 0q0.390625 -1.15625 1.328125 -2.015625q0.953125 -0.859375 2.171875 -1.359375q1.21875 -0.5 2.421875 -0.5q0.921875 0 1.4375 0.109375q0.515625 0.09375 0.953125 0.296875l0 3.671875q-0.640625 -0.3125 -1.375 -0.46875q-0.734375 -0.171875 -1.5 -0.171875q-1.484375 0 -2.71875 0.84375q-1.234375 0.84375 -1.984375 2.25q-0.734375 1.390625 -0.734375 3.0625l0 11.40625l-3.40625 0zm20.731262 0.640625q-2.28125 0 -4.015625 -0.875q-1.71875 -0.890625 -2.703125 -2.421875q-0.96875 -1.546875 -0.96875 -3.5q0 -2.25 1.15625 -3.78125q1.171875 -1.546875 3.125 -2.328125q1.953125 -0.78125 4.3125 -0.78125q1.359375 0 2.515625 0.234375q1.171875 0.21875 2.03125 0.515625q0.859375 0.296875 1.296875 0.578125l0 -1.234375q0 -2.328125 -1.640625 -3.6875q-1.640625 -1.359375 -4.0 -1.359375q-1.671875 0 -3.140625 0.75q-1.453125 0.734375 -2.296875 2.046875l-2.5625 -1.921875q0.796875 -1.1875 2.0 -2.0625q1.203125 -0.890625 2.734375 -1.375q1.546875 -0.484375 3.265625 -0.484375q4.15625 0 6.515625 2.203125q2.375 2.203125 2.375 5.921875l0 12.921875l-3.25 0l0 -2.921875l-0.15625 0q-0.515625 0.875 -1.484375 1.703125q-0.953125 0.8125 -2.265625 1.328125q-1.296875 0.53125 -2.84375 0.53125zm0.3125 -3.0q1.765625 0 3.21875 -0.875q1.46875 -0.890625 2.34375 -2.359375q0.875 -1.484375 0.875 -3.25q-0.921875 -0.640625 -2.296875 -1.03125q-1.375 -0.40625 -3.015625 -0.40625q-2.921875 0 -4.28125 1.203125q-1.359375 1.203125 -1.359375 2.953125q0 1.6875 1.28125 2.734375q1.28125 1.03125 3.234375 1.03125zm23.446228 3.0q-3.0 0 -5.34375 -1.421875q-2.34375 -1.421875 -3.671875 -3.875q-1.3125 -2.46875 -1.3125 -5.546875q0 -3.109375 1.3125 -5.546875q1.328125 -2.453125 3.671875 -3.875q2.34375 -1.421875 5.34375 -1.421875q3.4375 0 5.65625 1.59375q2.21875 1.578125 3.140625 4.046875l-3.078125 1.28125q-0.765625 -1.875 -2.265625 -2.84375q-1.5 -0.984375 -3.625 -0.984375q-1.796875 0 -3.34375 0.953125q-1.53125 0.953125 -2.5 2.703125q-0.953125 1.734375 -0.953125 4.09375q0 2.328125 0.953125 4.09375q0.96875 1.75 2.5 2.71875q1.546875 0.953125 3.34375 0.953125q2.171875 0 3.71875 -1.0q1.5625 -1.0 2.328125 -2.84375l3.046875 1.28125q-1.0 2.359375 -3.25 4.0q-2.234375 1.640625 -5.671875 1.640625zm21.468079 0q-2.953125 0 -5.25 -1.390625q-2.296875 -1.40625 -3.609375 -3.84375q-1.296875 -2.453125 -1.296875 -5.5625q0 -2.921875 1.21875 -5.40625q1.21875 -2.484375 3.4375 -3.984375q2.21875 -1.5 5.21875 -1.5q3.046875 0 5.21875 1.34375q2.1875 1.34375 3.359375 3.703125q1.1875 2.359375 1.1875 5.40625q0 0.28125 -0.015625 0.5625q-0.015625 0.265625 -0.0625 0.46875l-17.5625 0l0 -2.796875l14.046875 0q-0.046875 -0.84375 -0.390625 -1.8125q-0.328125 -0.984375 -1.078125 -1.84375q-0.734375 -0.859375 -1.875 -1.390625q-1.140625 -0.546875 -2.828125 -0.546875q-2.0 0 -3.453125 1.015625q-1.453125 1.015625 -2.234375 2.78125q-0.78125 1.75 -0.78125 4.0q0 2.59375 1.0 4.3125q1.0 1.71875 2.578125 2.5625q1.578125 0.84375 3.296875 0.84375q2.234375 0 3.6875 -1.0625q1.46875 -1.0625 2.359375 -2.625l2.875 1.40625q-1.203125 2.3125 -3.453125 3.84375q-2.234375 1.515625 -5.59375 1.515625zm23.409363 -0.640625l0 -28.640625l9.640625 0q2.390625 0 4.421875 1.078125q2.046875 1.078125 3.265625 2.984375q1.234375 1.890625 1.234375 4.421875q0 2.46875 -1.234375 4.390625q-1.21875 1.921875 -3.265625 3.0q-2.03125 1.078125 -4.421875 1.078125l-7.84375 0l0 -3.234375l7.921875 0q1.671875 0 2.875 -0.78125q1.203125 -0.78125 1.859375 -1.96875q0.65625 -1.203125 0.65625 -2.484375q0 -1.28125 -0.65625 -2.484375q-0.65625 -1.203125 -1.859375 -1.984375q-1.203125 -0.78125 -2.875 -0.78125l-6.328125 0l0 25.40625l-3.390625 0zm22.352478 0l0 -20.40625l3.25 0l0 3.28125l0.15625 0q0.390625 -1.15625 1.328125 -2.015625q0.953125 -0.859375 2.171875 -1.359375q1.21875 -0.5 2.421875 -0.5q0.921875 0 1.4375 0.109375q0.515625 0.09375 0.953125 0.296875l0 3.671875q-0.640625 -0.3125 -1.375 -0.46875q-0.734375 -0.171875 -1.5 -0.171875q-1.484375 0 -2.71875 0.84375q-1.234375 0.84375 -1.984375 2.25q-0.734375 1.390625 -0.734375 3.0625l0 11.40625l-3.40625 0zm23.206238 0.640625q-3.078125 0 -5.4375 -1.4375q-2.359375 -1.4375 -3.703125 -3.890625q-1.34375 -2.46875 -1.34375 -5.515625q0 -3.03125 1.34375 -5.484375q1.34375 -2.46875 3.703125 -3.90625q2.359375 -1.453125 5.4375 -1.453125q3.078125 0 5.4375 1.46875q2.359375 1.453125 3.703125 3.921875q1.34375 2.453125 1.34375 5.453125q0 3.046875 -1.34375 5.515625q-1.34375 2.453125 -3.703125 3.890625q-2.359375 1.4375 -5.4375 1.4375zm0 -3.078125q1.84375 0 3.453125 -0.921875q1.625 -0.921875 2.625 -2.65625q1.0 -1.75 1.0 -4.1875q0 -2.4375 -1.0 -4.171875q-1.0 -1.75 -2.625 -2.65625q-1.609375 -0.921875 -3.453125 -0.921875q-1.84375 0 -3.484375 0.921875q-1.640625 0.90625 -2.640625 2.65625q-1.0 1.734375 -1.0 4.171875q0 2.4375 1.0 4.1875q1.0 1.734375 2.640625 2.65625q1.640625 0.921875 3.484375 0.921875zm23.683716 3.078125q-3.0 0 -5.34375 -1.421875q-2.34375 -1.421875 -3.671875 -3.875q-1.3125 -2.46875 -1.3125 -5.546875q0 -3.109375 1.3125 -5.546875q1.328125 -2.453125 3.671875 -3.875q2.34375 -1.421875 5.34375 -1.421875q3.4375 0 5.65625 1.59375q2.21875 1.578125 3.140625 4.046875l-3.078125 1.28125q-0.765625 -1.875 -2.265625 -2.84375q-1.5 -0.984375 -3.625 -0.984375q-1.796875 0 -3.34375 0.953125q-1.53125 0.953125 -2.5 2.703125q-0.953125 1.734375 -0.953125 4.09375q0 2.328125 0.953125 4.09375q0.96875 1.75 2.5 2.71875q1.546875 0.953125 3.34375 0.953125q2.171875 0 3.71875 -1.0q1.5625 -1.0 2.328125 -2.84375l3.046875 1.28125q-1.0 2.359375 -3.25 4.0q-2.234375 1.640625 -5.671875 1.640625zm21.46814 0q-2.953125 0 -5.25 -1.390625q-2.296875 -1.40625 -3.609375 -3.84375q-1.296875 -2.453125 -1.296875 -5.5625q0 -2.921875 1.21875 -5.40625q1.21875 -2.484375 3.4375 -3.984375q2.21875 -1.5 5.21875 -1.5q3.046875 0 5.21875 1.34375q2.1875 1.34375 3.359375 3.703125q1.1875 2.359375 1.1875 5.40625q0 0.28125 -0.015625 0.5625q-0.015625 0.265625 -0.0625 0.46875l-17.5625 0l0 -2.796875l14.046875 0q-0.046875 -0.84375 -0.390625 -1.8125q-0.328125 -0.984375 -1.078125 -1.84375q-0.734375 -0.859375 -1.875 -1.390625q-1.140625 -0.546875 -2.828125 -0.546875q-2.0 0 -3.453125 1.015625q-1.453125 1.015625 -2.234375 2.78125q-0.78125 1.75 -0.78125 4.0q0 2.59375 1.0 4.3125q1.0 1.71875 2.578125 2.5625q1.578125 0.84375 3.296875 0.84375q2.234375 0 3.6875 -1.0625q1.46875 -1.0625 2.359375 -2.625l2.875 1.40625q-1.203125 2.3125 -3.453125 3.84375q-2.234375 1.515625 -5.59375 1.515625zm20.766846 0q-2.28125 0 -4.03125 -0.734375q-1.734375 -0.75 -2.875 -2.0q-1.140625 -1.265625 -1.6875 -2.75l3.03125 -1.359375q0.796875 1.84375 2.3125 2.84375q1.53125 1.0 3.484375 1.0q1.890625 0 3.140625 -0.75q1.265625 -0.765625 1.265625 -2.25q0 -0.921875 -0.53125 -1.53125q-0.515625 -0.625 -1.515625 -1.0625q-1.0 -0.453125 -2.4375 -0.8125l-2.484375 -0.640625q-1.4375 -0.390625 -2.71875 -1.109375q-1.28125 -0.71875 -2.0625 -1.859375q-0.765625 -1.140625 -0.765625 -2.78125q0 -1.84375 1.078125 -3.15625q1.078125 -1.328125 2.828125 -2.03125q1.765625 -0.703125 3.765625 -0.703125q1.765625 0 3.296875 0.5q1.546875 0.5 2.703125 1.484375q1.15625 0.984375 1.765625 2.421875l-2.96875 1.359375q-0.75 -1.515625 -2.03125 -2.109375q-1.28125 -0.609375 -2.84375 -0.609375q-1.671875 0 -2.921875 0.75q-1.234375 0.734375 -1.234375 2.015625q0 1.28125 1.015625 1.921875q1.015625 0.625 2.5 1.03125l2.96875 0.765625q3.0 0.75 4.515625 2.25q1.515625 1.5 1.515625 3.671875q0 1.90625 -1.078125 3.3125q-1.078125 1.40625 -2.921875 2.171875q-1.84375 0.75 -4.078125 0.75zm18.919922 0q-2.28125 0 -4.03125 -0.734375q-1.734375 -0.75 -2.875 -2.0q-1.140625 -1.265625 -1.6875 -2.75l3.03125 -1.359375q0.796875 1.84375 2.3125 2.84375q1.53125 1.0 3.484375 1.0q1.890625 0 3.140625 -0.75q1.265625 -0.765625 1.265625 -2.25q0 -0.921875 -0.53125 -1.53125q-0.515625 -0.625 -1.515625 -1.0625q-1.0 -0.453125 -2.4375 -0.8125l-2.484375 -0.640625q-1.4375 -0.390625 -2.71875 -1.109375q-1.28125 -0.71875 -2.0625 -1.859375q-0.765625 -1.140625 -0.765625 -2.78125q0 -1.84375 1.078125 -3.15625q1.078125 -1.328125 2.828125 -2.03125q1.765625 -0.703125 3.765625 -0.703125q1.765625 0 3.296875 0.5q1.546875 0.5 2.703125 1.484375q1.15625 0.984375 1.765625 2.421875l-2.96875 1.359375q-0.75 -1.515625 -2.03125 -2.109375q-1.28125 -0.609375 -2.84375 -0.609375q-1.671875 0 -2.921875 0.75q-1.234375 0.734375 -1.234375 2.015625q0 1.28125 1.015625 1.921875q1.015625 0.625 2.5 1.03125l2.96875 0.765625q3.0 0.75 4.515625 2.25q1.515625 1.5 1.515625 3.671875q0 1.90625 -1.078125 3.3125q-1.078125 1.40625 -2.921875 2.171875q-1.84375 0.75 -4.078125 0.75zm21.401245 0q-3.078125 0 -5.4375 -1.4375q-2.359375 -1.4375 -3.703125 -3.890625q-1.34375 -2.46875 -1.34375 -5.515625q0 -3.03125 1.34375 -5.484375q1.34375 -2.46875 3.703125 -3.90625q2.359375 -1.453125 5.4375 -1.453125q3.078125 0 5.4375 1.46875q2.359375 1.453125 3.703125 3.921875q1.34375 2.453125 1.34375 5.453125q0 3.046875 -1.34375 5.515625q-1.34375 2.453125 -3.703125 3.890625q-2.359375 1.4375 -5.4375 1.4375zm0 -3.078125q1.84375 0 3.453125 -0.921875q1.625 -0.921875 2.625 -2.65625q1.0 -1.75 1.0 -4.1875q0 -2.4375 -1.0 -4.171875q-1.0 -1.75 -2.625 -2.65625q-1.609375 -0.921875 -3.453125 -0.921875q-1.84375 0 -3.484375 0.921875q-1.640625 0.90625 -2.640625 2.65625q-1.0 1.734375 -1.0 4.171875q0 2.4375 1.0 4.1875q1.0 1.734375 2.640625 2.65625q1.640625 0.921875 3.484375 0.921875zm14.033752 2.4375l0 -20.40625l3.25 0l0 3.28125l0.15625 0q0.390625 -1.15625 1.328125 -2.015625q0.953125 -0.859375 2.171875 -1.359375q1.21875 -0.5 2.421875 -0.5q0.921875 0 1.4375 0.109375q0.515625 0.09375 0.953125 0.296875l0 3.671875q-0.640625 -0.3125 -1.375 -0.46875q-0.734375 -0.171875 -1.5 -0.171875q-1.484375 0 -2.71875 0.84375q-1.234375 0.84375 -1.984375 2.25q-0.734375 1.390625 -0.734375 3.0625l0 11.40625l-3.40625 0z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m302.02145 514.7615l84.28345 0l0 68.031494l-84.28345 0z" fill-rule="evenodd"/><path fill="#a2c4c9" d="m415.14496 514.7615l84.28345 0l0 68.031494l-84.28345 0z" fill-rule="evenodd"/><path fill="#a2c4c9" d="m528.26843 514.7615l84.28351 0l0 68.031494l-84.28351 0z" fill-rule="evenodd"/><path fill="#a2c4c9" d="m641.39197 514.7615l84.28345 0l0 68.031494l-84.28345 0z" fill-rule="evenodd"/><path fill="#a2c4c9" d="m918.27075 514.7615l84.28351 0l0 68.031494l-84.28351 0z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m783.4539 506.72226l77.03937 0l0 84.031525l-77.03937 0z" fill-rule="evenodd"/><path fill="#ffffff" d="m806.8133 561.56226l0 -3.734375l3.734375 0l0 3.734375l-3.734375 0zm12.453125 0l0 -3.734375l3.734375 0l0 3.734375l-3.734375 0zm12.421875 0l0 -3.734375l3.75 0l0 3.734375l-3.75 0z" fill-rule="nonzero"/><path fill="#a2c4c9" d="m1282.1277 134.41129l91.90552 0l0 68.031494l-91.90552 0z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m1370.2344 136.11328l363.90552 0l0 67.87401l-363.90552 0z" fill-rule="evenodd"/><path fill="#000000" d="m1394.6875 178.15327l0 -15.953125l2.03125 0l0 15.953125l-2.03125 0zm-4.828125 -15.234375l0 -1.953125l11.65625 0l0 1.953125l-11.65625 0zm12.362549 15.234375l0 -12.234375l1.9375 0l0 1.96875l0.09375 0q0.25 -0.703125 0.8125 -1.21875q0.5625 -0.515625 1.296875 -0.8125q0.734375 -0.296875 1.453125 -0.296875q0.546875 0 0.859375 0.0625q0.3125 0.046875 0.578125 0.171875l0 2.203125q-0.390625 -0.1875 -0.84375 -0.28125q-0.4375 -0.09375 -0.890625 -0.09375q-0.890625 0 -1.640625 0.5q-0.734375 0.5 -1.1875 1.34375q-0.4375 0.84375 -0.4375 1.84375l0 6.84375l-2.03125 0zm12.426147 0.390625q-1.359375 0 -2.40625 -0.53125q-1.03125 -0.53125 -1.625 -1.453125q-0.578125 -0.921875 -0.578125 -2.109375q0 -1.34375 0.6875 -2.265625q0.703125 -0.921875 1.875 -1.390625q1.1875 -0.46875 2.59375 -0.46875q0.828125 0 1.515625 0.140625q0.703125 0.125 1.21875 0.3125q0.515625 0.171875 0.78125 0.34375l0 -0.75q0 -1.390625 -0.984375 -2.203125q-0.984375 -0.8125 -2.40625 -0.8125q-1.0 0 -1.875 0.453125q-0.875 0.4375 -1.390625 1.21875l-1.53125 -1.140625q0.484375 -0.71875 1.203125 -1.25q0.71875 -0.53125 1.640625 -0.8125q0.921875 -0.296875 1.953125 -0.296875q2.5 0 3.90625 1.328125q1.421875 1.3125 1.421875 3.546875l0 7.75l-1.9375 0l0 -1.75l-0.09375 0q-0.3125 0.53125 -0.890625 1.03125q-0.578125 0.484375 -1.359375 0.796875q-0.78125 0.3125 -1.71875 0.3125zm0.1875 -1.8125q1.0625 0 1.9375 -0.515625q0.875 -0.53125 1.40625 -1.421875q0.53125 -0.890625 0.53125 -1.9375q-0.5625 -0.390625 -1.390625 -0.625q-0.828125 -0.25 -1.8125 -0.25q-1.75 0 -2.5625 0.71875q-0.8125 0.71875 -0.8125 1.78125q0 1.015625 0.765625 1.640625q0.765625 0.609375 1.9375 0.609375zm14.077148 1.8125q-1.8125 0 -3.21875 -0.84375q-1.390625 -0.859375 -2.1875 -2.328125q-0.796875 -1.484375 -0.796875 -3.34375q0 -1.859375 0.796875 -3.328125q0.796875 -1.46875 2.1875 -2.3125q1.40625 -0.859375 3.21875 -0.859375q2.0625 0 3.390625 0.953125q1.328125 0.9375 1.890625 2.4375l-1.859375 0.765625q-0.453125 -1.125 -1.359375 -1.71875q-0.890625 -0.59375 -2.171875 -0.59375q-1.078125 0 -2.0 0.578125q-0.921875 0.578125 -1.5 1.625q-0.578125 1.046875 -0.578125 2.453125q0 1.390625 0.578125 2.453125q0.578125 1.0625 1.5 1.640625q0.921875 0.5625 2.0 0.5625q1.296875 0 2.234375 -0.59375q0.9375 -0.59375 1.390625 -1.703125l1.828125 0.765625q-0.59375 1.421875 -1.9375 2.40625q-1.34375 0.984375 -3.40625 0.984375zm12.874634 0q-1.765625 0 -3.15625 -0.84375q-1.375 -0.84375 -2.15625 -2.296875q-0.78125 -1.46875 -0.78125 -3.34375q0 -1.75 0.734375 -3.234375q0.734375 -1.5 2.0625 -2.390625q1.328125 -0.90625 3.140625 -0.90625q1.8125 0 3.125 0.8125q1.3125 0.796875 2.015625 2.21875q0.703125 1.40625 0.703125 3.234375q0 0.171875 -0.015625 0.34375q0 0.15625 -0.03125 0.28125l-10.53125 0l0 -1.6875l8.421875 0q-0.015625 -0.5 -0.21875 -1.078125q-0.203125 -0.59375 -0.65625 -1.109375q-0.4375 -0.515625 -1.125 -0.84375q-0.6875 -0.328125 -1.6875 -0.328125q-1.203125 0 -2.078125 0.625q-0.875 0.609375 -1.34375 1.671875q-0.46875 1.046875 -0.46875 2.390625q0 1.5625 0.59375 2.59375q0.609375 1.03125 1.546875 1.53125q0.953125 0.5 1.984375 0.5q1.34375 0 2.21875 -0.625q0.875 -0.640625 1.40625 -1.578125l1.734375 0.84375q-0.734375 1.390625 -2.078125 2.3125q-1.34375 0.90625 -3.359375 0.90625zm14.048584 -0.390625l0 -17.1875l5.78125 0q1.4375 0 2.65625 0.65625q1.234375 0.640625 1.96875 1.78125q0.734375 1.140625 0.734375 2.65625q0 1.484375 -0.734375 2.640625q-0.734375 1.15625 -1.96875 1.796875q-1.21875 0.640625 -2.65625 0.640625l-4.703125 0l0 -1.9375l4.75 0q1.015625 0 1.734375 -0.46875q0.71875 -0.46875 1.109375 -1.1875q0.390625 -0.71875 0.390625 -1.484375q0 -0.765625 -0.390625 -1.484375q-0.390625 -0.71875 -1.109375 -1.1875q-0.71875 -0.46875 -1.734375 -0.46875l-3.796875 0l0 15.234375l-2.03125 0zm13.417725 0l0 -12.234375l1.9375 0l0 1.96875l0.09375 0q0.25 -0.703125 0.8125 -1.21875q0.5625 -0.515625 1.296875 -0.8125q0.734375 -0.296875 1.453125 -0.296875q0.546875 0 0.859375 0.0625q0.3125 0.046875 0.578125 0.171875l0 2.203125q-0.390625 -0.1875 -0.84375 -0.28125q-0.4375 -0.09375 -0.890625 -0.09375q-0.890625 0 -1.640625 0.5q-0.734375 0.5 -1.1875 1.34375q-0.4375 0.84375 -0.4375 1.84375l0 6.84375l-2.03125 0zm13.920654 0.390625q-1.859375 0 -3.28125 -0.859375q-1.40625 -0.875 -2.21875 -2.34375q-0.796875 -1.484375 -0.796875 -3.3125q0 -1.8125 0.796875 -3.28125q0.8125 -1.484375 2.21875 -2.34375q1.421875 -0.875 3.28125 -0.875q1.84375 0 3.25 0.875q1.421875 0.875 2.21875 2.359375q0.8125 1.46875 0.8125 3.265625q0 1.828125 -0.8125 3.3125q-0.796875 1.46875 -2.21875 2.34375q-1.40625 0.859375 -3.25 0.859375zm0 -1.859375q1.09375 0 2.0625 -0.546875q0.984375 -0.546875 1.578125 -1.59375q0.609375 -1.046875 0.609375 -2.515625q0 -1.453125 -0.609375 -2.5q-0.59375 -1.046875 -1.578125 -1.59375q-0.96875 -0.5625 -2.0625 -0.5625q-1.109375 0 -2.09375 0.5625q-0.984375 0.546875 -1.59375 1.59375q-0.59375 1.046875 -0.59375 2.5q0 1.46875 0.59375 2.515625q0.609375 1.046875 1.59375 1.59375q0.984375 0.546875 2.09375 0.546875zm14.210205 1.859375q-1.8125 0 -3.21875 -0.84375q-1.390625 -0.859375 -2.1875 -2.328125q-0.796875 -1.484375 -0.796875 -3.34375q0 -1.859375 0.796875 -3.328125q0.796875 -1.46875 2.1875 -2.3125q1.40625 -0.859375 3.21875 -0.859375q2.0625 0 3.390625 0.953125q1.328125 0.9375 1.890625 2.4375l-1.859375 0.765625q-0.453125 -1.125 -1.359375 -1.71875q-0.890625 -0.59375 -2.171875 -0.59375q-1.078125 0 -2.0 0.578125q-0.921875 0.578125 -1.5 1.625q-0.578125 1.046875 -0.578125 2.453125q0 1.390625 0.578125 2.453125q0.578125 1.0625 1.5 1.640625q0.921875 0.5625 2.0 0.5625q1.296875 0 2.234375 -0.59375q0.9375 -0.59375 1.390625 -1.703125l1.828125 0.765625q-0.59375 1.421875 -1.9375 2.40625q-1.34375 0.984375 -3.40625 0.984375zm12.874512 0q-1.765625 0 -3.15625 -0.84375q-1.375 -0.84375 -2.15625 -2.296875q-0.78125 -1.46875 -0.78125 -3.34375q0 -1.75 0.734375 -3.234375q0.734375 -1.5 2.0625 -2.390625q1.328125 -0.90625 3.140625 -0.90625q1.8125 0 3.125 0.8125q1.3125 0.796875 2.015625 2.21875q0.703125 1.40625 0.703125 3.234375q0 0.171875 -0.015625 0.34375q0 0.15625 -0.03125 0.28125l-10.53125 0l0 -1.6875l8.421875 0q-0.015625 -0.5 -0.21875 -1.078125q-0.203125 -0.59375 -0.65625 -1.109375q-0.4375 -0.515625 -1.125 -0.84375q-0.6875 -0.328125 -1.6875 -0.328125q-1.203125 0 -2.078125 0.625q-0.875 0.609375 -1.34375 1.671875q-0.46875 1.046875 -0.46875 2.390625q0 1.5625 0.59375 2.59375q0.609375 1.03125 1.546875 1.53125q0.953125 0.5 1.984375 0.5q1.34375 0 2.21875 -0.625q0.875 -0.640625 1.40625 -1.578125l1.734375 0.84375q-0.734375 1.390625 -2.078125 2.3125q-1.34375 0.90625 -3.359375 0.90625zm12.460083 0q-1.359375 0 -2.40625 -0.4375q-1.046875 -0.453125 -1.734375 -1.203125q-0.6875 -0.765625 -1.015625 -1.65625l1.828125 -0.8125q0.46875 1.109375 1.375 1.703125q0.921875 0.59375 2.09375 0.59375q1.140625 0 1.890625 -0.453125q0.75 -0.453125 0.75 -1.34375q0 -0.546875 -0.3125 -0.921875q-0.3125 -0.375 -0.90625 -0.640625q-0.59375 -0.265625 -1.46875 -0.46875l-1.484375 -0.390625q-0.859375 -0.234375 -1.640625 -0.671875q-0.765625 -0.4375 -1.234375 -1.109375q-0.453125 -0.6875 -0.453125 -1.671875q0 -1.109375 0.640625 -1.890625q0.65625 -0.796875 1.703125 -1.21875q1.0625 -0.421875 2.25 -0.421875q1.0625 0 1.984375 0.296875q0.921875 0.296875 1.609375 0.890625q0.703125 0.59375 1.0625 1.453125l-1.765625 0.8125q-0.453125 -0.90625 -1.234375 -1.265625q-0.765625 -0.359375 -1.703125 -0.359375q-1.0 0 -1.75 0.453125q-0.734375 0.4375 -0.734375 1.203125q0 0.765625 0.609375 1.15625q0.609375 0.375 1.5 0.609375l1.78125 0.46875q1.796875 0.453125 2.703125 1.359375q0.90625 0.890625 0.90625 2.1875q0 1.15625 -0.640625 2.0q-0.640625 0.828125 -1.75 1.296875q-1.109375 0.453125 -2.453125 0.453125zm11.352051 0q-1.359375 0 -2.40625 -0.4375q-1.046875 -0.453125 -1.734375 -1.203125q-0.6875 -0.765625 -1.015625 -1.65625l1.828125 -0.8125q0.46875 1.109375 1.375 1.703125q0.921875 0.59375 2.09375 0.59375q1.140625 0 1.890625 -0.453125q0.75 -0.453125 0.75 -1.34375q0 -0.546875 -0.3125 -0.921875q-0.3125 -0.375 -0.90625 -0.640625q-0.59375 -0.265625 -1.46875 -0.46875l-1.484375 -0.390625q-0.859375 -0.234375 -1.640625 -0.671875q-0.765625 -0.4375 -1.234375 -1.109375q-0.453125 -0.6875 -0.453125 -1.671875q0 -1.109375 0.640625 -1.890625q0.65625 -0.796875 1.703125 -1.21875q1.0625 -0.421875 2.25 -0.421875q1.0625 0 1.984375 0.296875q0.921875 0.296875 1.609375 0.890625q0.703125 0.59375 1.0625 1.453125l-1.765625 0.8125q-0.453125 -0.90625 -1.234375 -1.265625q-0.765625 -0.359375 -1.703125 -0.359375q-1.0 0 -1.75 0.453125q-0.734375 0.4375 -0.734375 1.203125q0 0.765625 0.609375 1.15625q0.609375 0.375 1.5 0.609375l1.78125 0.46875q1.796875 0.453125 2.703125 1.359375q0.90625 0.890625 0.90625 2.1875q0 1.15625 -0.640625 2.0q-0.640625 0.828125 -1.75 1.296875q-1.109375 0.453125 -2.453125 0.453125zm12.846924 0q-1.859375 0 -3.28125 -0.859375q-1.40625 -0.875 -2.21875 -2.34375q-0.796875 -1.484375 -0.796875 -3.3125q0 -1.8125 0.796875 -3.28125q0.8125 -1.484375 2.21875 -2.34375q1.421875 -0.875 3.28125 -0.875q1.84375 0 3.25 0.875q1.421875 0.875 2.21875 2.359375q0.8125 1.46875 0.8125 3.265625q0 1.828125 -0.8125 3.3125q-0.796875 1.46875 -2.21875 2.34375q-1.40625 0.859375 -3.25 0.859375zm0 -1.859375q1.09375 0 2.0625 -0.546875q0.984375 -0.546875 1.578125 -1.59375q0.609375 -1.046875 0.609375 -2.515625q0 -1.453125 -0.609375 -2.5q-0.59375 -1.046875 -1.578125 -1.59375q-0.96875 -0.5625 -2.0625 -0.5625q-1.109375 0 -2.09375 0.5625q-0.984375 0.546875 -1.59375 1.59375q-0.59375 1.046875 -0.59375 2.5q0 1.46875 0.59375 2.515625q0.609375 1.046875 1.59375 1.59375q0.984375 0.546875 2.09375 0.546875zm8.42334 1.46875l0 -12.234375l1.9375 0l0 1.96875l0.09375 0q0.25 -0.703125 0.8125 -1.21875q0.5625 -0.515625 1.296875 -0.8125q0.734375 -0.296875 1.453125 -0.296875q0.546875 0 0.859375 0.0625q0.3125 0.046875 0.578125 0.171875l0 2.203125q-0.390625 -0.1875 -0.84375 -0.28125q-0.4375 -0.09375 -0.890625 -0.09375q-0.890625 0 -1.640625 0.5q-0.734375 0.5 -1.1875 1.34375q-0.4375 0.84375 -0.4375 1.84375l0 6.84375l-2.03125 0zm14.974121 0l0 -17.1875l5.78125 0q1.4375 0 2.65625 0.65625q1.234375 0.640625 1.96875 1.78125q0.734375 1.140625 0.734375 2.65625q0 1.484375 -0.734375 2.640625q-0.734375 1.15625 -1.96875 1.796875q-1.21875 0.640625 -2.65625 0.640625l-4.703125 0l0 -1.9375l4.75 0q1.015625 0 1.734375 -0.46875q0.71875 -0.46875 1.109375 -1.1875q0.390625 -0.71875 0.390625 -1.484375q0 -0.765625 -0.390625 -1.484375q-0.390625 -0.71875 -1.109375 -1.1875q-0.71875 -0.46875 -1.734375 -0.46875l-3.796875 0l0 15.234375l-2.03125 0zm16.917725 4.5625q-0.078125 0.171875 -0.171875 0.375q-0.09375 0.203125 -0.109375 0.25l-2.125 0q0.15625 -0.3125 0.3125 -0.6875q0.171875 -0.375 0.421875 -0.90625q0.140625 -0.3125 0.25 -0.578125q0.125 -0.25 0.28125 -0.578125q0.15625 -0.328125 0.375 -0.8125l1.671875 -3.640625l0.46875 -1.125l3.6875 -9.09375l2.203125 0l-6.328125 14.609375q-0.09375 0.21875 -0.296875 0.6875q-0.203125 0.46875 -0.390625 0.90625q-0.171875 0.453125 -0.25 0.59375zm0.453125 -5.0625l-5.1875 -11.734375l2.21875 0l3.84375 9.09375l0.1875 0l-1.0625 2.640625zm7.755127 -11.734375l7.171875 0l0 1.84375l-7.171875 0l0 -1.84375zm2.140625 9.015625l0 -12.484375l2.03125 0l0 11.984375q0 0.953125 0.390625 1.484375q0.40625 0.53125 1.3125 0.53125q0.40625 0 0.734375 -0.109375q0.34375 -0.125 0.609375 -0.296875l0 1.984375q-0.3125 0.15625 -0.6875 0.234375q-0.359375 0.078125 -0.984375 0.078125q-1.53125 0 -2.46875 -0.890625q-0.9375 -0.90625 -0.9375 -2.515625zm7.58313 3.21875l0 -17.1875l2.03125 0l0 5.0625l-0.09375 1.6875l0.09375 0q0.484375 -0.921875 1.578125 -1.546875q1.09375 -0.640625 2.40625 -0.640625q1.5625 0 2.5625 0.625q1.015625 0.625 1.515625 1.703125q0.515625 1.0625 0.515625 2.453125l0 7.84375l-2.046875 0l0 -7.53125q0 -1.1875 -0.421875 -1.890625q-0.421875 -0.703125 -1.109375 -1.03125q-0.671875 -0.328125 -1.484375 -0.328125q-1.015625 0 -1.8125 0.578125q-0.78125 0.578125 -1.25 1.5q-0.453125 0.90625 -0.453125 1.890625l0 6.8125l-2.03125 0zm19.176514 0.390625q-1.859375 0 -3.28125 -0.859375q-1.40625 -0.875 -2.21875 -2.34375q-0.796875 -1.484375 -0.796875 -3.3125q0 -1.8125 0.796875 -3.28125q0.8125 -1.484375 2.21875 -2.34375q1.421875 -0.875 3.28125 -0.875q1.84375 0 3.25 0.875q1.421875 0.875 2.21875 2.359375q0.8125 1.46875 0.8125 3.265625q0 1.828125 -0.8125 3.3125q-0.796875 1.46875 -2.21875 2.34375q-1.40625 0.859375 -3.25 0.859375zm0 -1.859375q1.09375 0 2.0625 -0.546875q0.984375 -0.546875 1.578125 -1.59375q0.609375 -1.046875 0.609375 -2.515625q0 -1.453125 -0.609375 -2.5q-0.59375 -1.046875 -1.578125 -1.59375q-0.96875 -0.5625 -2.0625 -0.5625q-1.109375 0 -2.09375 0.5625q-0.984375 0.546875 -1.59375 1.59375q-0.59375 1.046875 -0.59375 2.5q0 1.46875 0.59375 2.515625q0.609375 1.046875 1.59375 1.59375q0.984375 0.546875 2.09375 0.546875zm8.42334 1.46875l0 -12.234375l1.9375 0l0 1.796875l0.09375 0q0.484375 -0.890625 1.578125 -1.53125q1.09375 -0.65625 2.390625 -0.65625q2.25 0 3.390625 1.3125q1.140625 1.296875 1.140625 3.46875l0 7.84375l-2.03125 0l0 -7.53125q0 -1.78125 -0.859375 -2.515625q-0.859375 -0.734375 -2.203125 -0.734375q-1.0 0 -1.765625 0.578125q-0.765625 0.5625 -1.203125 1.453125q-0.4375 0.875 -0.4375 1.859375l0 6.890625l-2.03125 0zm17.155762 0l6.53125 -17.1875l2.296875 0l6.53125 17.1875l-2.234375 0l-4.71875 -12.84375l-0.671875 -1.84375l-0.109375 0l-0.671875 1.84375l-4.71875 12.84375l-2.234375 0zm7.4375 -4.734375l0 -1.90625l3.96875 0l0.6875 1.90625l-4.65625 0zm-4.65625 0l0.703125 -1.90625l3.953125 0l0 1.90625l-4.65625 0zm14.861206 4.734375l0 -17.1875l5.78125 0q1.4375 0 2.65625 0.65625q1.234375 0.640625 1.96875 1.78125q0.734375 1.140625 0.734375 2.65625q0 1.484375 -0.734375 2.640625q-0.734375 1.15625 -1.96875 1.796875q-1.21875 0.640625 -2.65625 0.640625l-4.703125 0l0 -1.9375l4.75 0q1.015625 0 1.734375 -0.46875q0.71875 -0.46875 1.109375 -1.1875q0.390625 -0.71875 0.390625 -1.484375q0 -0.765625 -0.390625 -1.484375q-0.390625 -0.71875 -1.109375 -1.1875q-0.71875 -0.46875 -1.734375 -0.46875l-3.796875 0l0 15.234375l-2.03125 0zm13.823975 0l0 -17.1875l2.03125 0l0 17.1875l-2.03125 0z" fill-rule="nonzero"/><path fill="#0b5394" d="m302.02145 704.55774l84.28345 0l0 68.031494l-84.28345 0z" fill-rule="evenodd"/><path fill="#ffffff" d="m337.97488 748.17346l0 -17.71875l2.265625 0l0 17.71875l-2.265625 0zm-5.359375 -16.921875l0 -2.15625l12.953125 0l0 2.15625l-12.953125 0zm19.231628 16.921875l0 -15.0625l-3.203125 2.296875l-1.171875 -1.734375l5.1875 -3.734375l1.453125 0l0 18.234375l-2.265625 0z" fill-rule="nonzero"/><path fill="#0b5394" d="m415.14496 704.55774l84.28345 0l0 68.031494l-84.28345 0z" fill-rule="evenodd"/><path fill="#ffffff" d="m449.1525 748.17346l0 -17.71875l2.265625 0l0 17.71875l-2.265625 0zm-5.359375 -16.921875l0 -2.15625l12.953125 0l0 2.15625l-12.953125 0zm14.856628 16.921875l0 -2.265625q0.078125 -0.078125 0.640625 -0.640625q0.5625 -0.5625 1.375 -1.390625q0.828125 -0.84375 1.734375 -1.765625q0.90625 -0.921875 1.6875 -1.734375q0.796875 -0.8125 1.25 -1.3125q0.828125 -0.90625 1.328125 -1.5625q0.515625 -0.65625 0.75 -1.28125q0.234375 -0.625 0.234375 -1.453125q0 -0.78125 -0.40625 -1.484375q-0.390625 -0.703125 -1.15625 -1.15625q-0.75 -0.453125 -1.890625 -0.453125q-1.078125 0 -1.8125 0.453125q-0.71875 0.453125 -1.125 1.078125q-0.40625 0.625 -0.53125 1.140625l-2.0625 -0.828125q0.171875 -0.5625 0.578125 -1.234375q0.421875 -0.6875 1.09375 -1.3125q0.6875 -0.625 1.65625 -1.03125q0.96875 -0.421875 2.25 -0.421875q1.734375 0 3.015625 0.734375q1.28125 0.734375 1.984375 1.921875q0.703125 1.171875 0.703125 2.5625q0 1.203125 -0.453125 2.28125q-0.453125 1.078125 -1.125 1.96875q-0.65625 0.875 -1.359375 1.53125q-0.34375 0.359375 -0.953125 0.96875q-0.609375 0.609375 -1.3125 1.3125q-0.6875 0.703125 -1.359375 1.375q-0.65625 0.671875 -1.15625 1.1875q-0.484375 0.5 -0.671875 0.65625l0 0l8.609375 0l0 2.15625l-11.515625 0z" fill-rule="nonzero"/><path fill="#0b5394" d="m528.26843 704.55774l84.28351 0l0 68.031494l-84.28351 0z" fill-rule="evenodd"/><path fill="#ffffff" d="m562.15607 748.17346l0 -17.71875l2.265625 0l0 17.71875l-2.265625 0zm-5.359375 -16.921875l0 -2.15625l12.953125 0l0 2.15625l-12.953125 0zm20.606628 17.34375q-1.0625 0 -2.265625 -0.453125q-1.203125 -0.453125 -2.1875 -1.453125q-0.984375 -1.0 -1.453125 -2.640625l2.046875 -0.859375q0.453125 1.546875 1.46875 2.40625q1.015625 0.84375 2.390625 0.84375q1.046875 0 1.90625 -0.4375q0.875 -0.4375 1.390625 -1.203125q0.515625 -0.78125 0.515625 -1.765625q0 -0.96875 -0.53125 -1.71875q-0.53125 -0.765625 -1.421875 -1.203125q-0.875 -0.4375 -1.9375 -0.4375l-1.578125 0l0 -2.109375l1.421875 0q0.953125 0 1.734375 -0.375q0.796875 -0.375 1.25 -1.0625q0.453125 -0.6875 0.453125 -1.625q0 -1.3125 -0.984375 -2.0625q-0.96875 -0.765625 -2.34375 -0.765625q-1.015625 0 -1.6875 0.390625q-0.65625 0.375 -1.046875 0.953125q-0.390625 0.578125 -0.578125 1.140625l-2.078125 -0.890625q0.265625 -0.796875 0.921875 -1.65625q0.671875 -0.875 1.78125 -1.484375q1.109375 -0.609375 2.6875 -0.609375q1.625 0 2.890625 0.640625q1.265625 0.640625 2.0 1.78125q0.734375 1.125 0.734375 2.5625q0 1.015625 -0.359375 1.796875q-0.359375 0.765625 -0.9375 1.296875q-0.578125 0.53125 -1.21875 0.84375l0 0.109375q0.859375 0.34375 1.5625 0.96875q0.703125 0.625 1.125 1.5q0.4375 0.859375 0.4375 1.984375q0 1.625 -0.828125 2.890625q-0.828125 1.265625 -2.21875 1.984375q-1.375 0.71875 -3.0625 0.71875z" fill-rule="nonzero"/><path fill="#0b5394" d="m641.39197 704.55774l84.28345 0l0 68.031494l-84.28345 0z" fill-rule="evenodd"/><path fill="#ffffff" d="m675.17316 748.17346l0 -17.71875l2.265625 0l0 17.71875l-2.265625 0zm-5.359314 -16.921875l0 -2.15625l12.953064 0l0 2.15625l-12.953064 0zm13.523254 13.0l0 -1.703125l8.53125 -12.609375l2.453125 0l0 18.234375l-2.25 0l0 -14.921875l-0.09375 0l-5.953125 8.875l10.640625 0l0 2.125l-13.328125 0z" fill-rule="nonzero"/><path fill="#0b5394" d="m918.27075 704.55774l84.28351 0l0 68.031494l-84.28351 0z" fill-rule="evenodd"/><path fill="#ffffff" d="m952.6118 748.17346l0 -17.71875l2.265625 0l0 17.71875l-2.265625 0zm-5.359375 -16.921875l0 -2.15625l12.953125 0l0 2.15625l-12.953125 0zm13.725403 16.921875l0 -13.59375l2.171875 0l0 2.0l0.09375 0q0.546875 -0.984375 1.75 -1.703125q1.21875 -0.71875 2.65625 -0.71875q2.5 0 3.765625 1.453125q1.265625 1.453125 1.265625 3.84375l0 8.71875l-2.265625 0l0 -8.375q0 -1.96875 -0.953125 -2.78125q-0.9375 -0.8125 -2.421875 -0.8125q-1.125 0 -1.984375 0.625q-0.84375 0.625 -1.328125 1.609375q-0.484375 0.984375 -0.484375 2.078125l0 7.65625l-2.265625 0z" fill-rule="nonzero"/><path fill="#0b5394" d="m1282.1277 218.02048l91.90552 0l0 68.031494l-91.90552 0z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m1370.2344 218.08353l363.90552 0l0 67.87402l-363.90552 0z" fill-rule="evenodd"/><path fill="#000000" d="m1394.6875 260.12354l0 -15.95314l2.03125 0l0 15.95314l-2.03125 0zm-4.828125 -15.23439l0 -1.953125l11.65625 0l0 1.953125l-11.65625 0zm12.362549 15.23439l0 -12.23439l1.9375 0l0 1.96875l0.09375 0q0.25 -0.703125 0.8125 -1.21875q0.5625 -0.515625 1.296875 -0.8125q0.734375 -0.296875 1.453125 -0.296875q0.546875 0 0.859375 0.0625q0.3125 0.046875 0.578125 0.171875l0 2.203125q-0.390625 -0.1875 -0.84375 -0.28125q-0.4375 -0.09375 -0.890625 -0.09375q-0.890625 0 -1.640625 0.5q-0.734375 0.5 -1.1875 1.34375q-0.4375 0.84375 -0.4375 1.84375l0 6.8437653l-2.03125 0zm12.426147 0.390625q-1.359375 0 -2.40625 -0.53125q-1.03125 -0.53125 -1.625 -1.453125q-0.578125 -0.921875 -0.578125 -2.109375q0 -1.3437653 0.6875 -2.2656403q0.703125 -0.921875 1.875 -1.390625q1.1875 -0.46875 2.59375 -0.46875q0.828125 0 1.515625 0.140625q0.703125 0.125 1.21875 0.3125q0.515625 0.171875 0.78125 0.34375l0 -0.75q0 -1.390625 -0.984375 -2.203125q-0.984375 -0.8125 -2.40625 -0.8125q-1.0 0 -1.875 0.453125q-0.875 0.4375 -1.390625 1.21875l-1.53125 -1.140625q0.484375 -0.71875 1.203125 -1.25q0.71875 -0.53125 1.640625 -0.8125q0.921875 -0.296875 1.953125 -0.296875q2.5 0 3.90625 1.328125q1.421875 1.3125 1.421875 3.546875l0 7.7500153l-1.9375 0l0 -1.75l-0.09375 0q-0.3125 0.53125 -0.890625 1.03125q-0.578125 0.484375 -1.359375 0.796875q-0.78125 0.3125 -1.71875 0.3125zm0.1875 -1.8125q1.0625 0 1.9375 -0.515625q0.875 -0.53125 1.40625 -1.421875q0.53125 -0.89064026 0.53125 -1.9375153q-0.5625 -0.390625 -1.390625 -0.625q-0.828125 -0.25 -1.8125 -0.25q-1.75 0 -2.5625 0.71875q-0.8125 0.71875 -0.8125 1.7812653q0 1.015625 0.765625 1.640625q0.765625 0.609375 1.9375 0.609375zm14.077148 1.8125q-1.8125 0 -3.21875 -0.84375q-1.390625 -0.859375 -2.1875 -2.328125q-0.796875 -1.4843903 -0.796875 -3.3437653q0 -1.859375 0.796875 -3.328125q0.796875 -1.46875 2.1875 -2.3125q1.40625 -0.859375 3.21875 -0.859375q2.0625 0 3.390625 0.953125q1.328125 0.9375 1.890625 2.4375l-1.859375 0.765625q-0.453125 -1.125 -1.359375 -1.71875q-0.890625 -0.59375 -2.171875 -0.59375q-1.078125 0 -2.0 0.578125q-0.921875 0.578125 -1.5 1.625q-0.578125 1.046875 -0.578125 2.453125q0 1.390625 0.578125 2.4531403q0.578125 1.0625 1.5 1.640625q0.921875 0.5625 2.0 0.5625q1.296875 0 2.234375 -0.59375q0.9375 -0.59375 1.390625 -1.703125l1.828125 0.765625q-0.59375 1.421875 -1.9375 2.40625q-1.34375 0.984375 -3.40625 0.984375zm12.874634 0q-1.765625 0 -3.15625 -0.84375q-1.375 -0.84375 -2.15625 -2.296875q-0.78125 -1.4687653 -0.78125 -3.3437653q0 -1.75 0.734375 -3.234375q0.734375 -1.5 2.0625 -2.390625q1.328125 -0.90625 3.140625 -0.90625q1.8125 0 3.125 0.8125q1.3125 0.796875 2.015625 2.21875q0.703125 1.40625 0.703125 3.234375q0 0.171875 -0.015625 0.34375q0 0.15625 -0.03125 0.28125l-10.53125 0l0 -1.6875l8.421875 0q-0.015625 -0.5 -0.21875 -1.078125q-0.203125 -0.59375 -0.65625 -1.109375q-0.4375 -0.515625 -1.125 -0.84375q-0.6875 -0.328125 -1.6875 -0.328125q-1.203125 0 -2.078125 0.625q-0.875 0.609375 -1.34375 1.671875q-0.46875 1.046875 -0.46875 2.390625q0 1.5625 0.59375 2.5937653q0.609375 1.03125 1.546875 1.53125q0.953125 0.5 1.984375 0.5q1.34375 0 2.21875 -0.625q0.875 -0.640625 1.40625 -1.578125l1.734375 0.84375q-0.734375 1.390625 -2.078125 2.3125q-1.34375 0.90625 -3.359375 0.90625zm14.048584 -0.390625l0 -17.187515l5.78125 0q1.4375 0 2.65625 0.65625q1.234375 0.640625 1.96875 1.78125q0.734375 1.140625 0.734375 2.65625q0 1.484375 -0.734375 2.640625q-0.734375 1.15625 -1.96875 1.796875q-1.21875 0.640625 -2.65625 0.640625l-4.703125 0l0 -1.9375l4.75 0q1.015625 0 1.734375 -0.46875q0.71875 -0.46875 1.109375 -1.1875q0.390625 -0.71875 0.390625 -1.484375q0 -0.765625 -0.390625 -1.484375q-0.390625 -0.71875 -1.109375 -1.1875q-0.71875 -0.46875 -1.734375 -0.46875l-3.796875 0l0 15.23439l-2.03125 0zm13.417725 0l0 -12.23439l1.9375 0l0 1.96875l0.09375 0q0.25 -0.703125 0.8125 -1.21875q0.5625 -0.515625 1.296875 -0.8125q0.734375 -0.296875 1.453125 -0.296875q0.546875 0 0.859375 0.0625q0.3125 0.046875 0.578125 0.171875l0 2.203125q-0.390625 -0.1875 -0.84375 -0.28125q-0.4375 -0.09375 -0.890625 -0.09375q-0.890625 0 -1.640625 0.5q-0.734375 0.5 -1.1875 1.34375q-0.4375 0.84375 -0.4375 1.84375l0 6.8437653l-2.03125 0zm13.920654 0.390625q-1.859375 0 -3.28125 -0.859375q-1.40625 -0.875 -2.21875 -2.34375q-0.796875 -1.4843903 -0.796875 -3.3125153q0 -1.8125 0.796875 -3.28125q0.8125 -1.484375 2.21875 -2.34375q1.421875 -0.875 3.28125 -0.875q1.84375 0 3.25 0.875q1.421875 0.875 2.21875 2.359375q0.8125 1.46875 0.8125 3.265625q0 1.828125 -0.8125 3.3125153q-0.796875 1.46875 -2.21875 2.34375q-1.40625 0.859375 -3.25 0.859375zm0 -1.859375q1.09375 0 2.0625 -0.546875q0.984375 -0.546875 1.578125 -1.59375q0.609375 -1.0468903 0.609375 -2.5156403q0 -1.453125 -0.609375 -2.5q-0.59375 -1.046875 -1.578125 -1.59375q-0.96875 -0.5625 -2.0625 -0.5625q-1.109375 0 -2.09375 0.5625q-0.984375 0.546875 -1.59375 1.59375q-0.59375 1.046875 -0.59375 2.5q0 1.46875 0.59375 2.5156403q0.609375 1.046875 1.59375 1.59375q0.984375 0.546875 2.09375 0.546875zm14.210205 1.859375q-1.8125 0 -3.21875 -0.84375q-1.390625 -0.859375 -2.1875 -2.328125q-0.796875 -1.4843903 -0.796875 -3.3437653q0 -1.859375 0.796875 -3.328125q0.796875 -1.46875 2.1875 -2.3125q1.40625 -0.859375 3.21875 -0.859375q2.0625 0 3.390625 0.953125q1.328125 0.9375 1.890625 2.4375l-1.859375 0.765625q-0.453125 -1.125 -1.359375 -1.71875q-0.890625 -0.59375 -2.171875 -0.59375q-1.078125 0 -2.0 0.578125q-0.921875 0.578125 -1.5 1.625q-0.578125 1.046875 -0.578125 2.453125q0 1.390625 0.578125 2.4531403q0.578125 1.0625 1.5 1.640625q0.921875 0.5625 2.0 0.5625q1.296875 0 2.234375 -0.59375q0.9375 -0.59375 1.390625 -1.703125l1.828125 0.765625q-0.59375 1.421875 -1.9375 2.40625q-1.34375 0.984375 -3.40625 0.984375zm12.874512 0q-1.765625 0 -3.15625 -0.84375q-1.375 -0.84375 -2.15625 -2.296875q-0.78125 -1.4687653 -0.78125 -3.3437653q0 -1.75 0.734375 -3.234375q0.734375 -1.5 2.0625 -2.390625q1.328125 -0.90625 3.140625 -0.90625q1.8125 0 3.125 0.8125q1.3125 0.796875 2.015625 2.21875q0.703125 1.40625 0.703125 3.234375q0 0.171875 -0.015625 0.34375q0 0.15625 -0.03125 0.28125l-10.53125 0l0 -1.6875l8.421875 0q-0.015625 -0.5 -0.21875 -1.078125q-0.203125 -0.59375 -0.65625 -1.109375q-0.4375 -0.515625 -1.125 -0.84375q-0.6875 -0.328125 -1.6875 -0.328125q-1.203125 0 -2.078125 0.625q-0.875 0.609375 -1.34375 1.671875q-0.46875 1.046875 -0.46875 2.390625q0 1.5625 0.59375 2.5937653q0.609375 1.03125 1.546875 1.53125q0.953125 0.5 1.984375 0.5q1.34375 0 2.21875 -0.625q0.875 -0.640625 1.40625 -1.578125l1.734375 0.84375q-0.734375 1.390625 -2.078125 2.3125q-1.34375 0.90625 -3.359375 0.90625zm12.460083 0q-1.359375 0 -2.40625 -0.4375q-1.046875 -0.453125 -1.734375 -1.203125q-0.6875 -0.765625 -1.015625 -1.65625l1.828125 -0.8125q0.46875 1.109375 1.375 1.703125q0.921875 0.59375 2.09375 0.59375q1.140625 0 1.890625 -0.453125q0.75 -0.453125 0.75 -1.34375q0 -0.546875 -0.3125 -0.92189026q-0.3125 -0.375 -0.90625 -0.640625q-0.59375 -0.265625 -1.46875 -0.46875l-1.484375 -0.390625q-0.859375 -0.234375 -1.640625 -0.671875q-0.765625 -0.4375 -1.234375 -1.109375q-0.453125 -0.6875 -0.453125 -1.671875q0 -1.109375 0.640625 -1.890625q0.65625 -0.796875 1.703125 -1.21875q1.0625 -0.421875 2.25 -0.421875q1.0625 0 1.984375 0.296875q0.921875 0.296875 1.609375 0.890625q0.703125 0.59375 1.0625 1.453125l-1.765625 0.8125q-0.453125 -0.90625 -1.234375 -1.265625q-0.765625 -0.359375 -1.703125 -0.359375q-1.0 0 -1.75 0.453125q-0.734375 0.4375 -0.734375 1.203125q0 0.765625 0.609375 1.15625q0.609375 0.375 1.5 0.609375l1.78125 0.46875q1.796875 0.453125 2.703125 1.359375q0.90625 0.890625 0.90625 2.1875153q0 1.15625 -0.640625 2.0q-0.640625 0.828125 -1.75 1.296875q-1.109375 0.453125 -2.453125 0.453125zm11.352051 0q-1.359375 0 -2.40625 -0.4375q-1.046875 -0.453125 -1.734375 -1.203125q-0.6875 -0.765625 -1.015625 -1.65625l1.828125 -0.8125q0.46875 1.109375 1.375 1.703125q0.921875 0.59375 2.09375 0.59375q1.140625 0 1.890625 -0.453125q0.75 -0.453125 0.75 -1.34375q0 -0.546875 -0.3125 -0.92189026q-0.3125 -0.375 -0.90625 -0.640625q-0.59375 -0.265625 -1.46875 -0.46875l-1.484375 -0.390625q-0.859375 -0.234375 -1.640625 -0.671875q-0.765625 -0.4375 -1.234375 -1.109375q-0.453125 -0.6875 -0.453125 -1.671875q0 -1.109375 0.640625 -1.890625q0.65625 -0.796875 1.703125 -1.21875q1.0625 -0.421875 2.25 -0.421875q1.0625 0 1.984375 0.296875q0.921875 0.296875 1.609375 0.890625q0.703125 0.59375 1.0625 1.453125l-1.765625 0.8125q-0.453125 -0.90625 -1.234375 -1.265625q-0.765625 -0.359375 -1.703125 -0.359375q-1.0 0 -1.75 0.453125q-0.734375 0.4375 -0.734375 1.203125q0 0.765625 0.609375 1.15625q0.609375 0.375 1.5 0.609375l1.78125 0.46875q1.796875 0.453125 2.703125 1.359375q0.90625 0.890625 0.90625 2.1875153q0 1.15625 -0.640625 2.0q-0.640625 0.828125 -1.75 1.296875q-1.109375 0.453125 -2.453125 0.453125zm12.846924 0q-1.859375 0 -3.28125 -0.859375q-1.40625 -0.875 -2.21875 -2.34375q-0.796875 -1.4843903 -0.796875 -3.3125153q0 -1.8125 0.796875 -3.28125q0.8125 -1.484375 2.21875 -2.34375q1.421875 -0.875 3.28125 -0.875q1.84375 0 3.25 0.875q1.421875 0.875 2.21875 2.359375q0.8125 1.46875 0.8125 3.265625q0 1.828125 -0.8125 3.3125153q-0.796875 1.46875 -2.21875 2.34375q-1.40625 0.859375 -3.25 0.859375zm0 -1.859375q1.09375 0 2.0625 -0.546875q0.984375 -0.546875 1.578125 -1.59375q0.609375 -1.0468903 0.609375 -2.5156403q0 -1.453125 -0.609375 -2.5q-0.59375 -1.046875 -1.578125 -1.59375q-0.96875 -0.5625 -2.0625 -0.5625q-1.109375 0 -2.09375 0.5625q-0.984375 0.546875 -1.59375 1.59375q-0.59375 1.046875 -0.59375 2.5q0 1.46875 0.59375 2.5156403q0.609375 1.046875 1.59375 1.59375q0.984375 0.546875 2.09375 0.546875zm8.42334 1.46875l0 -12.23439l1.9375 0l0 1.96875l0.09375 0q0.25 -0.703125 0.8125 -1.21875q0.5625 -0.515625 1.296875 -0.8125q0.734375 -0.296875 1.453125 -0.296875q0.546875 0 0.859375 0.0625q0.3125 0.046875 0.578125 0.171875l0 2.203125q-0.390625 -0.1875 -0.84375 -0.28125q-0.4375 -0.09375 -0.890625 -0.09375q-0.890625 0 -1.640625 0.5q-0.734375 0.5 -1.1875 1.34375q-0.4375 0.84375 -0.4375 1.84375l0 6.8437653l-2.03125 0zm19.051025 0.390625q-1.25 0 -2.46875 -0.53125q-1.21875 -0.53125 -2.15625 -1.5625q-0.9375 -1.03125 -1.375 -2.5625153l1.9375 -0.796875q0.421875 1.4843903 1.5 2.5000153q1.078125 1.0 2.609375 1.0q0.96875 0 1.765625 -0.34375q0.8125 -0.34375 1.296875 -1.015625q0.484375 -0.671875 0.484375 -1.6406403q0 -0.859375 -0.421875 -1.453125q-0.40625 -0.609375 -1.25 -1.078125q-0.84375 -0.46875 -2.0625 -0.890625l-1.078125 -0.390625q-0.71875 -0.265625 -1.453125 -0.640625q-0.734375 -0.375 -1.34375 -0.90625q-0.609375 -0.546875 -0.96875 -1.296875q-0.359375 -0.75 -0.359375 -1.796875q0 -1.234375 0.671875 -2.265625q0.671875 -1.03125 1.84375 -1.65625q1.171875 -0.625 2.703125 -0.625q1.609375 0 2.703125 0.59375q1.09375 0.578125 1.71875 1.390625q0.640625 0.796875 0.859375 1.46875l-1.890625 0.8125q-0.140625 -0.5 -0.546875 -1.03125q-0.40625 -0.546875 -1.09375 -0.921875q-0.6875 -0.375 -1.71875 -0.375q-0.84375 0 -1.578125 0.34375q-0.734375 0.328125 -1.171875 0.921875q-0.4375 0.578125 -0.4375 1.34375q0 1.09375 0.859375 1.71875q0.859375 0.609375 2.328125 1.125l1.125 0.375q0.765625 0.265625 1.5625 0.65625q0.8125 0.375 1.5 0.984375q0.703125 0.59375 1.140625 1.46875q0.4375 0.875 0.4375 2.109375q0 1.3125153 -0.515625 2.2656403q-0.515625 0.9375 -1.359375 1.546875q-0.84375 0.59375 -1.84375 0.875q-0.984375 0.28125 -1.953125 0.28125zm8.284912 -0.390625l0 -17.187515l2.03125 0l0 5.0625l-0.09375 1.6875l0.09375 0q0.484375 -0.921875 1.578125 -1.546875q1.09375 -0.640625 2.40625 -0.640625q1.5625 0 2.5625 0.625q1.015625 0.625 1.515625 1.703125q0.515625 1.0625 0.515625 2.453125l0 7.8437653l-2.046875 0l0 -7.5312653q0 -1.1875 -0.421875 -1.890625q-0.421875 -0.703125 -1.109375 -1.03125q-0.671875 -0.328125 -1.484375 -0.328125q-1.015625 0 -1.8125 0.578125q-0.78125 0.578125 -1.25 1.5q-0.453125 0.90625 -0.453125 1.890625l0 6.8125153l-2.03125 0zm18.973389 0.390625q-1.765625 0 -3.15625 -0.84375q-1.375 -0.84375 -2.15625 -2.296875q-0.78125 -1.4687653 -0.78125 -3.3437653q0 -1.75 0.734375 -3.234375q0.734375 -1.5 2.0625 -2.390625q1.328125 -0.90625 3.140625 -0.90625q1.8125 0 3.125 0.8125q1.3125 0.796875 2.015625 2.21875q0.703125 1.40625 0.703125 3.234375q0 0.171875 -0.015625 0.34375q0 0.15625 -0.03125 0.28125l-10.53125 0l0 -1.6875l8.421875 0q-0.015625 -0.5 -0.21875 -1.078125q-0.203125 -0.59375 -0.65625 -1.109375q-0.4375 -0.515625 -1.125 -0.84375q-0.6875 -0.328125 -1.6875 -0.328125q-1.203125 0 -2.078125 0.625q-0.875 0.609375 -1.34375 1.671875q-0.46875 1.046875 -0.46875 2.390625q0 1.5625 0.59375 2.5937653q0.609375 1.03125 1.546875 1.53125q0.953125 0.5 1.984375 0.5q1.34375 0 2.21875 -0.625q0.875 -0.640625 1.40625 -1.578125l1.734375 0.84375q-0.734375 1.390625 -2.078125 2.3125q-1.34375 0.90625 -3.359375 0.90625zm8.074463 -0.390625l0 -17.187515l2.03125 0l0 17.187515l-2.03125 0zm5.063965 0l0 -17.187515l2.03125 0l0 17.187515l-2.03125 0zm14.897583 3.265625q-0.71875 -0.84375 -1.484375 -2.0q-0.75 -1.140625 -1.375 -2.546875q-0.625 -1.40625 -1.015625 -3.0156403q-0.390625 -1.625 -0.390625 -3.4375q0 -1.796875 0.390625 -3.40625q0.390625 -1.625 1.015625 -3.03125q0.625 -1.40625 1.375 -2.546875q0.765625 -1.15625 1.484375 -2.0l1.484375 1.0625q-1.0 1.1875 -1.859375 2.78125q-0.84375 1.578125 -1.359375 3.40625q-0.515625 1.828125 -0.515625 3.734375q0 1.921875 0.515625 3.7500153q0.515625 1.828125 1.359375 3.421875q0.859375 1.578125 1.859375 2.765625l-1.484375 1.0625zm11.9552 -2.875q-1.890625 0 -3.515625 -0.6875q-1.625 -0.6875 -2.84375 -1.890625q-1.203125 -1.21875 -1.875 -2.8593903q-0.671875 -1.65625 -0.671875 -3.546875q0 -1.921875 0.671875 -3.546875q0.671875 -1.640625 1.875 -2.84375q1.21875 -1.21875 2.84375 -1.890625q1.625 -0.6875 3.515625 -0.6875q1.3125 0 2.453125 0.34375q1.140625 0.328125 2.09375 0.953125q0.96875 0.625 1.734375 1.5l-1.453125 1.421875q-0.65625 -0.796875 -1.390625 -1.296875q-0.71875 -0.5 -1.578125 -0.734375q-0.859375 -0.25 -1.859375 -0.25q-1.84375 0 -3.40625 0.875q-1.5625 0.859375 -2.5 2.4375q-0.9375 1.5625 -0.9375 3.71875q0 2.140625 0.9375 3.734375q0.9375 1.5781403 2.5 2.4375153q1.5625 0.859375 3.40625 0.859375q1.125 0 2.078125 -0.3125q0.953125 -0.3125 1.75 -0.890625q0.796875 -0.578125 1.46875 -1.3593903l1.5 1.4375153q-0.75 0.890625 -1.796875 1.59375q-1.046875 0.6875 -2.3125 1.09375q-1.25 0.390625 -2.6875 0.390625zm13.658936 -1.6875l0 -10.42189l1.671875 0l0 10.42189l-1.671875 0zm-4.375 -4.3593903l0 -1.6875l10.421875 0l0 1.6875l-10.421875 0zm17.766968 4.3593903l0 -10.42189l1.671875 0l0 10.42189l-1.671875 0zm-4.375 -4.3593903l0 -1.6875l10.421875 0l0 1.6875l-10.421875 0zm13.876343 8.92189l-1.484375 -1.0625q1.0 -1.1875 1.84375 -2.765625q0.859375 -1.59375 1.375 -3.421875q0.515625 -1.8281403 0.515625 -3.7500153q0 -1.90625 -0.515625 -3.734375q-0.515625 -1.828125 -1.375 -3.40625q-0.84375 -1.59375 -1.84375 -2.78125l1.484375 -1.0625q0.953125 1.109375 1.9375 2.78125q0.984375 1.65625 1.65625 3.734375q0.671875 2.078125 0.671875 4.46875q0 1.8125 -0.390625 3.4375q-0.390625 1.6093903 -1.015625 3.0156403q-0.625 1.40625 -1.390625 2.546875q-0.75 1.15625 -1.46875 2.0z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m344.1691 582.7924l0 121.763794" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m344.1691 586.21954l0 114.909546" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m344.1691 586.21954l1.1245728 1.1245728l-1.1245728 -3.0897827l-1.1246033 3.0897827z" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m344.1691 701.1291l-1.1246033 -1.1245728l1.1246033 3.0897827l1.1245728 -3.0897827z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m457.28668 582.79297l0 121.763794" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m457.28665 586.2201l0 114.90961" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m457.28665 586.2201l1.1246033 1.1245728l-1.1246033 -3.0897827l-1.1245728 3.0897827z" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m457.28665 701.1297l-1.1245728 -1.1246338l1.1245728 3.0897827l1.1246033 -3.0897827z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m570.4102 582.79297l0 121.763794" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m570.4102 586.2201l0 114.90961" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m570.4102 586.2201l1.1245728 1.1245728l-1.1245728 -3.0897827l-1.1246338 3.0897827z" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m570.4102 701.1297l-1.1246338 -1.1246338l1.1246338 3.0897827l1.1245728 -3.0897827z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m683.5337 582.79297l0 121.763794" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m683.5337 586.2201l0 114.90961" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m683.5337 586.2201l1.1245728 1.1245728l-1.1245728 -3.0897827l-1.1245728 3.0897827z" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m683.5337 701.1297l-1.1245728 -1.1246338l1.1245728 3.0897827l1.1245728 -3.0897827z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m960.4125 582.79297l0 121.763794" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m960.4125 586.2201l0 114.90961" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m960.4125 586.2201l1.1245728 1.1245728l-1.1245728 -3.0897827l-1.1245728 3.0897827z" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m960.4125 701.1297l-1.1245728 -1.1246338l1.1245728 3.0897827l1.1245728 -3.0897827z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m783.4539 696.5185l77.03937 0l0 84.031494l-77.03937 0z" fill-rule="evenodd"/><path fill="#000000" d="m806.8133 751.3585l0 -3.734375l3.734375 0l0 3.734375l-3.734375 0zm12.453125 0l0 -3.734375l3.734375 0l0 3.734375l-3.734375 0zm12.421875 0l0 -3.734375l3.75 0l0 3.734375l-3.75 0z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m55.661366 582.79205l266.11023 0l0 130.20477l-266.11023 0z" fill-rule="evenodd"/><path fill="#000000" d="m104.75586 627.3921l0 -19.078125l2.265625 0l0 19.078125l-2.265625 0zm1.203125 -8.6875l0 -2.15625l11.40625 0l0 2.15625l-11.40625 0zm10.875 8.6875l0 -19.078125l2.265625 0l0 19.078125l-2.265625 0zm10.449738 0l0 -17.71875l2.265625 0l0 17.71875l-2.265625 0zm-5.359375 -16.921875l0 -2.15625l12.953125 0l0 2.15625l-12.953125 0zm19.700378 16.921875l0 -17.71875l2.265625 0l0 17.71875l-2.265625 0zm-5.359375 -16.921875l0 -2.15625l12.953125 0l0 2.15625l-12.953125 0zm15.778503 16.921875l0 -19.078125l6.4375 0q1.59375 0 2.953125 0.71875q1.359375 0.71875 2.171875 1.984375q0.8125 1.265625 0.8125 2.9375q0 1.65625 -0.8125 2.9375q-0.8125 1.28125 -2.171875 2.0q-1.359375 0.71875 -2.953125 0.71875l-5.234375 0l0 -2.15625l5.28125 0q1.125 0 1.921875 -0.515625q0.796875 -0.53125 1.234375 -1.328125q0.4375 -0.796875 0.4375 -1.65625q0 -0.84375 -0.4375 -1.640625q-0.4375 -0.8125 -1.234375 -1.328125q-0.796875 -0.515625 -1.921875 -0.515625l-4.21875 0l0 16.921875l-2.265625 0zm21.538177 0l0 -19.078125l6.453125 0q1.609375 0 2.953125 0.71875q1.34375 0.71875 2.15625 1.984375q0.8125 1.265625 0.8125 2.9375q0 1.015625 -0.375 2.015625q-0.375 1.0 -1.15625 1.828125q-0.78125 0.828125 -2.0 1.328125q-1.21875 0.484375 -2.890625 0.484375l-4.75 0l0 -2.109375l5.125 0q0.984375 0 1.828125 -0.453125q0.859375 -0.453125 1.390625 -1.25q0.53125 -0.796875 0.53125 -1.84375q0 -0.84375 -0.4375 -1.640625q-0.4375 -0.8125 -1.234375 -1.328125q-0.796875 -0.515625 -1.921875 -0.515625l-4.21875 0l0 16.921875l-2.265625 0zm4.625 -8.75l2.46875 -0.125l6.03125 8.765625l0 0.109375l-2.671875 0l-5.828125 -8.75zm10.995499 8.75l0 -19.078125l6.4375 0q1.59375 0 2.953125 0.71875q1.359375 0.71875 2.171875 1.984375q0.8125 1.265625 0.8125 2.9375q0 1.65625 -0.8125 2.9375q-0.8125 1.28125 -2.171875 2.0q-1.359375 0.71875 -2.953125 0.71875l-5.234375 0l0 -2.15625l5.28125 0q1.125 0 1.921875 -0.515625q0.796875 -0.53125 1.234375 -1.328125q0.4375 -0.796875 0.4375 -1.65625q0 -0.84375 -0.4375 -1.640625q-0.4375 -0.8125 -1.234375 -1.328125q-0.796875 -0.515625 -1.921875 -0.515625l-4.21875 0l0 16.921875l-2.265625 0zm24.05603 0.421875q-2.109375 0 -3.90625 -0.75q-1.796875 -0.765625 -3.15625 -2.109375q-1.34375 -1.34375 -2.09375 -3.171875q-0.734375 -1.828125 -0.734375 -3.9375q0 -2.125 0.734375 -3.9375q0.75 -1.8125 2.09375 -3.15625q1.359375 -1.359375 3.15625 -2.109375q1.796875 -0.765625 3.90625 -0.765625q1.46875 0 2.734375 0.375q1.265625 0.375 2.328125 1.078125q1.0625 0.6875 1.921875 1.671875l-1.625 1.5625q-0.71875 -0.875 -1.546875 -1.421875q-0.8125 -0.5625 -1.765625 -0.828125q-0.9375 -0.28125 -2.046875 -0.28125q-2.0625 0 -3.796875 0.96875q-1.71875 0.953125 -2.765625 2.703125q-1.046875 1.75 -1.046875 4.140625q0 2.375 1.046875 4.140625q1.046875 1.75 2.765625 2.71875q1.734375 0.953125 3.796875 0.953125q1.25 0 2.296875 -0.34375q1.0625 -0.34375 1.953125 -0.984375q0.890625 -0.640625 1.640625 -1.515625l1.640625 1.59375q-0.8125 0.984375 -1.984375 1.765625q-1.15625 0.765625 -2.5625 1.203125q-1.390625 0.4375 -2.984375 0.4375zm14.813675 0q-1.515625 0 -2.6875 -0.484375q-1.15625 -0.5 -1.921875 -1.328125q-0.75 -0.84375 -1.125 -1.828125l2.03125 -0.90625q0.53125 1.21875 1.546875 1.890625q1.015625 0.65625 2.3125 0.65625q1.265625 0 2.09375 -0.5q0.84375 -0.5 0.84375 -1.5q0 -0.609375 -0.34375 -1.015625q-0.34375 -0.421875 -1.015625 -0.703125q-0.671875 -0.296875 -1.625 -0.546875l-1.65625 -0.421875q-0.953125 -0.265625 -1.8125 -0.734375q-0.84375 -0.484375 -1.375 -1.25q-0.515625 -0.765625 -0.515625 -1.859375q0 -1.21875 0.71875 -2.09375q0.71875 -0.890625 1.890625 -1.34375q1.171875 -0.46875 2.515625 -0.46875q1.171875 0 2.1875 0.328125q1.03125 0.328125 1.796875 0.984375q0.78125 0.65625 1.1875 1.609375l-1.984375 0.90625q-0.5 -1.015625 -1.359375 -1.40625q-0.84375 -0.40625 -1.890625 -0.40625q-1.109375 0 -1.9375 0.5q-0.828125 0.484375 -0.828125 1.34375q0 0.859375 0.671875 1.28125q0.6875 0.421875 1.671875 0.6875l1.96875 0.515625q2.0 0.5 3.015625 1.5q1.015625 1.0 1.015625 2.4375q0 1.28125 -0.71875 2.21875q-0.71875 0.921875 -1.953125 1.4375q-1.21875 0.5 -2.71875 0.5zm12.587372 -11.96875l0 -2.046875l8.265625 0l0 2.046875l-8.265625 0zm2.5 11.546875l0 -15.0625q0 -1.328125 0.578125 -2.265625q0.59375 -0.953125 1.59375 -1.453125q1.0 -0.515625 2.171875 -0.515625q0.78125 0 1.234375 0.09375q0.453125 0.09375 0.765625 0.21875l0 2.21875q-0.34375 -0.140625 -0.796875 -0.28125q-0.453125 -0.140625 -1.125 -0.140625q-0.828125 0 -1.5 0.59375q-0.65625 0.59375 -0.65625 1.765625l0 14.828125l-2.265625 0zm13.9635315 0.421875q-2.0468903 0 -3.6250153 -0.953125q-1.5625 -0.96875 -2.46875 -2.59375q-0.890625 -1.640625 -0.890625 -3.671875q0 -2.03125 0.890625 -3.671875q0.90625 -1.640625 2.46875 -2.59375q1.578125 -0.953125 3.6250153 -0.953125q2.0625 0 3.625 0.96875q1.578125 0.96875 2.46875 2.609375q0.890625 1.640625 0.890625 3.640625q0 2.03125 -0.890625 3.671875q-0.890625 1.625 -2.46875 2.59375q-1.5625 0.953125 -3.625 0.953125zm0 -2.046875q1.234375 0 2.3125 -0.609375q1.078125 -0.625 1.734375 -1.78125q0.671875 -1.15625 0.671875 -2.78125q0 -1.625 -0.671875 -2.78125q-0.65625 -1.171875 -1.734375 -1.78125q-1.078125 -0.609375 -2.3125 -0.609375q-1.2187653 0 -2.3125153 0.609375q-1.09375 0.609375 -1.765625 1.78125q-0.65625 1.15625 -0.65625 2.78125q0 1.625 0.65625 2.78125q0.671875 1.15625 1.765625 1.78125q1.09375 0.609375 2.3125153 0.609375zm9.354767 1.625l0 -13.59375l2.171875 0l0 2.1875l0.09375 0q0.28125 -0.78125 0.90625 -1.34375q0.625 -0.578125 1.4375 -0.90625q0.8125 -0.34375 1.609375 -0.34375q0.609375 0 0.953125 0.078125q0.359375 0.0625 0.640625 0.1875l0 2.453125q-0.421875 -0.203125 -0.921875 -0.3125q-0.484375 -0.109375 -0.984375 -0.109375q-1.0 0 -1.828125 0.5625q-0.8125 0.5625 -1.3125 1.5q-0.5 0.921875 -0.5 2.046875l0 7.59375l-2.265625 0z" fill-rule="nonzero"/><path fill="#000000" d="m120.615944 659.3921l0 -19.078125l2.265625 0l0 19.078125l-2.265625 0zm11.756752 0.421875q-2.046875 0 -3.625 -0.953125q-1.5625076 -0.96875 -2.4687576 -2.59375q-0.890625 -1.640625 -0.890625 -3.671875q0 -2.03125 0.890625 -3.671875q0.90625 -1.640625 2.4687576 -2.59375q1.578125 -0.953125 3.625 -0.953125q2.0625 0 3.625 0.96875q1.578125 0.96875 2.46875 2.609375q0.890625 1.640625 0.890625 3.640625q0 2.03125 -0.890625 3.671875q-0.890625 1.625 -2.46875 2.59375q-1.5625 0.953125 -3.625 0.953125zm0 -2.046875q1.234375 0 2.3125 -0.609375q1.078125 -0.625 1.734375 -1.78125q0.671875 -1.15625 0.671875 -2.78125q0 -1.625 -0.671875 -2.78125q-0.65625 -1.171875 -1.734375 -1.78125q-1.078125 -0.609375 -2.3125 -0.609375q-1.21875 0 -2.3125 0.609375q-1.09375 0.609375 -1.765625 1.78125q-0.6562576 1.15625 -0.6562576 2.78125q0 1.625 0.6562576 2.78125q0.671875 1.15625 1.765625 1.78125q1.09375 0.609375 2.3125 0.609375zm13.98082 2.046875q-1.53125 0 -2.671875 -0.578125q-1.140625 -0.59375 -1.796875 -1.609375q-0.65625 -1.03125 -0.65625 -2.34375q0 -1.484375 0.765625 -2.515625q0.78125 -1.03125 2.078125 -1.546875q1.3125 -0.515625 2.890625 -0.515625q0.90625 0 1.671875 0.15625q0.78125 0.140625 1.34375 0.34375q0.578125 0.1875 0.875 0.375l0 -0.828125q0 -1.546875 -1.09375 -2.453125q-1.09375 -0.90625 -2.671875 -0.90625q-1.109375 0 -2.09375 0.5q-0.96875 0.484375 -1.53125 1.375l-1.703125 -1.28125q0.53125 -0.796875 1.328125 -1.375q0.8125 -0.59375 1.828125 -0.90625q1.03125 -0.328125 2.171875 -0.328125q2.78125 0 4.34375 1.46875q1.578125 1.453125 1.578125 3.9375l0 8.609375l-2.15625 0l0 -1.953125l-0.109375 0q-0.34375 0.59375 -0.984375 1.140625q-0.640625 0.546875 -1.515625 0.890625q-0.859375 0.34375 -1.890625 0.34375zm0.203125 -2.0q1.171875 0 2.140625 -0.578125q0.984375 -0.59375 1.5625 -1.578125q0.59375 -0.984375 0.59375 -2.15625q-0.609375 -0.421875 -1.53125 -0.6875q-0.921875 -0.265625 -2.015625 -0.265625q-1.953125 0 -2.859375 0.796875q-0.90625 0.796875 -0.90625 1.96875q0 1.125 0.859375 1.8125q0.859375 0.6875 2.15625 0.6875zm15.255463 2.0q-1.84375 0 -3.328125 -0.921875q-1.46875 -0.9375 -2.328125 -2.5625q-0.859375 -1.625 -0.859375 -3.734375q0 -2.109375 0.859375 -3.734375q0.859375 -1.625 2.328125 -2.546875q1.484375 -0.9375 3.328125 -0.9375q1.09375 0 2.0 0.34375q0.90625 0.34375 1.578125 0.90625q0.6875 0.5625 1.0625 1.203125l0.09375 0l-0.09375 -1.90625l0 -5.609375l2.265625 0l0 19.078125l-2.171875 0l0 -2.0l-0.09375 0q-0.375 0.609375 -1.0625 1.171875q-0.671875 0.5625 -1.578125 0.90625q-0.90625 0.34375 -2.0 0.34375zm0.234375 -2.046875q1.171875 0 2.1875 -0.625q1.03125 -0.625 1.671875 -1.78125q0.640625 -1.171875 0.640625 -2.765625q0 -1.59375 -0.640625 -2.75q-0.640625 -1.171875 -1.671875 -1.796875q-1.015625 -0.625 -2.1875 -0.625q-1.171875 0 -2.203125 0.625q-1.015625 0.625 -1.65625 1.796875q-0.640625 1.15625 -0.640625 2.75q0 1.578125 0.640625 2.75q0.640625 1.171875 1.65625 1.796875q1.03125 0.625 2.203125 0.625zm10.234497 1.625l0 -13.59375l2.265625 0l0 13.59375l-2.265625 0zm1.125 -16.09375q-0.671875 0 -1.15625 -0.484375q-0.46875 -0.484375 -0.46875 -1.140625q0 -0.703125 0.46875 -1.171875q0.484375 -0.46875 1.15625 -0.46875q0.6875 0 1.15625 0.46875q0.46875 0.46875 0.46875 1.171875q0 0.65625 -0.46875 1.140625q-0.46875 0.484375 -1.15625 0.484375zm4.680542 16.09375l0 -13.59375l2.171875 0l0 2.0l0.09375 0q0.546875 -0.984375 1.75 -1.703125q1.21875 -0.71875 2.6562347 -0.71875q2.5 0 3.765625 1.453125q1.265625 1.453125 1.265625 3.84375l0 8.71875l-2.265625 0l0 -8.375q0 -1.96875 -0.953125 -2.78125q-0.9375 -0.8125 -2.421875 -0.8125q-1.1249847 0 -1.9843597 0.625q-0.84375 0.625 -1.328125 1.609375q-0.484375 0.984375 -0.484375 2.078125l0 7.65625l-2.265625 0zm20.80513 6.1875q-1.734375 0 -2.984375 -0.578125q-1.234375 -0.578125 -2.0 -1.453125q-0.75 -0.875 -1.078125 -1.78125l2.078125 -0.875q0.4375 1.109375 1.453125 1.859375q1.03125 0.765625 2.53125 0.765625q2.125 0 3.265625 -1.234375q1.15625 -1.234375 1.15625 -3.453125l0 -1.515625l-0.109375 0q-0.640625 0.984375 -1.84375 1.671875q-1.1875 0.671875 -2.796875 0.671875q-1.75 0 -3.203125 -0.90625q-1.453125 -0.90625 -2.3125 -2.515625q-0.859375 -1.609375 -0.859375 -3.71875q0 -2.109375 0.859375 -3.71875q0.859375 -1.609375 2.3125 -2.515625q1.453125 -0.90625 3.203125 -0.90625q1.609375 0 2.796875 0.6875q1.203125 0.671875 1.84375 1.671875l0.109375 0l0 -1.9375l2.15625 0l0 13.0625q0 2.265625 -0.875 3.75q-0.859375 1.5 -2.34375 2.234375q-1.46875 0.734375 -3.359375 0.734375zm0 -7.96875q1.1875 0 2.203125 -0.59375q1.015625 -0.609375 1.609375 -1.75q0.609375 -1.15625 0.609375 -2.75q0 -1.65625 -0.609375 -2.78125q-0.59375 -1.140625 -1.609375 -1.71875q-1.015625 -0.59375 -2.203125 -0.59375q-1.203125 0 -2.21875 0.609375q-1.015625 0.59375 -1.625 1.734375q-0.609375 1.125 -0.609375 2.75q0 1.625 0.609375 2.765625q0.609375 1.125 1.625 1.734375q1.015625 0.59375 2.21875 0.59375zm20.472961 2.203125q-1.53125 0 -2.671875 -0.578125q-1.140625 -0.59375 -1.796875 -1.609375q-0.65625 -1.03125 -0.65625 -2.34375q0 -1.484375 0.765625 -2.515625q0.78125 -1.03125 2.078125 -1.546875q1.3125 -0.515625 2.890625 -0.515625q0.90625 0 1.671875 0.15625q0.78125 0.140625 1.34375 0.34375q0.578125 0.1875 0.875 0.375l0 -0.828125q0 -1.546875 -1.09375 -2.453125q-1.09375 -0.90625 -2.671875 -0.90625q-1.109375 0 -2.09375 0.5q-0.96875 0.484375 -1.53125 1.375l-1.703125 -1.28125q0.53125 -0.796875 1.328125 -1.375q0.8125 -0.59375 1.828125 -0.90625q1.03125 -0.328125 2.171875 -0.328125q2.78125 0 4.34375 1.46875q1.578125 1.453125 1.578125 3.9375l0 8.609375l-2.15625 0l0 -1.953125l-0.109375 0q-0.34375 0.59375 -0.984375 1.140625q-0.640625 0.546875 -1.515625 0.890625q-0.859375 0.34375 -1.890625 0.34375zm0.203125 -2.0q1.171875 0 2.140625 -0.578125q0.984375 -0.59375 1.5625 -1.578125q0.59375 -0.984375 0.59375 -2.15625q-0.609375 -0.421875 -1.53125 -0.6875q-0.921875 -0.265625 -2.015625 -0.265625q-1.953125 0 -2.859375 0.796875q-0.90625 0.796875 -0.90625 1.96875q0 1.125 0.859375 1.8125q0.859375 0.6875 2.15625 0.6875zm9.591919 1.578125l0 -13.59375l2.171875 0l0 2.0l0.09375 0q0.546875 -0.984375 1.75 -1.703125q1.21875 -0.71875 2.65625 -0.71875q2.5 0 3.765625 1.453125q1.265625 1.453125 1.265625 3.84375l0 8.71875l-2.265625 0l0 -8.375q0 -1.96875 -0.953125 -2.78125q-0.9375 -0.8125 -2.421875 -0.8125q-1.125 0 -1.984375 0.625q-0.84375 0.625 -1.328125 1.609375q-0.484375 0.984375 -0.484375 2.078125l0 7.65625l-2.265625 0zm20.750977 0.421875q-1.84375 0 -3.328125 -0.921875q-1.46875 -0.9375 -2.328125 -2.5625q-0.859375 -1.625 -0.859375 -3.734375q0 -2.109375 0.859375 -3.734375q0.859375 -1.625 2.328125 -2.546875q1.484375 -0.9375 3.328125 -0.9375q1.09375 0 2.0 0.34375q0.90625 0.34375 1.578125 0.90625q0.6875 0.5625 1.0625 1.203125l0.09375 0l-0.09375 -1.90625l0 -5.609375l2.2656403 0l0 19.078125l-2.1718903 0l0 -2.0l-0.09375 0q-0.375 0.609375 -1.0625 1.171875q-0.671875 0.5625 -1.578125 0.90625q-0.90625 0.34375 -2.0 0.34375zm0.234375 -2.046875q1.171875 0 2.1875 -0.625q1.03125 -0.625 1.671875 -1.78125q0.640625 -1.171875 0.640625 -2.765625q0 -1.59375 -0.640625 -2.75q-0.640625 -1.171875 -1.671875 -1.796875q-1.015625 -0.625 -2.1875 -0.625q-1.171875 0 -2.203125 0.625q-1.015625 0.625 -1.65625 1.796875q-0.640625 1.15625 -0.640625 2.75q0 1.578125 0.640625 2.75q0.640625 1.171875 1.65625 1.796875q1.03125 0.625 2.203125 0.625z" fill-rule="nonzero"/><path fill="#000000" d="m106.463135 697.1421l0 -5.890625l0.09375 -1.859375l-0.09375 0q-0.375 0.609375 -1.0625 1.171875q-0.671875 0.5625 -1.578125 0.90625q-0.90625 0.34375 -2.0 0.34375q-1.84375 0 -3.328125 -0.921875q-1.46875 -0.9375 -2.328125 -2.5625q-0.859375 -1.625 -0.859375 -3.734375q0 -2.109375 0.859375 -3.734375q0.859375 -1.625 2.328125 -2.546875q1.484375 -0.9375 3.328125 -0.9375q1.09375 0 2.0 0.34375q0.90625 0.34375 1.578125 0.90625q0.6875 0.5625 1.0625 1.203125l0.09375 0l0 -2.03125l2.171875 0l0 19.34375l-2.265625 0zm-4.40625 -7.375q1.171875 0 2.1875 -0.625q1.03125 -0.625 1.671875 -1.78125q0.640625 -1.171875 0.640625 -2.765625q0 -1.59375 -0.640625 -2.75q-0.640625 -1.171875 -1.671875 -1.796875q-1.015625 -0.625 -2.1875 -0.625q-1.171875 0 -2.203125 0.625q-1.015625 0.625 -1.65625 1.796875q-0.640625 1.15625 -0.640625 2.75q0 1.578125 0.640625 2.75q0.640625 1.171875 1.65625 1.796875q1.03125 0.625 2.203125 0.625zm14.953247 2.046875q-2.5 0 -3.765625 -1.453125q-1.265625 -1.46875 -1.265625 -4.0l0 -8.5625l2.265625 0l0 8.203125q0 2.03125 0.921875 2.90625q0.9375 0.859375 2.328125 0.859375q1.203125 0 2.078125 -0.625q0.875 -0.625 1.359375 -1.609375q0.484375 -0.984375 0.484375 -2.0625l0 -7.671875l2.265625 0l0 13.59375l-2.171875 0l0 -1.96875l-0.09375 0q-0.359375 0.640625 -1.046875 1.1875q-0.671875 0.546875 -1.53125 0.875q-0.859375 0.328125 -1.828125 0.328125zm15.94577 0q-1.984375 0 -3.515625 -0.921875q-1.53125 -0.9375 -2.40625 -2.5625q-0.859375 -1.625 -0.859375 -3.703125q0 -1.953125 0.8125 -3.59375q0.8125 -1.65625 2.296875 -2.65625q1.484375 -1.0 3.484375 -1.0q2.015625 0 3.46875 0.890625q1.453125 0.890625 2.234375 2.46875q0.796875 1.5625 0.796875 3.59375q0 0.1875 -0.015625 0.375q-0.015625 0.1875 -0.03125 0.3125l-11.703125 0l0 -1.859375l9.359375 0q-0.03125 -0.5625 -0.265625 -1.21875q-0.21875 -0.65625 -0.71875 -1.21875q-0.484375 -0.578125 -1.25 -0.9375q-0.765625 -0.359375 -1.875 -0.359375q-1.34375 0 -2.3125 0.6875q-0.96875 0.671875 -1.5 1.84375q-0.515625 1.171875 -0.515625 2.671875q0 1.734375 0.671875 2.875q0.671875 1.140625 1.71875 1.703125q1.046875 0.5625 2.203125 0.5625q1.484375 0 2.453125 -0.703125q0.984375 -0.703125 1.5625 -1.75l1.921875 0.9375q-0.796875 1.546875 -2.296875 2.5625q-1.484375 1.0 -3.71875 1.0zm8.944916 -0.421875l0 -13.59375l2.171875 0l0 2.1875l0.09375 0q0.28125 -0.78125 0.90625 -1.34375q0.625 -0.578125 1.4375 -0.90625q0.8125 -0.34375 1.609375 -0.34375q0.609375 0 0.953125 0.078125q0.359375 0.0625 0.640625 0.1875l0 2.453125q-0.421875 -0.203125 -0.921875 -0.3125q-0.484375 -0.109375 -0.984375 -0.109375q-1.0 0 -1.828125 0.5625q-0.8125 0.5625 -1.3125 1.5q-0.5 0.921875 -0.5 2.046875l0 7.59375l-2.265625 0zm14.488983 5.0625q-0.09375 0.1875 -0.203125 0.40625q-0.09375 0.234375 -0.125 0.28125l-2.34375 0q0.15625 -0.34375 0.34375 -0.765625q0.1875 -0.40625 0.453125 -0.984375q0.15625 -0.34375 0.28125 -0.640625q0.140625 -0.296875 0.3125 -0.65625q0.1875 -0.359375 0.421875 -0.890625l1.859375 -4.046875l0.515625 -1.25l4.109375 -10.109375l2.4375 0l-7.03125 16.234375q-0.109375 0.234375 -0.34375 0.75q-0.21875 0.53125 -0.421875 1.015625q-0.1875 0.5 -0.265625 0.65625zm0.5 -5.625l-5.765625 -13.03125l2.453125 0l4.265625 10.109375l0.21875 0l-1.171875 2.921875zm9.753662 0.5625l0 -13.59375l2.265625 0l0 13.59375l-2.265625 0zm1.125 -16.09375q-0.671875 0 -1.15625 -0.484375q-0.46875 -0.484375 -0.46875 -1.140625q0 -0.703125 0.46875 -1.171875q0.484375 -0.46875 1.15625 -0.46875q0.6875 0 1.15625 0.46875q0.46875 0.46875 0.46875 1.171875q0 0.65625 -0.46875 1.140625q-0.46875 0.484375 -1.15625 0.484375zm4.680542 16.09375l0 -13.59375l2.171875 0l0 2.0l0.09375 0q0.546875 -0.984375 1.75 -1.703125q1.21875 -0.71875 2.65625 -0.71875q2.5 0 3.765625 1.453125q1.265625 1.453125 1.265625 3.84375l0 8.71875l-2.265625 0l0 -8.375q0 -1.96875 -0.953125 -2.78125q-0.9375 -0.8125 -2.421875 -0.8125q-1.125 0 -1.984375 0.625q-0.84375 0.625 -1.328125 1.609375q-0.484375 0.984375 -0.484375 2.078125l0 7.65625l-2.265625 0zm20.805145 6.1875q-1.734375 0 -2.984375 -0.578125q-1.234375 -0.578125 -2.0 -1.453125q-0.75 -0.875 -1.078125 -1.78125l2.078125 -0.875q0.4375 1.109375 1.453125 1.859375q1.03125 0.765625 2.53125 0.765625q2.125 0 3.265625 -1.234375q1.15625 -1.234375 1.15625 -3.453125l0 -1.515625l-0.109375 0q-0.640625 0.984375 -1.84375 1.671875q-1.1875 0.671875 -2.796875 0.671875q-1.75 0 -3.203125 -0.90625q-1.453125 -0.90625 -2.3125 -2.515625q-0.859375 -1.609375 -0.859375 -3.71875q0 -2.109375 0.859375 -3.71875q0.859375 -1.609375 2.3125 -2.515625q1.453125 -0.90625 3.203125 -0.90625q1.609375 0 2.796875 0.6875q1.203125 0.671875 1.84375 1.671875l0.109375 0l0 -1.9375l2.15625 0l0 13.0625q0 2.265625 -0.875 3.75q-0.859375 1.5 -2.34375 2.234375q-1.46875 0.734375 -3.359375 0.734375zm0 -7.96875q1.1875 0 2.203125 -0.59375q1.015625 -0.609375 1.609375 -1.75q0.609375 -1.15625 0.609375 -2.75q0 -1.65625 -0.609375 -2.78125q-0.59375 -1.140625 -1.609375 -1.71875q-1.015625 -0.59375 -2.203125 -0.59375q-1.203125 0 -2.21875 0.609375q-1.015625 0.59375 -1.625 1.734375q-0.609375 1.125 -0.609375 2.75q0 1.625 0.609375 2.765625q0.609375 1.125 1.625 1.734375q1.015625 0.59375 2.21875 0.59375zm15.191711 -11.8125l7.96875 0l0 2.046875l-7.96875 0l0 -2.046875zm2.375 10.015625l0 -13.859375l2.265625 0l0 13.3125q0 1.0625 0.4375 1.65625q0.4375 0.578125 1.453125 0.578125q0.453125 0 0.828125 -0.125q0.375 -0.140625 0.65625 -0.328125l0 2.203125q-0.34375 0.171875 -0.765625 0.265625q-0.40625 0.09375 -1.09375 0.09375q-1.703125 0 -2.75 -1.0q-1.03125 -1.0 -1.03125 -2.796875zm8.276367 3.578125l0 -13.59375l2.171875 0l0 2.1875l0.09375 0q0.28125 -0.78125 0.90625 -1.34375q0.625 -0.578125 1.4375 -0.90625q0.8125 -0.34375 1.609375 -0.34375q0.609375 0 0.953125 0.078125q0.359375 0.0625 0.640625 0.1875l0 2.453125q-0.421875 -0.203125 -0.921875 -0.3125q-0.484375 -0.109375 -0.984375 -0.109375q-1.0 0 -1.828125 0.5625q-0.8125 0.5625 -1.3125 1.5q-0.5 0.921875 -0.5 2.046875l0 7.59375l-2.265625 0zm13.822098 0.421875q-1.53125 0 -2.671875 -0.578125q-1.140625 -0.59375 -1.796875 -1.609375q-0.65625 -1.03125 -0.65625 -2.34375q0 -1.484375 0.765625 -2.515625q0.78125 -1.03125 2.078125 -1.546875q1.3125 -0.515625 2.890625 -0.515625q0.90625 0 1.671875 0.15625q0.78125 0.140625 1.34375 0.34375q0.578125 0.1875 0.875 0.375l0 -0.828125q0 -1.546875 -1.09375 -2.453125q-1.09375 -0.90625 -2.671875 -0.90625q-1.109375 0 -2.09375 0.5q-0.96875 0.484375 -1.53125 1.375l-1.703125 -1.28125q0.53125 -0.796875 1.328125 -1.375q0.8125 -0.59375 1.828125 -0.90625q1.03125 -0.328125 2.171875 -0.328125q2.78125 0 4.34375 1.46875q1.578125 1.453125 1.578125 3.9375l0 8.609375l-2.15625 0l0 -1.953125l-0.109375 0q-0.34375 0.59375 -0.984375 1.140625q-0.640625 0.546875 -1.515625 0.890625q-0.859375 0.34375 -1.890625 0.34375zm0.203125 -2.0q1.171875 0 2.140625 -0.578125q0.984375 -0.59375 1.5625 -1.578125q0.59375 -0.984375 0.59375 -2.15625q-0.609375 -0.421875 -1.53125 -0.6875q-0.921875 -0.265625 -2.015625 -0.265625q-1.953125 0 -2.859375 0.796875q-0.90625 0.796875 -0.90625 1.96875q0 1.125 0.859375 1.8125q0.859375 0.6875 2.15625 0.6875zm15.630463 2.0q-2.0 0 -3.5625 -0.9375q-1.5625 -0.953125 -2.453125 -2.59375q-0.875 -1.640625 -0.875 -3.6875q0 -2.078125 0.875 -3.703125q0.890625 -1.625 2.453125 -2.5625q1.5625 -0.953125 3.5625 -0.953125q2.28125 0 3.765625 1.046875q1.484375 1.046875 2.09375 2.703125l-2.046875 0.859375q-0.515625 -1.25 -1.515625 -1.90625q-1.0 -0.65625 -2.40625 -0.65625q-1.203125 0 -2.234375 0.640625q-1.015625 0.640625 -1.65625 1.796875q-0.640625 1.15625 -0.640625 2.734375q0 1.546875 0.640625 2.71875q0.640625 1.171875 1.65625 1.8125q1.03125 0.640625 2.234375 0.640625q1.4375 0 2.46875 -0.65625q1.046875 -0.671875 1.546875 -1.90625l2.03125 0.859375q-0.671875 1.5625 -2.171875 2.65625q-1.484375 1.09375 -3.765625 1.09375zm14.311569 0q-1.984375 0 -3.515625 -0.921875q-1.53125 -0.9375 -2.40625 -2.5625q-0.859375 -1.625 -0.859375 -3.703125q0 -1.953125 0.8125 -3.59375q0.8125 -1.65625 2.296875 -2.65625q1.484375 -1.0 3.484375 -1.0q2.015625 0 3.46875 0.890625q1.453125 0.890625 2.234375 2.46875q0.796875 1.5625 0.796875 3.59375q0 0.1875 -0.015625 0.375q-0.015625 0.1875 -0.03125 0.3125l-11.703125 0l0 -1.859375l9.359375 0q-0.03125 -0.5625 -0.265625 -1.21875q-0.21875 -0.65625 -0.71875 -1.21875q-0.484375 -0.578125 -1.25 -0.9375q-0.765625 -0.359375 -1.875 -0.359375q-1.34375 0 -2.3125 0.6875q-0.96875 0.671875 -1.5 1.84375q-0.515625 1.171875 -0.515625 2.671875q0 1.734375 0.671875 2.875q0.671875 1.140625 1.71875 1.703125q1.046875 0.5625 2.203125 0.5625q1.484375 0 2.453125 -0.703125q0.984375 -0.703125 1.5625 -1.75l1.921875 0.9375q-0.796875 1.546875 -2.296875 2.5625q-1.484375 1.0 -3.71875 1.0zm13.828247 0q-1.515625 0 -2.6875 -0.484375q-1.15625 -0.5 -1.921875 -1.328125q-0.75 -0.84375 -1.125 -1.828125l2.03125 -0.90625q0.53125 1.21875 1.546875 1.890625q1.015625 0.65625 2.3125 0.65625q1.265625 0 2.09375 -0.5q0.84375 -0.5 0.84375 -1.5q0 -0.609375 -0.34375 -1.015625q-0.34375 -0.421875 -1.015625 -0.703125q-0.671875 -0.296875 -1.625 -0.546875l-1.65625 -0.421875q-0.953125 -0.265625 -1.8125 -0.734375q-0.84375 -0.484375 -1.375 -1.25q-0.515625 -0.765625 -0.515625 -1.859375q0 -1.21875 0.71875 -2.09375q0.71875 -0.890625 1.890625 -1.34375q1.171875 -0.46875 2.515625 -0.46875q1.171875 0 2.1875 0.328125q1.03125 0.328125 1.796875 0.984375q0.78125 0.65625 1.1875 1.609375l-1.984375 0.90625q-0.5 -1.015625 -1.359375 -1.40625q-0.84375 -0.40625 -1.890625 -0.40625q-1.109375 0 -1.9375 0.5q-0.828125 0.484375 -0.828125 1.34375q0 0.859375 0.671875 1.28125q0.6875 0.421875 1.671875 0.6875l1.96875 0.515625q2.0 0.5 3.015625 1.5q1.015625 1.0 1.015625 2.4375q0 1.28125 -0.71875 2.21875q-0.71875 0.921875 -1.953125 1.4375q-1.21875 0.5 -2.71875 0.5z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m1277.4027 54.143036l101.35437 0l0 64.661415l-101.35437 0z" fill-rule="evenodd"/><path fill="#000000" d="m1298.1527 93.62304l0 -15.265625l2.875 0l0 9.015625l0 6.25l-2.875 0zm9.171875 0l-5.296875 -7.53125l1.984375 -2.28125l6.828125 9.640625l0 0.171875l-3.515625 0zm-7.6875 -2.8125l0.046875 -4.015625l7.109375 -8.4375l3.46875 0l0 0.171875l-10.625 12.28125zm17.52417 3.15625q-1.65625 0 -2.953125 -0.75q-1.28125 -0.75 -2.015625 -2.046875q-0.734375 -1.296875 -0.734375 -2.96875q0 -1.546875 0.71875 -2.875q0.734375 -1.328125 1.984375 -2.125q1.265625 -0.796875 2.890625 -0.796875q1.75 0 2.9375 0.75q1.203125 0.75 1.8125 2.0q0.625 1.234375 0.625 2.75q0 0.34375 -0.03125 0.59375q-0.015625 0.234375 -0.03125 0.34375l-9.34375 0l0 -1.984375l6.71875 0q-0.03125 -0.359375 -0.21875 -0.734375q-0.1875 -0.390625 -0.53125 -0.703125q-0.34375 -0.328125 -0.828125 -0.53125q-0.46875 -0.203125 -1.09375 -0.203125q-0.8125 0 -1.484375 0.4375q-0.671875 0.421875 -1.0625 1.1875q-0.375 0.75 -0.375 1.78125q0 1.09375 0.40625 1.859375q0.40625 0.75 1.09375 1.140625q0.703125 0.390625 1.546875 0.390625q1.0 0 1.6875 -0.421875q0.6875 -0.4375 1.0625 -1.109375l2.265625 1.109375q-0.765625 1.34375 -2.0 2.125q-1.234375 0.78125 -3.046875 0.78125zm11.4938965 3.765625q-0.0625 0.15625 -0.125 0.265625q-0.046875 0.125 -0.09375 0.234375l-3.0 0q0.078125 -0.171875 0.3125 -0.71875q0.25 -0.546875 0.421875 -0.921875q0.140625 -0.265625 0.296875 -0.671875q0.171875 -0.40625 0.359375 -0.796875q0.1875 -0.390625 0.28125 -0.609375l1.65625 -3.640625l0.5625 -1.359375l2.71875 -6.765625l3.109375 0l-5.609375 12.921875q-0.234375 0.5625 -0.421875 0.953125q-0.171875 0.40625 -0.28125 0.671875q-0.109375 0.28125 -0.1875 0.4375zm-0.75 -4.28125l-4.734375 -10.703125l3.171875 0l2.8125 6.765625l0.328125 0l-1.578125 3.9375z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m1210.1438 423.41046l-174.45667 0" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m1210.1438 423.41043l-171.02966 0" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m1039.1141 423.41043l1.1246338 -1.1245728l-3.0897217 1.1245728l3.0897217 1.1245728z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m1210.1438 482.35168l-174.45667 0" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m1206.7167 482.35165l-171.02954 0" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m1206.7167 482.35165l-1.1245117 1.1246033l3.0897217 -1.1246033l-3.0897217 -1.1245728z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m1211.0206 387.81873l447.05505 0l0 71.1181l-447.05505 0z" fill-rule="evenodd"/><path fill="#000000" d="m1246.7812 427.62186q0 0.78125 -0.34375 1.359375q-0.34375 0.5625 -0.875 0.921875q-0.53125 0.375 -1.21875 0.5625q-0.671875 0.171875 -1.375 0.171875q-0.890625 0 -1.640625 -0.234375q-0.75 -0.25 -1.3125 -0.734375q-0.578125 -0.46875 -0.953125 -1.140625q-0.359375 -0.6875 -0.46875 -1.578125l-2.46875 0q0.046875 1.21875 0.546875 2.21875q0.5 1.0 1.359375 1.734375q0.96875 0.859375 2.265625 1.328125q1.3125 0.453125 2.671875 0.453125q1.125 0 2.25 -0.3125q1.140625 -0.328125 2.046875 -0.96875q0.890625 -0.625 1.453125 -1.578125q0.5625 -0.953125 0.5625 -2.21875q0 -1.265625 -0.53125 -2.21875q-0.515625 -0.96875 -1.375 -1.640625q-0.875 -0.71875 -1.9375 -1.171875q-1.046875 -0.46875 -2.09375 -0.796875q-0.640625 -0.1875 -1.359375 -0.4375q-0.71875 -0.265625 -1.34375 -0.65625q-0.625 -0.390625 -1.03125 -0.953125q-0.390625 -0.578125 -0.40625 -1.390625q0 -0.765625 0.296875 -1.328125q0.3125 -0.578125 0.828125 -0.96875q0.515625 -0.390625 1.171875 -0.578125q0.65625 -0.203125 1.34375 -0.203125q0.859375 0 1.546875 0.265625q0.6875 0.265625 1.171875 0.75q0.5 0.484375 0.796875 1.15625q0.296875 0.671875 0.40625 1.484375l2.46875 0q-0.03125 -1.296875 -0.5625 -2.34375q-0.53125 -1.0625 -1.40625 -1.8125q-0.890625 -0.734375 -2.046875 -1.140625q-1.140625 -0.421875 -2.375 -0.421875q-1.125 0 -2.234375 0.359375q-1.09375 0.34375 -1.96875 1.0q-0.875 0.671875 -1.421875 1.640625q-0.53125 0.953125 -0.53125 2.171875q0 1.21875 0.53125 2.125q0.546875 0.90625 1.40625 1.578125q0.84375 0.640625 1.875 1.109375q1.046875 0.46875 2.046875 0.78125q0.6875 0.234375 1.4375 0.515625q0.75 0.265625 1.375 0.65625q0.625 0.421875 1.015625 1.03125q0.40625 0.59375 0.40625 1.453125zm16.496338 -3.96875l0 -2.03125l-7.90625 0l0 -6.09375l9.109375 0l0 -2.0625l-11.515625 0l0 18.953125l11.625 0l0 -2.046875l-9.21875 0l0 -6.71875l7.90625 0zm8.293335 6.71875l0 -16.90625l-2.40625 0l0 18.953125l11.671875 0l0 -2.046875l-9.265625 0zm23.699463 -6.71875l0 -2.03125l-7.90625 0l0 -6.09375l9.109375 0l0 -2.0625l-11.515625 0l0 18.953125l11.625 0l0 -2.046875l-9.21875 0l0 -6.71875l7.90625 0zm17.840088 3.0625l-2.40625 0q-0.109375 0.859375 -0.40625 1.59375q-0.28125 0.734375 -0.765625 1.265625q-0.46875 0.546875 -1.171875 0.859375q-0.703125 0.296875 -1.640625 0.296875q-0.875 0 -1.53125 -0.28125q-0.640625 -0.28125 -1.125 -0.78125q-0.484375 -0.484375 -0.796875 -1.109375q-0.3125 -0.640625 -0.515625 -1.375q-0.1875 -0.71875 -0.28125 -1.453125q-0.078125 -0.75 -0.078125 -1.453125l0 -2.671875q0 -0.703125 0.078125 -1.453125q0.09375 -0.75 0.28125 -1.46875q0.203125 -0.71875 0.515625 -1.34375q0.328125 -0.640625 0.8125 -1.125q0.46875 -0.484375 1.125 -0.765625q0.65625 -0.28125 1.515625 -0.28125q0.9375 0 1.640625 0.328125q0.703125 0.3125 1.171875 0.875q0.484375 0.5625 0.765625 1.3125q0.296875 0.734375 0.40625 1.59375l2.40625 0q-0.15625 -1.359375 -0.640625 -2.46875q-0.484375 -1.125 -1.296875 -1.9375q-0.796875 -0.796875 -1.921875 -1.234375q-1.125 -0.4375 -2.53125 -0.4375q-1.1875 0 -2.140625 0.34375q-0.953125 0.328125 -1.6875 0.90625q-0.765625 0.59375 -1.3125 1.390625q-0.53125 0.796875 -0.875 1.734375q-0.375 0.9375 -0.546875 1.96875q-0.171875 1.03125 -0.1875 2.078125l0 2.65625q0.015625 1.046875 0.1875 2.078125q0.171875 1.03125 0.546875 1.96875q0.34375 0.9375 0.875 1.734375q0.546875 0.78125 1.3125 1.375q0.734375 0.578125 1.6875 0.921875q0.96875 0.328125 2.140625 0.328125q1.359375 0 2.46875 -0.4375q1.125 -0.4375 1.9375 -1.234375q0.8125 -0.78125 1.3125 -1.875q0.515625 -1.109375 0.671875 -2.421875zm16.511963 -11.1875l0 -2.0625l-14.0625 0l0 2.0625l5.859375 0l0 16.890625l2.34375 0l0 -16.890625l5.859375 0zm24.91455 -0.609375l-2.421875 0l0 3.421875l-3.703125 0l0 1.859375l3.703125 0l0 7.65625q0 1.296875 0.34375 2.21875q0.34375 0.90625 0.9375 1.484375q0.609375 0.59375 1.421875 0.859375q0.8125 0.265625 1.75 0.265625q0.5625 0 1.125 -0.0625q0.5625 -0.046875 1.078125 -0.15625q0.5 -0.078125 0.921875 -0.21875q0.4375 -0.140625 0.734375 -0.328125l-0.34375 -1.703125q-0.21875 0.0625 -0.5625 0.140625q-0.34375 0.0625 -0.734375 0.125q-0.40625 0.0625 -0.828125 0.109375q-0.40625 0.046875 -0.796875 0.046875q-0.53125 0 -1.015625 -0.125q-0.46875 -0.140625 -0.828125 -0.46875q-0.375 -0.3125 -0.578125 -0.84375q-0.203125 -0.53125 -0.203125 -1.34375l0 -7.65625l5.359375 0l0 -1.859375l-5.359375 0l0 -3.421875zm19.652588 13.765625q0 0.34375 -0.140625 0.640625q-0.125 0.296875 -0.375 0.546875q-0.390625 0.40625 -1.109375 0.640625q-0.71875 0.21875 -1.6875 0.21875q-0.609375 0 -1.234375 -0.125q-0.625 -0.140625 -1.125 -0.453125q-0.53125 -0.3125 -0.875 -0.8125q-0.34375 -0.515625 -0.390625 -1.25l-2.40625 0q0 0.890625 0.40625 1.734375q0.40625 0.828125 1.1875 1.453125q0.765625 0.640625 1.890625 1.03125q1.125 0.375 2.546875 0.375q1.25 0 2.296875 -0.296875q1.0625 -0.3125 1.828125 -0.859375q0.75 -0.546875 1.171875 -1.3125q0.421875 -0.765625 0.421875 -1.703125q0 -0.875 -0.375 -1.53125q-0.359375 -0.671875 -1.0625 -1.171875q-0.703125 -0.5 -1.734375 -0.859375q-1.03125 -0.359375 -2.328125 -0.625q-1.0 -0.203125 -1.640625 -0.40625q-0.640625 -0.203125 -1.015625 -0.46875q-0.390625 -0.265625 -0.546875 -0.578125q-0.15625 -0.328125 -0.15625 -0.75q0 -0.40625 0.1875 -0.78125q0.203125 -0.375 0.59375 -0.65625q0.375 -0.296875 0.953125 -0.453125q0.578125 -0.171875 1.34375 -0.171875q0.75 0 1.328125 0.203125q0.59375 0.203125 1.015625 0.546875q0.421875 0.34375 0.65625 0.78125q0.234375 0.421875 0.234375 0.890625l2.40625 0q0 -0.9375 -0.40625 -1.734375q-0.390625 -0.796875 -1.125 -1.390625q-0.71875 -0.609375 -1.765625 -0.9375q-1.046875 -0.34375 -2.34375 -0.34375q-1.203125 0 -2.21875 0.328125q-1.0 0.328125 -1.734375 0.875q-0.734375 0.5625 -1.140625 1.3125q-0.390625 0.75 -0.390625 1.609375q0 0.875 0.390625 1.53125q0.390625 0.65625 1.09375 1.140625q0.703125 0.5 1.703125 0.84375q1.0 0.34375 2.203125 0.59375q1.0 0.203125 1.65625 0.453125q0.671875 0.234375 1.0625 0.515625q0.40625 0.28125 0.578125 0.625q0.171875 0.34375 0.171875 0.78125zm12.746338 3.171875l0 -2.28125l-2.625 0l0 2.328125q0 1.09375 -0.265625 2.125q-0.25 1.03125 -0.875 1.9375l1.5 0.828125q1.03125 -0.921875 1.640625 -2.28125q0.625 -1.359375 0.625 -2.65625zm25.430298 -6.59375l0 0.265625q0 1.515625 0.390625 2.828125q0.40625 1.296875 1.140625 2.265625q0.734375 0.96875 1.765625 1.515625q1.03125 0.546875 2.3125 0.546875q1.296875 0 2.265625 -0.4375q0.984375 -0.453125 1.6875 -1.3125l0.109375 1.484375l2.203125 0l0 -20.0l-2.40625 0l0 7.3125q-0.6875 -0.8125 -1.640625 -1.234375q-0.9375 -0.421875 -2.203125 -0.421875q-1.28125 0 -2.328125 0.53125q-1.03125 0.53125 -1.765625 1.484375q-0.734375 0.953125 -1.140625 2.28125q-0.390625 1.3125 -0.390625 2.890625zm2.40625 0.265625l0 -0.265625q0 -1.03125 0.21875 -1.953125q0.21875 -0.9375 0.6875 -1.65625q0.453125 -0.703125 1.171875 -1.109375q0.734375 -0.421875 1.75 -0.421875q1.1875 0 1.96875 0.5625q0.796875 0.546875 1.265625 1.40625l0 6.53125q-0.46875 0.921875 -1.265625 1.46875q-0.78125 0.546875 -2.0 0.546875q-1.0 0 -1.71875 -0.40625q-0.71875 -0.421875 -1.171875 -1.125q-0.46875 -0.703125 -0.6875 -1.625q-0.21875 -0.921875 -0.21875 -1.953125zm23.324463 6.890625l2.1875 0l0 -14.078125l-2.421875 0l0 10.09375q-0.203125 0.453125 -0.5 0.84375q-0.28125 0.375 -0.671875 0.65625q-0.46875 0.34375 -1.109375 0.53125q-0.640625 0.1875 -1.4375 0.1875q-0.703125 0 -1.234375 -0.1875q-0.515625 -0.1875 -0.875 -0.640625q-0.375 -0.4375 -0.5625 -1.1875q-0.171875 -0.765625 -0.171875 -1.90625l0 -8.390625l-2.40625 0l0 8.359375q0 1.578125 0.34375 2.703125q0.34375 1.125 0.984375 1.859375q0.640625 0.734375 1.53125 1.078125q0.90625 0.34375 2.0 0.34375q1.390625 0 2.4375 -0.578125q1.0625 -0.578125 1.765625 -1.625l0.140625 1.9375zm15.949463 -14.34375q-1.546875 0 -2.75 0.671875q-1.203125 0.671875 -2.0625 1.828125l-0.015625 -0.359375l-0.09375 -1.875l-2.296875 0l0 14.078125l2.421875 0l0 -9.03125q0.234375 -0.65625 0.59375 -1.171875q0.359375 -0.53125 0.84375 -0.90625q0.53125 -0.40625 1.234375 -0.625q0.71875 -0.21875 1.609375 -0.21875q0.6875 0 1.3125 0.078125q0.640625 0.0625 1.34375 0.21875l0.328125 -2.34375q-0.375 -0.15625 -1.09375 -0.25q-0.71875 -0.09375 -1.375 -0.09375zm33.305176 5.96875l0 -2.03125l-7.890625 0l0 -6.484375l9.203125 0l0 -2.0625l-11.640625 0l0 18.953125l2.4375 0l0 -8.375l7.890625 0zm11.715088 0.640625l3.734375 7.734375l2.53125 0l0.015625 -0.15625l-4.015625 -8.09375q0.78125 -0.34375 1.421875 -0.8125q0.65625 -0.484375 1.125 -1.109375q0.46875 -0.625 0.734375 -1.390625q0.265625 -0.78125 0.265625 -1.703125q0 -1.421875 -0.5 -2.484375q-0.5 -1.0625 -1.375 -1.765625q-0.890625 -0.703125 -2.0625 -1.046875q-1.171875 -0.359375 -2.5 -0.390625l-5.546875 0l0 18.953125l2.390625 0l0 -7.734375l3.78125 0zm-3.78125 -1.96875l0 -7.265625l3.15625 0q0.875 0.015625 1.609375 0.25q0.734375 0.234375 1.28125 0.6875q0.53125 0.46875 0.828125 1.15625q0.296875 0.671875 0.296875 1.578125q0 0.859375 -0.3125 1.53125q-0.3125 0.65625 -0.84375 1.109375q-0.546875 0.453125 -1.265625 0.703125q-0.71875 0.234375 -1.515625 0.25l-3.234375 0zm25.840088 1.328125l0 -2.171875q-0.015625 -1.015625 -0.1875 -2.046875q-0.15625 -1.046875 -0.5 -2.015625q-0.34375 -0.953125 -0.875 -1.796875q-0.515625 -0.84375 -1.25 -1.46875q-0.734375 -0.625 -1.6875 -0.984375q-0.9375 -0.359375 -2.109375 -0.359375q-1.171875 0 -2.125 0.359375q-0.9375 0.359375 -1.671875 0.984375q-0.71875 0.640625 -1.25 1.484375q-0.53125 0.84375 -0.875 1.796875q-0.34375 0.96875 -0.515625 2.0q-0.15625 1.03125 -0.171875 2.046875l0 2.171875q0.015625 1.015625 0.1875 2.046875q0.171875 1.015625 0.515625 1.984375q0.34375 0.953125 0.875 1.796875q0.53125 0.84375 1.265625 1.46875q0.734375 0.625 1.671875 0.984375q0.9375 0.359375 2.109375 0.359375q1.171875 0 2.109375 -0.359375q0.953125 -0.359375 1.6875 -0.984375q0.71875 -0.625 1.25 -1.453125q0.53125 -0.84375 0.859375 -1.796875q0.34375 -0.96875 0.5 -2.0q0.171875 -1.03125 0.1875 -2.046875zm-2.390625 -2.1875l0 2.1875q0 0.671875 -0.078125 1.421875q-0.078125 0.734375 -0.25 1.453125q-0.203125 0.71875 -0.515625 1.375q-0.296875 0.65625 -0.75 1.15625q-0.46875 0.515625 -1.125 0.8125q-0.640625 0.296875 -1.484375 0.296875q-0.84375 0 -1.484375 -0.296875q-0.625 -0.296875 -1.09375 -0.8125q-0.46875 -0.5 -0.78125 -1.15625q-0.3125 -0.671875 -0.515625 -1.390625q-0.1875 -0.71875 -0.28125 -1.453125q-0.078125 -0.75 -0.09375 -1.40625l0 -2.1875q0.015625 -0.671875 0.09375 -1.40625q0.09375 -0.734375 0.28125 -1.46875q0.1875 -0.703125 0.5 -1.359375q0.3125 -0.671875 0.78125 -1.171875q0.46875 -0.5 1.09375 -0.796875q0.640625 -0.296875 1.484375 -0.296875q0.84375 0 1.46875 0.296875q0.640625 0.28125 1.109375 0.78125q0.46875 0.515625 0.78125 1.171875q0.3125 0.65625 0.515625 1.375q0.1875 0.71875 0.265625 1.453125q0.078125 0.734375 0.078125 1.421875zm8.699463 -8.390625l-2.984375 0l0 18.953125l2.34375 0l0 -7.5625l-0.1875 -7.640625l3.203125 9.9375l1.390625 0l3.5 -10.203125l-0.203125 7.90625l0 7.5625l2.34375 0l0 -18.953125l-2.984375 0l-3.34375 9.484375l-3.078125 -9.484375zm38.711426 15.21875q0 0.34375 -0.140625 0.640625q-0.125 0.296875 -0.375 0.546875q-0.390625 0.40625 -1.109375 0.640625q-0.71875 0.21875 -1.6875 0.21875q-0.609375 0 -1.234375 -0.125q-0.625 -0.140625 -1.125 -0.453125q-0.53125 -0.3125 -0.875 -0.8125q-0.34375 -0.515625 -0.390625 -1.25l-2.40625 0q0 0.890625 0.40625 1.734375q0.40625 0.828125 1.1875 1.453125q0.765625 0.640625 1.890625 1.03125q1.125 0.375 2.546875 0.375q1.25 0 2.296875 -0.296875q1.0625 -0.3125 1.828125 -0.859375q0.75 -0.546875 1.171875 -1.3125q0.421875 -0.765625 0.421875 -1.703125q0 -0.875 -0.375 -1.53125q-0.359375 -0.671875 -1.0625 -1.171875q-0.703125 -0.5 -1.734375 -0.859375q-1.03125 -0.359375 -2.328125 -0.625q-1.0 -0.203125 -1.640625 -0.40625q-0.640625 -0.203125 -1.015625 -0.46875q-0.390625 -0.265625 -0.546875 -0.578125q-0.15625 -0.328125 -0.15625 -0.75q0 -0.40625 0.1875 -0.78125q0.203125 -0.375 0.59375 -0.65625q0.375 -0.296875 0.953125 -0.453125q0.578125 -0.171875 1.34375 -0.171875q0.75 0 1.328125 0.203125q0.59375 0.203125 1.015625 0.546875q0.421875 0.34375 0.65625 0.78125q0.234375 0.421875 0.234375 0.890625l2.40625 0q0 -0.9375 -0.40625 -1.734375q-0.390625 -0.796875 -1.125 -1.390625q-0.71875 -0.609375 -1.765625 -0.9375q-1.046875 -0.34375 -2.34375 -0.34375q-1.203125 0 -2.21875 0.328125q-1.0 0.328125 -1.734375 0.875q-0.734375 0.5625 -1.140625 1.3125q-0.390625 0.75 -0.390625 1.609375q0 0.875 0.390625 1.53125q0.390625 0.65625 1.09375 1.140625q0.703125 0.5 1.703125 0.84375q1.0 0.34375 2.203125 0.59375q1.0 0.203125 1.65625 0.453125q0.671875 0.234375 1.0625 0.515625q0.40625 0.28125 0.578125 0.625q0.171875 0.34375 0.171875 0.78125zm7.011963 -16.265625l0 2.109375l4.796875 0l0 15.8125l-4.796875 0l0 2.078125l11.796875 0l0 -2.078125l-4.59375 0l0 -17.921875l-7.203125 0zm15.996338 5.921875l0 2.09375l4.796875 0l0 9.90625l-4.796875 0l0 2.078125l11.796875 0l0 -2.078125l-4.59375 0l0 -12.0l-7.203125 0zm4.484375 -3.703125q0 0.59375 0.34375 0.984375q0.359375 0.390625 1.09375 0.390625q0.71875 0 1.078125 -0.390625q0.375 -0.390625 0.375 -0.984375q0 -0.328125 -0.109375 -0.59375q-0.109375 -0.28125 -0.3125 -0.453125q-0.171875 -0.171875 -0.4375 -0.265625q-0.25 -0.109375 -0.59375 -0.109375q-0.34375 0 -0.609375 0.109375q-0.265625 0.09375 -0.4375 0.265625q-0.203125 0.1875 -0.296875 0.46875q-0.09375 0.265625 -0.09375 0.578125zm17.136963 16.09375q-1.140625 0 -1.90625 -0.453125q-0.75 -0.453125 -1.203125 -1.171875q-0.46875 -0.71875 -0.671875 -1.625q-0.203125 -0.90625 -0.203125 -1.828125l0 -0.546875q0 -0.90625 0.203125 -1.796875q0.203125 -0.90625 0.671875 -1.640625q0.46875 -0.71875 1.21875 -1.15625q0.765625 -0.453125 1.890625 -0.453125q0.734375 0 1.359375 0.25q0.625 0.234375 1.09375 0.671875q0.453125 0.421875 0.71875 1.0q0.265625 0.5625 0.28125 1.203125l2.265625 0q0 -1.078125 -0.421875 -2.015625q-0.421875 -0.9375 -1.1875 -1.625q-0.765625 -0.671875 -1.8125 -1.0625q-1.046875 -0.40625 -2.296875 -0.40625q-1.59375 0 -2.796875 0.578125q-1.203125 0.5625 -2.0 1.53125q-0.796875 0.96875 -1.203125 2.25q-0.40625 1.28125 -0.40625 2.671875l0 0.546875q0 1.40625 0.40625 2.671875q0.40625 1.265625 1.203125 2.234375q0.796875 0.96875 2.0 1.546875q1.203125 0.578125 2.796875 0.578125q1.125 0 2.140625 -0.375q1.03125 -0.390625 1.828125 -1.046875q0.796875 -0.640625 1.265625 -1.484375q0.484375 -0.859375 0.484375 -1.796875l-2.265625 0q-0.015625 0.578125 -0.3125 1.09375q-0.296875 0.5 -0.765625 0.859375q-0.484375 0.375 -1.109375 0.59375q-0.609375 0.203125 -1.265625 0.203125zm16.215088 1.953125q2.0625 0 3.453125 -0.828125q1.40625 -0.828125 2.109375 -1.890625l-1.46875 -1.15625q-0.671875 0.859375 -1.671875 1.390625q-1.0 0.515625 -2.296875 0.515625q-0.96875 0 -1.78125 -0.359375q-0.796875 -0.375 -1.375 -1.015625q-0.5625 -0.59375 -0.890625 -1.375q-0.3125 -0.78125 -0.390625 -1.8125l0 -0.09375l10.046875 0l0 -1.078125q0 -1.46875 -0.375 -2.71875q-0.359375 -1.265625 -1.109375 -2.203125q-0.765625 -0.9375 -1.90625 -1.453125q-1.125 -0.53125 -2.65625 -0.53125q-1.203125 0 -2.359375 0.5q-1.15625 0.484375 -2.046875 1.40625q-0.921875 0.9375 -1.46875 2.296875q-0.546875 1.34375 -0.546875 3.046875l0 0.546875q0 1.46875 0.5 2.71875q0.5 1.25 1.375 2.15625q0.890625 0.921875 2.125 1.4375q1.25 0.5 2.734375 0.5zm-0.3125 -12.625q0.921875 0 1.59375 0.34375q0.671875 0.328125 1.109375 0.875q0.453125 0.546875 0.6875 1.296875q0.25 0.75 0.25 1.390625l0 0.125l-7.5625 0q0.140625 -0.984375 0.5 -1.71875q0.359375 -0.75 0.875 -1.28125q0.515625 -0.5 1.15625 -0.765625q0.65625 -0.265625 1.390625 -0.265625z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m1220.203 444.61627l327.71655 0l0 265.03937l-327.71655 0z" fill-rule="evenodd"/><path fill="#000000" d="m1247.6718 471.68503l-3.609375 0l0 3.453125l-3.21875 0l0 2.65625l3.21875 0l0 6.40625q0 1.390625 0.359375 2.390625q0.359375 1.0 1.046875 1.640625q0.65625 0.640625 1.59375 0.953125q0.953125 0.296875 2.109375 0.296875q0.59375 0 1.21875 -0.0625q0.625 -0.046875 1.1875 -0.15625q0.578125 -0.09375 1.078125 -0.25q0.515625 -0.171875 0.890625 -0.390625l-0.359375 -2.484375q-0.234375 0.0625 -0.578125 0.125q-0.34375 0.0625 -0.734375 0.109375q-0.40625 0.078125 -0.84375 0.125q-0.4375 0.03125 -0.859375 0.03125q-0.5625 0 -1.03125 -0.125q-0.46875 -0.140625 -0.78125 -0.453125q-0.328125 -0.296875 -0.515625 -0.8125q-0.171875 -0.515625 -0.171875 -1.28125l0 -6.0625l5.265625 0l0 -2.65625l-5.265625 0l0 -3.453125zm18.449463 13.671875q0 0.3125 -0.171875 0.59375q-0.15625 0.265625 -0.484375 0.484375q-0.328125 0.203125 -0.859375 0.328125q-0.53125 0.125 -1.234375 0.125q-0.578125 0 -1.140625 -0.109375q-0.546875 -0.125 -0.984375 -0.390625q-0.421875 -0.25 -0.703125 -0.65625q-0.265625 -0.40625 -0.3125 -0.984375l-3.4375 0q0 0.859375 0.421875 1.71875q0.421875 0.84375 1.234375 1.515625q0.828125 0.671875 2.046875 1.09375q1.21875 0.40625 2.796875 0.40625q1.453125 0 2.625 -0.3125q1.171875 -0.3125 2.0 -0.890625q0.8125 -0.578125 1.265625 -1.359375q0.453125 -0.78125 0.453125 -1.71875q0 -1.015625 -0.453125 -1.75q-0.453125 -0.75 -1.234375 -1.265625q-0.796875 -0.5 -1.890625 -0.828125q-1.09375 -0.328125 -2.375 -0.53125q-0.890625 -0.140625 -1.46875 -0.3125q-0.5625 -0.171875 -0.890625 -0.390625q-0.328125 -0.21875 -0.46875 -0.484375q-0.125 -0.265625 -0.125 -0.578125q0 -0.3125 0.140625 -0.59375q0.15625 -0.28125 0.453125 -0.484375q0.3125 -0.234375 0.796875 -0.359375q0.5 -0.140625 1.15625 -0.140625q0.78125 0 1.328125 0.203125q0.5625 0.1875 0.890625 0.515625q0.234375 0.25 0.34375 0.5625q0.125 0.296875 0.125 0.65625l3.609375 0q0 -0.984375 -0.4375 -1.8125q-0.421875 -0.828125 -1.21875 -1.453125q-0.828125 -0.59375 -2.0 -0.9375q-1.171875 -0.34375 -2.640625 -0.34375q-1.40625 0 -2.53125 0.359375q-1.109375 0.34375 -1.890625 0.9375q-0.78125 0.609375 -1.203125 1.40625q-0.40625 0.78125 -0.40625 1.65625q0 0.90625 0.40625 1.59375q0.40625 0.6875 1.140625 1.203125q0.734375 0.53125 1.75 0.90625q1.015625 0.375 2.25 0.625q0.984375 0.171875 1.625 0.359375q0.640625 0.1875 1.03125 0.390625q0.375 0.21875 0.515625 0.46875q0.15625 0.25 0.15625 0.578125zm70.51294 -3.28125l0 0.265625q0 1.53125 0.375 2.84375q0.390625 1.3125 1.09375 2.265625q0.703125 0.953125 1.71875 1.5q1.03125 0.53125 2.328125 0.53125q1.171875 0 2.0625 -0.453125q0.90625 -0.46875 1.578125 -1.28125l0.1875 1.46875l3.265625 0l0 -20.0l-3.640625 0l0 7.171875q-0.640625 -0.71875 -1.484375 -1.109375q-0.84375 -0.40625 -1.953125 -0.40625q-1.3125 0 -2.34375 0.53125q-1.015625 0.515625 -1.71875 1.453125q-0.71875 0.953125 -1.09375 2.28125q-0.375 1.328125 -0.375 2.9375zm3.625 0.265625l0 -0.265625q0 -0.890625 0.15625 -1.65625q0.15625 -0.78125 0.5 -1.375q0.328125 -0.5625 0.859375 -0.890625q0.546875 -0.34375 1.3125 -0.34375q0.9375 0 1.546875 0.40625q0.609375 0.390625 0.96875 1.109375l0 5.703125q-0.359375 0.703125 -0.984375 1.109375q-0.609375 0.390625 -1.546875 0.390625q-0.765625 0 -1.3125 -0.3125q-0.53125 -0.328125 -0.859375 -0.90625q-0.34375 -0.5625 -0.5 -1.3125q-0.140625 -0.765625 -0.140625 -1.65625zm21.949585 6.875l3.265625 0l0 -14.078125l-3.625 0l0 9.875q-0.171875 0.34375 -0.40625 0.625q-0.234375 0.265625 -0.53125 0.453125q-0.34375 0.21875 -0.8125 0.34375q-0.46875 0.109375 -1.03125 0.109375q-0.640625 0 -1.09375 -0.15625q-0.4375 -0.15625 -0.703125 -0.515625q-0.28125 -0.359375 -0.40625 -0.9375q-0.109375 -0.59375 -0.109375 -1.453125l0 -8.34375l-3.625 0l0 8.328125q0 1.59375 0.359375 2.734375q0.359375 1.140625 1.0 1.875q0.65625 0.734375 1.5625 1.078125q0.921875 0.328125 2.015625 0.328125q1.1875 0 2.1875 -0.59375q1.015625 -0.59375 1.734375 -1.65625l0.21875 1.984375zm16.918213 -14.34375q-1.421875 0 -2.625 0.6875q-1.1875 0.671875 -2.046875 1.875l-0.03125 -0.3125l-0.140625 -1.984375l-3.375 0l0 14.078125l3.625 0l0 -8.453125q0.234375 -0.609375 0.609375 -1.046875q0.390625 -0.4375 0.921875 -0.734375q0.4375 -0.25 1.0 -0.359375q0.578125 -0.125 1.28125 -0.125q0.671875 0 1.421875 0.078125q0.75 0.078125 1.46875 0.25l0.53125 -3.59375q-0.4375 -0.140625 -1.140625 -0.25q-0.6875 -0.109375 -1.5 -0.109375zm76.497314 -3.1875l-3.609375 0l0 3.453125l-3.21875 0l0 2.65625l3.21875 0l0 6.40625q0 1.390625 0.359375 2.390625q0.359375 1.0 1.046875 1.640625q0.65625 0.640625 1.59375 0.953125q0.953125 0.296875 2.109375 0.296875q0.59375 0 1.21875 -0.0625q0.625 -0.046875 1.1875 -0.15625q0.578125 -0.09375 1.078125 -0.25q0.515625 -0.171875 0.890625 -0.390625l-0.359375 -2.484375q-0.234375 0.0625 -0.578125 0.125q-0.34375 0.0625 -0.734375 0.109375q-0.40625 0.078125 -0.84375 0.125q-0.4375 0.03125 -0.859375 0.03125q-0.5625 0 -1.03125 -0.125q-0.46875 -0.140625 -0.78125 -0.453125q-0.328125 -0.296875 -0.515625 -0.8125q-0.171875 -0.515625 -0.171875 -1.28125l0 -6.0625l5.265625 0l0 -2.65625l-5.265625 0l0 -3.453125zm19.480713 3.1875q-1.421875 0 -2.625 0.6875q-1.1875 0.671875 -2.046875 1.875l-0.03125 -0.3125l-0.140625 -1.984375l-3.375 0l0 14.078125l3.625 0l0 -8.453125q0.234375 -0.609375 0.609375 -1.046875q0.390625 -0.4375 0.921875 -0.734375q0.4375 -0.25 1.0 -0.359375q0.578125 -0.125 1.28125 -0.125q0.671875 0 1.421875 0.078125q0.75 0.078125 1.46875 0.25l0.53125 -3.59375q-0.4375 -0.140625 -1.140625 -0.25q-0.6875 -0.109375 -1.5 -0.109375zm14.68396 14.34375l3.65625 0l0 -0.21875q-0.265625 -0.53125 -0.40625 -1.265625q-0.140625 -0.75 -0.140625 -1.890625l0 -6.03125q0 -1.234375 -0.46875 -2.15625q-0.453125 -0.9375 -1.25 -1.546875q-0.8125 -0.609375 -1.921875 -0.921875q-1.09375 -0.3125 -2.359375 -0.3125q-1.421875 0 -2.53125 0.359375q-1.09375 0.359375 -1.84375 0.96875q-0.765625 0.609375 -1.171875 1.421875q-0.390625 0.796875 -0.390625 1.703125l3.609375 0q0 -0.375 0.109375 -0.6875q0.125 -0.3125 0.359375 -0.53125q0.25 -0.25 0.65625 -0.390625q0.421875 -0.140625 1.0 -0.140625q0.65625 0 1.140625 0.171875q0.484375 0.15625 0.8125 0.453125q0.3125 0.296875 0.46875 0.6875q0.15625 0.390625 0.15625 0.890625l0 0.828125l-2.015625 0q-1.5625 0 -2.765625 0.28125q-1.203125 0.28125 -2.03125 0.8125q-0.90625 0.59375 -1.359375 1.5q-0.453125 0.90625 -0.453125 2.078125q0 0.90625 0.359375 1.6875q0.359375 0.765625 1.015625 1.328125q0.65625 0.5625 1.5625 0.875q0.90625 0.3125 1.984375 0.3125q0.65625 0 1.21875 -0.125q0.578125 -0.125 1.0625 -0.34375q0.46875 -0.21875 0.859375 -0.5q0.390625 -0.28125 0.6875 -0.609375q0.078125 0.375 0.15625 0.71875q0.09375 0.328125 0.234375 0.59375zm-3.453125 -2.53125q-0.515625 0 -0.90625 -0.125q-0.375 -0.125 -0.625 -0.34375q-0.265625 -0.21875 -0.40625 -0.53125q-0.140625 -0.328125 -0.140625 -0.703125q0 -0.484375 0.1875 -0.890625q0.1875 -0.40625 0.5625 -0.703125q0.375 -0.296875 0.96875 -0.453125q0.609375 -0.15625 1.453125 -0.15625l1.84375 0l0 2.421875q-0.15625 0.265625 -0.4375 0.53125q-0.265625 0.265625 -0.640625 0.484375q-0.375 0.203125 -0.84375 0.34375q-0.46875 0.125 -1.015625 0.125zm16.871338 -0.09375q-0.859375 0 -1.4375 -0.34375q-0.5625 -0.359375 -0.890625 -0.953125q-0.328125 -0.5625 -0.46875 -1.328125q-0.125 -0.765625 -0.125 -1.578125l0 -0.390625q0 -0.8125 0.140625 -1.5625q0.140625 -0.765625 0.46875 -1.34375q0.328125 -0.59375 0.890625 -0.9375q0.5625 -0.359375 1.421875 -0.359375q0.578125 0 1.0625 0.203125q0.5 0.203125 0.859375 0.546875q0.34375 0.34375 0.53125 0.8125q0.1875 0.46875 0.15625 0.984375l3.40625 0q0.03125 -1.25 -0.421875 -2.25q-0.4375 -1.015625 -1.21875 -1.734375q-0.8125 -0.703125 -1.921875 -1.09375q-1.109375 -0.390625 -2.40625 -0.390625q-1.625 0 -2.859375 0.5625q-1.234375 0.5625 -2.0625 1.515625q-0.828125 0.96875 -1.25 2.265625q-0.421875 1.296875 -0.421875 2.78125l0 0.390625q0 1.484375 0.421875 2.78125q0.4375 1.28125 1.265625 2.25q0.8125 0.96875 2.046875 1.515625q1.25 0.546875 2.890625 0.546875q1.203125 0 2.28125 -0.375q1.09375 -0.390625 1.921875 -1.0625q0.8125 -0.671875 1.28125 -1.609375q0.484375 -0.9375 0.453125 -2.03125l-3.40625 0q0.03125 0.484375 -0.171875 0.890625q-0.203125 0.40625 -0.5625 0.6875q-0.375 0.296875 -0.859375 0.453125q-0.46875 0.15625 -1.015625 0.15625zm16.605713 2.890625q2.046875 0 3.53125 -0.796875q1.5 -0.796875 2.1875 -1.765625l-1.796875 -1.953125q-0.625 0.796875 -1.640625 1.203125q-1.0 0.40625 -2.0625 0.40625q-0.75 0 -1.390625 -0.234375q-0.640625 -0.234375 -1.140625 -0.671875q-0.46875 -0.40625 -0.765625 -0.921875q-0.28125 -0.515625 -0.46875 -1.359375l0 -0.046875l9.5625 0l0 -1.53125q0 -1.546875 -0.4375 -2.828125q-0.421875 -1.28125 -1.25 -2.203125q-0.828125 -0.90625 -2.03125 -1.40625q-1.1875 -0.5 -2.703125 -0.5q-1.46875 0 -2.71875 0.53125q-1.25 0.53125 -2.15625 1.5q-0.921875 0.96875 -1.4375 2.296875q-0.5 1.328125 -0.5 2.953125l0 0.515625q0 1.421875 0.515625 2.671875q0.515625 1.234375 1.46875 2.140625q0.953125 0.921875 2.28125 1.46875q1.328125 0.53125 2.953125 0.53125zm-0.4375 -11.6875q0.6875 0 1.21875 0.21875q0.53125 0.21875 0.890625 0.578125q0.375 0.375 0.578125 0.890625q0.203125 0.5 0.203125 1.078125l0 0.28125l-5.9375 0q0.140625 -0.6875 0.390625 -1.234375q0.265625 -0.5625 0.65625 -0.96875q0.375 -0.40625 0.875 -0.625q0.515625 -0.21875 1.125 -0.21875z" fill-rule="nonzero"/><path fill="#000000" d="m1249.2811 521.21625l0 -18.953094l-0.203125 0l-7.171875 2.765625l0 2.203125l4.96875 -1.890625l0 15.8749695l2.40625 0zm20.074463 -7.25l0 -4.4374695q0 -1.8125 -0.421875 -3.203125q-0.40625 -1.40625 -1.1875 -2.375q-0.796875 -0.953125 -1.9375 -1.453125q-1.140625 -0.5 -2.59375 -0.5q-1.4375 0 -2.578125 0.5q-1.140625 0.5 -1.921875 1.453125q-0.796875 0.96875 -1.21875 2.375q-0.40625 1.390625 -0.40625 3.203125l0 4.4374695q0 1.8125 0.40625 3.21875q0.421875 1.390625 1.21875 2.34375q0.796875 0.96875 1.9375 1.46875q1.140625 0.484375 2.59375 0.484375q1.453125 0 2.578125 -0.5q1.140625 -0.5 1.921875 -1.453125q0.78125 -0.953125 1.1875 -2.34375q0.421875 -1.40625 0.421875 -3.21875zm-9.84375 -0.484375q0 -0.328125 0 -0.65625q0 -0.328125 0 -0.65625l0 -3.1718445q0 -1.328125 0.265625 -2.328125q0.265625 -1.015625 0.8125 -1.65625q0.453125 -0.515625 1.109375 -0.78125q0.65625 -0.265625 1.515625 -0.265625q0.84375 0 1.484375 0.25q0.65625 0.25 1.09375 0.75q0.453125 0.46875 0.71875 1.203125q0.265625 0.71875 0.375 1.65625l-7.375 5.6562195zm7.421875 1.0q0 1.390625 -0.28125 2.421875q-0.265625 1.015625 -0.875 1.65625q-0.4375 0.46875 -1.078125 0.71875q-0.640625 0.25 -1.453125 0.25q-0.78125 0 -1.40625 -0.21875q-0.625 -0.234375 -1.0625 -0.6875q-0.484375 -0.46875 -0.796875 -1.1875q-0.296875 -0.71875 -0.40625 -1.671875l7.359375 -5.6405945q0 0.375 0 0.921875q0 0.546875 0 0.75l0 2.6874695zm18.418213 -0.515625l0 -4.4374695q0 -1.8125 -0.421875 -3.203125q-0.40625 -1.40625 -1.1875 -2.375q-0.796875 -0.953125 -1.9375 -1.453125q-1.140625 -0.5 -2.59375 -0.5q-1.4375 0 -2.578125 0.5q-1.140625 0.5 -1.921875 1.453125q-0.796875 0.96875 -1.21875 2.375q-0.40625 1.390625 -0.40625 3.203125l0 4.4374695q0 1.8125 0.40625 3.21875q0.421875 1.390625 1.21875 2.34375q0.796875 0.96875 1.9375 1.46875q1.140625 0.484375 2.59375 0.484375q1.453125 0 2.578125 -0.5q1.140625 -0.5 1.921875 -1.453125q0.78125 -0.953125 1.1875 -2.34375q0.421875 -1.40625 0.421875 -3.21875zm-9.84375 -0.484375q0 -0.328125 0 -0.65625q0 -0.328125 0 -0.65625l0 -3.1718445q0 -1.328125 0.265625 -2.328125q0.265625 -1.015625 0.8125 -1.65625q0.453125 -0.515625 1.109375 -0.78125q0.65625 -0.265625 1.515625 -0.265625q0.84375 0 1.484375 0.25q0.65625 0.25 1.09375 0.75q0.453125 0.46875 0.71875 1.203125q0.265625 0.71875 0.375 1.65625l-7.375 5.6562195zm7.421875 1.0q0 1.390625 -0.28125 2.421875q-0.265625 1.015625 -0.875 1.65625q-0.4375 0.46875 -1.078125 0.71875q-0.640625 0.25 -1.453125 0.25q-0.78125 0 -1.40625 -0.21875q-0.625 -0.234375 -1.0625 -0.6875q-0.484375 -0.46875 -0.796875 -1.1875q-0.296875 -0.71875 -0.40625 -1.671875l7.359375 -5.6405945q0 0.375 0 0.921875q0 0.546875 0 0.75l0 2.6874695zm62.3291 6.734375l0 -18.953094l-0.203125 0l-7.171875 2.765625l0 2.203125l4.96875 -1.890625l0 15.8749695l2.40625 0zm10.77771 -2.140625l-0.25 0l0 2.15625l0.25 0q2.34375 0 3.96875 -0.640625q1.640625 -0.65625 2.5625 -1.6875q1.53125 -1.71875 2.0 -3.9375q0.46875 -2.21875 0.46875 -4.4218445l0 -0.875q0 -1.546875 -0.4375 -3.09375q-0.421875 -1.5625 -1.28125 -2.609375q-0.71875 -0.875 -1.796875 -1.421875q-1.078125 -0.546875 -2.53125 -0.546875q-1.453125 0 -2.5625 0.53125q-1.09375 0.53125 -1.828125 1.421875q-0.734375 0.890625 -1.109375 2.09375q-0.375 1.1875 -0.375 2.515625q0 1.15625 0.3125 2.296875q0.3125 1.125 0.984375 2.0155945q0.65625 0.90625 1.671875 1.453125q1.03125 0.546875 2.4375 0.546875q0.71875 0 1.34375 -0.140625q0.625 -0.15625 1.125 -0.40625q0.515625 -0.265625 0.90625 -0.609375q0.40625 -0.34375 0.71875 -0.734375l0 0.015625q-0.0625 1.203125 -0.375 2.328125q-0.3125 1.109375 -1.203125 2.09375q-0.75 0.796875 -1.96875 1.234375q-1.21875 0.421875 -3.03125 0.421875zm2.875 -6.1875q-0.875 0 -1.515625 -0.375q-0.640625 -0.390625 -1.0625 -1.0155945q-0.4375 -0.625 -0.640625 -1.421875q-0.203125 -0.8125 -0.203125 -1.640625q0 -0.9375 0.234375 -1.75q0.234375 -0.8125 0.6875 -1.40625q0.4375 -0.59375 1.078125 -0.921875q0.65625 -0.34375 1.515625 -0.34375q0.734375 0 1.390625 0.359375q0.65625 0.359375 1.171875 1.046875q0.484375 0.6875 0.78125 1.671875q0.296875 0.984375 0.296875 2.25l0 0.78125q-0.21875 0.609375 -0.609375 1.125q-0.375 0.5 -0.859375 0.8593445q-0.5 0.375 -1.078125 0.578125q-0.578125 0.203125 -1.1875 0.203125zm103.29053 -8.5624695l0 -2.0625l-14.0625 0l0 2.0625l5.859375 0l0 16.890594l2.34375 0l0 -16.890594l5.859375 0zm5.808838 14.7499695l-0.25 0l0 2.15625l0.25 0q2.34375 0 3.96875 -0.640625q1.640625 -0.65625 2.5625 -1.6875q1.53125 -1.71875 2.0 -3.9375q0.46875 -2.21875 0.46875 -4.4218445l0 -0.875q0 -1.546875 -0.4375 -3.09375q-0.421875 -1.5625 -1.28125 -2.609375q-0.71875 -0.875 -1.796875 -1.421875q-1.078125 -0.546875 -2.53125 -0.546875q-1.453125 0 -2.5625 0.53125q-1.09375 0.53125 -1.828125 1.421875q-0.734375 0.890625 -1.109375 2.09375q-0.375 1.1875 -0.375 2.515625q0 1.15625 0.3125 2.296875q0.3125 1.125 0.984375 2.0155945q0.65625 0.90625 1.671875 1.453125q1.03125 0.546875 2.4375 0.546875q0.71875 0 1.34375 -0.140625q0.625 -0.15625 1.125 -0.40625q0.515625 -0.265625 0.90625 -0.609375q0.40625 -0.34375 0.71875 -0.734375l0 0.015625q-0.0625 1.203125 -0.375 2.328125q-0.3125 1.109375 -1.203125 2.09375q-0.75 0.796875 -1.96875 1.234375q-1.21875 0.421875 -3.03125 0.421875zm2.875 -6.1875q-0.875 0 -1.515625 -0.375q-0.640625 -0.390625 -1.0625 -1.0155945q-0.4375 -0.625 -0.640625 -1.421875q-0.203125 -0.8125 -0.203125 -1.640625q0 -0.9375 0.234375 -1.75q0.234375 -0.8125 0.6875 -1.40625q0.4375 -0.59375 1.078125 -0.921875q0.65625 -0.34375 1.515625 -0.34375q0.734375 0 1.390625 0.359375q0.65625 0.359375 1.171875 1.046875q0.484375 0.6875 0.78125 1.671875q0.296875 0.984375 0.296875 2.25l0 0.78125q-0.21875 0.609375 -0.609375 1.125q-0.375 0.5 -0.859375 0.8593445q-0.5 0.375 -1.078125 0.578125q-0.578125 0.203125 -1.1875 0.203125zm13.12146 6.1875l-0.25 0l0 2.15625l0.25 0q2.34375 0 3.96875 -0.640625q1.640625 -0.65625 2.5625 -1.6875q1.53125 -1.71875 2.0 -3.9375q0.46875 -2.21875 0.46875 -4.4218445l0 -0.875q0 -1.546875 -0.4375 -3.09375q-0.421875 -1.5625 -1.28125 -2.609375q-0.71875 -0.875 -1.796875 -1.421875q-1.078125 -0.546875 -2.53125 -0.546875q-1.453125 0 -2.5625 0.53125q-1.09375 0.53125 -1.828125 1.421875q-0.734375 0.890625 -1.109375 2.09375q-0.375 1.1875 -0.375 2.515625q0 1.15625 0.3125 2.296875q0.3125 1.125 0.984375 2.0155945q0.65625 0.90625 1.671875 1.453125q1.03125 0.546875 2.4375 0.546875q0.71875 0 1.34375 -0.140625q0.625 -0.15625 1.125 -0.40625q0.515625 -0.265625 0.90625 -0.609375q0.40625 -0.34375 0.71875 -0.734375l0 0.015625q-0.0625 1.203125 -0.375 2.328125q-0.3125 1.109375 -1.203125 2.09375q-0.75 0.796875 -1.96875 1.234375q-1.21875 0.421875 -3.03125 0.421875zm2.875 -6.1875q-0.875 0 -1.515625 -0.375q-0.640625 -0.390625 -1.0625 -1.0155945q-0.4375 -0.625 -0.640625 -1.421875q-0.203125 -0.8125 -0.203125 -1.640625q0 -0.9375 0.234375 -1.75q0.234375 -0.8125 0.6875 -1.40625q0.4375 -0.59375 1.078125 -0.921875q0.65625 -0.34375 1.515625 -0.34375q0.734375 0 1.390625 0.359375q0.65625 0.359375 1.171875 1.046875q0.484375 0.6875 0.78125 1.671875q0.296875 0.984375 0.296875 2.25l0 0.78125q-0.21875 0.609375 -0.609375 1.125q-0.375 0.5 -0.859375 0.8593445q-0.5 0.375 -1.078125 0.578125q-0.578125 0.203125 -1.1875 0.203125zm22.418213 1.078125l0 -4.4374695q0 -1.8125 -0.421875 -3.203125q-0.40625 -1.40625 -1.1875 -2.375q-0.796875 -0.953125 -1.9375 -1.453125q-1.140625 -0.5 -2.59375 -0.5q-1.4375 0 -2.578125 0.5q-1.140625 0.5 -1.921875 1.453125q-0.796875 0.96875 -1.21875 2.375q-0.40625 1.390625 -0.40625 3.203125l0 4.4374695q0 1.8125 0.40625 3.21875q0.421875 1.390625 1.21875 2.34375q0.796875 0.96875 1.9375 1.46875q1.140625 0.484375 2.59375 0.484375q1.453125 0 2.578125 -0.5q1.140625 -0.5 1.921875 -1.453125q0.78125 -0.953125 1.1875 -2.34375q0.421875 -1.40625 0.421875 -3.21875zm-9.84375 -0.484375q0 -0.328125 0 -0.65625q0 -0.328125 0 -0.65625l0 -3.1718445q0 -1.328125 0.265625 -2.328125q0.265625 -1.015625 0.8125 -1.65625q0.453125 -0.515625 1.109375 -0.78125q0.65625 -0.265625 1.515625 -0.265625q0.84375 0 1.484375 0.25q0.65625 0.25 1.09375 0.75q0.453125 0.46875 0.71875 1.203125q0.265625 0.71875 0.375 1.65625l-7.375 5.6562195zm7.421875 1.0q0 1.390625 -0.28125 2.421875q-0.265625 1.015625 -0.875 1.65625q-0.4375 0.46875 -1.078125 0.71875q-0.640625 0.25 -1.453125 0.25q-0.78125 0 -1.40625 -0.21875q-0.625 -0.234375 -1.0625 -0.6875q-0.484375 -0.46875 -0.796875 -1.1875q-0.296875 -0.71875 -0.40625 -1.671875l7.359375 -5.6405945q0 0.375 0 0.921875q0 0.546875 0 0.75l0 2.6874695z" fill-rule="nonzero"/><path fill="#000000" d="m1249.2811 553.21625l0 -18.953125l-0.203125 0l-7.171875 2.765625l0 2.203125l4.96875 -1.890625l0 15.875l2.40625 0zm19.808838 0l0 -1.96875l-9.515625 0l5.09375 -5.515625q0.703125 -0.78125 1.34375 -1.5625q0.65625 -0.796875 1.15625 -1.59375q0.46875 -0.8125 0.75 -1.640625q0.296875 -0.828125 0.296875 -1.671875q0 -1.125 -0.40625 -2.09375q-0.390625 -0.96875 -1.125 -1.671875q-0.75 -0.703125 -1.828125 -1.09375q-1.0625 -0.40625 -2.40625 -0.40625q-1.453125 0 -2.59375 0.46875q-1.140625 0.46875 -1.921875 1.265625q-0.796875 0.796875 -1.21875 1.859375q-0.40625 1.0625 -0.40625 2.265625l2.421875 0q0 -0.890625 0.21875 -1.609375q0.234375 -0.71875 0.703125 -1.21875q0.453125 -0.5 1.140625 -0.765625q0.703125 -0.28125 1.65625 -0.28125q0.78125 0 1.390625 0.265625q0.625 0.265625 1.0625 0.71875q0.4375 0.46875 0.65625 1.09375q0.234375 0.609375 0.234375 1.3125q0 0.5625 -0.140625 1.109375q-0.140625 0.53125 -0.484375 1.140625q-0.34375 0.609375 -0.90625 1.328125q-0.546875 0.71875 -1.390625 1.640625l-6.203125 6.890625l0 1.734375l12.421875 0zm16.261963 -7.25l0 -4.4375q0 -1.8125 -0.421875 -3.203125q-0.40625 -1.40625 -1.1875 -2.375q-0.796875 -0.953125 -1.9375 -1.453125q-1.140625 -0.5 -2.59375 -0.5q-1.4375 0 -2.578125 0.5q-1.140625 0.5 -1.921875 1.453125q-0.796875 0.96875 -1.21875 2.375q-0.40625 1.390625 -0.40625 3.203125l0 4.4375q0 1.8125 0.40625 3.21875q0.421875 1.390625 1.21875 2.34375q0.796875 0.96875 1.9375 1.46875q1.140625 0.484375 2.59375 0.484375q1.453125 0 2.578125 -0.5q1.140625 -0.5 1.921875 -1.453125q0.78125 -0.953125 1.1875 -2.34375q0.421875 -1.40625 0.421875 -3.21875zm-9.84375 -0.484375q0 -0.328125 0 -0.65625q0 -0.328125 0 -0.65625l0 -3.171875q0 -1.328125 0.265625 -2.328125q0.265625 -1.015625 0.8125 -1.65625q0.453125 -0.515625 1.109375 -0.78125q0.65625 -0.265625 1.515625 -0.265625q0.84375 0 1.484375 0.25q0.65625 0.25 1.09375 0.75q0.453125 0.46875 0.71875 1.203125q0.265625 0.71875 0.375 1.65625l-7.375 5.65625zm7.421875 1.0q0 1.390625 -0.28125 2.421875q-0.265625 1.015625 -0.875 1.65625q-0.4375 0.46875 -1.078125 0.71875q-0.640625 0.25 -1.453125 0.25q-0.78125 0 -1.40625 -0.21875q-0.625 -0.234375 -1.0625 -0.6875q-0.484375 -0.46875 -0.796875 -1.1875q-0.296875 -0.71875 -0.40625 -1.671875l7.359375 -5.640625q0 0.375 0 0.921875q0 0.546875 0 0.75l0 2.6875zm66.1416 6.734375l0 -1.96875l-9.515625 0l5.09375 -5.515625q0.703125 -0.78125 1.34375 -1.5625q0.65625 -0.796875 1.15625 -1.59375q0.46875 -0.8125 0.75 -1.640625q0.296875 -0.828125 0.296875 -1.671875q0 -1.125 -0.40625 -2.09375q-0.390625 -0.96875 -1.125 -1.671875q-0.75 -0.703125 -1.828125 -1.09375q-1.0625 -0.40625 -2.40625 -0.40625q-1.453125 0 -2.59375 0.46875q-1.140625 0.46875 -1.921875 1.265625q-0.796875 0.796875 -1.21875 1.859375q-0.40625 1.0625 -0.40625 2.265625l2.421875 0q0 -0.890625 0.21875 -1.609375q0.234375 -0.71875 0.703125 -1.21875q0.453125 -0.5 1.140625 -0.765625q0.703125 -0.28125 1.65625 -0.28125q0.78125 0 1.390625 0.265625q0.625 0.265625 1.0625 0.71875q0.4375 0.46875 0.65625 1.09375q0.234375 0.609375 0.234375 1.3125q0 0.5625 -0.140625 1.109375q-0.140625 0.53125 -0.484375 1.140625q-0.34375 0.609375 -0.90625 1.328125q-0.546875 0.71875 -1.390625 1.640625l-6.203125 6.890625l0 1.734375l12.421875 0zm7.18396 -10.640625l0 1.953125l1.71875 0q0.890625 0 1.640625 0.21875q0.75 0.203125 1.28125 0.640625q0.53125 0.421875 0.8125 1.0625q0.296875 0.640625 0.296875 1.53125q0 0.90625 -0.25 1.5625q-0.25 0.65625 -0.71875 1.09375q-0.46875 0.4375 -1.140625 0.65625q-0.65625 0.21875 -1.484375 0.21875q-0.8125 0 -1.484375 -0.234375q-0.671875 -0.234375 -1.140625 -0.671875q-0.46875 -0.421875 -0.734375 -1.03125q-0.25 -0.609375 -0.25 -1.359375l-2.40625 0q0 1.25 0.46875 2.234375q0.484375 0.96875 1.296875 1.640625q0.796875 0.6875 1.890625 1.046875q1.109375 0.34375 2.359375 0.34375q1.28125 0 2.375 -0.375q1.109375 -0.375 1.90625 -1.09375q0.8125 -0.703125 1.265625 -1.71875q0.453125 -1.03125 0.453125 -2.359375q0 -0.65625 -0.15625 -1.3125q-0.140625 -0.671875 -0.5 -1.265625q-0.375 -0.59375 -1.0 -1.078125q-0.625 -0.484375 -1.546875 -0.78125q0.765625 -0.328125 1.3125 -0.8125q0.5625 -0.5 0.921875 -1.046875q0.34375 -0.546875 0.515625 -1.125q0.171875 -0.59375 0.171875 -1.140625q0 -1.3125 -0.421875 -2.3125q-0.40625 -1.015625 -1.171875 -1.6875q-0.75 -0.671875 -1.8125 -1.015625q-1.046875 -0.359375 -2.328125 -0.359375q-1.3125 0 -2.390625 0.421875q-1.0625 0.40625 -1.828125 1.109375q-0.78125 0.71875 -1.203125 1.671875q-0.421875 0.953125 -0.421875 2.03125l2.421875 0q0 -0.734375 0.25 -1.328125q0.25 -0.609375 0.703125 -1.03125q0.4375 -0.4375 1.0625 -0.65625q0.640625 -0.234375 1.40625 -0.234375q0.796875 0 1.40625 0.203125q0.625 0.203125 1.046875 0.609375q0.421875 0.40625 0.640625 1.046875q0.234375 0.625 0.234375 1.46875q0 0.71875 -0.25 1.328125q-0.25 0.59375 -0.71875 1.015625q-0.484375 0.4375 -1.1875 0.6875q-0.6875 0.234375 -1.59375 0.234375l-1.71875 0zm105.94678 -6.25l0 -2.0625l-14.0625 0l0 2.0625l5.859375 0l0 16.890625l2.34375 0l0 -16.890625l5.859375 0zm6.027588 6.25l0 1.953125l1.71875 0q0.890625 0 1.640625 0.21875q0.75 0.203125 1.28125 0.640625q0.53125 0.421875 0.8125 1.0625q0.296875 0.640625 0.296875 1.53125q0 0.90625 -0.25 1.5625q-0.25 0.65625 -0.71875 1.09375q-0.46875 0.4375 -1.140625 0.65625q-0.65625 0.21875 -1.484375 0.21875q-0.8125 0 -1.484375 -0.234375q-0.671875 -0.234375 -1.140625 -0.671875q-0.46875 -0.421875 -0.734375 -1.03125q-0.25 -0.609375 -0.25 -1.359375l-2.40625 0q0 1.25 0.46875 2.234375q0.484375 0.96875 1.296875 1.640625q0.796875 0.6875 1.890625 1.046875q1.109375 0.34375 2.359375 0.34375q1.28125 0 2.375 -0.375q1.109375 -0.375 1.90625 -1.09375q0.8125 -0.703125 1.265625 -1.71875q0.453125 -1.03125 0.453125 -2.359375q0 -0.65625 -0.15625 -1.3125q-0.140625 -0.671875 -0.5 -1.265625q-0.375 -0.59375 -1.0 -1.078125q-0.625 -0.484375 -1.546875 -0.78125q0.765625 -0.328125 1.3125 -0.8125q0.5625 -0.5 0.921875 -1.046875q0.34375 -0.546875 0.515625 -1.125q0.171875 -0.59375 0.171875 -1.140625q0 -1.3125 -0.421875 -2.3125q-0.40625 -1.015625 -1.171875 -1.6875q-0.75 -0.671875 -1.8125 -1.015625q-1.046875 -0.359375 -2.328125 -0.359375q-1.3125 0 -2.390625 0.421875q-1.0625 0.40625 -1.828125 1.109375q-0.78125 0.71875 -1.203125 1.671875q-0.421875 0.953125 -0.421875 2.03125l2.421875 0q0 -0.734375 0.25 -1.328125q0.25 -0.609375 0.703125 -1.03125q0.4375 -0.4375 1.0625 -0.65625q0.640625 -0.234375 1.40625 -0.234375q0.796875 0 1.40625 0.203125q0.625 0.203125 1.046875 0.609375q0.421875 0.40625 0.640625 1.046875q0.234375 0.625 0.234375 1.46875q0 0.71875 -0.25 1.328125q-0.25 0.59375 -0.71875 1.015625q-0.484375 0.4375 -1.1875 0.6875q-0.6875 0.234375 -1.59375 0.234375l-1.71875 0zm22.949585 4.28125l0 -12.59375l-2.5625 0l-8.5 13.140625l0 1.40625l8.65625 0l0 4.40625l2.40625 0l0 -4.40625l2.640625 0l0 -1.953125l-2.640625 0zm-8.390625 0l5.59375 -8.65625l0.390625 -0.734375l0 9.390625l-5.984375 0zm17.215088 4.21875l-0.25 0l0 2.15625l0.25 0q2.34375 0 3.96875 -0.640625q1.640625 -0.65625 2.5625 -1.6875q1.53125 -1.71875 2.0 -3.9375q0.46875 -2.21875 0.46875 -4.421875l0 -0.875q0 -1.546875 -0.4375 -3.09375q-0.421875 -1.5625 -1.28125 -2.609375q-0.71875 -0.875 -1.796875 -1.421875q-1.078125 -0.546875 -2.53125 -0.546875q-1.453125 0 -2.5625 0.53125q-1.09375 0.53125 -1.828125 1.421875q-0.734375 0.890625 -1.109375 2.09375q-0.375 1.1875 -0.375 2.515625q0 1.15625 0.3125 2.296875q0.3125 1.125 0.984375 2.015625q0.65625 0.90625 1.671875 1.453125q1.03125 0.546875 2.4375 0.546875q0.71875 0 1.34375 -0.140625q0.625 -0.15625 1.125 -0.40625q0.515625 -0.265625 0.90625 -0.609375q0.40625 -0.34375 0.71875 -0.734375l0 0.015625q-0.0625 1.203125 -0.375 2.328125q-0.3125 1.109375 -1.203125 2.09375q-0.75 0.796875 -1.96875 1.234375q-1.21875 0.421875 -3.03125 0.421875zm2.875 -6.1875q-0.875 0 -1.515625 -0.375q-0.640625 -0.390625 -1.0625 -1.015625q-0.4375 -0.625 -0.640625 -1.421875q-0.203125 -0.8125 -0.203125 -1.640625q0 -0.9375 0.234375 -1.75q0.234375 -0.8125 0.6875 -1.40625q0.4375 -0.59375 1.078125 -0.921875q0.65625 -0.34375 1.515625 -0.34375q0.734375 0 1.390625 0.359375q0.65625 0.359375 1.171875 1.046875q0.484375 0.6875 0.78125 1.671875q0.296875 0.984375 0.296875 2.25l0 0.78125q-0.21875 0.609375 -0.609375 1.125q-0.375 0.5 -0.859375 0.859375q-0.5 0.375 -1.078125 0.578125q-0.578125 0.203125 -1.1875 0.203125z" fill-rule="nonzero"/><path fill="#000000" d="m1244.2811 574.5756l0 1.953125l1.71875 0q0.890625 0 1.640625 0.21875q0.75 0.203125 1.28125 0.640625q0.53125 0.421875 0.8125 1.0625q0.296875 0.640625 0.296875 1.53125q0 0.90625 -0.25 1.5625q-0.25 0.65625 -0.71875 1.09375q-0.46875 0.4375 -1.140625 0.65625q-0.65625 0.21875 -1.484375 0.21875q-0.8125 0 -1.484375 -0.234375q-0.671875 -0.234375 -1.140625 -0.671875q-0.46875 -0.421875 -0.734375 -1.03125q-0.25 -0.609375 -0.25 -1.359375l-2.40625 0q0 1.25 0.46875 2.234375q0.484375 0.96875 1.296875 1.640625q0.796875 0.6875 1.890625 1.046875q1.109375 0.34375 2.359375 0.34375q1.28125 0 2.375 -0.375q1.109375 -0.375 1.90625 -1.09375q0.8125 -0.703125 1.265625 -1.71875q0.453125 -1.03125 0.453125 -2.359375q0 -0.65625 -0.15625 -1.3125q-0.140625 -0.671875 -0.5 -1.265625q-0.375 -0.59375 -1.0 -1.078125q-0.625 -0.484375 -1.546875 -0.78125q0.765625 -0.328125 1.3125 -0.8125q0.5625 -0.5 0.921875 -1.046875q0.34375 -0.546875 0.515625 -1.125q0.171875 -0.59375 0.171875 -1.140625q0 -1.3125 -0.421875 -2.3125q-0.40625 -1.015625 -1.171875 -1.6875q-0.75 -0.671875 -1.8125 -1.015625q-1.046875 -0.359375 -2.328125 -0.359375q-1.3125 0 -2.390625 0.421875q-1.0625 0.40625 -1.828125 1.109375q-0.78125 0.71875 -1.203125 1.671875q-0.421875 0.953125 -0.421875 2.03125l2.421875 0q0 -0.734375 0.25 -1.328125q0.25 -0.609375 0.703125 -1.03125q0.4375 -0.4375 1.0625 -0.65625q0.640625 -0.234375 1.40625 -0.234375q0.796875 0 1.40625 0.203125q0.625 0.203125 1.046875 0.609375q0.421875 0.40625 0.640625 1.046875q0.234375 0.625 0.234375 1.46875q0 0.71875 -0.25 1.328125q-0.25 0.59375 -0.71875 1.015625q-0.484375 0.4375 -1.1875 0.6875q-0.6875 0.234375 -1.59375 0.234375l-1.71875 0zm25.074463 3.390625l0 -4.4375q0 -1.8125 -0.421875 -3.203125q-0.40625 -1.40625 -1.1875 -2.375q-0.796875 -0.953125 -1.9375 -1.453125q-1.140625 -0.5 -2.59375 -0.5q-1.4375 0 -2.578125 0.5q-1.140625 0.5 -1.921875 1.453125q-0.796875 0.96875 -1.21875 2.375q-0.40625 1.390625 -0.40625 3.203125l0 4.4375q0 1.8125 0.40625 3.21875q0.421875 1.390625 1.21875 2.34375q0.796875 0.96875 1.9375 1.46875q1.140625 0.484375 2.59375 0.484375q1.453125 0 2.578125 -0.5q1.140625 -0.5 1.921875 -1.453125q0.78125 -0.953125 1.1875 -2.34375q0.421875 -1.40625 0.421875 -3.21875zm-9.84375 -0.484375q0 -0.328125 0 -0.65625q0 -0.328125 0 -0.65625l0 -3.171875q0 -1.328125 0.265625 -2.328125q0.265625 -1.015625 0.8125 -1.65625q0.453125 -0.515625 1.109375 -0.78125q0.65625 -0.265625 1.515625 -0.265625q0.84375 0 1.484375 0.25q0.65625 0.25 1.09375 0.75q0.453125 0.46875 0.71875 1.203125q0.265625 0.71875 0.375 1.65625l-7.375 5.65625zm7.421875 1.0q0 1.390625 -0.28125 2.421875q-0.265625 1.015625 -0.875 1.65625q-0.4375 0.46875 -1.078125 0.71875q-0.640625 0.25 -1.453125 0.25q-0.78125 0 -1.40625 -0.21875q-0.625 -0.234375 -1.0625 -0.6875q-0.484375 -0.46875 -0.796875 -1.1875q-0.296875 -0.71875 -0.40625 -1.671875l7.359375 -5.640625q0 0.375 0 0.921875q0 0.546875 0 0.75l0 2.6875zm18.418213 -0.515625l0 -4.4375q0 -1.8125 -0.421875 -3.203125q-0.40625 -1.40625 -1.1875 -2.375q-0.796875 -0.953125 -1.9375 -1.453125q-1.140625 -0.5 -2.59375 -0.5q-1.4375 0 -2.578125 0.5q-1.140625 0.5 -1.921875 1.453125q-0.796875 0.96875 -1.21875 2.375q-0.40625 1.390625 -0.40625 3.203125l0 4.4375q0 1.8125 0.40625 3.21875q0.421875 1.390625 1.21875 2.34375q0.796875 0.96875 1.9375 1.46875q1.140625 0.484375 2.59375 0.484375q1.453125 0 2.578125 -0.5q1.140625 -0.5 1.921875 -1.453125q0.78125 -0.953125 1.1875 -2.34375q0.421875 -1.40625 0.421875 -3.21875zm-9.84375 -0.484375q0 -0.328125 0 -0.65625q0 -0.328125 0 -0.65625l0 -3.171875q0 -1.328125 0.265625 -2.328125q0.265625 -1.015625 0.8125 -1.65625q0.453125 -0.515625 1.109375 -0.78125q0.65625 -0.265625 1.515625 -0.265625q0.84375 0 1.484375 0.25q0.65625 0.25 1.09375 0.75q0.453125 0.46875 0.71875 1.203125q0.265625 0.71875 0.375 1.65625l-7.375 5.65625zm7.421875 1.0q0 1.390625 -0.28125 2.421875q-0.265625 1.015625 -0.875 1.65625q-0.4375 0.46875 -1.078125 0.71875q-0.640625 0.25 -1.453125 0.25q-0.78125 0 -1.40625 -0.21875q-0.625 -0.234375 -1.0625 -0.6875q-0.484375 -0.46875 -0.796875 -1.1875q-0.296875 -0.71875 -0.40625 -1.671875l7.359375 -5.640625q0 0.375 0 0.921875q0 0.546875 0 0.75l0 2.6875zm62.3291 6.734375l0 -18.953125l-0.203125 0l-7.171875 2.765625l0 2.203125l4.96875 -1.890625l0 15.875l2.40625 0zm10.99646 -10.640625l0 1.953125l1.71875 0q0.890625 0 1.640625 0.21875q0.75 0.203125 1.28125 0.640625q0.53125 0.421875 0.8125 1.0625q0.296875 0.640625 0.296875 1.53125q0 0.90625 -0.25 1.5625q-0.25 0.65625 -0.71875 1.09375q-0.46875 0.4375 -1.140625 0.65625q-0.65625 0.21875 -1.484375 0.21875q-0.8125 0 -1.484375 -0.234375q-0.671875 -0.234375 -1.140625 -0.671875q-0.46875 -0.421875 -0.734375 -1.03125q-0.25 -0.609375 -0.25 -1.359375l-2.40625 0q0 1.25 0.46875 2.234375q0.484375 0.96875 1.296875 1.640625q0.796875 0.6875 1.890625 1.046875q1.109375 0.34375 2.359375 0.34375q1.28125 0 2.375 -0.375q1.109375 -0.375 1.90625 -1.09375q0.8125 -0.703125 1.265625 -1.71875q0.453125 -1.03125 0.453125 -2.359375q0 -0.65625 -0.15625 -1.3125q-0.140625 -0.671875 -0.5 -1.265625q-0.375 -0.59375 -1.0 -1.078125q-0.625 -0.484375 -1.546875 -0.78125q0.765625 -0.328125 1.3125 -0.8125q0.5625 -0.5 0.921875 -1.046875q0.34375 -0.546875 0.515625 -1.125q0.171875 -0.59375 0.171875 -1.140625q0 -1.3125 -0.421875 -2.3125q-0.40625 -1.015625 -1.171875 -1.6875q-0.75 -0.671875 -1.8125 -1.015625q-1.046875 -0.359375 -2.328125 -0.359375q-1.3125 0 -2.390625 0.421875q-1.0625 0.40625 -1.828125 1.109375q-0.78125 0.71875 -1.203125 1.671875q-0.421875 0.953125 -0.421875 2.03125l2.421875 0q0 -0.734375 0.25 -1.328125q0.25 -0.609375 0.703125 -1.03125q0.4375 -0.4375 1.0625 -0.65625q0.640625 -0.234375 1.40625 -0.234375q0.796875 0 1.40625 0.203125q0.625 0.203125 1.046875 0.609375q0.421875 0.40625 0.640625 1.046875q0.234375 0.625 0.234375 1.46875q0 0.71875 -0.25 1.328125q-0.25 0.59375 -0.71875 1.015625q-0.484375 0.4375 -1.1875 0.6875q-0.6875 0.234375 -1.59375 0.234375l-1.71875 0zm105.94678 -6.25l0 -2.0625l-14.0625 0l0 2.0625l5.859375 0l0 16.890625l2.34375 0l0 -16.890625l5.859375 0zm15.215088 -0.703125l0 -1.359375l-12.8125 0l0 2.109375l10.265625 0l-7.828125 16.84375l2.53125 0l7.84375 -17.59375zm13.762085 11.234375l0 -12.59375l-2.5625 0l-8.5 13.140625l0 1.40625l8.65625 0l0 4.40625l2.40625 0l0 -4.40625l2.640625 0l0 -1.953125l-2.640625 0zm-8.390625 0l5.59375 -8.65625l0.390625 -0.734375l0 9.390625l-5.984375 0z" fill-rule="nonzero"/><path fill="#000000" d="m1251.2343 610.8569l0 -12.59375l-2.5625 0l-8.5 13.140625l0 1.40625l8.65625 0l0 4.40625l2.40625 0l0 -4.40625l2.640625 0l0 -1.953125l-2.640625 0zm-8.390625 0l5.59375 -8.65625l0.390625 -0.734375l0 9.390625l-5.984375 0zm15.480713 -3.140625l1.921875 0.5q0.328125 -0.296875 0.625 -0.515625q0.3125 -0.21875 0.671875 -0.375q0.34375 -0.15625 0.78125 -0.234375q0.4375 -0.09375 1.03125 -0.09375q0.921875 0 1.625 0.328125q0.703125 0.328125 1.171875 0.890625q0.484375 0.5625 0.734375 1.34375q0.265625 0.765625 0.265625 1.640625q0 0.984375 -0.234375 1.78125q-0.21875 0.78125 -0.671875 1.34375q-0.453125 0.578125 -1.125 0.890625q-0.65625 0.296875 -1.515625 0.296875q-1.515625 0 -2.484375 -0.828125q-0.96875 -0.84375 -1.203125 -2.453125l-2.28125 0q0.125 1.3125 0.640625 2.3125q0.515625 0.984375 1.328125 1.625q0.78125 0.65625 1.8125 0.984375q1.03125 0.328125 2.1875 0.328125q1.515625 0 2.625 -0.46875q1.125 -0.484375 1.875 -1.328125q0.71875 -0.84375 1.078125 -2.0q0.375 -1.15625 0.375 -2.515625q0 -1.421875 -0.390625 -2.59375q-0.390625 -1.171875 -1.125 -2.0q-0.71875 -0.828125 -1.765625 -1.28125q-1.046875 -0.453125 -2.359375 -0.453125q-1.03125 0 -1.8125 0.25q-0.78125 0.234375 -1.328125 0.5625l0.53125 -5.046875l7.6875 0l0 -2.34375l-9.71875 0l-0.953125 9.453125zm27.027588 2.25l0 -4.4375q0 -1.8125 -0.421875 -3.203125q-0.40625 -1.40625 -1.1875 -2.375q-0.796875 -0.953125 -1.9375 -1.453125q-1.140625 -0.5 -2.59375 -0.5q-1.4375 0 -2.578125 0.5q-1.140625 0.5 -1.921875 1.453125q-0.796875 0.96875 -1.21875 2.375q-0.40625 1.390625 -0.40625 3.203125l0 4.4375q0 1.8125 0.40625 3.21875q0.421875 1.390625 1.21875 2.34375q0.796875 0.96875 1.9375 1.46875q1.140625 0.484375 2.59375 0.484375q1.453125 0 2.578125 -0.5q1.140625 -0.5 1.921875 -1.453125q0.78125 -0.953125 1.1875 -2.34375q0.421875 -1.40625 0.421875 -3.21875zm-9.84375 -0.484375q0 -0.328125 0 -0.65625q0 -0.328125 0 -0.65625l0 -3.171875q0 -1.328125 0.265625 -2.328125q0.265625 -1.015625 0.8125 -1.65625q0.453125 -0.515625 1.109375 -0.78125q0.65625 -0.265625 1.515625 -0.265625q0.84375 0 1.484375 0.25q0.65625 0.25 1.09375 0.75q0.453125 0.46875 0.71875 1.203125q0.265625 0.71875 0.375 1.65625l-7.375 5.65625zm7.421875 1.0q0 1.390625 -0.28125 2.421875q-0.265625 1.015625 -0.875 1.65625q-0.4375 0.46875 -1.078125 0.71875q-0.640625 0.25 -1.453125 0.25q-0.78125 0 -1.40625 -0.21875q-0.625 -0.234375 -1.0625 -0.6875q-0.484375 -0.46875 -0.796875 -1.1875q-0.296875 -0.71875 -0.40625 -1.671875l7.359375 -5.640625q0 0.375 0 0.921875q0 0.546875 0 0.75l0 2.6875zm57.3291 -3.90625l0 1.953125l1.71875 0q0.890625 0 1.640625 0.21875q0.75 0.203125 1.28125 0.640625q0.53125 0.421875 0.8125 1.0625q0.296875 0.640625 0.296875 1.53125q0 0.90625 -0.25 1.5625q-0.25 0.65625 -0.71875 1.09375q-0.46875 0.4375 -1.140625 0.65625q-0.65625 0.21875 -1.484375 0.21875q-0.8125 0 -1.484375 -0.234375q-0.671875 -0.234375 -1.140625 -0.671875q-0.46875 -0.421875 -0.734375 -1.03125q-0.25 -0.609375 -0.25 -1.359375l-2.40625 0q0 1.25 0.46875 2.234375q0.484375 0.96875 1.296875 1.640625q0.796875 0.6875 1.890625 1.046875q1.109375 0.34375 2.359375 0.34375q1.28125 0 2.375 -0.375q1.109375 -0.375 1.90625 -1.09375q0.8125 -0.703125 1.265625 -1.71875q0.453125 -1.03125 0.453125 -2.359375q0 -0.65625 -0.15625 -1.3125q-0.140625 -0.671875 -0.5 -1.265625q-0.375 -0.59375 -1.0 -1.078125q-0.625 -0.484375 -1.546875 -0.78125q0.765625 -0.328125 1.3125 -0.8125q0.5625 -0.5 0.921875 -1.046875q0.34375 -0.546875 0.515625 -1.125q0.171875 -0.59375 0.171875 -1.140625q0 -1.3125 -0.421875 -2.3125q-0.40625 -1.015625 -1.171875 -1.6875q-0.75 -0.671875 -1.8125 -1.015625q-1.046875 -0.359375 -2.328125 -0.359375q-1.3125 0 -2.390625 0.421875q-1.0625 0.40625 -1.828125 1.109375q-0.78125 0.71875 -1.203125 1.671875q-0.421875 0.953125 -0.421875 2.03125l2.421875 0q0 -0.734375 0.25 -1.328125q0.25 -0.609375 0.703125 -1.03125q0.4375 -0.4375 1.0625 -0.65625q0.640625 -0.234375 1.40625 -0.234375q0.796875 0 1.40625 0.203125q0.625 0.203125 1.046875 0.609375q0.421875 0.40625 0.640625 1.046875q0.234375 0.625 0.234375 1.46875q0 0.71875 -0.25 1.328125q-0.25 0.59375 -0.71875 1.015625q-0.484375 0.4375 -1.1875 0.6875q-0.6875 0.234375 -1.59375 0.234375l-1.71875 0zm25.074585 3.390625l0 -4.4375q0 -1.8125 -0.421875 -3.203125q-0.40625 -1.40625 -1.1875 -2.375q-0.796875 -0.953125 -1.9375 -1.453125q-1.140625 -0.5 -2.59375 -0.5q-1.4375 0 -2.578125 0.5q-1.140625 0.5 -1.921875 1.453125q-0.796875 0.96875 -1.21875 2.375q-0.40625 1.390625 -0.40625 3.203125l0 4.4375q0 1.8125 0.40625 3.21875q0.421875 1.390625 1.21875 2.34375q0.796875 0.96875 1.9375 1.46875q1.140625 0.484375 2.59375 0.484375q1.453125 0 2.578125 -0.5q1.140625 -0.5 1.921875 -1.453125q0.78125 -0.953125 1.1875 -2.34375q0.421875 -1.40625 0.421875 -3.21875zm-9.84375 -0.484375q0 -0.328125 0 -0.65625q0 -0.328125 0 -0.65625l0 -3.171875q0 -1.328125 0.265625 -2.328125q0.265625 -1.015625 0.8125 -1.65625q0.453125 -0.515625 1.109375 -0.78125q0.65625 -0.265625 1.515625 -0.265625q0.84375 0 1.484375 0.25q0.65625 0.25 1.09375 0.75q0.453125 0.46875 0.71875 1.203125q0.265625 0.71875 0.375 1.65625l-7.375 5.65625zm7.421875 1.0q0 1.390625 -0.28125 2.421875q-0.265625 1.015625 -0.875 1.65625q-0.4375 0.46875 -1.078125 0.71875q-0.640625 0.25 -1.453125 0.25q-0.78125 0 -1.40625 -0.21875q-0.625 -0.234375 -1.0625 -0.6875q-0.484375 -0.46875 -0.796875 -1.1875q-0.296875 -0.71875 -0.40625 -1.671875l7.359375 -5.640625q0 0.375 0 0.921875q0 0.546875 0 0.75l0 2.6875zm99.29053 -10.15625l0 -2.0625l-14.0625 0l0 2.0625l5.859375 0l0 16.890625l2.34375 0l0 -16.890625l5.859375 0zm6.027588 6.25l0 1.953125l1.71875 0q0.890625 0 1.640625 0.21875q0.75 0.203125 1.28125 0.640625q0.53125 0.421875 0.8125 1.0625q0.296875 0.640625 0.296875 1.53125q0 0.90625 -0.25 1.5625q-0.25 0.65625 -0.71875 1.09375q-0.46875 0.4375 -1.140625 0.65625q-0.65625 0.21875 -1.484375 0.21875q-0.8125 0 -1.484375 -0.234375q-0.671875 -0.234375 -1.140625 -0.671875q-0.46875 -0.421875 -0.734375 -1.03125q-0.25 -0.609375 -0.25 -1.359375l-2.40625 0q0 1.25 0.46875 2.234375q0.484375 0.96875 1.296875 1.640625q0.796875 0.6875 1.890625 1.046875q1.109375 0.34375 2.359375 0.34375q1.28125 0 2.375 -0.375q1.109375 -0.375 1.90625 -1.09375q0.8125 -0.703125 1.265625 -1.71875q0.453125 -1.03125 0.453125 -2.359375q0 -0.65625 -0.15625 -1.3125q-0.140625 -0.671875 -0.5 -1.265625q-0.375 -0.59375 -1.0 -1.078125q-0.625 -0.484375 -1.546875 -0.78125q0.765625 -0.328125 1.3125 -0.8125q0.5625 -0.5 0.921875 -1.046875q0.34375 -0.546875 0.515625 -1.125q0.171875 -0.59375 0.171875 -1.140625q0 -1.3125 -0.421875 -2.3125q-0.40625 -1.015625 -1.171875 -1.6875q-0.75 -0.671875 -1.8125 -1.015625q-1.046875 -0.359375 -2.328125 -0.359375q-1.3125 0 -2.390625 0.421875q-1.0625 0.40625 -1.828125 1.109375q-0.78125 0.71875 -1.203125 1.671875q-0.421875 0.953125 -0.421875 2.03125l2.421875 0q0 -0.734375 0.25 -1.328125q0.25 -0.609375 0.703125 -1.03125q0.4375 -0.4375 1.0625 -0.65625q0.640625 -0.234375 1.40625 -0.234375q0.796875 0 1.40625 0.203125q0.625 0.203125 1.046875 0.609375q0.421875 0.40625 0.640625 1.046875q0.234375 0.625 0.234375 1.46875q0 0.71875 -0.25 1.328125q-0.25 0.59375 -0.71875 1.015625q-0.484375 0.4375 -1.1875 0.6875q-0.6875 0.234375 -1.59375 0.234375l-1.71875 0z" fill-rule="nonzero"/><path fill="#000000" d="m1251.2343 642.8569l0 -12.59375l-2.5625 0l-8.5 13.140625l0 1.40625l8.65625 0l0 4.40625l2.40625 0l0 -4.40625l2.640625 0l0 -1.953125l-2.640625 0zm-8.390625 0l5.59375 -8.65625l0.390625 -0.734375l0 9.390625l-5.984375 0zm26.277588 -7.640625q0 -1.234375 -0.4375 -2.203125q-0.421875 -0.984375 -1.171875 -1.640625q-0.75 -0.671875 -1.78125 -1.015625q-1.03125 -0.359375 -2.234375 -0.359375q-1.203125 0 -2.234375 0.359375q-1.015625 0.34375 -1.734375 1.015625q-0.75 0.65625 -1.171875 1.640625q-0.40625 0.96875 -0.40625 2.203125q0 0.703125 0.1875 1.34375q0.203125 0.625 0.546875 1.15625q0.359375 0.546875 0.859375 0.984375q0.515625 0.4375 1.140625 0.75q-0.734375 0.3125 -1.328125 0.78125q-0.578125 0.46875 -1.0 1.0625q-0.40625 0.59375 -0.640625 1.3125q-0.21875 0.703125 -0.21875 1.5q0 1.296875 0.46875 2.296875q0.46875 1.0 1.296875 1.6875q0.8125 0.6875 1.90625 1.046875q1.09375 0.34375 2.375 0.34375q1.25 0 2.34375 -0.359375q1.09375 -0.359375 1.90625 -1.046875q0.8125 -0.6875 1.28125 -1.6875q0.484375 -1.0 0.484375 -2.28125q0 -0.796875 -0.234375 -1.5q-0.21875 -0.703125 -0.640625 -1.296875q-0.421875 -0.609375 -1.015625 -1.0625q-0.59375 -0.46875 -1.328125 -0.78125q0.421875 -0.203125 0.78125 -0.46875q0.359375 -0.265625 0.671875 -0.5625q0.625 -0.625 0.96875 -1.453125q0.34375 -0.828125 0.359375 -1.765625zm-1.96875 8.828125q0 0.828125 -0.265625 1.46875q-0.265625 0.640625 -0.734375 1.09375q-0.484375 0.4375 -1.15625 0.671875q-0.65625 0.234375 -1.453125 0.234375q-0.828125 0 -1.5 -0.234375q-0.671875 -0.234375 -1.140625 -0.671875q-0.46875 -0.453125 -0.734375 -1.09375q-0.25 -0.640625 -0.25 -1.46875q0 -0.796875 0.25 -1.453125q0.265625 -0.65625 0.734375 -1.140625q0.46875 -0.46875 1.125 -0.734375q0.671875 -0.265625 1.484375 -0.265625q0.796875 0 1.46875 0.265625q0.671875 0.265625 1.15625 0.734375q0.484375 0.484375 0.75 1.140625q0.265625 0.65625 0.265625 1.453125zm-0.453125 -8.796875q0 0.734375 -0.234375 1.328125q-0.234375 0.59375 -0.640625 1.015625q-0.4375 0.4375 -1.03125 0.671875q-0.578125 0.21875 -1.265625 0.21875q-0.703125 0 -1.28125 -0.21875q-0.578125 -0.234375 -1.0 -0.671875q-0.421875 -0.421875 -0.65625 -1.0q-0.21875 -0.59375 -0.21875 -1.34375q0 -0.734375 0.21875 -1.34375q0.234375 -0.609375 0.65625 -1.03125q0.40625 -0.4375 0.96875 -0.65625q0.578125 -0.234375 1.28125 -0.234375q0.703125 0 1.28125 0.25q0.59375 0.234375 1.03125 0.65625q0.421875 0.453125 0.65625 1.046875q0.234375 0.59375 0.234375 1.3125zm18.652588 6.71875l0 -4.4375q0 -1.8125 -0.421875 -3.203125q-0.40625 -1.40625 -1.1875 -2.375q-0.796875 -0.953125 -1.9375 -1.453125q-1.140625 -0.5 -2.59375 -0.5q-1.4375 0 -2.578125 0.5q-1.140625 0.5 -1.921875 1.453125q-0.796875 0.96875 -1.21875 2.375q-0.40625 1.390625 -0.40625 3.203125l0 4.4375q0 1.8125 0.40625 3.21875q0.421875 1.390625 1.21875 2.34375q0.796875 0.96875 1.9375 1.46875q1.140625 0.484375 2.59375 0.484375q1.453125 0 2.578125 -0.5q1.140625 -0.5 1.921875 -1.453125q0.78125 -0.953125 1.1875 -2.34375q0.421875 -1.40625 0.421875 -3.21875zm-9.84375 -0.484375q0 -0.328125 0 -0.65625q0 -0.328125 0 -0.65625l0 -3.171875q0 -1.328125 0.265625 -2.328125q0.265625 -1.015625 0.8125 -1.65625q0.453125 -0.515625 1.109375 -0.78125q0.65625 -0.265625 1.515625 -0.265625q0.84375 0 1.484375 0.25q0.65625 0.25 1.09375 0.75q0.453125 0.46875 0.71875 1.203125q0.265625 0.71875 0.375 1.65625l-7.375 5.65625zm7.421875 1.0q0 1.390625 -0.28125 2.421875q-0.265625 1.015625 -0.875 1.65625q-0.4375 0.46875 -1.078125 0.71875q-0.640625 0.25 -1.453125 0.25q-0.78125 0 -1.40625 -0.21875q-0.625 -0.234375 -1.0625 -0.6875q-0.484375 -0.46875 -0.796875 -1.1875q-0.296875 -0.71875 -0.40625 -1.671875l7.359375 -5.640625q0 0.375 0 0.921875q0 0.546875 0 0.75l0 2.6875zm62.3291 6.734375l0 -18.953125l-0.203125 0l-7.171875 2.765625l0 2.203125l4.96875 -1.890625l0 15.875l2.40625 0zm20.074585 -7.25l0 -4.4375q0 -1.8125 -0.421875 -3.203125q-0.40625 -1.40625 -1.1875 -2.375q-0.796875 -0.953125 -1.9375 -1.453125q-1.140625 -0.5 -2.59375 -0.5q-1.4375 0 -2.578125 0.5q-1.140625 0.5 -1.921875 1.453125q-0.796875 0.96875 -1.21875 2.375q-0.40625 1.390625 -0.40625 3.203125l0 4.4375q0 1.8125 0.40625 3.21875q0.421875 1.390625 1.21875 2.34375q0.796875 0.96875 1.9375 1.46875q1.140625 0.484375 2.59375 0.484375q1.453125 0 2.578125 -0.5q1.140625 -0.5 1.921875 -1.453125q0.78125 -0.953125 1.1875 -2.34375q0.421875 -1.40625 0.421875 -3.21875zm-9.84375 -0.484375q0 -0.328125 0 -0.65625q0 -0.328125 0 -0.65625l0 -3.171875q0 -1.328125 0.265625 -2.328125q0.265625 -1.015625 0.8125 -1.65625q0.453125 -0.515625 1.109375 -0.78125q0.65625 -0.265625 1.515625 -0.265625q0.84375 0 1.484375 0.25q0.65625 0.25 1.09375 0.75q0.453125 0.46875 0.71875 1.203125q0.265625 0.71875 0.375 1.65625l-7.375 5.65625zm7.421875 1.0q0 1.390625 -0.28125 2.421875q-0.265625 1.015625 -0.875 1.65625q-0.4375 0.46875 -1.078125 0.71875q-0.640625 0.25 -1.453125 0.25q-0.78125 0 -1.40625 -0.21875q-0.625 -0.234375 -1.0625 -0.6875q-0.484375 -0.46875 -0.796875 -1.1875q-0.296875 -0.71875 -0.40625 -1.671875l7.359375 -5.640625q0 0.375 0 0.921875q0 0.546875 0 0.75l0 2.6875zm99.29053 -10.15625l0 -2.0625l-14.0625 0l0 2.0625l5.859375 0l0 16.890625l2.34375 0l0 -16.890625l5.859375 0zm11.027588 16.890625l0 -18.953125l-0.203125 0l-7.171875 2.765625l0 2.203125l4.96875 -1.890625l0 15.875l2.40625 0zm9.043335 -9.5l1.921875 0.5q0.328125 -0.296875 0.625 -0.515625q0.3125 -0.21875 0.671875 -0.375q0.34375 -0.15625 0.78125 -0.234375q0.4375 -0.09375 1.03125 -0.09375q0.921875 0 1.625 0.328125q0.703125 0.328125 1.171875 0.890625q0.484375 0.5625 0.734375 1.34375q0.265625 0.765625 0.265625 1.640625q0 0.984375 -0.234375 1.78125q-0.21875 0.78125 -0.671875 1.34375q-0.453125 0.578125 -1.125 0.890625q-0.65625 0.296875 -1.515625 0.296875q-1.515625 0 -2.484375 -0.828125q-0.96875 -0.84375 -1.203125 -2.453125l-2.28125 0q0.125 1.3125 0.640625 2.3125q0.515625 0.984375 1.328125 1.625q0.78125 0.65625 1.8125 0.984375q1.03125 0.328125 2.1875 0.328125q1.515625 0 2.625 -0.46875q1.125 -0.484375 1.875 -1.328125q0.71875 -0.84375 1.078125 -2.0q0.375 -1.15625 0.375 -2.515625q0 -1.421875 -0.390625 -2.59375q-0.390625 -1.171875 -1.125 -2.0q-0.71875 -0.828125 -1.765625 -1.28125q-1.046875 -0.453125 -2.359375 -0.453125q-1.03125 0 -1.8125 0.25q-0.78125 0.234375 -1.328125 0.5625l0.53125 -5.046875l7.6875 0l0 -2.34375l-9.71875 0l-0.953125 9.453125zm26.793213 -4.5q0 -1.234375 -0.4375 -2.203125q-0.421875 -0.984375 -1.171875 -1.640625q-0.75 -0.671875 -1.78125 -1.015625q-1.03125 -0.359375 -2.234375 -0.359375q-1.203125 0 -2.234375 0.359375q-1.015625 0.34375 -1.734375 1.015625q-0.75 0.65625 -1.171875 1.640625q-0.40625 0.96875 -0.40625 2.203125q0 0.703125 0.1875 1.34375q0.203125 0.625 0.546875 1.15625q0.359375 0.546875 0.859375 0.984375q0.515625 0.4375 1.140625 0.75q-0.734375 0.3125 -1.328125 0.78125q-0.578125 0.46875 -1.0 1.0625q-0.40625 0.59375 -0.640625 1.3125q-0.21875 0.703125 -0.21875 1.5q0 1.296875 0.46875 2.296875q0.46875 1.0 1.296875 1.6875q0.8125 0.6875 1.90625 1.046875q1.09375 0.34375 2.375 0.34375q1.25 0 2.34375 -0.359375q1.09375 -0.359375 1.90625 -1.046875q0.8125 -0.6875 1.28125 -1.6875q0.484375 -1.0 0.484375 -2.28125q0 -0.796875 -0.234375 -1.5q-0.21875 -0.703125 -0.640625 -1.296875q-0.421875 -0.609375 -1.015625 -1.0625q-0.59375 -0.46875 -1.328125 -0.78125q0.421875 -0.203125 0.78125 -0.46875q0.359375 -0.265625 0.671875 -0.5625q0.625 -0.625 0.96875 -1.453125q0.34375 -0.828125 0.359375 -1.765625zm-1.96875 8.828125q0 0.828125 -0.265625 1.46875q-0.265625 0.640625 -0.734375 1.09375q-0.484375 0.4375 -1.15625 0.671875q-0.65625 0.234375 -1.453125 0.234375q-0.828125 0 -1.5 -0.234375q-0.671875 -0.234375 -1.140625 -0.671875q-0.46875 -0.453125 -0.734375 -1.09375q-0.25 -0.640625 -0.25 -1.46875q0 -0.796875 0.25 -1.453125q0.265625 -0.65625 0.734375 -1.140625q0.46875 -0.46875 1.125 -0.734375q0.671875 -0.265625 1.484375 -0.265625q0.796875 0 1.46875 0.265625q0.671875 0.265625 1.15625 0.734375q0.484375 0.484375 0.75 1.140625q0.265625 0.65625 0.265625 1.453125zm-0.453125 -8.796875q0 0.734375 -0.234375 1.328125q-0.234375 0.59375 -0.640625 1.015625q-0.4375 0.4375 -1.03125 0.671875q-0.578125 0.21875 -1.265625 0.21875q-0.703125 0 -1.28125 -0.21875q-0.578125 -0.234375 -1.0 -0.671875q-0.421875 -0.421875 -0.65625 -1.0q-0.21875 -0.59375 -0.21875 -1.34375q0 -0.734375 0.21875 -1.34375q0.234375 -0.609375 0.65625 -1.03125q0.40625 -0.4375 0.96875 -0.65625q0.578125 -0.234375 1.28125 -0.234375q0.703125 0 1.28125 0.25q0.59375 0.234375 1.03125 0.65625q0.421875 0.453125 0.65625 1.046875q0.234375 0.59375 0.234375 1.3125z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m654.32294 382.9236l0 -134.77165" fill-rule="evenodd"/><path stroke="#000000" stroke-width="1.0" stroke-linejoin="round" stroke-linecap="butt" d="m654.32294 382.92358l0 -128.77162" fill-rule="evenodd"/><path fill="#000000" stroke="#000000" stroke-width="1.0" stroke-linecap="butt" d="m655.9747 254.15196l-1.6517334 -4.538101l-1.6517334 4.538101z" fill-rule="evenodd"/><path fill="#000000" fill-opacity="0.0" d="m432.08923 273.5223l195.52759 0l0 84.031494l-195.52759 0z" fill-rule="evenodd"/><path fill="#000000" d="m457.84775 308.1223l0 -19.078125l11.046875 0l0 2.15625l-8.78125 0l0 16.921875l-2.265625 0zm1.203125 -8.265625l0 -2.125l8.984375 0l0 2.125l-8.984375 0zm17.76065 8.6875q-1.984375 0 -3.515625 -0.921875q-1.53125 -0.9375 -2.40625 -2.5625q-0.859375 -1.625 -0.859375 -3.703125q0 -1.953125 0.8125 -3.59375q0.8125 -1.65625 2.296875 -2.65625q1.484375 -1.0 3.484375 -1.0q2.015625 0 3.46875 0.890625q1.453125 0.890625 2.234375 2.46875q0.796875 1.5625 0.796875 3.59375q0 0.1875 -0.015625 0.375q-0.015625 0.1875 -0.03125 0.3125l-11.703125 0l0 -1.859375l9.359375 0q-0.03125 -0.5625 -0.265625 -1.21875q-0.21875 -0.65625 -0.71875 -1.21875q-0.484375 -0.578125 -1.25 -0.9375q-0.765625 -0.359375 -1.875 -0.359375q-1.34375 0 -2.3125 0.6875q-0.96875 0.671875 -1.5 1.84375q-0.515625 1.171875 -0.515625 2.671875q0 1.734375 0.671875 2.875q0.671875 1.140625 1.71875 1.703125q1.046875 0.5625 2.203125 0.5625q1.484375 0 2.453125 -0.703125q0.984375 -0.703125 1.5625 -1.75l1.921875 0.9375q-0.796875 1.546875 -2.296875 2.5625q-1.484375 1.0 -3.71875 1.0zm7.623047 -14.015625l7.96875 0l0 2.046875l-7.96875 0l0 -2.046875zm2.375 10.015625l0 -13.859375l2.265625 0l0 13.3125q0 1.0625 0.4375 1.65625q0.4375 0.578125 1.453125 0.578125q0.453125 0 0.828125 -0.125q0.375 -0.140625 0.65625 -0.328125l0 2.203125q-0.34375 0.171875 -0.765625 0.265625q-0.40625 0.09375 -1.09375 0.09375q-1.703125 0 -2.75 -1.0q-1.03125 -1.0 -1.03125 -2.796875zm13.861572 4.0q-2.0 0 -3.5625 -0.9375q-1.5625 -0.953125 -2.453125 -2.59375q-0.875 -1.640625 -0.875 -3.6875q0 -2.078125 0.875 -3.703125q0.890625 -1.625 2.453125 -2.5625q1.5625 -0.953125 3.5625 -0.953125q2.28125 0 3.765625 1.046875q1.484375 1.046875 2.09375 2.703125l-2.046875 0.859375q-0.515625 -1.25 -1.515625 -1.90625q-1.0 -0.65625 -2.40625 -0.65625q-1.203125 0 -2.234375 0.640625q-1.015625 0.640625 -1.65625 1.796875q-0.640625 1.15625 -0.640625 2.734375q0 1.546875 0.640625 2.71875q0.640625 1.171875 1.65625 1.8125q1.03125 0.640625 2.234375 0.640625q1.4375 0 2.46875 -0.65625q1.046875 -0.671875 1.546875 -1.90625l2.03125 0.859375q-0.671875 1.5625 -2.171875 2.65625q-1.484375 1.09375 -3.765625 1.09375zm8.382385 -0.421875l0 -19.078125l2.265625 0l0 5.609375l-0.09375 1.875l0.09375 0q0.546875 -1.015625 1.75 -1.71875q1.21875 -0.703125 2.6875 -0.703125q1.734375 0 2.84375 0.6875q1.125 0.6875 1.6875 1.875q0.5625 1.1875 0.5625 2.734375l0 8.71875l-2.265625 0l0 -8.375q0 -1.296875 -0.46875 -2.078125q-0.46875 -0.796875 -1.234375 -1.15625q-0.75 -0.359375 -1.65625 -0.359375q-1.125 0 -2.015625 0.640625q-0.875 0.640625 -1.390625 1.65625q-0.5 1.015625 -0.5 2.09375l0 7.578125l-2.265625 0zm20.296448 -13.59375l7.96875 0l0 2.046875l-7.96875 0l0 -2.046875zm2.375 10.015625l0 -13.859375l2.265625 0l0 13.3125q0 1.0625 0.4375 1.65625q0.4375 0.578125 1.453125 0.578125q0.453125 0 0.828125 -0.125q0.375 -0.140625 0.65625 -0.328125l0 2.203125q-0.34375 0.171875 -0.765625 0.265625q-0.40625 0.09375 -1.09375 0.09375q-1.703125 0 -2.75 -1.0q-1.03125 -1.0 -1.03125 -2.796875zm8.276367 3.578125l0 -13.59375l2.171875 0l0 2.1875l0.09375 0q0.28125 -0.78125 0.90625 -1.34375q0.625 -0.578125 1.4375 -0.90625q0.8125 -0.34375 1.609375 -0.34375q0.609375 0 0.953125 0.078125q0.359375 0.0625 0.640625 0.1875l0 2.453125q-0.421875 -0.203125 -0.921875 -0.3125q-0.484375 -0.109375 -0.984375 -0.109375q-1.0 0 -1.828125 0.5625q-0.8125 0.5625 -1.3125 1.5q-0.5 0.921875 -0.5 2.046875l0 7.59375l-2.265625 0zm13.8220825 0.421875q-1.53125 0 -2.671875 -0.578125q-1.140625 -0.59375 -1.796875 -1.609375q-0.65625 -1.03125 -0.65625 -2.34375q0 -1.484375 0.765625 -2.515625q0.78125 -1.03125 2.078125 -1.546875q1.3125 -0.515625 2.890625 -0.515625q0.90625 0 1.671875 0.15625q0.78125 0.140625 1.34375 0.34375q0.578125 0.1875 0.875 0.375l0 -0.828125q0 -1.546875 -1.09375 -2.453125q-1.09375 -0.90625 -2.671875 -0.90625q-1.109375 0 -2.09375 0.5q-0.96875 0.484375 -1.53125 1.375l-1.703125 -1.28125q0.53125 -0.796875 1.328125 -1.375q0.8125 -0.59375 1.828125 -0.90625q1.03125 -0.328125 2.171875 -0.328125q2.78125 0 4.34375 1.46875q1.578125 1.453125 1.578125 3.9375l0 8.609375l-2.15625 0l0 -1.953125l-0.109375 0q-0.34375 0.59375 -0.984375 1.140625q-0.640625 0.546875 -1.515625 0.890625q-0.859375 0.34375 -1.890625 0.34375zm0.203125 -2.0q1.171875 0 2.140625 -0.578125q0.984375 -0.59375 1.5625 -1.578125q0.59375 -0.984375 0.59375 -2.15625q-0.609375 -0.421875 -1.53125 -0.6875q-0.921875 -0.265625 -2.015625 -0.265625q-1.953125 0 -2.859375 0.796875q-0.90625 0.796875 -0.90625 1.96875q0 1.125 0.859375 1.8125q0.859375 0.6875 2.15625 0.6875zm15.630493 2.0q-2.0 0 -3.5625 -0.9375q-1.5625 -0.953125 -2.453125 -2.59375q-0.875 -1.640625 -0.875 -3.6875q0 -2.078125 0.875 -3.703125q0.890625 -1.625 2.453125 -2.5625q1.5625 -0.953125 3.5625 -0.953125q2.28125 0 3.765625 1.046875q1.484375 1.046875 2.09375 2.703125l-2.046875 0.859375q-0.515625 -1.25 -1.515625 -1.90625q-1.0 -0.65625 -2.40625 -0.65625q-1.203125 0 -2.234375 0.640625q-1.015625 0.640625 -1.65625 1.796875q-0.640625 1.15625 -0.640625 2.734375q0 1.546875 0.640625 2.71875q0.640625 1.171875 1.65625 1.8125q1.03125 0.640625 2.234375 0.640625q1.4375 0 2.46875 -0.65625q1.046875 -0.671875 1.546875 -1.90625l2.03125 0.859375q-0.671875 1.5625 -2.171875 2.65625q-1.484375 1.09375 -3.765625 1.09375zm14.311523 0q-1.984375 0 -3.515625 -0.921875q-1.53125 -0.9375 -2.40625 -2.5625q-0.859375 -1.625 -0.859375 -3.703125q0 -1.953125 0.8125 -3.59375q0.8125 -1.65625 2.296875 -2.65625q1.484375 -1.0 3.484375 -1.0q2.015625 0 3.46875 0.890625q1.453125 0.890625 2.234375 2.46875q0.796875 1.5625 0.796875 3.59375q0 0.1875 -0.015625 0.375q-0.015625 0.1875 -0.03125 0.3125l-11.703125 0l0 -1.859375l9.359375 0q-0.03125 -0.5625 -0.265625 -1.21875q-0.21875 -0.65625 -0.71875 -1.21875q-0.484375 -0.578125 -1.25 -0.9375q-0.765625 -0.359375 -1.875 -0.359375q-1.34375 0 -2.3125 0.6875q-0.96875 0.671875 -1.5 1.84375q-0.515625 1.171875 -0.515625 2.671875q0 1.734375 0.671875 2.875q0.671875 1.140625 1.71875 1.703125q1.046875 0.5625 2.203125 0.5625q1.484375 0 2.453125 -0.703125q0.984375 -0.703125 1.5625 -1.75l1.921875 0.9375q-0.796875 1.546875 -2.296875 2.5625q-1.484375 1.0 -3.71875 1.0zm13.828247 0q-1.515625 0 -2.6875 -0.484375q-1.15625 -0.5 -1.921875 -1.328125q-0.75 -0.84375 -1.125 -1.828125l2.03125 -0.90625q0.53125 1.21875 1.546875 1.890625q1.015625 0.65625 2.3125 0.65625q1.265625 0 2.09375 -0.5q0.84375 -0.5 0.84375 -1.5q0 -0.609375 -0.34375 -1.015625q-0.34375 -0.421875 -1.015625 -0.703125q-0.671875 -0.296875 -1.625 -0.546875l-1.65625 -0.421875q-0.953125 -0.265625 -1.8125 -0.734375q-0.84375 -0.484375 -1.375 -1.25q-0.515625 -0.765625 -0.515625 -1.859375q0 -1.21875 0.71875 -2.09375q0.71875 -0.890625 1.890625 -1.34375q1.171875 -0.46875 2.515625 -0.46875q1.171875 0 2.1875 0.328125q1.03125 0.328125 1.796875 0.984375q0.78125 0.65625 1.1875 1.609375l-1.984375 0.90625q-0.5 -1.015625 -1.359375 -1.40625q-0.84375 -0.40625 -1.890625 -0.40625q-1.109375 0 -1.9375 0.5q-0.828125 0.484375 -0.828125 1.34375q0 0.859375 0.671875 1.28125q0.6875 0.421875 1.671875 0.6875l1.96875 0.515625q2.0 0.5 3.015625 1.5q1.015625 1.0 1.015625 2.4375q0 1.28125 -0.71875 2.21875q-0.71875 0.921875 -1.953125 1.4375q-1.21875 0.5 -2.71875 0.5z" fill-rule="nonzero"/><path fill="#000000" d="m492.17838 328.57544l0 -2.046875l8.265625 0l0 2.046875l-8.265625 0zm2.5 11.546875l0 -15.0625q0 -1.328125 0.578125 -2.265625q0.59375 -0.953125 1.59375 -1.453125q1.0 -0.515625 2.171875 -0.515625q0.78125 0 1.234375 0.09375q0.453125 0.09375 0.765625 0.21875l0 2.21875q-0.34375 -0.140625 -0.796875 -0.28125q-0.453125 -0.140625 -1.125 -0.140625q-0.828125 0 -1.5 0.59375q-0.65625 0.59375 -0.65625 1.765625l0 14.828125l-2.265625 0zm8.524567 0l0 -13.59375l2.171875 0l0 2.1875l0.09375 0q0.28125 -0.78125 0.90625 -1.34375q0.625 -0.578125 1.4375 -0.90625q0.8125 -0.34375 1.609375 -0.34375q0.609375 0 0.953125 0.078125q0.359375 0.0625 0.640625 0.1875l0 2.453125q-0.421875 -0.203125 -0.921875 -0.3125q-0.484375 -0.109375 -0.984375 -0.109375q-1.0 0 -1.828125 0.5625q-0.8125 0.5625 -1.3125 1.5q-0.5 0.921875 -0.5 2.046875l0 7.59375l-2.265625 0zm15.46167 0.421875q-2.046875 0 -3.625 -0.953125q-1.5625 -0.96875 -2.46875 -2.59375q-0.890625 -1.640625 -0.890625 -3.671875q0 -2.03125 0.890625 -3.671875q0.90625 -1.640625 2.46875 -2.59375q1.578125 -0.953125 3.625 -0.953125q2.0625 0 3.625 0.96875q1.578125 0.96875 2.46875 2.609375q0.890625 1.640625 0.890625 3.640625q0 2.03125 -0.890625 3.671875q-0.890625 1.625 -2.46875 2.59375q-1.5625 0.953125 -3.625 0.953125zm0 -2.046875q1.234375 0 2.3125 -0.609375q1.078125 -0.625 1.734375 -1.78125q0.671875 -1.15625 0.671875 -2.78125q0 -1.625 -0.671875 -2.78125q-0.65625 -1.171875 -1.734375 -1.78125q-1.078125 -0.609375 -2.3125 -0.609375q-1.21875 0 -2.3125 0.609375q-1.09375 0.609375 -1.765625 1.78125q-0.65625 1.15625 -0.65625 2.78125q0 1.625 0.65625 2.78125q0.671875 1.15625 1.765625 1.78125q1.09375 0.609375 2.3125 0.609375zm9.354797 1.625l0 -13.59375l2.171875 0l0 2.0l0.09375 0q0.375 -0.671875 1.03125 -1.21875q0.65625 -0.546875 1.484375 -0.875q0.84375 -0.328125 1.734375 -0.328125q1.515625 0 2.609375 0.734375q1.09375 0.71875 1.578125 1.90625q0.6875 -1.15625 1.859375 -1.890625q1.171875 -0.75 2.796875 -0.75q2.421875 0 3.5625 1.46875q1.15625 1.453125 1.15625 3.828125l0 8.71875l-2.234375 0l0 -8.375q0 -1.96875 -0.8125 -2.78125q-0.796875 -0.8125 -2.25 -0.8125q-1.046875 0 -1.859375 0.609375q-0.8125 0.59375 -1.28125 1.5625q-0.46875 0.96875 -0.46875 2.125l0 7.671875l-2.265625 0l0 -8.34375q0 -1.96875 -0.796875 -2.796875q-0.796875 -0.828125 -2.234375 -0.828125q-1.046875 0 -1.859375 0.609375q-0.8125 0.609375 -1.28125 1.609375q-0.46875 0.984375 -0.46875 2.125l0 7.625l-2.265625 0zm24.824097 0.15625q-0.71875 0 -1.21875 -0.484375q-0.484375 -0.5 -0.484375 -1.21875q0 -0.6875 0.484375 -1.171875q0.5 -0.5 1.21875 -0.5q0.71875 0 1.203125 0.5q0.5 0.484375 0.5 1.171875q0 0.71875 -0.5 1.21875q-0.484375 0.484375 -1.203125 0.484375zm6.125 0q-0.71875 0 -1.21875 -0.484375q-0.484375 -0.5 -0.484375 -1.21875q0 -0.6875 0.484375 -1.171875q0.5 -0.5 1.21875 -0.5q0.71875 0 1.203186 0.5q0.5 0.484375 0.5 1.171875q0 0.71875 -0.5 1.21875q-0.48443604 0.484375 -1.203186 0.484375zm6.140686 0q-0.71875 0 -1.21875 -0.484375q-0.484375 -0.5 -0.484375 -1.21875q0 -0.6875 0.484375 -1.171875q0.5 -0.5 1.21875 -0.5q0.71875 0 1.203125 0.5q0.5 0.484375 0.5 1.171875q0 0.71875 -0.5 1.21875q-0.484375 0.484375 -1.203125 0.484375z" fill-rule="nonzero"/><path fill="#38761d" d="m268.7358 109.21426l150.86612 0l0 121.76377l-150.86612 0z" fill-rule="evenodd"/><path fill="#ffffff" d="m296.76755 163.69614l7.25 -19.078125l2.546875 0l7.25 19.078125l-2.46875 0l-5.25 -14.265625l-0.75 -2.046875l-0.109375 0l-0.75 2.046875l-5.25 14.265625l-2.46875 0zm8.25 -5.25l0 -2.140625l4.40625 0l0.765625 2.140625l-5.171875 0zm-5.171875 0l0.78125 -2.140625l4.390625 0l0 2.140625l-5.171875 0zm16.04712 5.25l0 -13.59375l2.171875 0l0 2.0l0.09375 0q0.546875 -0.984375 1.75 -1.703125q1.21875 -0.71875 2.65625 -0.71875q2.5 0 3.765625 1.453125q1.265625 1.453125 1.265625 3.84375l0 8.71875l-2.265625 0l0 -8.375q0 -1.96875 -0.953125 -2.78125q-0.9375 -0.8125 -2.421875 -0.8125q-1.125 0 -1.984375 0.625q-0.84375 0.625 -1.328125 1.609375q-0.484375 0.984375 -0.484375 2.078125l0 7.65625l-2.265625 0zm20.750977 0.421875q-1.84375 0 -3.328125 -0.921875q-1.46875 -0.9375 -2.328125 -2.5625q-0.859375 -1.625 -0.859375 -3.734375q0 -2.109375 0.859375 -3.734375q0.859375 -1.625 2.328125 -2.546875q1.484375 -0.9375 3.328125 -0.9375q1.09375 0 2.0 0.34375q0.90625 0.34375 1.578125 0.90625q0.6875 0.5625 1.0625 1.203125l0.09375 0l-0.09375 -1.90625l0 -5.609375l2.265625 0l0 19.078125l-2.171875 0l0 -2.0l-0.09375 0q-0.375 0.609375 -1.0625 1.171875q-0.671875 0.5625 -1.578125 0.90625q-0.90625 0.34375 -2.0 0.34375zm0.234375 -2.046875q1.171875 0 2.1875 -0.625q1.03125 -0.625 1.671875 -1.78125q0.640625 -1.171875 0.640625 -2.765625q0 -1.59375 -0.640625 -2.75q-0.640625 -1.171875 -1.671875 -1.796875q-1.015625 -0.625 -2.1875 -0.625q-1.171875 0 -2.203125 0.625q-1.015625 0.625 -1.65625 1.796875q-0.640625 1.15625 -0.640625 2.75q0 1.578125 0.640625 2.75q0.640625 1.171875 1.65625 1.796875q1.03125 0.625 2.203125 0.625zm10.015747 1.625l0 -13.59375l2.171875 0l0 2.1875l0.09375 0q0.28125 -0.78125 0.90625 -1.34375q0.625 -0.578125 1.4375 -0.90625q0.8125 -0.34375 1.609375 -0.34375q0.609375 0 0.953125 0.078125q0.359375 0.0625 0.640625 0.1875l0 2.453125q-0.421875 -0.203125 -0.921875 -0.3125q-0.484375 -0.109375 -0.984375 -0.109375q-1.0 0 -1.828125 0.5625q-0.8125 0.5625 -1.3125 1.5q-0.5 0.921875 -0.5 2.046875l0 7.59375l-2.265625 0zm15.4617 0.421875q-2.046875 0 -3.625 -0.953125q-1.5625 -0.96875 -2.46875 -2.59375q-0.890625 -1.640625 -0.890625 -3.671875q0 -2.03125 0.890625 -3.671875q0.90625 -1.640625 2.46875 -2.59375q1.578125 -0.953125 3.625 -0.953125q2.0625 0 3.625 0.96875q1.578125 0.96875 2.46875 2.609375q0.890625 1.640625 0.890625 3.640625q0 2.03125 -0.890625 3.671875q-0.890625 1.625 -2.46875 2.59375q-1.5625 0.953125 -3.625 0.953125zm0 -2.046875q1.234375 0 2.3125 -0.609375q1.078125 -0.625 1.734375 -1.78125q0.671875 -1.15625 0.671875 -2.78125q0 -1.625 -0.671875 -2.78125q-0.65625 -1.171875 -1.734375 -1.78125q-1.078125 -0.609375 -2.3125 -0.609375q-1.21875 0 -2.3125 0.609375q-1.09375 0.609375 -1.765625 1.78125q-0.65625 1.15625 -0.65625 2.78125q0 1.625 0.65625 2.78125q0.671875 1.15625 1.765625 1.78125q1.09375 0.609375 2.3125 0.609375zm9.573517 1.625l0 -13.59375l2.265625 0l0 13.59375l-2.265625 0zm1.125 -16.09375q-0.671875 0 -1.15625 -0.484375q-0.46875 -0.484375 -0.46875 -1.140625q0 -0.703125 0.46875 -1.171875q0.484375 -0.46875 1.15625 -0.46875q0.6875 0 1.15625 0.46875q0.46875 0.46875 0.46875 1.171875q0 0.65625 -0.46875 1.140625q-0.46875 0.484375 -1.15625 0.484375zm10.344086 16.515625q-1.84375 0 -3.328125 -0.921875q-1.46875 -0.9375 -2.328125 -2.5625q-0.859375 -1.625 -0.859375 -3.734375q0 -2.109375 0.859375 -3.734375q0.859375 -1.625 2.328125 -2.546875q1.484375 -0.9375 3.328125 -0.9375q1.09375 0 2.0 0.34375q0.90625 0.34375 1.578125 0.90625q0.6875 0.5625 1.0625 1.203125l0.09375 0l-0.09375 -1.90625l0 -5.609375l2.265625 0l0 19.078125l-2.171875 0l0 -2.0l-0.09375 0q-0.375 0.609375 -1.0625 1.171875q-0.671875 0.5625 -1.578125 0.90625q-0.90625 0.34375 -2.0 0.34375zm0.234375 -2.046875q1.171875 0 2.1875 -0.625q1.03125 -0.625 1.671875 -1.78125q0.640625 -1.171875 0.640625 -2.765625q0 -1.59375 -0.640625 -2.75q-0.640625 -1.171875 -1.671875 -1.796875q-1.015625 -0.625 -2.1875 -0.625q-1.171875 0 -2.203125 0.625q-1.015625 0.625 -1.65625 1.796875q-0.640625 1.15625 -0.640625 2.75q0 1.578125 0.640625 2.75q0.640625 1.171875 1.65625 1.796875q1.03125 0.625 2.203125 0.625z" fill-rule="nonzero"/><path fill="#ffffff" d="m293.84805 195.69614l0 -19.078125l2.265625 0l0 16.921875l8.34375 0l0 2.15625l-10.609375 0zm17.474213 0.421875q-1.53125 0 -2.671875 -0.578125q-1.140625 -0.59375 -1.796875 -1.609375q-0.65625 -1.03125 -0.65625 -2.34375q0 -1.484375 0.765625 -2.515625q0.78125 -1.03125 2.078125 -1.546875q1.3125 -0.515625 2.890625 -0.515625q0.90625 0 1.671875 0.15625q0.78125 0.140625 1.34375 0.34375q0.578125 0.1875 0.875 0.375l0 -0.828125q0 -1.546875 -1.09375 -2.453125q-1.09375 -0.90625 -2.671875 -0.90625q-1.109375 0 -2.09375 0.5q-0.96875 0.484375 -1.53125 1.375l-1.703125 -1.28125q0.53125 -0.796875 1.328125 -1.375q0.8125 -0.59375 1.828125 -0.90625q1.03125 -0.328125 2.171875 -0.328125q2.78125 0 4.34375 1.46875q1.578125 1.453125 1.578125 3.9375l0 8.609375l-2.15625 0l0 -1.953125l-0.109375 0q-0.34375 0.59375 -0.984375 1.140625q-0.640625 0.546875 -1.515625 0.890625q-0.859375 0.34375 -1.890625 0.34375zm0.203125 -2.0q1.171875 0 2.140625 -0.578125q0.984375 -0.59375 1.5625 -1.578125q0.59375 -0.984375 0.59375 -2.15625q-0.609375 -0.421875 -1.53125 -0.6875q-0.921875 -0.265625 -2.015625 -0.265625q-1.953125 0 -2.859375 0.796875q-0.90625 0.796875 -0.90625 1.96875q0 1.125 0.859375 1.8125q0.859375 0.6875 2.15625 0.6875zm16.498169 2.0q-1.09375 0 -2.0 -0.34375q-0.90625 -0.34375 -1.59375 -0.90625q-0.671875 -0.5625 -1.046875 -1.171875l-0.09375 0l0 2.0l-2.171875 0l0 -19.078125l2.265625 0l0 5.609375l-0.09375 1.90625l0.09375 0q0.375 -0.640625 1.046875 -1.203125q0.6875 -0.5625 1.59375 -0.90625q0.90625 -0.34375 2.0 -0.34375q1.875 0 3.328125 0.9375q1.46875 0.921875 2.328125 2.546875q0.859375 1.625 0.859375 3.734375q0 2.109375 -0.859375 3.734375q-0.859375 1.625 -2.328125 2.5625q-1.453125 0.921875 -3.328125 0.921875zm-0.234375 -2.046875q1.171875 0 2.1875 -0.625q1.03125 -0.625 1.65625 -1.796875q0.640625 -1.171875 0.640625 -2.75q0 -1.59375 -0.640625 -2.75q-0.625 -1.171875 -1.65625 -1.796875q-1.015625 -0.625 -2.1875 -0.625q-1.171875 0 -2.203125 0.625q-1.015625 0.625 -1.65625 1.796875q-0.640625 1.15625 -0.640625 2.75q0 1.59375 0.640625 2.765625q0.640625 1.15625 1.65625 1.78125q1.03125 0.625 2.203125 0.625zm16.012482 1.625l0 -19.078125l2.265625 0l0 19.078125l-2.265625 0zm6.0776367 0l0 -13.59375l2.171875 0l0 2.0l0.09375 0q0.546875 -0.984375 1.75 -1.703125q1.21875 -0.71875 2.65625 -0.71875q2.5 0 3.765625 1.453125q1.265625 1.453125 1.265625 3.84375l0 8.71875l-2.265625 0l0 -8.375q0 -1.96875 -0.953125 -2.78125q-0.9375 -0.8125 -2.421875 -0.8125q-1.125 0 -1.984375 0.625q-0.84375 0.625 -1.328125 1.609375q-0.484375 0.984375 -0.484375 2.078125l0 7.65625l-2.265625 0zm13.765564 -11.546875l0 -2.046875l8.265625 0l0 2.046875l-8.265625 0zm2.5 11.546875l0 -15.0625q0 -1.328125 0.578125 -2.265625q0.59375 -0.953125 1.59375 -1.453125q1.0 -0.515625 2.171875 -0.515625q0.78125 0 1.234375 0.09375q0.453125 0.09375 0.765625 0.21875l0 2.21875q-0.34375 -0.140625 -0.796875 -0.28125q-0.453125 -0.140625 -1.125 -0.140625q-0.828125 0 -1.5 0.59375q-0.65625 0.59375 -0.65625 1.765625l0 14.828125l-2.265625 0zm8.524567 0l0 -13.59375l2.171875 0l0 2.1875l0.09375 0q0.28125 -0.78125 0.90625 -1.34375q0.625 -0.578125 1.4375 -0.90625q0.8125 -0.34375 1.609375 -0.34375q0.609375 0 0.953125 0.078125q0.359375 0.0625 0.640625 0.1875l0 2.453125q-0.421875 -0.203125 -0.921875 -0.3125q-0.484375 -0.109375 -0.984375 -0.109375q-1.0 0 -1.828125 0.5625q-0.8125 0.5625 -1.3125 1.5q-0.5 0.921875 -0.5 2.046875l0 7.59375l-2.265625 0zm13.8220825 0.421875q-1.53125 0 -2.671875 -0.578125q-1.140625 -0.59375 -1.796875 -1.609375q-0.65625 -1.03125 -0.65625 -2.34375q0 -1.484375 0.765625 -2.515625q0.78125 -1.03125 2.078125 -1.546875q1.3125 -0.515625 2.890625 -0.515625q0.90625 0 1.671875 0.15625q0.78125 0.140625 1.34375 0.34375q0.578125 0.1875 0.875 0.375l0 -0.828125q0 -1.546875 -1.09375 -2.453125q-1.09375 -0.90625 -2.671875 -0.90625q-1.109375 0 -2.09375 0.5q-0.96875 0.484375 -1.53125 1.375l-1.703125 -1.28125q0.53125 -0.796875 1.328125 -1.375q0.8125 -0.59375 1.828125 -0.90625q1.03125 -0.328125 2.171875 -0.328125q2.78125 0 4.34375 1.46875q1.578125 1.453125 1.578125 3.9375l0 8.609375l-2.15625 0l0 -1.953125l-0.109375 0q-0.34375 0.59375 -0.984375 1.140625q-0.640625 0.546875 -1.515625 0.890625q-0.859375 0.34375 -1.890625 0.34375zm0.203125 -2.0q1.171875 0 2.140625 -0.578125q0.984375 -0.59375 1.5625 -1.578125q0.59375 -0.984375 0.59375 -2.15625q-0.609375 -0.421875 -1.53125 -0.6875q-0.921875 -0.265625 -2.015625 -0.265625q-1.953125 0 -2.859375 0.796875q-0.90625 0.796875 -0.90625 1.96875q0 1.125 0.859375 1.8125q0.859375 0.6875 2.15625 0.6875z" fill-rule="nonzero"/><path fill="#38761d" d="m476.75162 109.215706l150.86612 0l0 121.76379l-150.86612 0z" fill-rule="evenodd"/><path fill="#ffffff" d="m504.98254 179.6976l0 -19.078125l2.265625 0l0 16.921875l8.34375 0l0 2.15625l-10.609375 0zm19.060486 0.421875q-2.046875 0 -3.625 -0.953125q-1.5625 -0.96875 -2.46875 -2.59375q-0.890625 -1.640625 -0.890625 -3.671875q0 -2.03125 0.890625 -3.671875q0.90625 -1.640625 2.46875 -2.59375q1.578125 -0.953125 3.625 -0.953125q2.0625 0 3.625 0.96875q1.578125 0.96875 2.46875 2.609375q0.890625 1.640625 0.890625 3.640625q0 2.03125 -0.890625 3.671875q-0.890625 1.625 -2.46875 2.59375q-1.5625 0.953125 -3.625 0.953125zm0 -2.046875q1.234375 0 2.3125 -0.609375q1.078125 -0.625 1.734375 -1.78125q0.671875 -1.15625 0.671875 -2.78125q0 -1.625 -0.671875 -2.78125q-0.65625 -1.171875 -1.734375 -1.78125q-1.078125 -0.609375 -2.3125 -0.609375q-1.21875 0 -2.3125 0.609375q-1.09375 0.609375 -1.765625 1.78125q-0.65625 1.15625 -0.65625 2.78125q0 1.625 0.65625 2.78125q0.671875 1.15625 1.765625 1.78125q1.09375 0.609375 2.3125 0.609375zm15.793335 2.046875q-2.0 0 -3.5625 -0.9375q-1.5625 -0.953125 -2.453125 -2.59375q-0.875 -1.640625 -0.875 -3.6875q0 -2.078125 0.875 -3.703125q0.890625 -1.625 2.453125 -2.5625q1.5625 -0.953125 3.5625 -0.953125q2.28125 0 3.765625 1.046875q1.484375 1.046875 2.09375 2.703125l-2.046875 0.859375q-0.515625 -1.25 -1.515625 -1.90625q-1.0 -0.65625 -2.40625 -0.65625q-1.203125 0 -2.234375 0.640625q-1.015625 0.640625 -1.65625 1.796875q-0.640625 1.15625 -0.640625 2.734375q0 1.546875 0.640625 2.71875q0.640625 1.171875 1.65625 1.8125q1.03125 0.640625 2.234375 0.640625q1.4375 0 2.46875 -0.65625q1.046875 -0.671875 1.546875 -1.90625l2.03125 0.859375q-0.671875 1.5625 -2.171875 2.65625q-1.484375 1.09375 -3.765625 1.09375zm12.608398 0q-1.53125 0 -2.671875 -0.578125q-1.140625 -0.59375 -1.796875 -1.609375q-0.65625 -1.03125 -0.65625 -2.34375q0 -1.484375 0.765625 -2.515625q0.78125 -1.03125 2.078125 -1.546875q1.3125 -0.515625 2.890625 -0.515625q0.90625 0 1.671875 0.15625q0.78125 0.140625 1.34375 0.34375q0.578125 0.1875 0.875 0.375l0 -0.828125q0 -1.546875 -1.09375 -2.453125q-1.09375 -0.90625 -2.671875 -0.90625q-1.109375 0 -2.09375 0.5q-0.96875 0.484375 -1.53125 1.375l-1.703125 -1.28125q0.53125 -0.796875 1.328125 -1.375q0.8125 -0.59375 1.828125 -0.90625q1.03125 -0.328125 2.171875 -0.328125q2.78125 0 4.34375 1.46875q1.578125 1.453125 1.578125 3.9375l0 8.609375l-2.15625 0l0 -1.953125l-0.109375 0q-0.34375 0.59375 -0.984375 1.140625q-0.640625 0.546875 -1.515625 0.890625q-0.859375 0.34375 -1.890625 0.34375zm0.203125 -2.0q1.171875 0 2.140625 -0.578125q0.984375 -0.59375 1.5625 -1.578125q0.59375 -0.984375 0.59375 -2.15625q-0.609375 -0.421875 -1.53125 -0.6875q-0.921875 -0.265625 -2.015625 -0.265625q-1.953125 0 -2.859375 0.796875q-0.90625 0.796875 -0.90625 1.96875q0 1.125 0.859375 1.8125q0.859375 0.6875 2.15625 0.6875zm9.591919 1.578125l0 -19.078125l2.265625 0l0 19.078125l-2.265625 0zm12.261841 0l0 -19.078125l11.046875 0l0 2.15625l-8.78125 0l0 16.921875l-2.265625 0zm1.203125 -8.265625l0 -2.125l8.984375 0l0 2.125l-8.984375 0zm18.295044 8.6875q-1.390625 0 -2.75 -0.578125q-1.359375 -0.59375 -2.40625 -1.734375q-1.03125 -1.140625 -1.515625 -2.859375l2.15625 -0.875q0.453125 1.65625 1.65625 2.78125q1.203125 1.109375 2.90625 1.109375q1.0625 0 1.953125 -0.375q0.90625 -0.390625 1.453125 -1.140625q0.546875 -0.75 0.546875 -1.8125q0 -0.96875 -0.46875 -1.625q-0.46875 -0.671875 -1.40625 -1.1875q-0.9375 -0.515625 -2.296875 -1.0l-1.1875 -0.421875q-0.8125 -0.296875 -1.625 -0.703125q-0.8125 -0.421875 -1.5 -1.015625q-0.671875 -0.609375 -1.078125 -1.4375q-0.390625 -0.84375 -0.390625 -2.0q0 -1.375 0.75 -2.515625q0.75 -1.15625 2.046875 -1.84375q1.3125 -0.703125 3.015625 -0.703125q1.78125 0 3.0 0.65625q1.21875 0.65625 1.921875 1.546875q0.703125 0.890625 0.9375 1.640625l-2.09375 0.90625q-0.171875 -0.5625 -0.625 -1.15625q-0.453125 -0.609375 -1.21875 -1.015625q-0.75 -0.421875 -1.890625 -0.421875q-0.9375 0 -1.75 0.375q-0.8125 0.375 -1.3125 1.03125q-0.484375 0.65625 -0.484375 1.5q0 1.203125 0.953125 1.90625q0.96875 0.6875 2.59375 1.25l1.25 0.421875q0.859375 0.296875 1.75 0.71875q0.890625 0.421875 1.65625 1.09375q0.78125 0.671875 1.25 1.640625q0.484375 0.96875 0.484375 2.328125q0 1.46875 -0.578125 2.53125q-0.5625 1.046875 -1.5 1.71875q-0.921875 0.65625 -2.03125 0.953125q-1.109375 0.3125 -2.171875 0.3125z" fill-rule="nonzero"/><path fill="#38761d" d="m666.2726 109.214195l150.86615 0l0 121.76379l-150.86615 0z" fill-rule="evenodd"/><path fill="#ffffff" d="m692.3311 163.69609l0 -19.078125l2.78125 0l9.5625 15.1875l0.109375 0l-0.109375 -3.6875l0 -11.5l2.265625 0l0 19.078125l-2.34375 0l-10.0 -15.90625l-0.09375 0l0.09375 3.671875l0 12.234375l-2.265625 0zm24.215271 0.421875q-1.984375 0 -3.515625 -0.921875q-1.53125 -0.9375 -2.40625 -2.5625q-0.859375 -1.625 -0.859375 -3.703125q0 -1.953125 0.8125 -3.59375q0.8125 -1.65625 2.296875 -2.65625q1.484375 -1.0 3.484375 -1.0q2.015625 0 3.46875 0.890625q1.453125 0.890625 2.234375 2.46875q0.796875 1.5625 0.796875 3.59375q0 0.1875 -0.015625 0.375q-0.015625 0.1875 -0.03125 0.3125l-11.703125 0l0 -1.859375l9.359375 0q-0.03125 -0.5625 -0.265625 -1.21875q-0.21875 -0.65625 -0.71875 -1.21875q-0.484375 -0.578125 -1.25 -0.9375q-0.765625 -0.359375 -1.875 -0.359375q-1.34375 0 -2.3125 0.6875q-0.96875 0.671875 -1.5 1.84375q-0.515625 1.171875 -0.515625 2.671875q0 1.734375 0.671875 2.875q0.671875 1.140625 1.71875 1.703125q1.046875 0.5625 2.203125 0.5625q1.484375 0 2.453125 -0.703125q0.984375 -0.703125 1.5625 -1.75l1.921875 0.9375q-0.796875 1.546875 -2.296875 2.5625q-1.484375 1.0 -3.71875 1.0zm7.623047 -14.015625l7.96875 0l0 2.046875l-7.96875 0l0 -2.046875zm2.375 10.015625l0 -13.859375l2.265625 0l0 13.3125q0 1.0625 0.4375 1.65625q0.4375 0.578125 1.453125 0.578125q0.453125 0 0.828125 -0.125q0.375 -0.140625 0.65625 -0.328125l0 2.203125q-0.34375 0.171875 -0.765625 0.265625q-0.40625 0.09375 -1.09375 0.09375q-1.703125 0 -2.75 -1.0q-1.03125 -1.0 -1.03125 -2.796875zm11.567017 3.578125l-4.375 -13.59375l2.34375 0l3.21875 10.734375l0.03125 0l3.4375 -10.734375l2.328125 0l3.4375 10.71875l0.015625 0l3.234375 -10.71875l2.296875 0l-4.40625 13.59375l-2.296875 0l-3.515625 -10.84375l0 0l-3.484375 10.84375l-2.265625 0zm23.82013 0.421875q-2.046875 0 -3.625 -0.953125q-1.5625 -0.96875 -2.46875 -2.59375q-0.890625 -1.640625 -0.890625 -3.671875q0 -2.03125 0.890625 -3.671875q0.90625 -1.640625 2.46875 -2.59375q1.578125 -0.953125 3.625 -0.953125q2.0625 0 3.625 0.96875q1.578125 0.96875 2.46875 2.609375q0.890625 1.640625 0.890625 3.640625q0 2.03125 -0.890625 3.671875q-0.890625 1.625 -2.46875 2.59375q-1.5625 0.953125 -3.625 0.953125zm0 -2.046875q1.234375 0 2.3125 -0.609375q1.078125 -0.625 1.734375 -1.78125q0.671875 -1.15625 0.671875 -2.78125q0 -1.625 -0.671875 -2.78125q-0.65625 -1.171875 -1.734375 -1.78125q-1.078125 -0.609375 -2.3125 -0.609375q-1.21875 0 -2.3125 0.609375q-1.09375 0.609375 -1.765625 1.78125q-0.65625 1.15625 -0.65625 2.78125q0 1.625 0.65625 2.78125q0.671875 1.15625 1.765625 1.78125q1.09375 0.609375 2.3125 0.609375zm9.354797 1.625l0 -13.59375l2.171875 0l0 2.1875l0.09375 0q0.28125 -0.78125 0.90625 -1.34375q0.625 -0.578125 1.4375 -0.90625q0.8125 -0.34375 1.609375 -0.34375q0.609375 0 0.953125 0.078125q0.359375 0.0625 0.640625 0.1875l0 2.453125q-0.421875 -0.203125 -0.921875 -0.3125q-0.484375 -0.109375 -0.984375 -0.109375q-1.0 0 -1.828125 0.5625q-0.8125 0.5625 -1.3125 1.5q-0.5 0.921875 -0.5 2.046875l0 7.59375l-2.265625 0zm10.129395 0l0 -19.078125l2.265625 0l0 19.078125l-2.265625 0zm0.859375 -2.765625l0 -2.90625l7.8125 -7.921875l2.875 0l0 0.109375l-10.6875 10.71875zm8.125 2.765625l-4.90625 -7.296875l1.609375 -1.578125l6.015625 8.765625l0 0.109375l-2.71875 0z" fill-rule="nonzero"/><path fill="#ffffff" d="m729.2631 195.69609l0 -19.078125l11.046814 0l0 2.15625l-8.78125 0l0 16.921875l-2.265564 0zm1.203064 -8.265625l0 -2.125l8.984375 0l0 2.125l-8.984375 0zm18.295044 8.6875q-1.390625 0 -2.75 -0.578125q-1.359375 -0.59375 -2.40625 -1.734375q-1.03125 -1.140625 -1.515625 -2.859375l2.15625 -0.875q0.453125 1.65625 1.65625 2.78125q1.203125 1.109375 2.90625 1.109375q1.0625 0 1.953125 -0.375q0.90625 -0.390625 1.453125 -1.140625q0.546875 -0.75 0.546875 -1.8125q0 -0.96875 -0.46875 -1.625q-0.46875 -0.671875 -1.40625 -1.1875q-0.9375 -0.515625 -2.296875 -1.0l-1.1875 -0.421875q-0.8125 -0.296875 -1.625 -0.703125q-0.8125 -0.421875 -1.5 -1.015625q-0.671875 -0.609375 -1.078125 -1.4375q-0.390625 -0.84375 -0.390625 -2.0q0 -1.375 0.75 -2.515625q0.75 -1.15625 2.046875 -1.84375q1.3125 -0.703125 3.015625 -0.703125q1.78125 0 3.0 0.65625q1.21875 0.65625 1.921875 1.546875q0.703125 0.890625 0.9375 1.640625l-2.09375 0.90625q-0.171875 -0.5625 -0.625 -1.15625q-0.453125 -0.609375 -1.21875 -1.015625q-0.75 -0.421875 -1.890625 -0.421875q-0.9375 0 -1.75 0.375q-0.8125 0.375 -1.3125 1.03125q-0.484375 0.65625 -0.484375 1.5q0 1.203125 0.953125 1.90625q0.96875 0.6875 2.59375 1.25l1.25 0.421875q0.859375 0.296875 1.75 0.71875q0.890625 0.421875 1.65625 1.09375q0.78125 0.671875 1.25 1.640625q0.484375 0.96875 0.484375 2.328125q0 1.46875 -0.578125 2.53125q-0.5625 1.046875 -1.5 1.71875q-0.921875 0.65625 -2.03125 0.953125q-1.109375 0.3125 -2.171875 0.3125z" fill-rule="nonzero"/><path fill="#000000" fill-opacity="0.0" d="m1052.5118 506.72177l150.86609 0l0 51.716522l-150.86609 0z" fill-rule="evenodd"/><path fill="#000000" d="m1072.4962 541.74365q-2.109375 0 -3.90625 -0.765625q-1.78125 -0.78125 -3.09375 -2.140625q-1.3125 -1.359375 -2.046875 -3.171875q-0.734375 -1.8125 -0.734375 -3.890625q0 -2.078125 0.734375 -3.875q0.734375 -1.796875 2.046875 -3.171875q1.3125 -1.375 3.09375 -2.140625q1.796875 -0.78125 3.90625 -0.78125q2.109375 0 3.890625 0.78125q1.78125 0.765625 3.09375 2.140625q1.328125 1.375 2.0625 3.171875q0.734375 1.796875 0.734375 3.875q0 2.078125 -0.734375 3.890625q-0.734375 1.796875 -2.0625 3.171875q-1.3125 1.359375 -3.09375 2.140625q-1.78125 0.765625 -3.890625 0.765625zm5.96875 0.78125l-4.84375 -7.234375l1.75 -1.1875l4.859375 7.265625l-1.765625 1.15625zm-5.96875 -2.9375q2.046875 0 3.75 -0.984375q1.71875 -0.984375 2.71875 -2.75q1.015625 -1.78125 1.015625 -4.078125q0 -2.3125 -1.015625 -4.0625q-1.0 -1.765625 -2.703125 -2.75q-1.6875 -1.0 -3.765625 -1.0q-2.046875 0 -3.765625 1.0q-1.703125 0.984375 -2.71875 2.75q-1.015625 1.75 -1.015625 4.0625q0 2.296875 1.015625 4.078125q1.015625 1.765625 2.71875 2.75q1.71875 0.984375 3.765625 0.984375zm17.5896 2.15625q-2.5 0 -3.765625 -1.453125q-1.265625 -1.46875 -1.265625 -4.0l0 -8.5625l2.265625 0l0 8.203125q0 2.03125 0.921875 2.90625q0.9375 0.859375 2.328125 0.859375q1.203125 0 2.078125 -0.625q0.875 -0.625 1.359375 -1.609375q0.484375 -0.984375 0.484375 -2.0625l0 -7.671875l2.265625 0l0 13.59375l-2.171875 0l0 -1.96875l-0.09375 0q-0.359375 0.640625 -1.046875 1.1875q-0.671875 0.546875 -1.53125 0.875q-0.859375 0.328125 -1.828125 0.328125zm15.945801 0q-1.984375 0 -3.515625 -0.921875q-1.53125 -0.9375 -2.40625 -2.5625q-0.859375 -1.625 -0.859375 -3.703125q0 -1.953125 0.8125 -3.59375q0.8125 -1.65625 2.296875 -2.65625q1.484375 -1.0 3.484375 -1.0q2.015625 0 3.46875 0.890625q1.453125 0.890625 2.234375 2.46875q0.796875 1.5625 0.796875 3.59375q0 0.1875 -0.015625 0.375q-0.015625 0.1875 -0.03125 0.3125l-11.703125 0l0 -1.859375l9.359375 0q-0.03125 -0.5625 -0.265625 -1.21875q-0.21875 -0.65625 -0.71875 -1.21875q-0.484375 -0.578125 -1.25 -0.9375q-0.765625 -0.359375 -1.875 -0.359375q-1.34375 0 -2.3125 0.6875q-0.96875 0.671875 -1.5 1.84375q-0.515625 1.171875 -0.515625 2.671875q0 1.734375 0.671875 2.875q0.671875 1.140625 1.71875 1.703125q1.046875 0.5625 2.203125 0.5625q1.484375 0 2.453125 -0.703125q0.984375 -0.703125 1.5625 -1.75l1.921875 0.9375q-0.796875 1.546875 -2.296875 2.5625q-1.484375 1.0 -3.71875 1.0zm8.944946 -0.421875l0 -13.59375l2.171875 0l0 2.1875l0.09375 0q0.28125 -0.78125 0.90625 -1.34375q0.625 -0.578125 1.4375 -0.90625q0.8125 -0.34375 1.609375 -0.34375q0.609375 0 0.953125 0.078125q0.359375 0.0625 0.640625 0.1875l0 2.453125q-0.421875 -0.203125 -0.921875 -0.3125q-0.484375 -0.109375 -0.984375 -0.109375q-1.0 0 -1.828125 0.5625q-0.8125 0.5625 -1.3125 1.5q-0.5 0.921875 -0.5 2.046875l0 7.59375l-2.265625 0zm14.489014 5.0625q-0.09375 0.1875 -0.203125 0.40625q-0.09375 0.234375 -0.125 0.28125l-2.34375 0q0.15625 -0.34375 0.34375 -0.765625q0.1875 -0.40625 0.453125 -0.984375q0.15625 -0.34375 0.28125 -0.640625q0.140625 -0.296875 0.3125 -0.65625q0.1875 -0.359375 0.421875 -0.890625l1.859375 -4.046875l0.515625 -1.25l4.109375 -10.109375l2.4375 0l-7.03125 16.234375q-0.109375 0.234375 -0.34375 0.75q-0.21875 0.53125 -0.421875 1.015625q-0.1875 0.5 -0.265625 0.65625zm0.5 -5.625l-5.765625 -13.03125l2.453125 0l4.265625 10.109375l0.21875 0l-1.171875 2.921875zm13.653442 0.5625l7.25 -19.078125l2.546875 0l7.25 19.078125l-2.46875 0l-5.25 -14.265625l-0.75 -2.046875l-0.109375 0l-0.75 2.046875l-5.25 14.265625l-2.46875 0zm8.25 -5.25l0 -2.140625l4.40625 0l0.765625 2.140625l-5.171875 0zm-5.171875 0l0.78125 -2.140625l4.390625 0l0 2.140625l-5.171875 0zm16.500244 5.25l0 -19.078125l6.4375 0q1.59375 0 2.953125 0.71875q1.359375 0.71875 2.171875 1.984375q0.8125 1.265625 0.8125 2.9375q0 1.65625 -0.8125 2.9375q-0.8125 1.28125 -2.171875 2.0q-1.359375 0.71875 -2.953125 0.71875l-5.234375 0l0 -2.15625l5.28125 0q1.125 0 1.921875 -0.515625q0.796875 -0.53125 1.234375 -1.328125q0.4375 -0.796875 0.4375 -1.65625q0 -0.84375 -0.4375 -1.640625q-0.4375 -0.8125 -1.234375 -1.328125q-0.796875 -0.515625 -1.921875 -0.515625l-4.21875 0l0 16.921875l-2.265625 0zm15.354004 0l0 -19.078125l2.265625 0l0 19.078125l-2.265625 0z" fill-rule="nonzero"/></g></svg>
\ No newline at end of file
diff --git a/docs/images/profile-diamond.png b/docs/images/profile-diamond.png
index 5f14756..8c12fbc 100644
--- a/docs/images/profile-diamond.png
+++ b/docs/images/profile-diamond.png
Binary files differ
diff --git a/docs/images/syssrv-apk-assets-focus.png b/docs/images/syssrv-apk-assets-focus.png
deleted file mode 100644
index c5070dd..0000000
--- a/docs/images/syssrv-apk-assets-focus.png
+++ /dev/null
Binary files differ
diff --git a/docs/images/syssrv-apk-assets-two.png b/docs/images/syssrv-apk-assets-two.png
deleted file mode 100644
index dea36ef..0000000
--- a/docs/images/syssrv-apk-assets-two.png
+++ /dev/null
Binary files differ
diff --git a/docs/instrumentation/heapprofd-api.md b/docs/instrumentation/heapprofd-api.md
index 88f6dff..0ef3f90 100644
--- a/docs/instrumentation/heapprofd-api.md
+++ b/docs/instrumentation/heapprofd-api.md
@@ -36,7 +36,7 @@
 ```
 $ cd perfetto
 perfetto/ $ tools/install-build-deps --android
-perfetto/ $ tools/build_all_configs.py --android
+perfetto/ $ tools/setup_all_configs.py --android
 perfetto/ $ ninja -C out/android_release_incl_heapprofd_arm64 \
 libheapprofd_standalone_client.so
 ```
diff --git a/docs/instrumentation/tracing-sdk.md b/docs/instrumentation/tracing-sdk.md
index e8916ea..75d10db 100644
--- a/docs/instrumentation/tracing-sdk.md
+++ b/docs/instrumentation/tracing-sdk.md
@@ -1,6 +1,6 @@
 # Tracing SDK
 
-The Perfetto Tracing SDK is a C++11 library that allows userspace applications
+The Perfetto Tracing SDK is a C++17 library that allows userspace applications
 to emit trace events and add more app-specific context to a Perfetto trace.
 
 When using the Tracing SDK there are two main aspects to consider:
@@ -30,12 +30,12 @@
 To start using the Client API, first check out the latest SDK release:
 
 ```bash
-git clone https://android.googlesource.com/platform/external/perfetto -b v26.0
+git clone https://android.googlesource.com/platform/external/perfetto -b v29.0
 ```
 
 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. The sources are self-contained and require only a C++11 compliant
+build systems. The sources are self-contained and require only a C++17 compliant
 standard library.
 
 For example, to add the SDK to a CMake project, edit your CMakeLists.txt:
@@ -52,6 +52,21 @@
 # Link the library to your main executable.
 add_executable(example example.cc)
 target_link_libraries(example perfetto ${CMAKE_THREAD_LIBS_INIT})
+
+if (WIN32)
+  # The perfetto library contains many symbols, so it needs the big object
+  # format.
+  target_compile_options(perfetto PRIVATE "/bigobj")
+  # Disable legacy features in windows.h.
+  add_definitions(-DWIN32_LEAN_AND_MEAN -DNOMINMAX)
+  # On Windows we should link to WinSock2.
+  target_link_libraries(example ws2_32)
+endif (WIN32)
+
+# Enable standards-compliant mode when using the Visual Studio compiler.
+if (MSVC)
+  target_compile_options(example PRIVATE "/permissive-")
+endif (MSVC)
 ```
 
 Next, initialize Perfetto in your program:
@@ -103,6 +118,7 @@
     perfetto::Category("network")
         .SetDescription("Network upload and download statistics"));
 
+PERFETTO_TRACK_EVENT_STATIC_STORAGE();
 ...
 
 int main(int argc, char** argv) {
diff --git a/docs/instrumentation/track-events.md b/docs/instrumentation/track-events.md
index e32a8b4..37f7796 100644
--- a/docs/instrumentation/track-events.md
+++ b/docs/instrumentation/track-events.md
@@ -285,7 +285,7 @@
 
 ```C++
 perfetto::DynamicCategory dynamic_category{"nodejs.something"};
-TRACE_EVENT(dynamic_category, "SomeEvent", ...);
+TRACE_EVENT_BEGIN(dynamic_category, "SomeEvent", ...);
 ```
 
 TIP: It's also possible to use dynamic event names by passing `nullptr` as
diff --git a/docs/quickstart/heap-profiling.md b/docs/quickstart/heap-profiling.md
index 6f9692f..3a58f20 100644
--- a/docs/quickstart/heap-profiling.md
+++ b/docs/quickstart/heap-profiling.md
@@ -60,7 +60,7 @@
 script. Then start the profile:
 
 ```bash
-python /path/to/heap_profile -n system_server 
+python /path/to/heap_profile -n system_server
 ```
 
 ## View profile
@@ -70,7 +70,7 @@
 _"Heap profile"_.
 
 ![Profile Diamond](/docs/images/profile-diamond.png)
-![Native Flamegraph](/docs/images/syssrv-apk-assets-two.png)
+![Native Flamegraph](/docs/images/native-heap-prof.png)
 
 ## Next steps
 
diff --git a/docs/quickstart/linux-tracing.md b/docs/quickstart/linux-tracing.md
index d60822a..6ec60b0 100644
--- a/docs/quickstart/linux-tracing.md
+++ b/docs/quickstart/linux-tracing.md
@@ -22,7 +22,7 @@
 3. Generate the build configuration
 ```bash
 tools/gn gen --args='is_debug=false' out/linux
-# Or use `tools/build_all_configs.py` to generate more build configs.
+# Or use `tools/setup_all_configs.py` to generate more build configs.
 ```
 
 4. Build the Linux tracing binaries (On Linux it uses a hermetic clang toolchain, downloaded as part of step 2):
diff --git a/docs/quickstart/trace-analysis.md b/docs/quickstart/trace-analysis.md
index 62269dc..fd0d5cc 100644
--- a/docs/quickstart/trace-analysis.md
+++ b/docs/quickstart/trace-analysis.md
@@ -20,7 +20,7 @@
 ./trace_processor trace.perfetto-trace
 
 # Start a local trace processor instance to replace wasm module in the UI
-./trace_processor trace.perfetto-trace --http
+./trace_processor trace.perfetto-trace --httpd
 ```
 
 NOTE: In HTTP mode the trace will be loaded into the `trace_processor` and
diff --git a/docs/quickstart/traceconv.md b/docs/quickstart/traceconv.md
index 58458a8..22b902c 100644
--- a/docs/quickstart/traceconv.md
+++ b/docs/quickstart/traceconv.md
@@ -16,7 +16,7 @@
 - `systrace`: the ftrace text format used by Android systrace
 - `profile` : pprof-like format. Either for traces with with
   [native heap profiler](/docs/data-sources/native-heap-profiler.md) dumps or
-  [callstack sampling](docs/quickstart/callstack-sampling) (note however
+  [callstack sampling](/docs/quickstart/callstack-sampling.md) (note however
   callstacks requires the `--perf` flag).
 
 ## Setup
diff --git a/docs/toc.md b/docs/toc.md
index 6ad0e7c..1d37192 100644
--- a/docs/toc.md
+++ b/docs/toc.md
@@ -1,5 +1,7 @@
 * [Overview](README.md)
 
+* [Tracing 101](tracing-101.md)
+
 * [Quickstart](#)
   * [Record traces on Android](quickstart/android-tracing.md)
   * [Record traces on Linux](quickstart/linux-tracing.md)
@@ -9,6 +11,8 @@
   * [Heap profiling](quickstart/heap-profiling.md)
   * [Callstack sampling on Android](quickstart/callstack-sampling.md)
 
+* [FAQ](faq.md)
+
 * [Case studies](#)
   * [Android boot tracing](case-studies/android-boot-tracing.md)
   * [Debugging memory usage](case-studies/memory.md)
@@ -16,8 +20,8 @@
 * [Data sources](#)
   * [Memory](#)
     * [Counters and events](data-sources/memory-counters.md)
-    * [Native heap profiler](data-sources/native-heap-profiler.md)
-    * [Java heap profiler](data-sources/java-heap-profiler.md)
+    * [Heap profiler](data-sources/native-heap-profiler.md)
+    * [Java heap dumps](data-sources/java-heap-profiler.md)
   * [CPU](#)
     * [Scheduling events](data-sources/cpu-scheduling.md)
     * [System calls](data-sources/syscalls.md)
@@ -25,6 +29,7 @@
   * [Power](#)
     * [Battery counters and rails](data-sources/battery-counters.md)
   * [Android system](#)
+    * [Android game intervention list](data-sources/android-game-intervention-list.md)
     * [Atrace instrumentation](data-sources/atrace.md)
     * [Android log (logcat)](data-sources/android-log.md)
     * [Android Janks](data-sources/frametimeline.md)
@@ -37,10 +42,12 @@
 * [Trace analysis](#)
   * [Trace Processor (SQL)](analysis/trace-processor.md)
   * [Batch Trace Processor](analysis/batch-trace-processor.md)
+  * [Standard library](analysis/stdlib-docs.autogen)
   * [Trace-based metrics](analysis/metrics.md)
   * [Common queries](analysis/common-queries.md)
   * [SQL tables](analysis/sql-tables.autogen)
   * [Stats table](analysis/sql-stats.autogen)
+  * [Pivot tables](analysis/pivot-tables.md)
 
 * [Trace visualization](#)
   * [Perfetto UI](visualization/perfetto-ui.md)
@@ -69,15 +76,17 @@
     * [Embedding Perfetto](contributing/embedding.md)
     * [Releasing the SDK](contributing/sdk-releasing.md)
     * [Chrome branches](contributing/chrome-branches.md)
+    * [UI development](contributing/ui-development.md)
+    * [Press](contributing/perfetto-in-the-press.md)
 
 * [Design documents](#)
     * [API and ABI surface](design-docs/api-and-abi.md)
+    * [Batch Trace Processor](design-docs/batch-trace-processor.md)
     * [Heapprofd design](design-docs/heapprofd-design.md)
     * [Heapprofd wire protocol](design-docs/heapprofd-wire-protocol.md)
     * [Heapprofd sampling](design-docs/heapprofd-sampling.md)
     * [Life of a tracing session](design-docs/life-of-a-tracing-session.md)
     * [Perfetto CI](design-docs/continuous-integration.md)
-    * [Pivot tables](design-docs/pivot-tables.md)
     * [ProtoZero](design-docs/protozero.md)
     * [Security model](design-docs/security-model.md)
     * [Statsd Checkpoint Atoms](design-docs/checkpoint-atoms.md)
diff --git a/docs/tracing-101.md b/docs/tracing-101.md
new file mode 100644
index 0000000..61ea985
--- /dev/null
+++ b/docs/tracing-101.md
@@ -0,0 +1,285 @@
+# Tracing 101
+*This page provides a birds-eye view of performance analysis using Perfetto.
+The aim is for to orient even people who have no idea what "tracing" is. You
+should walk away from this page with a good understanding of the available
+capabilities in Perfetto and how they can be used to improve performance of a
+system.*
+
+## Introduction to...
+### Performance
+Performance analysis is concerned with making software run *better*.
+The definition of *better* varies widely and depends on the situation.
+Examples include:
+* performing the same work using fewer resources (CPU, memory,
+  network, battery, etc.)
+* increasing utilization of available resources
+* identifying and eliminating unnecessary work altogether
+
+Much of the difficulty in improving performance comes from
+identifying the root cause of performance issues. Modern software systems are
+complicated, having a lot of components and a web of cross-interactions.
+Techniques which help engineers understand the execution of a system
+and pinpoint issues that are critical.
+
+**Tracing** and **profiling** are two such widely-used techniques for
+performance analysis. **Perfetto** is an open-source suite of tools, combining
+tracing and profiling to give users powerful insights into their system.
+
+### Tracing
+**Tracing** involves collecting highly detailed data about the execution
+of a system. A single continuous session of recording is called a trace file
+or **trace** for short.
+
+Traces contain enough detail to fully reconstruct the timeline of events.
+They often include low-level kernel events like scheduler context switches,
+thread wakeups, syscalls, etc. With the "right" trace, reproduction of a
+performance bug is not needed as the trace provides all necessary context.
+
+Application code is also **instrumented** in areas of the program which are
+considered to be *important*. This instrumentation keeps track of what the
+program was doing over time (e.g. which functions were being run, or how long
+each call took) and context about the execution (e.g. what were the parameters
+to a function call, or why was a function run).
+
+The level of detail in traces makes it impractical to read traces directly
+like a log file in all but the simplest cases. Instead, a combination of
+**trace analysis** libraries and **trace viewers** are used. Trace analysis
+libraries provide a way for users to extract and summarize trace events in
+a programmatic manner. Trace viewers visualize the events in a trace on a
+timeline which give users a graphical view of what their system was doing
+over time.
+
+#### Logging vs tracing
+A good intuition is that logging is to functional testing what
+tracing is to performance analysis. Tracing is, in a sense, "structured"
+logging: instead of having arbitrary strings emitted from parts of the system,
+tracing reflects the detailed state of a system in a structured way to allow
+reconstruction of the timeline of events.
+
+Moreover, tracing frameworks (like Perfetto) place heavy emphasis
+on having minimal overhead. This is essential so that the framework
+does not significantly disrupt whatever is being measured: modern frameworks
+are fast enough that they can measure execution at the nanosecond level
+without significantly impacting the execution speed of the program.
+
+*Small aside: theoretically, tracing frameworks are powerful enough to act as
+a logging system as well. However, the utilization of each in practice is
+different enough that the two tend to be separate.*
+
+#### Metrics vs tracing
+Metrics are numerical values which track the performance of a system over time.
+Usually metrics map to high-level concepts. Examples of metrics include: CPU
+usage, memory usage, network bandwidth, etc. Metrics are collected directly from
+the app or operating system while the program is running.
+
+After glimpsing the power of tracing, a natural question arises: why bother
+with high level metrics at all? Why not instead just use use tracing and
+compute metrics on resulting traces? In some settings, this may indeed be the
+right approach. In local and lab situations using **trace-based metrics**,
+where metrics are computed from traces instead of collecting them directly,
+is a powerful approach. If a metric regresses, it's easy to open the trace
+to root cause why that happened.
+
+However, trace-based metrics are not a universal solution. When running in
+production, the heavyweight nature of traces can make it impractical to collect
+them 24/7. Computing a metric with a trace can take megabytes of data vs bytes
+for direct metric collection.
+
+Using metrics is the right choice when you want to understand the performance
+of a system over time but do not want to or can not pay the cost of collecting
+traces. In these situations, traces should be used as a **root-causing** tool.
+When your metrics show there is a problem, targeted tracing can be rolled out
+to understand why the regression may have happened.
+
+### Profiling
+**Profiling** involves sampling some usage of a resource by
+a program. A single continuous session of recording is known as a **profile**.
+
+Each sample collects the function callstack (i.e. the line of code along with
+all calling functions). Generally this information is aggregated across the
+profile. For each seen callstack, the aggregation gives the percentage of usage
+of the resource by that callstack. By far the most common types of profiling are
+**memory profiling** and **CPU profiling**.
+
+Memory profiling is used to understand which parts of a program are allocating
+memory on the heap. The profiler generally hooks into `malloc` (and `free`)
+calls of a native (C/C++/Rust/etc.) program to sample the callstacks
+calling `malloc`. Information about how many bytes were allocated is also
+retained. CPU profiling is used for understanding where the program is
+spending CPU time. The profiler captures the callstack running on a CPU
+over time. Generally this is done periodically (e.g. every 50ms), but can be
+also be done when certain events happen in the operating system.
+
+#### Profiling vs tracing
+There are two main questions for comparing profiling and tracing:
+1. Why profile my program statistically when I can just trace *everything*?
+2. Why use tracing to reconstruct the timeline of events when profiling gives me
+   the exact line of code using the most resources?
+
+##### When to use profiling over tracing
+Traces cannot feasibly capture execution of extreme high frequency
+events e.g. every function call. Profiling tools fill this niche: by
+sampling, they can significantly cut down on how much information they store.
+The statistical nature of profilers are rarely a problem; the sampling
+algorithms for profilers are specifically designed to capture data which is
+highly representative of the real resource use.
+
+*Aside: a handful of very specialized tracing tools exist which
+can capture every function call (e.g.
+[magic-trace](https://github.com/janestreet/magic-trace)) but they output
+*gigabytes* of data every second which make them impractical for anything
+beyond investigating tiny snippets of code. They also generally have higher
+overhead than general purpose tracing tools.*
+
+##### When to use tracing over profiling
+While profilers give callstacks where resources are being used, they lack
+information about *why* that happened. For example, why was malloc being called
+by function *foo()* so many times? All they say is *foo()* allocated X bytes
+over Y calls to `malloc`. Traces are excellent at providing this exact context:
+application instrumentation and low-level kernel events together provide
+deep insight into why code was run in the first place.
+
+NOTE: Perfetto supports collecting, analyzing and visualizing both profiles
+and traces at the same time so you can have the best of both worlds!
+
+## Perfetto
+Perfetto is a suite of tools for performance analysis of software. Its purpose
+is to empower engineers to understand where resources are being used by their
+systems. It helps identify the changes they can make to improve performance
+and verify the impact of those changes.
+
+NOTE: In Perfetto, since profiles and traces can be collected simultaneously, 
+we call everything a "trace" even if it may contain (only) profiling data
+inside.
+
+### Recording traces
+Perfetto is highly configurable when it comes to recording traces. There are
+literally hundreds of knobs which can be tweaked to control what data is
+collected, how it should be collected, how much information a trace should
+contain etc.
+
+[Record traces on Linux quickstart](/docs/quickstart/linux-tracing.md) is
+a good place to start if you're unfamiliar with Perfetto. For Android
+developers,
+[Record traces on Android quickstart](/docs/quickstart/android-tracing.md) will
+be more applicable. The [trace configuration](/docs/concepts/config.md) page
+is also useful to consult as a reference.
+
+The following sub-sections give an overview of various points worth considering
+when recording Perfetto traces.
+
+#### Kernel tracing
+Perfetto integrates closely with the Linux kernel's
+[ftrace](https://www.kernel.org/doc/Documentation/trace/ftrace.txt) tracing
+system to record kernel events (e.g. scheduling, syscalls, wakeups). The
+[scheduling](/docs/data-sources/cpu-scheduling.md),
+[syscall](/docs/data-sources/syscalls.md) and
+[CPU frequency](/docs/data-sources/cpu-freq.md) data source pages give
+examples of configuring ftrace collection.
+
+Natively supported ftrace events can be found in the fields of
+[this proto message](/docs/reference/trace-packet-proto.autogen#FtraceEvent).
+Perfetto also supports collecting ftrace events it does not natively understand
+(i.e. it does not have a protobuf message for) as a
+["generic"](/docs/reference/trace-packet-proto.autogen#GenericFtraceEvent)
+events. These events are encoded as key-value pairs, similar to a JSON
+dictionary.
+
+It is strongly discouraged to rely on generic events for production use cases:
+the inefficient encoding causes trace size bloat and the
+[trace processor](/docs/analysis/trace-processor.md) cannot parse them
+meaningfully. Instead, support should be added for parsing important ftrace
+events to Perfetto:
+[here](/docs/contributing/common-tasks.md#add-a-new-ftrace-event) is a simple
+set of steps to follow which are found.
+
+#### Instrumentation with Perfetto SDK
+Perfetto has a [C++ SDK](https://perfetto.dev/docs/instrumentation/tracing-sdk)
+which can be used to instrument programs to emit tracing events. The SDK is
+designed to be very low-overhead and is distributed in an "amalgamated" form
+of a one `.cc` and one `.h` file, making it easy to integrate in any build
+system.
+
+A C SDK is under active development and should be available for general
+usage by Q2 2023. See [this doc](https://bit.ly/perfetto-c) for details (note
+viewing this doc requires being a member of
+[this group](https://groups.google.com/forum/#!forum/perfetto-dev))
+
+A Java/Kotlin SDK for Android (as a
+[JetPack library](https://developer.android.com/jetpack/androidx)).
+This is under development but there is no set timescale for when an official
+release will happen.
+
+##### android.os.Trace (atrace) vs Perfetto SDK
+NOTE: This section is only relevant for Android platform developers or Android
+app developers with tracing experience. Other readers can safely skip this
+section.
+
+Perfetto has significant advantages over atrace. Some of the biggest advantages
+include:
+* performance: tracing to Perfetto from system/app code requires just a memory
+  write which is far faster than the syscall latency imposed by atrace. This
+  generally makes Perfetto anywhere from 3-4x faster than atrace
+* features: atrace's API is extremely limited, lacking support for debug
+  arguments, custom clocks, flow events. Perfetto has a far richer API allowing
+  natural representation of data-flow.
+* trace size: Perfetto supports various features (delta encoded timestamps,
+  interned strings, protobuf encoding) which vastly reduce to size of trace
+  files.
+
+Unfortunately, there are also some downsides:
+* dedicated thread: a thread dedicated to Perfetto is necessary for every
+  process which wants to trace to Perfetto.
+* wakeups on tracing start: currently, when tracing starts, every process
+  registered for tracing is woken up which significantly limits how many
+  processes can be traced. This limitation should be removed in coming quarters.
+
+For now, the recommendation from the Perfetto team is to continue utilizing
+atrace for most usecases: if you think you have a usecase which would benefit
+from the SDK, please reach out to the team directly. By mid-2023, significant
+progress should be made addressing the limitations of the current SDK allowing
+more widespread adoption of the SDK.
+
+<!--
+TODO(lalitm): write the remainder of the doc using the following template
+
+#### Native heap profiling
+
+#### Java heap graphs
+
+#### Callstack sampling
+
+
+#### Flight recorder tracing
+TODO(lalitm): write this.
+
+##### Field tracing
+TODO(lalitm): write this.
+
+#### Clock sync
+TODO(lalitm): write this.
+
+
+#### Analysis
+TODO(lalitm): write this.
+* Trace processing
+* UI
+* httpd mode
+* metrics
+* Python
+
+
+The remainder of this
+page will focus on the applications of Perfetto to solve various performance
+related problems.
+
+## Solving problems with Perfetto
+TODO(lalitm): write this.
+* When to look into callstack sampling
+* When to use memory profiling
+* When to look at scheduling latency
+
+
+TODO(lalitm): write this.
+
+-->
\ No newline at end of file
diff --git a/examples/sdk/BUILD.gn b/examples/sdk/BUILD.gn
index 219d642..17e5fad 100644
--- a/examples/sdk/BUILD.gn
+++ b/examples/sdk/BUILD.gn
@@ -28,6 +28,21 @@
   ]
 }
 
+executable("example_system_wide") {
+  sources = [
+    "example_system_wide.cc",
+    "trace_categories.cc",
+    "trace_categories.h",
+  ]
+  defines = [ "PERFETTO_SDK_EXAMPLE_USE_INTERNAL_HEADERS" ]
+  testonly = true
+  deps = [
+    "../..:libperfetto_client_experimental",
+    "../../gn:default_deps",
+    "../../src/base",
+  ]
+}
+
 executable("example_custom_data_source") {
   sources = [ "example_custom_data_source.cc" ]
   defines = [ "PERFETTO_SDK_EXAMPLE_USE_INTERNAL_HEADERS" ]
diff --git a/examples/sdk/CMakeLists.txt b/examples/sdk/CMakeLists.txt
index ed15e93..00508dc 100644
--- a/examples/sdk/CMakeLists.txt
+++ b/examples/sdk/CMakeLists.txt
@@ -15,7 +15,7 @@
 cmake_minimum_required(VERSION 3.13)
 project(perfetto-sdk-example)
 
-set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD 17)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
 find_package(Threads)
diff --git a/examples/sdk/README.md b/examples/sdk/README.md
index d51a171..e25c308 100644
--- a/examples/sdk/README.md
+++ b/examples/sdk/README.md
@@ -8,14 +8,14 @@
 Dependencies:
 
 - [CMake](https://cmake.org/)
-- C++11
+- C++17
 
 ## Building
 
 First, check out the latest Perfetto release:
 
 ```bash
-git clone https://android.googlesource.com/platform/external/perfetto -b v26.0
+git clone https://android.googlesource.com/platform/external/perfetto -b v29.0
 ```
 
 Then, build using CMake:
diff --git a/examples/sdk/example_startup_trace.cc b/examples/sdk/example_startup_trace.cc
index a8af67b..1cf3799 100644
--- a/examples/sdk/example_startup_trace.cc
+++ b/examples/sdk/example_startup_trace.cc
@@ -33,7 +33,6 @@
 #include <perfetto.h>
 #endif
 
-#include <unistd.h>
 #include <fstream>
 #include <iostream>
 #include <thread>
@@ -97,7 +96,8 @@
   std::ofstream output;
   const char* filename = "example_startup_trace.pftrace";
   output.open(filename, std::ios::out | std::ios::binary);
-  output.write(&trace_data[0], static_cast<std::streamsize>(trace_data.size()));
+  output.write(trace_data.data(),
+               static_cast<std::streamsize>(trace_data.size()));
   output.close();
   PERFETTO_LOG(
       "Trace written in %s file. To read this trace in "
diff --git a/examples/sdk/example_system_wide.cc b/examples/sdk/example_system_wide.cc
index b7101de..87fb7a5 100644
--- a/examples/sdk/example_system_wide.cc
+++ b/examples/sdk/example_system_wide.cc
@@ -15,6 +15,45 @@
  */
 
 // This example demonstrates system-wide tracing with Perfetto.
+//
+// 1). To use it, first build the `tracebox` and this file. The tracebox will
+// internally build tracing service (traced, which is long running
+// process / daemon ) and perfetto consumer client, and many other perfetto
+// tracing related tools.
+// `ninja -C out/default/ tracebox example_system_wide`
+//
+// 2). Run traced (long running process), and open another terminal tab.
+// `./out/default/tracebox traced`
+//
+// 3). Run this file. This is main application to trace.
+// `./out/default/example_system_wide`
+//
+// 4). Use perfetto client to start a session and record trace in a file.
+// `./out/default/tracebox perfetto -c /tmp/trace_config.txt --txt
+//      -o /tmp/trace_output`
+//
+// but before running that command, put following trace config (protobuf config)
+// in a file named `/tmp/trace_config.txt`
+// This can also be copied from: https://pastebin.com/embed_iframe/ufmtBBuq
+// ---------------------
+// buffers: {
+//     size_kb: 63488
+// }
+// data_sources: {
+//     config {
+//         name: "track_event"
+//     }
+// }
+// duration_ms: 10000
+// ---------------------
+// After running the command above, trace will be saved in `/tmp/trace_output`
+// file. It is a binary content. We can read it by running:
+// `./tools/traceconv text /tmp/trace_output`
+// Or we can use "Open Trace File" option in the perfetto UI
+// (https://ui.perfetto.dev)
+//
+// Learn More:
+// https://perfetto.dev/docs/quickstart/linux-tracing#capturing-a-trace
 
 #include "trace_categories.h"
 
@@ -23,10 +62,12 @@
 #include <fstream>
 #include <thread>
 
+namespace {
+
 class Observer : public perfetto::TrackEventSessionObserver {
  public:
   Observer() { perfetto::TrackEvent::AddSessionObserver(this); }
-  ~Observer() { perfetto::TrackEvent::RemoveSessionObserver(this); }
+  ~Observer() override { perfetto::TrackEvent::RemoveSessionObserver(this); }
 
   void OnStart(const perfetto::DataSourceBase::StartArgs&) override {
     std::unique_lock<std::mutex> lock(mutex);
@@ -67,6 +108,8 @@
   DrawPlayer(2);
 }
 
+}  // namespace
+
 int main(int, const char**) {
   InitializePerfetto();
 
diff --git a/gn/BUILD.gn b/gn/BUILD.gn
index 54f4266..0130ce7 100644
--- a/gn/BUILD.gn
+++ b/gn/BUILD.gn
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 import("perfetto.gni")
+import("perfetto_python.gni")
 import("proto_library.gni")
 
 if (perfetto_root_path == "//") {
@@ -89,6 +90,7 @@
     "PERFETTO_STDERR_CRASH_DUMP=$enable_perfetto_stderr_crash_dump",
     "PERFETTO_X64_CPU_OPT=$enable_perfetto_x64_cpu_opt",
     "PERFETTO_LLVM_DEMANGLE=$enable_perfetto_llvm_demangle",
+    "PERFETTO_SYSTEM_CONSUMER=$enable_perfetto_system_consumer",
   ]
 
   rel_out_path = rebase_path(gen_header_path, "$root_build_dir")
@@ -209,8 +211,6 @@
   "../src/protozero/protoc_plugin:*",
   "../src/protozero/filtering:filter_util",
   "../src/trace_processor:trace_processor_shell",
-  "../src/trace_processor/util:proto_profiler",
-  "../src/trace_processor/util:unittests",
   "../src/protozero/filtering:filter_util",
   "../tools/*",
   "../src/tools/*",
@@ -405,6 +405,13 @@
   }
 }
 
+# Allows overriding platform-specific functionality used by base at a
+# build-system level. This allows e.g. different implementations of base
+# functions in Google3.
+group("base_platform") {
+  public_deps = [ "../src/base:perfetto_base_default_platform" ]
+}
+
 # Used by fuzzers.
 if (enable_perfetto_fuzzers && use_libfuzzer) {
   group("libfuzzer") {
@@ -412,3 +419,12 @@
     public_deps = [ "//buildtools:libfuzzer" ]
   }
 }
+
+# Python libraries which need to be installed on the system
+# or provided (for other build systems).
+perfetto_py_library("pandas_py") {
+}
+perfetto_py_library("tp_vendor_py") {
+}
+perfetto_py_library("protobuf_py") {
+}
diff --git a/gn/perfetto.gni b/gn/perfetto.gni
index 5d72402..04c07ca 100644
--- a/gn/perfetto.gni
+++ b/gn/perfetto.gni
@@ -211,7 +211,7 @@
   # dllexport/import adds extra complexity for little benefit. Te only reason
   # for monolithic_binaries=false is saving binary size, which matters mainly on
   # Android. See also comments on PERFETTO_EXPORT_ENTRYPOINT in compiler.h.
-  monolithic_binaries = is_win
+  monolithic_binaries = !perfetto_build_with_android && (is_win || is_mac)
 
   # Whether DLOG should be enabled on debug builds (""), all builds ("on"), or
   # none ("off"). We disable it by default for embedders to avoid spamming their
@@ -245,6 +245,14 @@
 }
 
 declare_args() {
+  # When false, it disables system backend consumer support in the Perfetto SDK.
+  # Saves ~300KB binary size.
+  if (!defined(enable_perfetto_system_consumer)) {
+    enable_perfetto_system_consumer = enable_perfetto_ipc
+  }
+}
+
+declare_args() {
   perfetto_enable_git_rev_version_header =
       enable_perfetto_version_gen && perfetto_build_standalone &&
       !is_perfetto_build_generator
@@ -269,7 +277,8 @@
 
   # Enables the optional SQLite percentile module.
   enable_perfetto_trace_processor_percentile =
-      enable_perfetto_trace_processor && perfetto_build_standalone
+      enable_perfetto_trace_processor &&
+      (perfetto_build_standalone || perfetto_build_with_android)
 
   # Enables the REPL interactive prompt in the trace processor.
   enable_perfetto_trace_processor_linenoise =
@@ -310,6 +319,12 @@
       perfetto_build_standalone && enable_perfetto_trace_processor_sqlite &&
       host_os != "win"
 
+  # Allows to build the perfetto.dev website.
+  # WARNING: if this flag is enabled, the build performs globbing at generation
+  # time. Incremental builds that add/remove files will not be supported without
+  # rerunning gn.
+  enable_perfetto_site = false
+
   # Skip buildtools dependency checks (needed for ChromeOS).
   skip_buildtools_check = false
 
@@ -317,6 +332,10 @@
   # from /usr/include instead of the hermetic one.
   perfetto_use_system_protobuf = false
 
+  # Used by CrOS system builds. Uses the system version of sqlite
+  # from /usr/include instead of the hermetic one.
+  perfetto_use_system_sqlite = false
+
   perfetto_use_system_zlib = false
 }
 
diff --git a/gn/perfetto_benchmarks.gni b/gn/perfetto_benchmarks.gni
index 561c2cf..d335ad7 100644
--- a/gn/perfetto_benchmarks.gni
+++ b/gn/perfetto_benchmarks.gni
@@ -25,6 +25,7 @@
   "src/trace_processor/rpc:benchmarks",
   "src/trace_processor/sqlite:benchmarks",
   "src/trace_processor/tables:benchmarks",
+  "src/trace_processor/util:benchmarks",
   "src/traced/probes/ftrace:benchmarks",
   "src/tracing:benchmarks",
   "src/tracing/core:benchmarks",
diff --git a/gn/perfetto_cc_proto_descriptor.gni b/gn/perfetto_cc_proto_descriptor.gni
index 57efba7..5b0a58e 100644
--- a/gn/perfetto_cc_proto_descriptor.gni
+++ b/gn/perfetto_cc_proto_descriptor.gni
@@ -50,5 +50,8 @@
     inputs = [ descriptor_file_path ]
     outputs = [ generated_header ]
     public_configs = [ ":${target_name}_config" ]
+    metadata = {
+      perfetto_action_type_for_generator = [ "cc_proto_descriptor" ]
+    }
   }
 }
diff --git a/gn/perfetto_integrationtests.gni b/gn/perfetto_integrationtests.gni
index 279a803..02c3b11 100644
--- a/gn/perfetto_integrationtests.gni
+++ b/gn/perfetto_integrationtests.gni
@@ -16,13 +16,15 @@
 
 perfetto_integrationtests_targets = [
   "gn:default_deps",
-  "gn:gtest_main",
   "src/tracing/test:client_api_integrationtests",
 ]
 
-if (enable_perfetto_ipc) {
+if (enable_perfetto_ipc && enable_perfetto_system_consumer) {
   perfetto_integrationtests_targets +=
-      [ "src/tracing/test:tracing_integration_test" ]
+      [
+        "src/tracing/test:tracing_integration_test",
+        "src/tracing:integrationtests",
+      ]
 }
 
 if (enable_perfetto_traced_probes) {
diff --git a/gn/perfetto_python.gni b/gn/perfetto_python.gni
new file mode 100644
index 0000000..fe9264d
--- /dev/null
+++ b/gn/perfetto_python.gni
@@ -0,0 +1,73 @@
+# Copyright (C) 2022 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")
+
+template("perfetto_py_library") {
+  action(target_name) {
+    forward_variables_from(invoker, [ "deps" ])
+
+    data = []
+    if (defined(invoker.data)) {
+      data += invoker.data
+    }
+
+    out_path = "$target_gen_dir/" + target_name
+    rebased_out_path =
+        rebase_path(target_gen_dir, root_build_dir) + "/" + target_name
+
+    sources = []
+    if (defined(invoker.sources)) {
+      sources += invoker.sources
+    }
+    script = "$perfetto_root_path/tools/touch_file.py"
+    args = [
+      "--output",
+      rebased_out_path,
+    ]
+    outputs = [ out_path ]
+    metadata = {
+      perfetto_action_type_for_generator = [ "python_library" ]
+      perfetto_data = data
+    }
+  }
+}
+
+template("perfetto_py_binary") {
+  action(target_name) {
+    forward_variables_from(invoker, [ "deps" ])
+
+    data = []
+    if (defined(invoker.data)) {
+      data += invoker.data
+    }
+
+    out_path = "$target_gen_dir/" + target_name
+    rebased_out_path =
+        rebase_path(target_gen_dir, root_build_dir) + "/" + target_name
+
+    sources = invoker.sources
+    script = "$perfetto_root_path/tools/touch_file.py"
+    args = [
+      "--output",
+      rebased_out_path,
+    ]
+    outputs = [ out_path ]
+    metadata = {
+      perfetto_action_type_for_generator = [ "python_binary" ]
+      perfetto_python_main = [ invoker.main ]
+      perfetto_data = data
+    }
+  }
+}
diff --git a/gn/perfetto_sql.gni b/gn/perfetto_sql.gni
new file mode 100644
index 0000000..d50864d
--- /dev/null
+++ b/gn/perfetto_sql.gni
@@ -0,0 +1,96 @@
+# Copyright (C) 2022 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")
+
+template("perfetto_sql_source_set") {
+  action("${target_name}") {
+    forward_variables_from(invoker, [ "deps" ])
+
+    out_path = "$target_gen_dir/" + target_name
+    rebased_out_path =
+        rebase_path(target_gen_dir, root_build_dir) + "/" + target_name
+
+    script = "$perfetto_root_path/tools/touch_file.py"
+    args = [
+      "--output",
+      rebased_out_path,
+    ]
+    inputs = invoker.sources
+    outputs = [ out_path ]
+    metadata = {
+      perfetto_sql_source_files = inputs
+      perfetto_action_type_for_generator = [ "sql_source_set" ]
+    }
+  }
+}
+
+template("perfetto_amalgamated_sql_header") {
+  invoker_target = target_name
+  gen_txt_file = "$target_gen_dir/${target_name}.txt"
+
+  generated_file("${invoker_target}_generated_file") {
+    forward_variables_from(invoker, [ "deps" ])
+    outputs = [ gen_txt_file ]
+    data_keys = [ "perfetto_sql_source_files" ]
+    rebase = root_build_dir
+  }
+
+  config("${invoker_target}_config") {
+    include_dirs = [ "${root_gen_dir}/${perfetto_root_path}" ]
+  }
+
+  action(invoker_target) {
+    deps = [ ":${invoker_target}_generated_file" ]
+    deps += invoker.deps
+
+    script = "$perfetto_root_path/tools/gen_amalgamated_sql.py"
+    generated_file = "${target_gen_dir}/" + invoker.generated_header
+    args = [
+      "--namespace",
+      invoker.namespace,
+      "--cpp-out",
+      rebase_path(generated_file, root_build_dir),
+      "--input-list-file",
+      rebase_path(gen_txt_file, root_build_dir),
+    ]
+    inputs = [ gen_txt_file ]
+    outputs = [ generated_file ]
+    public_configs = [ ":${invoker_target}_config" ]
+    metadata = {
+      perfetto_action_type_for_generator = [ "sql_amalgamation" ]
+    }
+  }
+
+  if (defined(invoker.generate_docs) && invoker.generate_docs &&
+      perfetto_build_standalone) {
+    action("${invoker_target}_json_docs") {
+      deps = [ ":${invoker_target}_generated_file" ]
+      deps += invoker.deps
+
+      script = "$perfetto_root_path/tools/gen_stdlib_docs_json.py"
+      generated_file = "${target_gen_dir}/stdlib_docs.json"
+
+      args = [
+        "--json-out",
+        rebase_path(generated_file, root_build_dir),
+        "--input-list-file",
+        rebase_path(gen_txt_file, root_build_dir),
+      ]
+      public_configs = [ ":${invoker_target}_config" ]
+      inputs = [ gen_txt_file ]
+      outputs = [ generated_file ]
+    }
+  }
+}
diff --git a/gn/perfetto_tp_tables.gni b/gn/perfetto_tp_tables.gni
new file mode 100644
index 0000000..da8cfae
--- /dev/null
+++ b/gn/perfetto_tp_tables.gni
@@ -0,0 +1,62 @@
+# Copyright (C) 2022 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")
+
+template("perfetto_tp_tables") {
+  config_name = target_name + "_config"
+  action_name = target_name
+
+  config(config_name) {
+    include_dirs = [ root_gen_dir ]
+  }
+
+  action(action_name) {
+    sources = invoker.sources
+    script = "${perfetto_root_path}/tools/gen_tp_table_headers.py"
+
+    outputs = []
+    foreach(table, invoker.sources) {
+      outputs += [ "$target_gen_dir/" + string_replace(table, ".py", "_py.h") ]
+    }
+
+    deps = [ "$perfetto_root_path/python:trace_processor_table_generator" ]
+    public_configs = [ ":$config_name" ]
+
+    gen_args = [
+      "--gen-dir",
+      rebase_path("$root_gen_dir", root_build_dir),
+    ]
+    input_args = [ "--inputs" ] + rebase_path(invoker.sources, root_build_dir)
+    output_args = [ "--outputs" ] + rebase_path(outputs, root_build_dir)
+    args = gen_args + input_args + output_args
+
+    metadata = {
+      perfetto_action_type_for_generator = [ "tp_tables" ]
+    }
+  }
+
+  if (defined(invoker.generate_docs) && invoker.generate_docs &&
+      perfetto_build_standalone) {
+    docs_name = target_name + "_docs"
+    action(docs_name) {
+      sources = invoker.sources
+      script = "$perfetto_root_path/tools/gen_tp_table_docs.py"
+      deps = [ "$perfetto_root_path/python:trace_processor_table_generator" ]
+      outputs = [ "$target_gen_dir/$docs_name.json" ]
+      args = [ "--out" ] + rebase_path(outputs, root_build_dir) +
+             rebase_path(invoker.sources, root_build_dir)
+    }
+  }
+}
diff --git a/gn/perfetto_unittests.gni b/gn/perfetto_unittests.gni
index 3af1962..76594fe 100644
--- a/gn/perfetto_unittests.gni
+++ b/gn/perfetto_unittests.gni
@@ -18,6 +18,7 @@
   "gn:default_deps",
   "gn:gtest_main",
   "src/base:unittests",
+  "src/base/threading:unittests",
   "src/protozero:unittests",
   "src/tracing/core:unittests",
   "src/tracing:unittests",
diff --git a/gn/standalone/BUILD.gn b/gn/standalone/BUILD.gn
index 582e9b8..bd21589 100644
--- a/gn/standalone/BUILD.gn
+++ b/gn/standalone/BUILD.gn
@@ -88,6 +88,10 @@
   } else if (!is_clang && !is_win) {
     # Use return std::move(...) for compatibility with old GCC compilers.
     cflags += [ "-Wno-redundant-move" ]
+
+    # Use after free detection in GCC is still not good enough: it still fails
+    # on very obvious false-positives in trace processor.
+    cflags += [ "-Wno-use-after-free" ]
   }
 }
 
@@ -107,6 +111,10 @@
 }
 
 config("c++11") {
+  if (!perfetto_cpp11_until_q1_2023) {
+    visibility = []
+  }
+
   # C++11 is the default on Windows.
   if (!is_win) {
     cflags_cc = [ "-std=c++11" ]
@@ -114,10 +122,16 @@
     # Enable standards-conforming compiler behavior.
     cflags_cc = [ "/permissive-" ]
   }
+  defines = [ "PERFETTO_ALLOW_SUB_CPP17" ]
 }
 
 # Used in buildtools dependencies for standalone builds.
 config("c++14") {
+  if (perfetto_cpp11_until_q1_2023) {
+    visibility = [ "//buildtools:llvm_demangle" ]
+  } else {
+    visibility = []
+  }
   if (is_win) {
     cflags_cc = [ "/std:c++14" ]
   } else {
@@ -127,6 +141,9 @@
 
 # Used in buildtools dependencies for standalone builds.
 config("c++17") {
+  if (perfetto_cpp11_until_q1_2023) {
+    visibility = [ "//buildtools:libunwindstack" ]
+  }
   if (is_win) {
     cflags_cc = [ "/std:c++17" ]
   } else {
@@ -134,6 +151,16 @@
   }
 }
 
+# Used in buildtools dependencies for standalone builds.
+config("c++20") {
+  visibility = [ "//buildtools:libc++config" ]
+  if (is_win) {
+    cflags_cc = [ "/std:c++20" ]
+  } else {
+    cflags_cc = [ "-std=c++20" ]
+  }
+}
+
 config("visibility_hidden") {
   if (!is_win) {
     cflags = [ "-fvisibility=hidden" ]
@@ -167,12 +194,20 @@
     ]
   }
 
+  if (is_clang && is_win) {
+    # clang-cl from version 16 does not like out-of-line definition of static
+    # constexpr, even in C++14 mode. Disable the deprecated warnings to work
+    # around the problem.
+    cflags += [ "-Wno-deprecated" ]
+  }
+
   if (is_win) {
     cflags += [
       "/bigobj",  # Some of our files are bigger than the regular limits.
       "/Gy",  # Enable function-level linking.
       "/FS",  # Preserve previous PDB behavior.
       "/utf-8",  # Assume UTF-8 by default to avoid code page dependencies.
+      "/Zc:__cplusplus",  # Allow use of __cplusplus macro.
     ]
     defines += [
       "_CRT_NONSTDC_NO_WARNINGS",
@@ -214,6 +249,13 @@
     not_needed([ "hermetic_clang_suppressions" ])
   }
 
+  if (non_hermetic_clang_stdlib != "") {
+    if (is_clang && !is_hermetic_clang && !is_wasm) {
+      cflags_cc += [ "-stdlib=" + non_hermetic_clang_stdlib ]
+      ldflags += [ "-stdlib=" + non_hermetic_clang_stdlib ]
+    }
+  }
+
   if (is_lto) {
     cflags += [ "-flto=full" ]
     ldflags += [ "-flto=full" ]
@@ -257,6 +299,12 @@
   }
 
   if (is_linux) {
+    # Enable LFS (large file support) for stat() and other syscalls.
+    cflags += [
+      "-D_FILE_OFFSET_BITS=64",
+      "-D_LARGEFILE_SOURCE",
+      "-D_LARGEFILE64_SOURCE",
+    ]
     libs += [
       "pthread",
       "rt",
diff --git a/gn/standalone/BUILDCONFIG.gn b/gn/standalone/BUILDCONFIG.gn
index 6f32686..772161b 100644
--- a/gn/standalone/BUILDCONFIG.gn
+++ b/gn/standalone/BUILDCONFIG.gn
@@ -17,6 +17,7 @@
   is_clang = true
   is_system_compiler = false
   is_lto = false
+  perfetto_cpp11_until_q1_2023 = false
 
   # This is defined here because it's needed below for determining the value of
   # |is_cross_compiling|.
@@ -65,7 +66,6 @@
 default_configs = [
   "//gn/standalone:debug_symbols",
   "//gn/standalone:default",
-  "//gn/standalone:c++11",
   "//gn/standalone:extra_warnings",
   "//gn/standalone:no_exceptions",
   "//gn/standalone:no_rtti",
@@ -74,6 +74,12 @@
   "//gn/standalone/sanitizers:sanitizers_cflags",
 ]
 
+if (perfetto_cpp11_until_q1_2023) {
+  default_configs += [ "//gn/standalone:c++11" ]
+} else {
+  default_configs += [ "//gn/standalone:c++17" ]
+}
+
 if (is_win) {
   default_configs += [ "//gn/standalone:win32_lean_and_mean" ]
 }
diff --git a/gn/standalone/glob.py b/gn/standalone/glob.py
new file mode 100644
index 0000000..79bffbd
--- /dev/null
+++ b/gn/standalone/glob.py
@@ -0,0 +1,79 @@
+# 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 list all files in a directory filtering by pattern.
+
+Do NOT use this script to pull in sources for GN targets. Globbing inputs is
+a bad idea, as it plays very badly with git leaving untracked files around. This
+script should be used only for cases where false positives won't affect the
+output of the build but just cause spurious re-runs (e.g. as input section of
+an "action" target).
+"""
+from __future__ import print_function
+import argparse
+import fnmatch
+import os
+import sys
+
+
+def make_parent_dirs(file_path):
+  directory = os.path.dirname(file_path)
+  if not os.path.exists(directory):
+    os.makedirs(directory)
+
+
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--filter', default=[], action='append')
+  parser.add_argument('--exclude', default=[], action='append')
+  parser.add_argument('--deps', default=None)
+  parser.add_argument('--output', default=None)
+  parser.add_argument('--root', required=True)
+  args = parser.parse_args()
+
+  if args.output:
+    make_parent_dirs(args.output)
+    fout = open(args.output, 'w')
+  else:
+    fout = sys.stdout
+
+  def writepath(path):
+    if args.deps:
+      path = '\t' + path
+    print(path, file=fout)
+
+  root = args.root
+  if not root.endswith('/'):
+    root += '/'
+  if not os.path.exists(root):
+    return 0
+
+  if args.deps:
+    print(args.deps + ':', file=fout)
+  for pardir, dirs, files in os.walk(root, topdown=True):
+    assert pardir.startswith(root)
+    relpar = pardir[len(root):]
+    dirs[:] = [d for d in dirs if os.path.join(relpar, d) not in args.exclude]
+    for fname in files:
+      fpath = os.path.join(pardir, fname)
+      match = len(args.filter) == 0
+      for filter in args.filter:
+        if fnmatch.fnmatch(fpath, filter):
+          match = True
+          break
+      if match:
+        writepath(fpath)
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/gn/standalone/libc++/BUILD.gn b/gn/standalone/libc++/BUILD.gn
index f6b6131..49d91ba 100644
--- a/gn/standalone/libc++/BUILD.gn
+++ b/gn/standalone/libc++/BUILD.gn
@@ -32,6 +32,7 @@
       "-isystem" + rebase_path("$libcxx_prefix/include", root_build_dir),
       "-isystem" + rebase_path("$libcxxabi_prefix/include", root_build_dir),
     ]
+    include_dirs = [ "//buildtools/libcxx_config" ]
 
     # Avoid linking both libc++ and libstdc++.
     ldflags = [ "-nostdlib++" ]
diff --git a/gn/standalone/libc++/libc++.gni b/gn/standalone/libc++/libc++.gni
index c4290b2..b271b89 100644
--- a/gn/standalone/libc++/libc++.gni
+++ b/gn/standalone/libc++/libc++.gni
@@ -36,11 +36,11 @@
   #    build/config/c++/c++.gni)
   # 2) The libstdc++ situation is too wild on Linux. Modern debian distros are
   #    fine but Ubuntu Trusty still ships a libstdc++ that doesn't fully
-  #    support C++11. Hence we enable this flag on Linux by default.
+  #    support C++17. Hence we enable this flag on Linux by default.
   #    We still retain libstdc++ coverage on the CI bots by overriding
   #    use_custom_libcxx=false when we target a modern library (see the
   #    GCC target in infra/ci/config.py).
-  use_custom_libcxx = is_linux && is_clang && !is_wasm
+  use_custom_libcxx = is_linux && is_hermetic_clang && !is_wasm
   custom_libcxx_is_static = !using_sanitizer
 }
 
diff --git a/gn/standalone/toolchain/BUILD.gn b/gn/standalone/toolchain/BUILD.gn
index 408f1f6..562ecaf 100644
--- a/gn/standalone/toolchain/BUILD.gn
+++ b/gn/standalone/toolchain/BUILD.gn
@@ -132,7 +132,13 @@
   } else if (target_os == "linux" && target_cpu == "x64") {
     _target_triplet = "x86_64-linux-gnu"
   } else if (target_os == "linux" && target_cpu == "x86") {
-    _target_triplet = "i686-linux-gnu"
+    # Chrome's packaging of clang uses i386 for x86 libs, so an i686 triplet
+    # fails to find the necessary sanitizer archives.
+    if (is_hermetic_clang && (is_asan || is_lsan)) {
+      _target_triplet = "i386-linux-gnu"
+    } else {
+      _target_triplet = "i686-linux-gnu"
+    }
   } else if (target_os == "android" && target_cpu == "arm64") {
     _target_triplet = "aarch64-linux-android"
   } else if (target_os == "android" && target_cpu == "arm") {
diff --git a/gn/standalone/toolchain/llvm.gni b/gn/standalone/toolchain/llvm.gni
index 75bc5ff..13bae47 100644
--- a/gn/standalone/toolchain/llvm.gni
+++ b/gn/standalone/toolchain/llvm.gni
@@ -16,6 +16,7 @@
 
 declare_args() {
   is_hermetic_clang = is_clang && (is_linux_host || is_win_host)
+  non_hermetic_clang_stdlib = ""
 }
 
 assert(!is_hermetic_clang || is_clang, "is_hermetic_clang requires is_clang")
diff --git a/gn/standalone/toolchain/win_find_msvc.py b/gn/standalone/toolchain/win_find_msvc.py
index 0badd1b..1628c07 100644
--- a/gn/standalone/toolchain/win_find_msvc.py
+++ b/gn/standalone/toolchain/win_find_msvc.py
@@ -34,7 +34,6 @@
 def ver_to_tuple(ver_str):
   """Turns '10.1.2' into [10,1,2] so it can be compared using > """
   parts = [int(x) for x in ver_str.split('.')]
-  assert (len(parts) == 4)
   return parts
 
 
@@ -63,16 +62,19 @@
     filt = lambda x: os.path.exists(os.path.join(x, 'ucrt', 'x64', 'ucrt.lib'))
     out[1] = find_max_subdir(lib_base, filt)
 
-  for version in ['BuildTools', 'Community', 'Professional']:
-    msvc_base = ('C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\'
-                 '{}\\VC\\Tools\\MSVC').format(version)
-    if os.path.exists(msvc_base):
-      filt = lambda x: os.path.exists(
-          os.path.join(x, 'lib', 'x64', 'libcmt.lib'))
-      max_msvc = find_max_subdir(msvc_base, filt)
-      if max_msvc is not None:
-        out[2] = os.path.join(msvc_base, max_msvc)
-      break
+  for year in ['2022', '2021', '2020', '2019']:
+    for version in [
+        'BuildTools', 'Community', 'Professional', 'Enterprise', 'Preview'
+    ]:
+      msvc_base = ('C:\\Program Files (x86)\\Microsoft Visual Studio\\'
+                   f'{year}\\{version}\\VC\\Tools\\MSVC')
+      if os.path.exists(msvc_base):
+        filt = lambda x: os.path.exists(
+            os.path.join(x, 'lib', 'x64', 'libcmt.lib'))
+        max_msvc = find_max_subdir(msvc_base, filt)
+        if max_msvc is not None:
+          out[2] = os.path.join(msvc_base, max_msvc)
+        break
 
   # Don't error in case of failure, GN scripts are supposed to deal with
   # failures and allow the user to override the dirs.
diff --git a/gn/write_buildflag_header.py b/gn/write_buildflag_header.py
index 617ab7d..30751a5 100644
--- a/gn/write_buildflag_header.py
+++ b/gn/write_buildflag_header.py
@@ -65,7 +65,8 @@
       flags.append((key, value))
 
   guard = '%s_' % args.out.upper()
-  guard = guard.replace('/', '_').replace('\\', '_').replace('.', '_')
+  guard = guard.replace('/', '_').replace('\\', '_').replace('.', '_') \
+          .replace('-', '_')
   lines = []
   lines.append('// Generated by %s' % os.path.basename(__file__))
   lines.append('')
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 502e9bf..c36d04b 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
@@ -34,7 +34,7 @@
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_FORCE_DCHECK_OFF() (0)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_VERBOSE_LOGS() (1)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_VERSION_GEN() (1)
-#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_PERCENTILE() (0)
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_PERCENTILE() (1)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_LINENOISE() (0)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_HTTPD() (1)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_JSON() (0)
@@ -45,6 +45,7 @@
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_STDERR_CRASH_DUMP() (0)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_X64_CPU_OPT() (0)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_LLVM_DEMANGLE() (0)
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_SYSTEM_CONSUMER() (1)
 
 // clang-format on
 #endif  // GEN_BUILD_CONFIG_PERFETTO_BUILD_FLAGS_H_
diff --git a/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h b/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h
index e0958d4..54fe273 100644
--- a/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h
+++ b/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h
@@ -45,6 +45,7 @@
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_STDERR_CRASH_DUMP() (0)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_X64_CPU_OPT() (0)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_LLVM_DEMANGLE() (1)
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_SYSTEM_CONSUMER() (1)
 
 // clang-format on
 #endif  // GEN_BUILD_CONFIG_PERFETTO_BUILD_FLAGS_H_
diff --git a/include/perfetto/base/compiler.h b/include/perfetto/base/compiler.h
index e95b676..c67e315 100644
--- a/include/perfetto/base/compiler.h
+++ b/include/perfetto/base/compiler.h
@@ -23,6 +23,22 @@
 #include "perfetto/base/build_config.h"
 #include "perfetto/public/compiler.h"
 
+#if __cplusplus >= 201703
+#define PERFETTO_IS_AT_LEAST_CPP17() 1
+#else
+#define PERFETTO_IS_AT_LEAST_CPP17() 0
+#endif
+
+#if !PERFETTO_IS_AT_LEAST_CPP17() && !defined(PERFETTO_ALLOW_SUB_CPP17)
+#error Perfetto is exploring a switch to C++17 in v34 (Feb 2023). During this \
+transitionary period, we are throwing an error when compiling Perfetto \
+with a standard less than C++17. Please reach out to \
+perfetto-dev@googlegroups.com if you have objections or thoughts on \
+this move. To continue compiling this release of Perfetto with \
+C++11/14, specify the define PERFETTO_ALLOW_SUB_CPP17. \
+*Note*: this define *will* stop working in v34 (Feb 2023).
+#endif
+
 // __has_attribute is supported only by clang and recent versions of GCC.
 // Add a layer to wrap the __has_attribute macro.
 #if defined(__has_attribute)
diff --git a/include/perfetto/base/thread_utils.h b/include/perfetto/base/thread_utils.h
index 6af9a57..be8b4db 100644
--- a/include/perfetto/base/thread_utils.h
+++ b/include/perfetto/base/thread_utils.h
@@ -20,6 +20,7 @@
 #include <stdint.h>
 
 #include "perfetto/base/build_config.h"
+#include "perfetto/base/export.h"
 
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
 extern "C" {
@@ -27,7 +28,6 @@
 __declspec(dllimport) unsigned long __stdcall GetCurrentThreadId();
 }
 #elif PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
-#include <zircon/process.h>
 #include <zircon/types.h>
 #elif PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
     PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
@@ -52,10 +52,9 @@
   return static_cast<pid_t>(syscall(__NR_gettid));
 }
 #elif PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
-using PlatformThreadId = zx_handle_t;
-inline PlatformThreadId GetThreadId() {
-  return zx_thread_self();
-}
+using PlatformThreadId = zx_koid_t;
+// Not inlined because the result is cached internally.
+PERFETTO_EXPORT_COMPONENT PlatformThreadId GetThreadId();
 #elif PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
 using PlatformThreadId = uint64_t;
 inline PlatformThreadId GetThreadId() {
diff --git a/include/perfetto/base/time.h b/include/perfetto/base/time.h
index 32abf2c..5849d3e 100644
--- a/include/perfetto/base/time.h
+++ b/include/perfetto/base/time.h
@@ -53,6 +53,9 @@
 
 TimeNanos GetWallTimeNs();
 TimeNanos GetThreadCPUTimeNs();
+inline TimeNanos GetWallTimeRawNs() {
+  return GetWallTimeNs();
+}
 
 // TODO: Clock that counts time during suspend is not implemented on Windows.
 inline TimeNanos GetBootTimeNs() {
@@ -72,6 +75,10 @@
   return TimeNanos(mach_absolute_time() * monotonic_timebase_factor);
 }
 
+inline TimeNanos GetWallTimeRawNs() {
+  return GetWallTimeNs();
+}
+
 // TODO: Clock that counts time during suspend is not implemented on Mac.
 inline TimeNanos GetBootTimeNs() {
   return GetWallTimeNs();
@@ -102,6 +109,10 @@
   return TimeNanos(static_cast<uint64_t>(emscripten_get_now()) * 1000000);
 }
 
+inline TimeNanos GetWallTimeRawNs() {
+  return GetWallTimeNs();
+}
+
 inline TimeNanos GetThreadCPUTimeNs() {
   return TimeNanos(0);
 }
@@ -121,6 +132,10 @@
   return TimeNanos(0);
 }
 
+inline TimeNanos GetWallTimeRawNs() {
+  return TimeNanos(0);
+}
+
 inline TimeNanos GetThreadCPUTimeNs() {
   return TimeNanos(0);
 }
@@ -155,6 +170,10 @@
   return GetTimeInternalNs(kWallTimeClockSource);
 }
 
+inline TimeNanos GetWallTimeRawNs() {
+  return GetTimeInternalNs(CLOCK_MONOTONIC_RAW);
+}
+
 inline TimeNanos GetThreadCPUTimeNs() {
   return GetTimeInternalNs(CLOCK_THREAD_CPUTIME_ID);
 }
diff --git a/include/perfetto/ext/base/BUILD.gn b/include/perfetto/ext/base/BUILD.gn
index 6b0ebb0..5a44be4 100644
--- a/include/perfetto/ext/base/BUILD.gn
+++ b/include/perfetto/ext/base/BUILD.gn
@@ -36,9 +36,11 @@
     "paged_memory.h",
     "periodic_task.h",
     "pipe.h",
+    "platform.h",
     "scoped_file.h",
     "small_set.h",
     "small_vector.h",
+    "status_or.h",
     "string_splitter.h",
     "string_utils.h",
     "string_view.h",
diff --git a/include/perfetto/ext/base/circular_queue.h b/include/perfetto/ext/base/circular_queue.h
index 8b48d22..d607562 100644
--- a/include/perfetto/ext/base/circular_queue.h
+++ b/include/perfetto/ext/base/circular_queue.h
@@ -191,6 +191,19 @@
     return *this;
   }
 
+  explicit CircularQueue(const CircularQueue& other) noexcept {
+    Grow(other.capacity());
+    for (const auto& e : const_cast<CircularQueue&>(other))
+      emplace_back(e);
+    PERFETTO_DCHECK(size() == other.size());
+  }
+
+  CircularQueue& operator=(const CircularQueue& other) noexcept {
+    this->~CircularQueue();           // Destroy the current state.
+    new (this) CircularQueue(other);  // Use the copy ctor above.
+    return *this;
+  }
+
   ~CircularQueue() {
     if (!entries_) {
       PERFETTO_DCHECK(empty());
@@ -249,9 +262,6 @@
 #endif
 
  private:
-  CircularQueue(const CircularQueue&) = delete;
-  CircularQueue& operator=(const CircularQueue&) = delete;
-
   void Grow(size_t new_capacity = 0) {
     // Capacity must be always a power of two. This allows Get() to use a simple
     // bitwise-AND for handling the wrapping instead of a full division.
diff --git a/include/perfetto/ext/base/ctrl_c_handler.h b/include/perfetto/ext/base/ctrl_c_handler.h
index 6c7b34c..d79387c 100644
--- a/include/perfetto/ext/base/ctrl_c_handler.h
+++ b/include/perfetto/ext/base/ctrl_c_handler.h
@@ -24,7 +24,7 @@
 // On Windows: installs a SetConsoleCtrlHandler() handler.
 // The passed handler must be async safe.
 using CtrlCHandlerFunction = void (*)();
-void InstallCtrCHandler(CtrlCHandlerFunction);
+void InstallCtrlCHandler(CtrlCHandlerFunction);
 
 }  // namespace base
 }  // namespace perfetto
diff --git a/include/perfetto/ext/base/file_utils.h b/include/perfetto/ext/base/file_utils.h
index 64e4126..7c7b8e0 100644
--- a/include/perfetto/ext/base/file_utils.h
+++ b/include/perfetto/ext/base/file_utils.h
@@ -64,6 +64,7 @@
 ScopedFile OpenFile(const std::string& path,
                     int flags,
                     FileOpenMode = kFileModeInvalid);
+ScopedFstream OpenFstream(const char* path, const char* mode);
 
 // This is an alias for close(). It's to avoid leaking Windows.h in headers.
 // Exported because ScopedFile is used in the /include/ext API by Chromium
diff --git a/include/perfetto/ext/base/flat_hash_map.h b/include/perfetto/ext/base/flat_hash_map.h
index 7759dbb..b392286 100644
--- a/include/perfetto/ext/base/flat_hash_map.h
+++ b/include/perfetto/ext/base/flat_hash_map.h
@@ -19,6 +19,7 @@
 
 #include "perfetto/base/compiler.h"
 #include "perfetto/base/logging.h"
+#include "perfetto/ext/base/hash.h"
 #include "perfetto/ext/base/utils.h"
 
 #include <algorithm>
@@ -81,7 +82,7 @@
 
 template <typename Key,
           typename Value,
-          typename Hasher = std::hash<Key>,
+          typename Hasher = base::Hash<Key>,
           typename Probe = QuadraticProbe,
           bool AppendOnly = false>
 class FlatHashMap {
diff --git a/include/perfetto/ext/base/hash.h b/include/perfetto/ext/base/hash.h
index b23ac01..548003e 100644
--- a/include/perfetto/ext/base/hash.h
+++ b/include/perfetto/ext/base/hash.h
@@ -19,6 +19,7 @@
 
 #include <stddef.h>
 #include <stdint.h>
+#include <string>
 #include <type_traits>
 #include <utility>
 
@@ -29,10 +30,10 @@
 // The algorithm used is FNV-1a as it is fast and easy to implement and has
 // relatively few collisions.
 // WARNING: This hash function should not be used for any cryptographic purpose.
-class Hash {
+class Hasher {
  public:
   // Creates an empty hash object
-  Hash() {}
+  Hasher() {}
 
   // Hashes a numeric value.
   template <
@@ -66,13 +67,15 @@
     Update(t.data(), t.size());
   }
 
+  void Update(const std::string& s) { Update(s.data(), s.size()); }
+
   uint64_t digest() const { return result_; }
 
   // Usage:
   // uint64_t hashed_value = Hash::Combine(33, false, "ABC", 458L, 3u, 'x');
   template <typename... Ts>
   static uint64_t Combine(Ts&&... args) {
-    Hash hasher;
+    Hasher hasher;
     hasher.UpdateAll(std::forward<Ts>(args)...);
     return hasher.digest();
   }
@@ -102,6 +105,32 @@
   size_t operator()(const T& x) const { return static_cast<size_t>(x); }
 };
 
+// base::Hash uses base::Hasher for integer values and falls base to std::hash
+// for other types. This is needed as std::hash for integers is just the
+// identity function and Perfetto uses open-addressing hash table, which are
+// very sensitive to hash quality and are known to degrade in performance
+// when using std::hash.
+template <typename T>
+struct Hash {
+  // Version for ints, using base::Hasher.
+  template <typename U = T>
+  auto operator()(const U& x) ->
+      typename std::enable_if<std::is_arithmetic<U>::value, size_t>::type
+      const {
+    Hasher hash;
+    hash.Update(x);
+    return static_cast<size_t>(hash.digest());
+  }
+
+  // Version for non-ints, falling back to std::hash.
+  template <typename U = T>
+  auto operator()(const U& x) ->
+      typename std::enable_if<!std::is_arithmetic<U>::value, size_t>::type
+      const {
+    return std::hash<U>()(x);
+  }
+};
+
 }  // namespace base
 }  // namespace perfetto
 
diff --git a/include/perfetto/ext/base/platform.h b/include/perfetto/ext/base/platform.h
new file mode 100644
index 0000000..352e84d
--- /dev/null
+++ b/include/perfetto/ext/base/platform.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 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_PLATFORM_H_
+#define INCLUDE_PERFETTO_EXT_BASE_PLATFORM_H_
+
+namespace perfetto {
+namespace base {
+namespace platform {
+
+// Executed before entering a syscall (e.g. poll, read, write etc) which might
+// block.
+// This is overridden in Google internal builds for dealing with userspace
+// scheduling.
+void BeforeMaybeBlockingSyscall();
+
+// Executed after entering a syscall (e.g. poll, read, write etc) which might
+// block.
+// This is overridden in Google internal builds for dealing with userspace
+// scheduling.
+void AfterMaybeBlockingSyscall();
+
+}  // namespace platform
+}  // namespace base
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_EXT_BASE_PLATFORM_H_
diff --git a/include/perfetto/ext/base/small_vector.h b/include/perfetto/ext/base/small_vector.h
index 15b1773..22f1232 100644
--- a/include/perfetto/ext/base/small_vector.h
+++ b/include/perfetto/ext/base/small_vector.h
@@ -110,6 +110,15 @@
     return static_cast<size_t>(end_of_storage_ - begin_);
   }
 
+  T& front() {
+    PERFETTO_DCHECK(!empty());
+    return begin_[0];
+  }
+  const T& front() const {
+    PERFETTO_DCHECK(!empty());
+    return begin_[0];
+  }
+
   T& back() {
     PERFETTO_DCHECK(!empty());
     return end_[-1];
diff --git a/include/perfetto/ext/base/status_or.h b/include/perfetto/ext/base/status_or.h
new file mode 100644
index 0000000..ee0df07
--- /dev/null
+++ b/include/perfetto/ext/base/status_or.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2022 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_STATUS_OR_H_
+#define INCLUDE_PERFETTO_EXT_BASE_STATUS_OR_H_
+
+#include "perfetto/base/status.h"
+#include "perfetto/ext/base/optional.h"
+
+namespace perfetto {
+namespace base {
+
+// Union of a object of type |T| with a |base::Status|. Useful for cases where
+// a |T| indicates a successful result of an operation and |base::Status|
+// represents an error.
+//
+// This class is modelled closely on absl::Status and should essentially 1:1
+// match it's API.
+template <typename T>
+class StatusOr {
+ public:
+  // Intentionally implicit to allow idomatic usage (e.g. returning value/status
+  // from base::StatusOr returning function).
+  StatusOr(base::Status status) : StatusOr(std::move(status), base::nullopt) {
+    if (status.ok()) {
+      // Matches what Abseil's approach towards OkStatus being passed to
+      // absl::StatusOr<T>.
+      PERFETTO_FATAL("base::OkStatus passed to StatusOr: this is not allowd");
+    }
+  }
+  StatusOr(T value) : StatusOr(base::OkStatus(), std::move(value)) {}
+
+  bool ok() const { return status_.ok(); }
+  const base::Status& status() const { return status_; }
+
+  T& value() {
+    PERFETTO_DCHECK(status_.ok());
+    return *value_;
+  }
+  const T& value() const { return *value_; }
+
+  T& operator*() { return value(); }
+  const T& operator*() const { return value(); }
+
+  T* operator->() { return &value(); }
+  const T* operator->() const { return &value(); }
+
+ private:
+  StatusOr(base::Status status, base::Optional<T> value)
+      : status_(std::move(status)), value_(std::move(value)) {
+    PERFETTO_DCHECK(!status_.ok() || value_.has_value());
+  }
+
+  base::Status status_;
+  base::Optional<T> value_;
+};
+
+}  // namespace base
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_EXT_BASE_STATUS_OR_H_
diff --git a/include/perfetto/ext/base/string_splitter.h b/include/perfetto/ext/base/string_splitter.h
index c68506c..3b6d8a6 100644
--- a/include/perfetto/ext/base/string_splitter.h
+++ b/include/perfetto/ext/base/string_splitter.h
@@ -28,19 +28,34 @@
 // The token returned in output are valid as long as the input string is valid.
 class StringSplitter {
  public:
+  // Whether an empty string (two delimiters side-to-side) is a valid token.
+  enum class EmptyTokenMode {
+    DISALLOW_EMPTY_TOKENS,
+    ALLOW_EMPTY_TOKENS,
+
+    DEFAULT = DISALLOW_EMPTY_TOKENS,
+  };
+
   // Can take ownership of the string if passed via std::move(), e.g.:
   // StringSplitter(std::move(str), '\n');
-  StringSplitter(std::string, char delimiter);
+  StringSplitter(std::string,
+                 char delimiter,
+                 EmptyTokenMode empty_token_mode = EmptyTokenMode::DEFAULT);
 
   // Splits a C-string. The input string will be forcefully null-terminated (so
   // str[size - 1] should be == '\0' or the last char will be truncated).
-  StringSplitter(char* str, size_t size, char delimiter);
+  StringSplitter(char* str,
+                 size_t size,
+                 char delimiter,
+                 EmptyTokenMode empty_token_mode = EmptyTokenMode::DEFAULT);
 
   // Splits the current token from an outer StringSplitter instance. This is to
   // chain splitters as follows:
   // for (base::StringSplitter lines(x, '\n'); ss.Next();)
   //   for (base::StringSplitter words(&lines, ' '); words.Next();)
-  StringSplitter(StringSplitter*, char delimiter);
+  StringSplitter(StringSplitter*,
+                 char delimiter,
+                 EmptyTokenMode empty_token_mode = EmptyTokenMode::DEFAULT);
 
   // Returns true if a token is found (in which case it will be stored in
   // cur_token()), false if no more tokens are found.
@@ -66,6 +81,7 @@
   char* next_;
   char* end_;  // STL-style, points one past the last char.
   const char delimiter_;
+  const EmptyTokenMode empty_token_mode_;
 };
 
 }  // namespace base
diff --git a/include/perfetto/ext/base/string_utils.h b/include/perfetto/ext/base/string_utils.h
index 593204f..bca19c8 100644
--- a/include/perfetto/ext/base/string_utils.h
+++ b/include/perfetto/ext/base/string_utils.h
@@ -109,6 +109,7 @@
                                      const std::string& delimiter);
 std::string StripPrefix(const std::string& str, const std::string& prefix);
 std::string StripSuffix(const std::string& str, const std::string& suffix);
+std::string TrimWhitespace(const std::string& str);
 std::string ToLower(const std::string& str);
 std::string ToUpper(const std::string& str);
 std::string StripChars(const std::string& str,
@@ -167,6 +168,20 @@
 size_t SprintfTrunc(char* dst, size_t dst_size, const char* fmt, ...)
     PERFETTO_PRINTF_FORMAT(3, 4);
 
+// Line number starts from 1
+struct LineWithOffset {
+  base::StringView line;
+  uint32_t line_offset;
+  uint32_t line_num;
+};
+
+// For given string and offset Pfinds a line with character for
+// which offset points, what number is this line (starts from 1), and the offset
+// inside this line. returns nullopt if the offset points to
+// line break character or exceeds string length.
+base::Optional<LineWithOffset> FindLineWithOffset(base::StringView str,
+                                                  uint32_t offset);
+
 // A helper class to facilitate construction and usage of write-once stack
 // strings.
 // Example usage:
diff --git a/include/perfetto/ext/base/string_view.h b/include/perfetto/ext/base/string_view.h
index 98e61cf..1fe3696 100644
--- a/include/perfetto/ext/base/string_view.h
+++ b/include/perfetto/ext/base/string_view.h
@@ -120,18 +120,25 @@
 #endif
   }
 
-  bool StartsWith(const StringView& other) {
+  bool StartsWith(const StringView& other) const {
     if (other.size() == 0)
       return true;
     if (size() == 0)
       return false;
     if (other.size() > size())
       return false;
-    for (uint32_t i = 0; i < other.size(); ++i) {
-      if (at(i) != other.at(i))
-        return false;
-    }
-    return true;
+    return memcmp(data(), other.data(), other.size()) == 0;
+  }
+
+  bool EndsWith(const StringView& other) const {
+    if (other.size() == 0)
+      return true;
+    if (size() == 0)
+      return false;
+    if (other.size() > size())
+      return false;
+    size_t off = size() - other.size();
+    return memcmp(data() + off, other.data(), other.size()) == 0;
   }
 
   std::string ToStdString() const {
@@ -139,7 +146,7 @@
   }
 
   uint64_t Hash() const {
-    base::Hash hasher;
+    base::Hasher hasher;
     hasher.Update(data_, size_);
     return hasher.digest();
   }
diff --git a/include/perfetto/ext/base/threading/BUILD.gn b/include/perfetto/ext/base/threading/BUILD.gn
new file mode 100644
index 0000000..ab12670
--- /dev/null
+++ b/include/perfetto/ext/base/threading/BUILD.gn
@@ -0,0 +1,23 @@
+# Copyright (C) 2023 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("threading") {
+  sources = [ "thread_pool.h" ]
+  deps = [
+    "..:base",
+    "../../../../../gn:default_deps",
+  ]
+}
diff --git a/include/perfetto/ext/base/threading/thread_pool.h b/include/perfetto/ext/base/threading/thread_pool.h
new file mode 100644
index 0000000..1a4b153
--- /dev/null
+++ b/include/perfetto/ext/base/threading/thread_pool.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2023 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_THREADING_THREAD_POOL_H_
+#define INCLUDE_PERFETTO_EXT_BASE_THREADING_THREAD_POOL_H_
+
+#include <condition_variable>
+#include <functional>
+#include <list>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include "perfetto/base/task_runner.h"
+#include "perfetto/ext/base/optional.h"
+
+namespace perfetto {
+namespace base {
+
+// Bounded thread pool designed for CPU-bound tasks.
+//
+// This is a classic bounded thread pool designed for running jobs which fully
+// occupy the CPU without blocking. IO bound tasks which block for long periods
+// of times will cause starvation for any other tasks which are waiting.
+// IO-heavy tasks should use base::TaskRunner and async-IO instead of using this
+// class.
+//
+// Threads are created when the thread pool is created and persist for the
+// lifetime of the ThreadPool. No new threads are created after construction.
+// When the ThreadPool is destroyed, any active tasks are completed and every
+// thread joined before returning from the destructor.
+//
+// Tasks are executed in a FIFO order without any notion of priority. If a
+// thread in the pool is free, it will be used to execute the task immediately.
+// Otherwise, it will be queued for execution when any thread becomes available.
+class ThreadPool {
+ public:
+  // Initializes this thread_pool |thread_count| threads.
+  explicit ThreadPool(uint32_t thread_count);
+  ~ThreadPool();
+
+  // Submits a task for execution by any thread in this thread pool.
+  //
+  // This task should not block for IO as this can cause starvation.
+  void PostTask(std::function<void()>);
+
+ private:
+  void RunThreadLoop();
+
+  ThreadPool(ThreadPool&&) = delete;
+  ThreadPool& operator=(ThreadPool&&) = delete;
+
+  // Start of mutex protected members.
+  std::mutex mutex_;
+  std::list<std::function<void()>> pending_tasks_;
+  std::condition_variable thread_waiter_;
+  uint32_t thread_waiting_count_ = 0;
+  bool quit_ = false;
+  // End of mutex protected members.
+
+  std::vector<std::thread> threads_;
+};
+
+}  // namespace base
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_EXT_BASE_THREADING_THREAD_POOL_H_
diff --git a/include/perfetto/ext/base/utils.h b/include/perfetto/ext/base/utils.h
index 1535c5e..99a9802 100644
--- a/include/perfetto/ext/base/utils.h
+++ b/include/perfetto/ext/base/utils.h
@@ -62,9 +62,9 @@
 // similar mm-related syscalls.
 uint32_t GetSysPageSize();
 
-template <typename T>
-constexpr size_t ArraySize(const T& array) {
-  return sizeof(array) / sizeof(array[0]);
+template <typename T, size_t TSize>
+constexpr size_t ArraySize(const T (&)[TSize]) {
+  return TSize;
 }
 
 // Function object which invokes 'free' on its parameter, which must be
@@ -98,6 +98,9 @@
 // setenv(2)-equivalent. Deals with Windows vs Posix discrepancies.
 void SetEnv(const std::string& key, const std::string& value);
 
+// unsetenv(2)-equivalent. Deals with Windows vs Posix discrepancies.
+void UnsetEnv(const std::string& key);
+
 // Calls mallopt(M_PURGE, 0) on Android. Does nothing on other platforms.
 // This forces the allocator to release freed memory. This is used to work
 // around various Scudo inefficiencies. See b/170217718.
diff --git a/include/perfetto/ext/base/uuid.h b/include/perfetto/ext/base/uuid.h
index 1b4c538..a23be40 100644
--- a/include/perfetto/ext/base/uuid.h
+++ b/include/perfetto/ext/base/uuid.h
@@ -38,6 +38,8 @@
 
   bool operator!=(const Uuid& other) const { return !(*this == other); }
 
+  explicit operator bool() const { return *this != Uuid(); }
+
   int64_t msb() const {
     int64_t result;
     memcpy(&result, data_.data() + 8, 8);
diff --git a/include/perfetto/ext/ipc/client.h b/include/perfetto/ext/ipc/client.h
index d04ac85..46e1e75 100644
--- a/include/perfetto/ext/ipc/client.h
+++ b/include/perfetto/ext/ipc/client.h
@@ -62,6 +62,7 @@
     base::ScopedSocketHandle socket_fd;
     const char* socket_name = nullptr;
     bool retry = false;  // Only for connecting with |socket_name|.
+    std::function<int(void)> receive_shmem_fd_cb_fuchsia;
   };
 
   static std::unique_ptr<Client> CreateInstance(ConnArgs, base::TaskRunner*);
diff --git a/include/perfetto/ext/ipc/host.h b/include/perfetto/ext/ipc/host.h
index 50c8d5c..0311089 100644
--- a/include/perfetto/ext/ipc/host.h
+++ b/include/perfetto/ext/ipc/host.h
@@ -65,9 +65,13 @@
   // registered).
   virtual bool ExposeService(std::unique_ptr<Service>) = 0;
 
-  // Accepts a pre-connected socket handle.
+  // Accepts a pre-connected socket handle and a callback used to send a
+  // shared memory FD to the remote client.
+  // The callback returns false if the FD could not be sent.
   // Should only be used in conjunction with CreateInstance_Fuchsia().
-  virtual void AdoptConnectedSocket_Fuchsia(base::ScopedSocketHandle) = 0;
+  virtual void AdoptConnectedSocket_Fuchsia(
+      base::ScopedSocketHandle,
+      std::function<bool(int)> send_fd_cb) = 0;
 };
 
 }  // namespace ipc
diff --git a/include/perfetto/ext/tracing/core/consumer.h b/include/perfetto/ext/tracing/core/consumer.h
index 949b727..7a7d81a 100644
--- a/include/perfetto/ext/tracing/core/consumer.h
+++ b/include/perfetto/ext/tracing/core/consumer.h
@@ -77,6 +77,11 @@
   // TracingService::ConsumerEndpoint::ObserveEvents() whenever one or more
   // ObservableEvents of enabled event types occur.
   virtual void OnObservableEvents(const ObservableEvents&) = 0;
+
+  // Called back by the Service (or transport layer) after invoking
+  // TracingService::ConsumerEndpoint::CloneSession().
+  // TODO(primiano): make pure virtual after various 3way patches.
+  virtual void OnSessionCloned(bool success, const std::string& error);
 };
 
 }  // namespace perfetto
diff --git a/include/perfetto/ext/tracing/core/tracing_service.h b/include/perfetto/ext/tracing/core/tracing_service.h
index c278812..68dc086 100644
--- a/include/perfetto/ext/tracing/core/tracing_service.h
+++ b/include/perfetto/ext/tracing/core/tracing_service.h
@@ -189,6 +189,12 @@
 
   virtual void DisableTracing() = 0;
 
+  // Clones an existing tracing session and attaches to it. The session is
+  // cloned in read-only mode and can only be used to read a snapshot of an
+  // existing tracing session. Will invoke Consumer::OnSessionCloned().
+  // TODO(primiano): make pure virtual after various 3way patches.
+  virtual void CloneSession(TracingSessionID);
+
   // Requests all data sources to flush their data immediately and invokes the
   // passed callback once all of them have acked the flush (in which case
   // the callback argument |success| will be true) or |timeout_ms| are elapsed
diff --git a/include/perfetto/protozero/proto_decoder.h b/include/perfetto/protozero/proto_decoder.h
index accacb9..2210532 100644
--- a/include/perfetto/protozero/proto_decoder.h
+++ b/include/perfetto/protozero/proto_decoder.h
@@ -258,7 +258,7 @@
   // points at the start of the next element to be decoded.
   // |read_ptr_| might be null if the backing proto field isn't set.
   const uint8_t* read_ptr_;
-  CppType curr_value_ = 0;
+  CppType curr_value_ = {};
 
   // Set to false once we've exhausted the iterator, or encountered an error.
   bool curr_value_valid_ = true;
diff --git a/include/perfetto/trace_processor/BUILD.gn b/include/perfetto/trace_processor/BUILD.gn
index e6e0131..d0dae7f 100644
--- a/include/perfetto/trace_processor/BUILD.gn
+++ b/include/perfetto/trace_processor/BUILD.gn
@@ -15,6 +15,7 @@
 source_set("trace_processor") {
   sources = [
     "iterator.h",
+    "metatrace_config.h",
     "read_trace.h",
     "ref_counted.h",
     "trace_processor.h",
diff --git a/include/perfetto/trace_processor/basic_types.h b/include/perfetto/trace_processor/basic_types.h
index 58c261f..92d8dbf 100644
--- a/include/perfetto/trace_processor/basic_types.h
+++ b/include/perfetto/trace_processor/basic_types.h
@@ -77,7 +77,8 @@
 enum class DropFtraceDataBefore {
   // Drops ftrace data before timestmap specified by the
   // TracingServiceEvent::tracing_started packet. If this packet is not in the
-  // trace, no data is dropped.
+  // trace, no data is dropped. If preserve_ftrace_buffer (from the trace
+  // config) is set, no data is dropped.
   // Note: this event was introduced in S+ so no data will be dropped on R-
   // traces.
   // This is the default approach.
@@ -94,6 +95,18 @@
   kAllDataSourcesStarted = 2,
 };
 
+// Enum which encodes which timestamp source (if any) should be used to drop
+// track event data before this timestamp.
+enum class DropTrackEventDataBefore {
+  // Retain all track events. This is the default approach.
+  kNoDrop = 0,
+
+  // Drops track events before the timestamp specified by the
+  // TrackEventRangeOfInterest trace packet. No data is dropped if this packet
+  // is not present in the trace.
+  kTrackEventRangeOfInterest = 1,
+};
+
 // Struct for configuring a TraceProcessor instance (see trace_processor.h).
 struct PERFETTO_EXPORT_COMPONENT Config {
   // Indicates the sortinng mode that trace processor should use on the passed
@@ -115,9 +128,27 @@
   DropFtraceDataBefore drop_ftrace_data_before =
       DropFtraceDataBefore::kTracingStarted;
 
+  // Indicates the source of timestamp before which track events should be
+  // dropped. See the enum documentation for more details.
+  DropTrackEventDataBefore drop_track_event_data_before =
+      DropTrackEventDataBefore::kNoDrop;
+
   // Any built-in metric proto or sql files matching these paths are skipped
   // during trace processor metric initialization.
   std::vector<std::string> skip_builtin_metric_paths;
+
+  // When set to true, the trace processor analyzes trace proto content, and
+  // exports the field path -> total size mapping into an SQL table.
+  //
+  // The analysis feature is hidden behind the flag so that the users who don't
+  // need this feature don't pay the performance costs.
+  //
+  // The flag has no impact on non-proto traces.
+  bool analyze_trace_proto_content = false;
+
+  // When set to true, trace processor will be augmented with a bunch of helpful
+  // features for local development such as extra SQL fuctions.
+  bool enable_dev_features = false;
 };
 
 // Represents a dynamically typed value returned by SQL.
@@ -195,6 +226,27 @@
   Type type = kNull;
 };
 
+// Data used to register a new SQL module.
+struct SqlModule {
+  // Must be unique among modules, or can be used to override existing module if
+  // |allow_module_override| is set.
+  std::string name;
+
+  // Pairs of strings used for |IMPORT| with the contents of SQL files being
+  // run. Strings should only contain alphanumeric characters and '.', where
+  // string before the first dot has to be module name.
+  //
+  // It is encouraged that import key should be the path to the SQL file being
+  // run, with slashes replaced by dots and without the SQL extension. For
+  // example, 'android/camera/junk.sql' would be imported by
+  // 'android.camera.junk'.
+  std::vector<std::pair<std::string, std::string>> files;
+
+  // If true, SqlModule will override registered module with the same name. Can
+  // only be set if enable_dev_features is true, otherwise will throw an error.
+  bool allow_module_override;
+};
+
 }  // namespace trace_processor
 }  // namespace perfetto
 
diff --git a/include/perfetto/trace_processor/metatrace_config.h b/include/perfetto/trace_processor/metatrace_config.h
new file mode 100644
index 0000000..6ffe4f0
--- /dev/null
+++ b/include/perfetto/trace_processor/metatrace_config.h
@@ -0,0 +1,46 @@
+// Copyright (C) 2022 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_TRACE_PROCESSOR_METATRACE_CONFIG_H_
+#define INCLUDE_PERFETTO_TRACE_PROCESSOR_METATRACE_CONFIG_H_
+
+#include <cstddef>
+
+namespace perfetto {
+namespace trace_processor {
+namespace metatrace {
+
+enum MetatraceCategories {
+  TOPLEVEL = 1 << 0,
+  QUERY = 1 << 1,
+  FUNCTION = 1 << 2,
+
+  NONE = 0,
+  ALL = TOPLEVEL | QUERY | FUNCTION,
+};
+
+struct MetatraceConfig {
+  MetatraceConfig();
+
+  MetatraceCategories categories = MetatraceCategories::ALL;
+  // Requested buffer size. The implemenation may choose to allocate a larger
+  // buffer size for efficiency.
+  size_t override_buffer_size = 0;
+};
+
+}  // namespace metatrace
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_TRACE_PROCESSOR_METATRACE_CONFIG_H_
diff --git a/include/perfetto/trace_processor/trace_blob_view.h b/include/perfetto/trace_processor/trace_blob_view.h
index 884d3a8..f140866 100644
--- a/include/perfetto/trace_processor/trace_blob_view.h
+++ b/include/perfetto/trace_processor/trace_blob_view.h
@@ -87,13 +87,13 @@
   TraceBlobView slice(const uint8_t* data, size_t length) const {
     PERFETTO_DCHECK(data >= data_);
     PERFETTO_DCHECK(data + length <= data_ + length_);
-    return TraceBlobView(data, static_cast<uint32_t>(length), blob_);
+    return TraceBlobView(blob_, data, static_cast<uint32_t>(length));
   }
 
   // Like slice() but takes an offset rather than a pointer as 1st argument.
   TraceBlobView slice_off(size_t off, size_t length) const {
     PERFETTO_DCHECK(off + length <= length_);
-    return TraceBlobView(data_ + off, static_cast<uint32_t>(length), blob_);
+    return TraceBlobView(blob_, data_ + off, static_cast<uint32_t>(length));
   }
 
   TraceBlobView copy() const { return slice(data_, length_); }
@@ -110,12 +110,12 @@
   size_t size() const { return length_; }
 
  private:
-  TraceBlobView(const uint8_t* data, uint32_t length, RefPtr<TraceBlob> blob)
-      : data_(data), length_(length), blob_(std::move(blob)) {}
+  TraceBlobView(RefPtr<TraceBlob> blob, const uint8_t* data, uint32_t length)
+      : blob_(std::move(blob)), data_(data), length_(length) {}
 
+  RefPtr<TraceBlob> blob_;
   const uint8_t* data_ = nullptr;
   uint32_t length_ = 0;
-  RefPtr<TraceBlob> blob_;
 };
 
 }  // namespace trace_processor
diff --git a/include/perfetto/trace_processor/trace_processor.h b/include/perfetto/trace_processor/trace_processor.h
index 028e504..9ed7ad4 100644
--- a/include/perfetto/trace_processor/trace_processor.h
+++ b/include/perfetto/trace_processor/trace_processor.h
@@ -24,6 +24,7 @@
 #include "perfetto/base/export.h"
 #include "perfetto/trace_processor/basic_types.h"
 #include "perfetto/trace_processor/iterator.h"
+#include "perfetto/trace_processor/metatrace_config.h"
 #include "perfetto/trace_processor/status.h"
 #include "perfetto/trace_processor/trace_processor_storage.h"
 
@@ -54,6 +55,16 @@
   // the returned iterator.
   virtual Iterator ExecuteQuery(const std::string& sql) = 0;
 
+  // Registers SQL files with the associated path under the module named
+  // |sql_module.name|. These modules can be run by using the |IMPORT| SQL
+  // function.
+  //
+  // For example, if you registered a module called "camera" with a file path
+  // "camera/cpu/metrics.sql" you can import it (run the file) using "SELECT
+  // IMPORT('camera.cpu.metrics');". The first word of the string has to be a
+  // module name and there can be only one module registered with a given name.
+  virtual base::Status RegisterSqlModule(SqlModule sql_module) = 0;
+
   // Registers a metric at the given path which will run the specified SQL.
   virtual base::Status RegisterMetric(const std::string& path,
                                       const std::string& sql) = 0;
@@ -111,7 +122,9 @@
   // Metatracing involves tracing trace processor itself to root-cause
   // performace issues in trace processor. See |DisableAndReadMetatrace| for
   // more information on the format of the metatrace.
-  virtual void EnableMetatrace() = 0;
+  using MetatraceConfig = metatrace::MetatraceConfig;
+  using MetatraceCategories = metatrace::MetatraceCategories;
+  virtual void EnableMetatrace(MetatraceConfig config = {}) = 0;
 
   // Disables "meta-tracing" of trace processor and writes the trace as a
   // sequence of |TracePackets| into |trace_proto| returning the status of this
diff --git a/include/perfetto/tracing/data_source.h b/include/perfetto/tracing/data_source.h
index b98a033..5065c5b 100644
--- a/include/perfetto/tracing/data_source.h
+++ b/include/perfetto/tracing/data_source.h
@@ -46,6 +46,11 @@
 
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 
+// DEPRECATED: Instead of using this macro, prefer specifying symbol linkage
+// attributes explicitly using the `_WITH_ATTRS` macro variants (e.g.,
+// PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS_WITH_ATTRS). This avoids
+// potential macro definition collisions between two libraries using Perfetto.
+//
 // 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
@@ -177,6 +182,20 @@
   constexpr static BufferExhaustedPolicy kBufferExhaustedPolicy =
       BufferExhaustedPolicy::kDrop;
 
+  // When this flag is false, we cannot have multiple instances of this data
+  // source. When a data source is already active and if we attempt
+  // to start another instance of that data source (via another tracing
+  // session), it will fail to start the second instance of data source.
+  static constexpr bool kSupportsMultipleInstances = true;
+
+  // When this flag is true, DataSource callbacks (OnSetup, OnStart, etc.) are
+  // called under the lock (the same that is used in GetDataSourceLocked
+  // function). This is not recommended because it can lead to deadlocks, but
+  // it was the default behavior for a long time and some embedders rely on it
+  // to protect concurrent access to the DataSource members. So we keep the
+  // "true" value as the default.
+  static constexpr bool kRequiresCallbacksUnderLock = true;
+
   // Argument passed to the lambda function passed to Trace() (below).
   class TraceContext {
    public:
@@ -195,17 +214,18 @@
     // safely read the last event from the trace buffer.
     // See PERFETTO_INTERNAL_ADD_EMPTY_EVENT macros for context.
     void AddEmptyTracePacket() {
-      // Only add a new empty packet if the previous packet wasn't empty.
-      // Otherwise, there's nothing to flush, so adding more empty packets
-      // serves no purpose.
-      if (tls_inst_->last_packet_was_empty)
+      // If nothing was written since the last empty packet, there's nothing to
+      // scrape, so adding more empty packets serves no purpose.
+      if (tls_inst_->trace_writer->written() ==
+          tls_inst_->last_empty_packet_position) {
         return;
-      tls_inst_->last_packet_was_empty = true;
+      }
       tls_inst_->trace_writer->NewTracePacket();
+      tls_inst_->last_empty_packet_position =
+          tls_inst_->trace_writer->written();
     }
 
     TracePacketHandle NewTracePacket() {
-      tls_inst_->last_packet_was_empty = false;
       return tls_inst_->trace_writer->NewTracePacket();
     }
 
@@ -467,7 +487,10 @@
           new DataSourceType(constructor_args...));
     };
     auto* tracing_impl = internal::TracingMuxer::Get();
-    return tracing_impl->RegisterDataSource(descriptor, factory,
+    internal::DataSourceParams params{
+        DataSourceType::kSupportsMultipleInstances,
+        DataSourceType::kRequiresCallbacksUnderLock};
+    return tracing_impl->RegisterDataSource(descriptor, factory, params,
                                             &static_state_);
   }
 
diff --git a/include/perfetto/tracing/event_context.h b/include/perfetto/tracing/event_context.h
index 616ac10..e88c89c 100644
--- a/include/perfetto/tracing/event_context.h
+++ b/include/perfetto/tracing/event_context.h
@@ -51,8 +51,11 @@
   // TODO(eseckler): Remove once Chromium has switched to client lib entirely.
   explicit EventContext(
       protos::pbzero::TrackEvent* event,
-      internal::TrackEventIncrementalState* incremental_state = nullptr)
-      : event_(event), incremental_state_(incremental_state) {}
+      internal::TrackEventIncrementalState* incremental_state = nullptr,
+      bool filter_debug_annotations = false)
+      : event_(event),
+        incremental_state_(incremental_state),
+        filter_debug_annotations_(filter_debug_annotations) {}
 
   ~EventContext();
 
@@ -60,6 +63,22 @@
     return incremental_state_;
   }
 
+  // Disclaimer: Experimental method, subject to change.
+  // Exposed publicly to emit some TrackEvent fields in Chromium only in local
+  // tracing. Long-term, we really shouldn't be (ab)using the
+  // filter_debug_annotation setting for this.
+  //
+  // TODO(kraskevich): Come up with a more precise name once we have more than
+  // one usecase.
+  bool ShouldFilterDebugAnnotations() const {
+    if (tls_state_) {
+      return tls_state_->filter_debug_annotations;
+    }
+    // In Chromium tls_state_ is nullptr, so we need to get this information
+    // from a separate field.
+    return filter_debug_annotations_;
+  }
+
   // Get a TrackEvent message to write typed arguments to.
   //
   // event() is a template method to allow callers to specify a subclass of
@@ -91,11 +110,11 @@
   // values directly to TRACE_EVENT (i.e. TRACE_EVENT(..., "arg", value, ...);)
   // but in rare cases (e.g. when an argument should be written conditionally)
   // EventContext::AddDebugAnnotation provides an explicit equivalent.
-  template <typename T>
-  void AddDebugAnnotation(const char* name, T&& value) {
+  template <typename EventNameType, typename T>
+  void AddDebugAnnotation(EventNameType&& name, T&& value) {
     if (tls_state_ && tls_state_->filter_debug_annotations)
       return;
-    auto annotation = AddDebugAnnotation(name);
+    auto annotation = AddDebugAnnotation(std::forward<EventNameType>(name));
     WriteIntoTracedValue(internal::CreateTracedValueFromProto(annotation, this),
                          std::forward<T>(value));
   }
@@ -114,6 +133,8 @@
   EventContext(const EventContext&) = delete;
 
   protos::pbzero::DebugAnnotation* AddDebugAnnotation(const char* name);
+  protos::pbzero::DebugAnnotation* AddDebugAnnotation(
+      ::perfetto::DynamicString name);
 
   TracePacketHandle trace_packet_;
   protos::pbzero::TrackEvent* event_;
@@ -122,6 +143,10 @@
   // are certain that it cannot be nullptr. Once we switch to client library in
   // chrome, we can make that happen.
   const internal::TrackEventTlsState* tls_state_ = nullptr;
+  // TODO(kraskevich): Come up with a more precise name once we have more than
+  // one usecase.
+  // TODO(kraskevich): Remove once Chromium has fully switched to client lib.
+  const bool filter_debug_annotations_ = false;
 };
 
 }  // namespace perfetto
diff --git a/include/perfetto/tracing/internal/data_source_internal.h b/include/perfetto/tracing/internal/data_source_internal.h
index 6e902e9..9d9c6a2 100644
--- a/include/perfetto/tracing/internal/data_source_internal.h
+++ b/include/perfetto/tracing/internal/data_source_internal.h
@@ -162,6 +162,14 @@
     static_assert(sizeof(valid_instances.load()) * 8 >= kMaxDataSourceInstances,
                   "kMaxDataSourceInstances too high");
   }
+
+  void ResetForTesting() {
+    id = 0;
+    index = kMaxDataSources;
+    valid_instances.store(0, std::memory_order_release);
+    instances = {};
+    incremental_state_generation.store(0, std::memory_order_release);
+  }
 };
 
 // Per-DataSource-instance thread-local state.
@@ -179,7 +187,7 @@
   BufferId buffer_id = 0;
   uint64_t data_source_instance_id = 0;
   bool is_intercepted = false;
-  bool last_packet_was_empty = false;
+  uint64_t last_empty_packet_position = 0;
   uint16_t startup_target_buffer_reservation = 0;
 };
 
diff --git a/include/perfetto/tracing/internal/system_tracing_backend.h b/include/perfetto/tracing/internal/system_tracing_backend.h
index e1b0ff3..ded54c5 100644
--- a/include/perfetto/tracing/internal/system_tracing_backend.h
+++ b/include/perfetto/tracing/internal/system_tracing_backend.h
@@ -35,6 +35,8 @@
 // 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 {
+
+// Full backend (with producer and consumer)
 class PERFETTO_EXPORT_COMPONENT SystemTracingBackend : public TracingBackend {
  public:
   static TracingBackend* GetInstance();
@@ -49,6 +51,22 @@
   SystemTracingBackend();
 };
 
+// Producer only backend.
+class PERFETTO_EXPORT_COMPONENT SystemTracingProducerOnlyBackend
+    : public TracingBackend {
+ public:
+  static TracingBackend* GetInstance();
+
+  // TracingBackend implementation.
+  std::unique_ptr<ProducerEndpoint> ConnectProducer(
+      const ConnectProducerArgs&) override;
+  std::unique_ptr<ConsumerEndpoint> ConnectConsumer(
+      const ConnectConsumerArgs&) override;
+
+ private:
+  SystemTracingProducerOnlyBackend();
+};
+
 }  // namespace internal
 }  // namespace perfetto
 
diff --git a/include/perfetto/tracing/internal/tracing_muxer.h b/include/perfetto/tracing/internal/tracing_muxer.h
index e067652..c8bc0e6 100644
--- a/include/perfetto/tracing/internal/tracing_muxer.h
+++ b/include/perfetto/tracing/internal/tracing_muxer.h
@@ -35,6 +35,11 @@
 
 namespace internal {
 
+struct DataSourceParams {
+  bool supports_multiple_instances;
+  bool requires_callbacks_under_lock;
+};
+
 struct DataSourceStaticState;
 
 // This class acts as a bridge between the public API methods and the
@@ -62,6 +67,7 @@
   using DataSourceFactory = std::function<std::unique_ptr<DataSourceBase>()>;
   virtual bool RegisterDataSource(const DataSourceDescriptor&,
                                   DataSourceFactory,
+                                  DataSourceParams,
                                   DataSourceStaticState*) = 0;
 
   // Updates the DataSourceDescriptor for the DataSource.
@@ -88,6 +94,15 @@
                                    InterceptorBase::TLSFactory,
                                    InterceptorBase::TracePacketCallback) = 0;
 
+  // Informs the tracing services to activate any of these triggers if any
+  // tracing session was waiting for them.
+  //
+  // Sends the trigger signal to all the initialized backends that are currently
+  // connected and that connect in the next `ttl_ms` milliseconds (but returns
+  // immediately anyway).
+  virtual void ActivateTriggers(const std::vector<std::string>&,
+                                uint32_t ttl_ms) = 0;
+
  protected:
   explicit TracingMuxer(Platform* platform) : platform_(platform) {}
 
diff --git a/include/perfetto/tracing/internal/track_event_data_source.h b/include/perfetto/tracing/internal/track_event_data_source.h
index ce2e0f0..7b1c87e 100644
--- a/include/perfetto/tracing/internal/track_event_data_source.h
+++ b/include/perfetto/tracing/internal/track_event_data_source.h
@@ -35,6 +35,28 @@
 
 namespace perfetto {
 
+namespace {
+
+class StopArgsImpl : public DataSourceBase::StopArgs {
+ public:
+  // HandleAsynchronously() can optionally be called to defer the tracing
+  // session stop and write track events just before stopping. This function
+  // returns a closure that must be invoked after the last track events have
+  // been emitted. The caller also needs to explicitly call
+  // TrackEvent::Flush() because no other implicit flushes will happen after
+  // the stop signal.
+  // See the comment in include/perfetto/tracing/data_source.h for more info.
+  std::function<void()> HandleStopAsynchronously() const override {
+    auto closure = std::move(async_stop_closure);
+    async_stop_closure = std::function<void()>();
+    return closure;
+  }
+
+  mutable std::function<void()> async_stop_closure;
+};
+
+}  // namespace
+
 // A function for converting an abstract timestamp into a
 // perfetto::TraceTimestamp struct. By specialising this template and defining
 // static ConvertTimestampToTraceTimeNs function in it the user can register
@@ -178,16 +200,18 @@
   using Base = DataSource<DataSourceType, TrackEventDataSourceTraits>;
 
  public:
+  static constexpr bool kRequiresCallbacksUnderLock = false;
+
   // Add or remove a session observer for this track event data source. The
   // observer will be notified about started and stopped tracing sessions.
   // Returns |true| if the observer was successfully added (i.e., the maximum
   // number of observers wasn't exceeded).
   static bool AddSessionObserver(TrackEventSessionObserver* observer) {
-    return TrackEventInternal::AddSessionObserver(observer);
+    return TrackEventInternal::AddSessionObserver(*Registry, observer);
   }
 
   static void RemoveSessionObserver(TrackEventSessionObserver* observer) {
-    TrackEventInternal::RemoveSessionObserver(observer);
+    TrackEventInternal::RemoveSessionObserver(*Registry, observer);
   }
 
   // DataSource implementation.
@@ -199,16 +223,31 @@
   }
 
   void OnStart(const DataSourceBase::StartArgs& args) override {
-    TrackEventInternal::OnStart(args);
+    TrackEventInternal::OnStart(*Registry, args);
   }
 
   void OnStop(const DataSourceBase::StopArgs& args) override {
-    TrackEventInternal::DisableTracing(*Registry, args);
+    auto outer_stop_closure = args.HandleStopAsynchronously();
+    StopArgsImpl inner_stop_args{};
+    uint32_t internal_instance_index = args.internal_instance_index;
+    inner_stop_args.internal_instance_index = internal_instance_index;
+    inner_stop_args.async_stop_closure = [internal_instance_index,
+                                          outer_stop_closure] {
+      TrackEventInternal::DisableTracing(*Registry, internal_instance_index);
+      outer_stop_closure();
+    };
+
+    TrackEventInternal::OnStop(*Registry, inner_stop_args);
+
+    // If inner_stop_args.HandleStopAsynchronously() hasn't been called,
+    // run the async closure here.
+    if (inner_stop_args.async_stop_closure)
+      std::move(inner_stop_args.async_stop_closure)();
   }
 
   void WillClearIncrementalState(
       const DataSourceBase::ClearIncrementalStateArgs& args) override {
-    TrackEventInternal::WillClearIncrementalState(args);
+    TrackEventInternal::WillClearIncrementalState(*Registry, args);
   }
 
   static void Flush() {
@@ -542,8 +581,21 @@
                     return true;
                   });
             }
-            if (!on_current_thread_track)
+            if (type == protos::pbzero::TrackEvent::TYPE_UNSPECIFIED) {
+              // Explicitly clear the track, so that the event is not associated
+              // with the default track, but instead uses the legacy mechanism
+              // based on the phase and pid/tid override.
+              event_ctx.event()->set_track_uuid(0);
+            } else if (!on_current_thread_track) {
+              // We emit these events using TrackDescriptors, and we cannot emit
+              // events on behalf of other processes using the TrackDescriptor
+              // format. Chrome is the only user of events with explicit process
+              // ids and currently only Chrome emits PHASE_MEMORY_DUMP events
+              // with an explicit process id, so we should be fine here.
+              // TODO(mohitms): Get rid of events with explicit process ids
+              // entirely.
               event_ctx.event()->set_track_uuid(track.uuid);
+            }
             WriteTrackEventArgs(std::move(event_ctx),
                                 std::forward<Arguments>(args)...);
           }  // event_ctx
diff --git a/include/perfetto/tracing/internal/track_event_internal.h b/include/perfetto/tracing/internal/track_event_internal.h
index 00aa3f5..281eac2 100644
--- a/include/perfetto/tracing/internal/track_event_internal.h
+++ b/include/perfetto/tracing/internal/track_event_internal.h
@@ -173,16 +173,22 @@
       const TrackEventCategoryRegistry&,
       bool (*register_data_source)(const DataSourceDescriptor&));
 
-  static bool AddSessionObserver(TrackEventSessionObserver*);
-  static void RemoveSessionObserver(TrackEventSessionObserver*);
+  static bool AddSessionObserver(const TrackEventCategoryRegistry&,
+                                 TrackEventSessionObserver*);
+  static void RemoveSessionObserver(const TrackEventCategoryRegistry&,
+                                    TrackEventSessionObserver*);
 
   static void EnableTracing(const TrackEventCategoryRegistry& registry,
                             const protos::gen::TrackEventConfig& config,
                             const DataSourceBase::SetupArgs&);
-  static void OnStart(const DataSourceBase::StartArgs&);
+  static void OnStart(const TrackEventCategoryRegistry&,
+                      const DataSourceBase::StartArgs&);
+  static void OnStop(const TrackEventCategoryRegistry&,
+                     const DataSourceBase::StopArgs&);
   static void DisableTracing(const TrackEventCategoryRegistry& registry,
-                             const DataSourceBase::StopArgs&);
+                             uint32_t internal_instance_index);
   static void WillClearIncrementalState(
+      const TrackEventCategoryRegistry&,
       const DataSourceBase::ClearIncrementalStateArgs&);
 
   static bool IsCategoryEnabled(const TrackEventCategoryRegistry& registry,
@@ -219,14 +225,15 @@
 
   // TODO(altimin): Remove this method once Chrome uses
   // EventContext::AddDebugAnnotation directly.
-  template <typename T>
+  template <typename NameType, typename ValueType>
   static void AddDebugAnnotation(perfetto::EventContext* event_ctx,
-                                 const char* name,
-                                 T&& value) {
-    auto annotation = AddDebugAnnotation(event_ctx, name);
+                                 NameType&& name,
+                                 ValueType&& value) {
+    auto annotation =
+        AddDebugAnnotation(event_ctx, std::forward<NameType>(name));
     WriteIntoTracedValue(
         internal::CreateTracedValueFromProto(annotation, event_ctx),
-        std::forward<T>(value));
+        std::forward<ValueType>(value));
   }
 
   // If the given track hasn't been seen by the trace writer yet, write a
@@ -263,14 +270,9 @@
 
   static TraceTimestamp GetTraceTime();
 
-  // Get the clock used by GetTimeNs().
-  static constexpr protos::pbzero::BuiltinClock GetClockId() {
-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) && \
-    !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-    return protos::pbzero::BUILTIN_CLOCK_BOOTTIME;
-#else
-    return protos::pbzero::BUILTIN_CLOCK_MONOTONIC;
-#endif
+  static inline protos::pbzero::BuiltinClock GetClockId() { return clock_; }
+  static inline void SetClockId(protos::pbzero::BuiltinClock clock) {
+    clock_ = clock;
   }
 
   static int GetSessionCount();
@@ -291,11 +293,18 @@
       TraceTimestamp,
       uint32_t seq_flags =
           protos::pbzero::TracePacket::SEQ_NEEDS_INCREMENTAL_STATE);
+
   static protos::pbzero::DebugAnnotation* AddDebugAnnotation(
       perfetto::EventContext*,
       const char* name);
 
+  static protos::pbzero::DebugAnnotation* AddDebugAnnotation(
+      perfetto::EventContext*,
+      perfetto::DynamicString name);
+
   static std::atomic<int> session_count_;
+
+  static protos::pbzero::BuiltinClock clock_;
 };
 
 template <typename TraceContext>
@@ -314,7 +323,7 @@
   }
   if (disable_incremental_timestamps) {
     if (timestamp_unit_multiplier == 1) {
-      default_clock = TrackEventInternal::GetClockId();
+      default_clock = static_cast<uint32_t>(TrackEventInternal::GetClockId());
     } else {
       default_clock = TrackEventIncrementalState::kClockIdAbsolute;
     }
diff --git a/include/perfetto/tracing/internal/track_event_macros.h b/include/perfetto/tracing/internal/track_event_macros.h
index d4e1017..63be387 100644
--- a/include/perfetto/tracing/internal/track_event_macros.h
+++ b/include/perfetto/tracing/internal/track_event_macros.h
@@ -41,14 +41,13 @@
 //   byte 0                      byte 1
 //   (inst0, inst1, ..., inst7), (inst0, inst1, ..., inst7)
 //
-#define PERFETTO_INTERNAL_DECLARE_CATEGORIES(...)                             \
+#define PERFETTO_INTERNAL_DECLARE_CATEGORIES(attrs, ...)                      \
   namespace internal {                                                        \
   constexpr ::perfetto::Category kCategories[] = {__VA_ARGS__};               \
   constexpr size_t kCategoryCount =                                           \
       sizeof(kCategories) / sizeof(kCategories[0]);                           \
   /* The per-instance enable/disable state per category */                    \
-  PERFETTO_COMPONENT_EXPORT extern std::atomic<uint8_t>                       \
-      g_category_state_storage[kCategoryCount];                               \
+  attrs extern std::atomic<uint8_t> g_category_state_storage[kCategoryCount]; \
   /* The category registry which mediates access to the above structures. */  \
   /* The registry is used for two purposes: */                                \
   /**/                                                                        \
@@ -69,32 +68,29 @@
   /* TODO(skyostil): Unify these using a C++17 inline constexpr variable. */  \
   constexpr ::perfetto::internal::TrackEventCategoryRegistry                  \
       kConstExprCategoryRegistry(kCategoryCount, &kCategories[0], nullptr);   \
-  PERFETTO_COMPONENT_EXPORT extern const ::perfetto::internal::               \
-      TrackEventCategoryRegistry kCategoryRegistry;                           \
+  attrs extern const ::perfetto::internal::TrackEventCategoryRegistry         \
+      kCategoryRegistry;                                                      \
   static_assert(kConstExprCategoryRegistry.ValidateCategories(),              \
                 "Invalid category names found");                              \
   }  // namespace internal
 
 // In a .cc file, declares storage for each category's runtime state.
-#define PERFETTO_INTERNAL_CATEGORY_STORAGE()             \
-  namespace internal {                                   \
-  PERFETTO_COMPONENT_EXPORT std::atomic<uint8_t>         \
-      g_category_state_storage[kCategoryCount];          \
-  PERFETTO_EXPORT_COMPONENT const ::perfetto::internal:: \
-      TrackEventCategoryRegistry kCategoryRegistry(      \
-          kCategoryCount,                                \
-          &kCategories[0],                               \
-          &g_category_state_storage[0]);                 \
+#define PERFETTO_INTERNAL_CATEGORY_STORAGE(attrs)                      \
+  namespace internal {                                                 \
+  attrs std::atomic<uint8_t> g_category_state_storage[kCategoryCount]; \
+  attrs const ::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.
 // `virtual ~TrackEvent` is added to avoid `-Wweak-vtables` warning.
 // Learn more : aosp/2019906
-#define PERFETTO_INTERNAL_DECLARE_TRACK_EVENT_DATA_SOURCE() \
-  struct PERFETTO_COMPONENT_EXPORT TrackEvent               \
-      : public ::perfetto::internal::TrackEventDataSource<  \
-            TrackEvent, &internal::kCategoryRegistry> {     \
-    virtual ~TrackEvent();                                  \
+#define PERFETTO_INTERNAL_DECLARE_TRACK_EVENT_DATA_SOURCE(attrs)               \
+  struct attrs TrackEvent : public ::perfetto::internal::TrackEventDataSource< \
+                                TrackEvent, &internal::kCategoryRegistry> {    \
+    virtual ~TrackEvent();                                                     \
   }
 
 #define PERFETTO_INTERNAL_DEFINE_TRACK_EVENT_DATA_SOURCE() \
@@ -104,9 +100,9 @@
 // index into the current category registry. A build error will be generated if
 // 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,                                                                \
+#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.
@@ -118,14 +114,15 @@
 // if so, emits one trace event with the given arguments.
 #define PERFETTO_INTERNAL_TRACK_EVENT(category, name, ...)                     \
   do {                                                                         \
-    perfetto::internal::ValidateEventNameType<decltype(name)>();               \
-    namespace tns = ::PERFETTO_TRACK_EVENT_NAMESPACE;                          \
+    ::perfetto::internal::ValidateEventNameType<decltype(name)>();             \
+    namespace tns = PERFETTO_TRACK_EVENT_NAMESPACE;                            \
     /* Compute the category index outside the lambda to work around a */       \
     /* GCC 7 bug */                                                            \
     static constexpr auto PERFETTO_UID(                                        \
         kCatIndex_ADD_TO_PERFETTO_DEFINE_CATEGORIES_IF_FAILS_) =               \
         PERFETTO_GET_CATEGORY_INDEX(category);                                 \
-    if (tns::internal::IsDynamicCategory(category)) {                          \
+    if (::PERFETTO_TRACK_EVENT_NAMESPACE::internal::IsDynamicCategory(         \
+            category)) {                                                       \
       tns::TrackEvent::CallIfEnabled(                                          \
           [&](uint32_t instances) PERFETTO_NO_THREAD_SAFETY_ANALYSIS {         \
             tns::TrackEvent::TraceForCategory(instances, category, name,       \
@@ -144,6 +141,15 @@
     }                                                                          \
   } while (false)
 
+// C++17 doesn't like a move constructor being defined for the EventFinalizer
+// class but C++11 and MSVC doesn't compile without it being defined so support
+// both.
+#if PERFETTO_IS_AT_LEAST_CPP17() && !PERFETTO_BUILDFLAG(PERFETTO_COMPILER_MSVC)
+#define PERFETTO_INTERNAL_EVENT_FINALIZER_KEYWORD delete
+#else
+#define PERFETTO_INTERNAL_EVENT_FINALIZER_KEYWORD default
+#endif
+
 #define PERFETTO_INTERNAL_SCOPED_TRACK_EVENT(category, name, ...)             \
   struct PERFETTO_UID(ScopedEvent) {                                          \
     struct EventFinalizer {                                                   \
@@ -160,7 +166,8 @@
       EventFinalizer(const EventFinalizer&) = delete;                         \
       inline EventFinalizer& operator=(const EventFinalizer&) = delete;       \
                                                                               \
-      EventFinalizer(EventFinalizer&&) = default;                             \
+      EventFinalizer(EventFinalizer&&) =                                      \
+          PERFETTO_INTERNAL_EVENT_FINALIZER_KEYWORD;                          \
       EventFinalizer& operator=(EventFinalizer&&) = delete;                   \
     } finalizer;                                                              \
   } PERFETTO_UID(scoped_event) {                                              \
@@ -170,12 +177,32 @@
     }()                                                                       \
   }
 
-#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(    \
+#if PERFETTO_BUILDFLAG(PERFETTO_COMPILER_GCC)
+// On GCC versions <9 there's a bug that prevents using captured constant
+// variables in constexpr evaluation inside a lambda:
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82643
+// TODO(khokhlov): Remove this fallback after Perfetto moves to a more recent
+// GCC version.
+#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)))
+#else  // !PERFETTO_BUILDFLAG(PERFETTO_COMPILER_GCC)
+#define PERFETTO_INTERNAL_CATEGORY_ENABLED(category)                     \
+  [&]() -> bool {                                                        \
+    using PERFETTO_TRACK_EVENT_NAMESPACE::TrackEvent;                    \
+    using ::PERFETTO_TRACK_EVENT_NAMESPACE::internal::IsDynamicCategory; \
+    constexpr auto PERFETTO_UID(index) =                                 \
+        PERFETTO_GET_CATEGORY_INDEX(category);                           \
+    constexpr auto PERFETTO_UID(dynamic) = IsDynamicCategory(category);  \
+    return PERFETTO_UID(dynamic)                                         \
+               ? TrackEvent::IsDynamicCategoryEnabled(                   \
+                     ::perfetto::DynamicCategory(category))              \
+               : TrackEvent::IsCategoryEnabled(PERFETTO_UID(index));     \
+  }()
+#endif  // !PERFETTO_BUILDFLAG(PERFETTO_COMPILER_GCC)
 
 // Emits an empty trace packet into the trace to ensure that the service can
 // safely read the last event from the trace buffer. This can be used to
@@ -187,12 +214,12 @@
 // read the last trace packet from an incomplete SMB chunk (crbug.com/1021571
 // and b/162206162) when scraping the SMB. Adding an empty trace packet ensures
 // that all prior events can be scraped by the service.
-#define PERFETTO_INTERNAL_ADD_EMPTY_EVENT()                                  \
-  do {                                                                       \
-    ::PERFETTO_TRACK_EVENT_NAMESPACE::TrackEvent::Trace(                     \
-        [](::PERFETTO_TRACK_EVENT_NAMESPACE::TrackEvent::TraceContext ctx) { \
-          ctx.AddEmptyTracePacket();                                         \
-        });                                                                  \
+#define PERFETTO_INTERNAL_ADD_EMPTY_EVENT()                                \
+  do {                                                                     \
+    PERFETTO_TRACK_EVENT_NAMESPACE::TrackEvent::Trace(                     \
+        [](PERFETTO_TRACK_EVENT_NAMESPACE::TrackEvent::TraceContext ctx) { \
+          ctx.AddEmptyTracePacket();                                       \
+        });                                                                \
   } while (false)
 
 #endif  // INCLUDE_PERFETTO_TRACING_INTERNAL_TRACK_EVENT_MACROS_H_
diff --git a/include/perfetto/tracing/internal/write_track_event_args.h b/include/perfetto/tracing/internal/write_track_event_args.h
index ba6c4e8..7d2aca9 100644
--- a/include/perfetto/tracing/internal/write_track_event_args.h
+++ b/include/perfetto/tracing/internal/write_track_event_args.h
@@ -81,8 +81,9 @@
 template <typename ArgumentFunction,
           typename ArgFunctionCheck = typename std::enable_if<
               IsValidTraceLambda<ArgumentFunction>()>::type>
-PERFETTO_ALWAYS_INLINE void WriteTrackEventArgs(EventContext event_ctx,
-                                                ArgumentFunction arg_function) {
+PERFETTO_ALWAYS_INLINE void WriteTrackEventArgs(
+    EventContext event_ctx,
+    const ArgumentFunction& arg_function) {
   arg_function(std::move(event_ctx));
 }
 
@@ -106,9 +107,10 @@
           typename... Args,
           typename ArgFunctionCheck = typename std::enable_if<
               IsValidTraceLambdaTakingReference<ArgumentFunction>()>::type>
-PERFETTO_ALWAYS_INLINE void WriteTrackEventArgs(EventContext event_ctx,
-                                                ArgumentFunction arg_function,
-                                                Args&&... args) {
+PERFETTO_ALWAYS_INLINE void WriteTrackEventArgs(
+    EventContext event_ctx,
+    const ArgumentFunction& arg_function,
+    Args&&... args) {
   // |arg_function| will capture EventContext by reference, so std::move isn't
   // needed.
   arg_function(event_ctx);
@@ -149,6 +151,16 @@
   WriteTrackEventArgs(std::move(event_ctx), std::forward<Args>(args)...);
 }
 
+// Write one debug annotation and recursively write the rest of the arguments.
+template <typename ArgValue, typename... Args>
+PERFETTO_ALWAYS_INLINE void WriteTrackEventArgs(EventContext event_ctx,
+                                                DynamicString arg_name,
+                                                ArgValue&& arg_value,
+                                                Args&&... args) {
+  event_ctx.AddDebugAnnotation(arg_name, std::forward<ArgValue>(arg_value));
+  WriteTrackEventArgs(std::move(event_ctx), std::forward<Args>(args)...);
+}
+
 }  // namespace internal
 }  // namespace perfetto
 
diff --git a/include/perfetto/tracing/platform.h b/include/perfetto/tracing/platform.h
index fde6b11..17328cc 100644
--- a/include/perfetto/tracing/platform.h
+++ b/include/perfetto/tracing/platform.h
@@ -104,6 +104,10 @@
   // in-process mode.
   virtual std::string GetCurrentProcessName() = 0;
 
+  // Tear down any persistent platform state (e.g., TLS variables). The platform
+  // interface must not be used after calling this function.
+  virtual void Shutdown();
+
  private:
   static base::PlatformProcessId process_id_;
 };
diff --git a/include/perfetto/tracing/traced_value.h b/include/perfetto/tracing/traced_value.h
index f06d234..366db4b 100644
--- a/include/perfetto/tracing/traced_value.h
+++ b/include/perfetto/tracing/traced_value.h
@@ -66,7 +66,7 @@
 //   about tracing).
 //
 //
-// After definiting a conversion method, the object can be used directly as a
+// After defining a conversion method, the object can be used directly as a
 // TRACE_EVENT argument:
 //
 // Foo foo;
@@ -177,9 +177,9 @@
   static TracedValue CreateFromProto(protos::pbzero::DebugAnnotation* proto,
                                      EventContext* event_context = nullptr);
 
-  inline explicit TracedValue(protos::pbzero::DebugAnnotation* annotation,
-                              EventContext* event_context,
-                              internal::CheckedScope* parent_scope)
+  inline TracedValue(protos::pbzero::DebugAnnotation* annotation,
+                     EventContext* event_context,
+                     internal::CheckedScope* parent_scope)
       : annotation_(annotation),
         event_context_(event_context),
         checked_scope_(parent_scope) {}
@@ -228,9 +228,9 @@
  private:
   friend class TracedValue;
 
-  inline explicit TracedArray(protos::pbzero::DebugAnnotation* annotation,
-                              EventContext* event_context,
-                              internal::CheckedScope* parent_scope)
+  inline TracedArray(protos::pbzero::DebugAnnotation* annotation,
+                     EventContext* event_context,
+                     internal::CheckedScope* parent_scope)
       : annotation_(annotation),
         event_context_(event_context),
         checked_scope_(parent_scope) {}
@@ -286,7 +286,7 @@
   // Create a |TracedDictionary| which will populate the given field of the
   // given |message|.
   template <typename MessageType, typename FieldMetadata>
-  inline explicit TracedDictionary(
+  inline TracedDictionary(
       MessageType* message,
       protozero::proto_utils::internal::FieldMetadataHelper<FieldMetadata>,
       EventContext* event_context,
diff --git a/include/perfetto/tracing/tracing.h b/include/perfetto/tracing/tracing.h
index 179869b..73496d4 100644
--- a/include/perfetto/tracing/tracing.h
+++ b/include/perfetto/tracing/tracing.h
@@ -116,6 +116,24 @@
   // callback instead of being logged directly.
   LogMessageCallback log_message_callback = nullptr;
 
+  // When this flag is set to false, it overrides
+  // `DataSource::kSupportsMultipleInstances` for all the data sources.
+  // As a result when a tracing session is already running and if we attempt to
+  // start another session, it will fail to start the data source which were
+  // already active.
+  bool supports_multiple_data_source_instances = true;
+
+  // If this flag is set the default clock for taking timestamps is overridden
+  // with CLOCK_MONOTONIC_RAW on platforms that support it.
+  bool use_monotonic_raw_clock = false;
+
+  // This flag can be set to false in order to avoid enabling the system
+  // consumer in Tracing::Initialize(), so that the linker can remove the unused
+  // consumer IPC implementation to reduce binary size. When this option is
+  // false, calling Tracing::NewTrace() on the system backend will fail. This
+  // setting only has an effect if kSystemBackend is specified in |backends|.
+  bool enable_system_consumer = true;
+
  protected:
   friend class Tracing;
   friend class internal::TracingMuxerImpl;
@@ -125,11 +143,13 @@
   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_) ==
+                    system_backend_factory_, dcheck_is_on_,
+                    enable_system_consumer) ==
            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_);
+                    other.system_backend_factory_, other.dcheck_is_on_,
+                    other.enable_system_consumer);
   }
 
   using BackendFactoryFunction = TracingBackend* (*)();
@@ -162,8 +182,13 @@
           &internal::InProcessTracingBackend::GetInstance;
     }
     if (args.backends & kSystemBackend) {
-      args_copy.system_backend_factory_ =
-          &internal::SystemTracingBackend::GetInstance;
+      if (args.enable_system_consumer) {
+        args_copy.system_backend_factory_ =
+            &internal::SystemTracingBackend::GetInstance;
+      } else {
+        args_copy.system_backend_factory_ =
+            &internal::SystemTracingProducerOnlyBackend::GetInstance;
+      }
     }
     InitializeInternal(args_copy);
   }
@@ -173,8 +198,6 @@
 
   // Start a new tracing session using the given tracing backend. Use
   // |kUnspecifiedBackend| to select an available backend automatically.
-  // For the moment this can be used only when initializing tracing in
-  // kInProcess mode. For the system mode use the 'bin/perfetto' cmdline client.
   static std::unique_ptr<TracingSession> NewTrace(
       BackendType = kUnspecifiedBackend);
 
@@ -257,6 +280,15 @@
       const TraceConfig& config,
       SetupStartupTracingOpts);
 
+  // Informs the tracing services to activate any of these triggers if any
+  // tracing session was waiting for them.
+  //
+  // Sends the trigger signal to all the initialized backends that are currently
+  // connected and that connect in the next `ttl_ms` milliseconds (but
+  // returns immediately anyway).
+  static void ActivateTriggers(const std::vector<std::string>& triggers,
+                               uint32_t ttl_ms);
+
  private:
   static void InitializeInternal(const TracingInitArgs&);
 
diff --git a/include/perfetto/tracing/track.h b/include/perfetto/tracing/track.h
index 612b7d9..fb5751c 100644
--- a/include/perfetto/tracing/track.h
+++ b/include/perfetto/tracing/track.h
@@ -193,7 +193,7 @@
 // A track for recording counter values with the TRACE_COUNTER macro. Counter
 // tracks can optionally be given units and other metadata. See
 // /protos/perfetto/trace/track_event/counter_descriptor.proto for details.
-class CounterTrack : public Track {
+class PERFETTO_EXPORT_COMPONENT CounterTrack : public Track {
   // A random value mixed into counter track uuids to avoid collisions with
   // other types of tracks.
   static constexpr uint64_t kCounterMagic = 0xb1a4a67d7970839eul;
diff --git a/include/perfetto/tracing/track_event.h b/include/perfetto/tracing/track_event.h
index c3e86d3..7366b85 100644
--- a/include/perfetto/tracing/track_event.h
+++ b/include/perfetto/tracing/track_event.h
@@ -17,7 +17,6 @@
 #ifndef INCLUDE_PERFETTO_TRACING_TRACK_EVENT_H_
 #define INCLUDE_PERFETTO_TRACING_TRACK_EVENT_H_
 
-#include "perfetto/base/time.h"
 #include "perfetto/tracing/internal/track_event_data_source.h"
 #include "perfetto/tracing/internal/track_event_internal.h"
 #include "perfetto/tracing/internal/track_event_macros.h"
@@ -116,6 +115,9 @@
 //  '----------------------------------'
 //
 
+// DEPRECATED: Please use PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE to implement
+// multiple track event category sets in one program.
+//
 // Each compilation unit can be in exactly one track event namespace,
 // allowing the overall program to use multiple track event data sources and
 // category lists if necessary. Use this macro to select the namespace for the
@@ -125,7 +127,7 @@
 // registration (see quickstart above) needs to happen for both namespaces
 // separately.
 #ifndef PERFETTO_TRACK_EVENT_NAMESPACE
-#define PERFETTO_TRACK_EVENT_NAMESPACE perfetto
+#define PERFETTO_TRACK_EVENT_NAMESPACE perfetto_track_event
 #endif
 
 // Deprecated; see perfetto::Category().
@@ -170,28 +172,85 @@
   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), ...
-#define PERFETTO_DEFINE_CATEGORIES(...)                        \
-  namespace PERFETTO_TRACK_EVENT_NAMESPACE {                   \
-  /* The list of category names */                             \
-  PERFETTO_INTERNAL_DECLARE_CATEGORIES(__VA_ARGS__)            \
-  /* The track event data source for this set of categories */ \
-  PERFETTO_INTERNAL_DECLARE_TRACK_EVENT_DATA_SOURCE();         \
-  } /* namespace PERFETTO_TRACK_EVENT_NAMESPACE */             \
-  PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS(                 \
-      PERFETTO_TRACK_EVENT_NAMESPACE::TrackEvent,              \
-      perfetto::internal::TrackEventDataSourceTraits)
+// this macro: perfetto::Category("cat1"), perfetto::Category("cat2"), ...
+// `ns` is the name of the namespace in which the categories should be declared.
+// `attrs` are linkage attributes for the underlying data source. See
+// PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS_WITH_ATTRS.
+//
+// Implementation note: the extra namespace (PERFETTO_TRACK_EVENT_NAMESPACE) is
+// kept here only for backward compatibility.
+#define PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE_WITH_ATTRS(ns, attrs, ...) \
+  namespace ns {                                                           \
+  namespace PERFETTO_TRACK_EVENT_NAMESPACE {                               \
+  /* The list of category names */                                         \
+  PERFETTO_INTERNAL_DECLARE_CATEGORIES(attrs, __VA_ARGS__)                 \
+  /* The track event data source for this set of categories */             \
+  PERFETTO_INTERNAL_DECLARE_TRACK_EVENT_DATA_SOURCE(attrs);                \
+  } /* namespace PERFETTO_TRACK_EVENT_NAMESPACE  */                        \
+  using PERFETTO_TRACK_EVENT_NAMESPACE::TrackEvent;                        \
+  } /* namespace ns */                                                     \
+  PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS_WITH_ATTRS(                  \
+      attrs, ns::PERFETTO_TRACK_EVENT_NAMESPACE::TrackEvent,               \
+      ::perfetto::internal::TrackEventDataSourceTraits)
+
+// Register the set of available categories by passing a list of categories to
+// this macro: perfetto::Category("cat1"), perfetto::Category("cat2"), ...
+// `ns` is the name of the namespace in which the categories should be declared.
+#define PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE(ns, ...) \
+  PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE_WITH_ATTRS(    \
+      ns, PERFETTO_COMPONENT_EXPORT, __VA_ARGS__)
+
+// Make categories in a given namespace the default ones used by track events
+// for the current translation unit. Can only be used *once* in a given global
+// or namespace scope.
+#define PERFETTO_USE_CATEGORIES_FROM_NAMESPACE(ns)                         \
+  namespace PERFETTO_TRACK_EVENT_NAMESPACE {                               \
+  using ::ns::PERFETTO_TRACK_EVENT_NAMESPACE::TrackEvent;                  \
+  namespace internal {                                                     \
+  using ::ns::PERFETTO_TRACK_EVENT_NAMESPACE::internal::kCategoryRegistry; \
+  using ::ns::PERFETTO_TRACK_EVENT_NAMESPACE::internal::                   \
+      kConstExprCategoryRegistry;                                          \
+  } /* namespace internal */                                               \
+  } /* namespace PERFETTO_TRACK_EVENT_NAMESPACE */                         \
+  PERFETTO_INTERNAL_SWALLOW_SEMICOLON()
+
+// Make categories in a given namespace the default ones used by track events
+// for the current block scope. Can only be used in a function or block scope.
+#define PERFETTO_USE_CATEGORIES_FROM_NAMESPACE_SCOPED(ns) \
+  namespace PERFETTO_TRACK_EVENT_NAMESPACE = ns::PERFETTO_TRACK_EVENT_NAMESPACE
+
+// Register categories in the default (global) namespace. Warning: only one set
+// of global categories can be defined in a single program. Create namespaced
+// categories with PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE to work around this
+// limitation.
+#define PERFETTO_DEFINE_CATEGORIES(...)                           \
+  PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE(perfetto, __VA_ARGS__); \
+  PERFETTO_USE_CATEGORIES_FROM_NAMESPACE(perfetto)
+
+// Allocate storage for each category by using this macro once per track event
+// namespace. `ns` is the name of the namespace in which the categories should
+// be declared and `attrs` specify linkage attributes for the data source.
+#define PERFETTO_TRACK_EVENT_STATIC_STORAGE_IN_NAMESPACE_WITH_ATTRS(ns, attrs) \
+  namespace ns {                                                               \
+  namespace PERFETTO_TRACK_EVENT_NAMESPACE {                                   \
+  PERFETTO_INTERNAL_CATEGORY_STORAGE(attrs)                                    \
+  PERFETTO_INTERNAL_DEFINE_TRACK_EVENT_DATA_SOURCE()                           \
+  } /* namespace PERFETTO_TRACK_EVENT_NAMESPACE */                             \
+  } /* namespace ns */                                                         \
+  PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS_WITH_ATTRS(                       \
+      attrs, ns::PERFETTO_TRACK_EVENT_NAMESPACE::TrackEvent,                   \
+      ::perfetto::internal::TrackEventDataSourceTraits)
 
 // Allocate storage for each category by using this macro once per track event
 // namespace.
-#define PERFETTO_TRACK_EVENT_STATIC_STORAGE()        \
-  namespace PERFETTO_TRACK_EVENT_NAMESPACE {         \
-  PERFETTO_INTERNAL_CATEGORY_STORAGE()               \
-  PERFETTO_INTERNAL_DEFINE_TRACK_EVENT_DATA_SOURCE() \
-  } /* namespace PERFETTO_TRACK_EVENT_NAMESPACE */   \
-  PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(        \
-      PERFETTO_TRACK_EVENT_NAMESPACE::TrackEvent,    \
-      perfetto::internal::TrackEventDataSourceTraits)
+#define PERFETTO_TRACK_EVENT_STATIC_STORAGE_IN_NAMESPACE(ns)   \
+  PERFETTO_TRACK_EVENT_STATIC_STORAGE_IN_NAMESPACE_WITH_ATTRS( \
+      ns, PERFETTO_COMPONENT_EXPORT)
+
+// Allocate storage for each category by using this macro once per track event
+// namespace.
+#define PERFETTO_TRACK_EVENT_STATIC_STORAGE() \
+  PERFETTO_TRACK_EVENT_STATIC_STORAGE_IN_NAMESPACE(perfetto)
 
 // Ignore GCC warning about a missing argument for a variadic macro parameter.
 #if defined(__GNUC__) || defined(__clang__)
diff --git a/include/perfetto/tracing/track_event_legacy.h b/include/perfetto/tracing/track_event_legacy.h
index 532b2ae..373e2a5 100644
--- a/include/perfetto/tracing/track_event_legacy.h
+++ b/include/perfetto/tracing/track_event_legacy.h
@@ -97,8 +97,6 @@
 static constexpr char TRACE_EVENT_PHASE_MEMORY_DUMP = 'v';
 static constexpr char TRACE_EVENT_PHASE_MARK = 'R';
 static constexpr char TRACE_EVENT_PHASE_CLOCK_SYNC = 'c';
-static constexpr char TRACE_EVENT_PHASE_ENTER_CONTEXT = '(';
-static constexpr char TRACE_EVENT_PHASE_LEAVE_CONTEXT = ')';
 
 // Flags for changing the behavior of TRACE_EVENT_API_ADD_TRACE_EVENT.
 static constexpr uint32_t TRACE_EVENT_FLAG_NONE =
@@ -265,6 +263,17 @@
       : raw_id_(static_cast<uint64_t>(raw_id)) {}
   explicit LegacyTraceId(int8_t raw_id)
       : raw_id_(static_cast<uint64_t>(raw_id)) {}
+// Different platforms disagree on which integer types are same and which
+// are different. E.g. on Mac size_t is considered a different type from
+// uint64_t even though it has the same size and signedness.
+// Below we add overloads for those types that are known to cause ambiguity.
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+  explicit LegacyTraceId(size_t raw_id) : raw_id_(raw_id) {}
+  explicit LegacyTraceId(intptr_t raw_id)
+      : raw_id_(static_cast<uint64_t>(raw_id)) {}
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  explicit LegacyTraceId(unsigned long raw_id) : raw_id_(raw_id) {}
+#endif
   explicit LegacyTraceId(LocalId raw_id) : raw_id_(raw_id.raw_id()) {
     id_flags_ = legacy::kTraceEventFlagHasLocalId;
   }
@@ -303,6 +312,16 @@
 namespace perfetto {
 namespace internal {
 
+template <typename T>
+bool IsEqual(T x, T y) {
+  return x == y;
+}
+
+template <typename T, typename U>
+bool IsEqual(T x, U y) {
+  return false;
+}
+
 class PERFETTO_EXPORT_COMPONENT TrackEventLegacy {
  public:
   static constexpr protos::pbzero::TrackEvent::Type PhaseToType(char phase) {
@@ -324,6 +343,7 @@
                                char phase,
                                uint32_t flags,
                                Args&&... args) PERFETTO_NO_INLINE {
+    PERFETTO_DCHECK(!(flags & TRACE_EVENT_FLAG_HAS_PROCESS_ID));
     AddDebugAnnotations(&ctx, std::forward<Args>(args)...);
     if (NeedLegacyFlags(phase, flags)) {
       auto legacy_event = ctx.event()->set_legacy_event();
@@ -351,6 +371,9 @@
     //    trace packet itself and make sure TrackEvent won't write one
     //    internally. This is already done at the call site.
     //
+    PERFETTO_DCHECK(PhaseToType(phase) ==
+                        protos::pbzero::TrackEvent::TYPE_UNSPECIFIED ||
+                    !(flags & TRACE_EVENT_FLAG_HAS_PROCESS_ID));
     flags |= id.id_flags();
     AddDebugAnnotations(&ctx, std::forward<Args>(args)...);
     if (NeedLegacyFlags(phase, flags)) {
@@ -365,6 +388,15 @@
             static_cast<int32_t>(legacy::ConvertThreadId(thread_id).tid);
         legacy_event->set_pid_override(pid_override);
         legacy_event->set_tid_override(-1);
+      } else {
+        // Only synchronous phases are supported for other threads. These phases
+        // are supported in TrackEvent types and receive a track_uuid
+        // association via TrackEventDataSource::TraceForCategoryImpl().
+        PERFETTO_DCHECK(PhaseToType(phase) !=
+                            protos::pbzero::TrackEvent::TYPE_UNSPECIFIED ||
+                        IsEqual(thread_id, TRACE_EVENT_API_CURRENT_THREAD_ID) ||
+                        legacy::ConvertThreadId(thread_id).tid ==
+                            ThreadTrack::Current().tid);
       }
     }
   }
@@ -372,23 +404,16 @@
   // No arguments.
   static void AddDebugAnnotations(EventContext*) {}
 
-  // One argument.
-  template <typename ArgType>
+  // N number of debug arguments.
+  template <typename ArgNameType, typename ArgType, typename... OtherArgs>
   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,
+                                  ArgNameType&& 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);
+                                  OtherArgs&&... more_args) {
+    TrackEventInternal::AddDebugAnnotation(ctx,
+                                           std::forward<ArgNameType>(arg_name),
+                                           std::forward<ArgType>(arg_value));
+    AddDebugAnnotations(ctx, std::forward<OtherArgs>(more_args)...);
   }
 
  private:
@@ -470,6 +495,7 @@
                                        thread_id, ...)                     \
   [&]() {                                                                  \
     using ::perfetto::internal::TrackEventInternal;                        \
+    PERFETTO_DCHECK(!(flags & TRACE_EVENT_FLAG_COPY));                     \
     /* First check the scope for instant events. */                        \
     if ((phase) == TRACE_EVENT_PHASE_INSTANT) {                            \
       /* Note: Avoids the need to set LegacyEvent::instant_event_scope. */ \
@@ -624,16 +650,17 @@
 #define TRACE_EVENT_COPY_INSTANT0(category_group, name, scope)        \
   INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group, \
                            ::perfetto::DynamicString{name}, scope)
-#define TRACE_EVENT_COPY_INSTANT1(category_group, name, scope, arg1_name,     \
-                                  arg1_val)                                   \
-  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group,         \
-                           ::perfetto::DynamicString{name}, scope, arg1_name, \
-                           arg1_val)
-#define TRACE_EVENT_COPY_INSTANT2(category_group, name, scope, arg1_name,     \
-                                  arg1_val, arg2_name, arg2_val)              \
-  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group,         \
-                           ::perfetto::DynamicString{name}, scope, arg1_name, \
-                           arg1_val, arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_INSTANT1(category_group, name, scope, arg1_name, \
+                                  arg1_val)                               \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group,     \
+                           ::perfetto::DynamicString{name}, scope,        \
+                           ::perfetto::DynamicString{arg1_name}, arg1_val)
+#define TRACE_EVENT_COPY_INSTANT2(category_group, name, scope, arg1_name,  \
+                                  arg1_val, arg2_name, arg2_val)           \
+  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group,      \
+                           ::perfetto::DynamicString{name}, scope,         \
+                           ::perfetto::DynamicString{arg1_name}, arg1_val, \
+                           ::perfetto::DynamicString{arg2_name}, arg2_val)
 #define TRACE_EVENT_INSTANT_WITH_FLAGS0(category_group, name, scope_and_flags) \
   INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_INSTANT, category_group, name,    \
                            scope_and_flags)
@@ -677,8 +704,9 @@
                                 arg2_name, arg2_val)                       \
   INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_BEGIN, category_group,        \
                            ::perfetto::DynamicString{name},                \
-                           TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val,     \
-                           arg2_name, arg2_val)
+                           TRACE_EVENT_FLAG_NONE,                          \
+                           ::perfetto::DynamicString{arg1_name}, arg1_val, \
+                           ::perfetto::DynamicString{arg2_name}, arg2_val)
 
 // Begin events with explicit timestamps.
 #define TRACE_EVENT_BEGIN_WITH_ID_TID_AND_TIMESTAMP0(category_group, name, id, \
@@ -697,14 +725,15 @@
   INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                    \
       TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group,                     \
       ::perfetto::DynamicString{name}, id, thread_id, timestamp,         \
-      TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
-#define TRACE_EVENT_COPY_BEGIN_WITH_ID_TID_AND_TIMESTAMP2(               \
-    category_group, name, id, thread_id, timestamp, arg1_name, arg1_val, \
-    arg2_name, arg2_val)                                                 \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                    \
-      TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group,                     \
-      ::perfetto::DynamicString{name}, id, thread_id, timestamp,         \
-      TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, arg2_name, arg2_val)
+      TRACE_EVENT_FLAG_NONE, ::perfetto::DynamicString{arg1_name}, arg1_val)
+#define TRACE_EVENT_COPY_BEGIN_WITH_ID_TID_AND_TIMESTAMP2(                   \
+    category_group, name, id, thread_id, timestamp, arg1_name, arg1_val,     \
+    arg2_name, arg2_val)                                                     \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                        \
+      TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group,                         \
+      ::perfetto::DynamicString{name}, id, thread_id, timestamp,             \
+      TRACE_EVENT_FLAG_NONE, ::perfetto::DynamicString{arg1_name}, arg1_val, \
+      ::perfetto::DynamicString{arg2_name}, arg2_val)
 
 // End events.
 #define TRACE_EVENT_END0(category_group, name)                          \
@@ -728,7 +757,8 @@
                               arg2_name, arg2_val)                            \
   INTERNAL_TRACE_EVENT_ADD(                                                   \
       TRACE_EVENT_PHASE_END, category_group, ::perfetto::DynamicString{name}, \
-      TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, arg2_name, arg2_val)
+      TRACE_EVENT_FLAG_NONE, ::perfetto::DynamicString{arg1_name}, arg1_val,  \
+      ::perfetto::DynamicString{arg2_name}, arg2_val)
 
 // Mark events.
 #define TRACE_EVENT_MARK_WITH_TIMESTAMP0(category_group, name, timestamp)  \
@@ -753,10 +783,10 @@
                            ::perfetto::DynamicString{name},        \
                            TRACE_EVENT_FLAG_NONE)
 
-#define TRACE_EVENT_COPY_MARK1(category_group, name, arg1_name, arg1_val) \
-  INTERNAL_TRACE_EVENT_ADD(TRACE_EVENT_PHASE_MARK, category_group,        \
-                           ::perfetto::DynamicString{name},               \
-                           TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+#define TRACE_EVENT_COPY_MARK1(category_group, name, arg1_name, arg1_val)      \
+  INTERNAL_TRACE_EVENT_ADD(                                                    \
+      TRACE_EVENT_PHASE_MARK, category_group, ::perfetto::DynamicString{name}, \
+      TRACE_EVENT_FLAG_NONE, ::perfetto::DynamicString{arg1_name}, arg1_val)
 
 #define TRACE_EVENT_COPY_MARK_WITH_TIMESTAMP(category_group, name, timestamp)  \
   INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP(                                     \
@@ -780,14 +810,15 @@
   INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                    \
       TRACE_EVENT_PHASE_ASYNC_END, category_group,                       \
       ::perfetto::DynamicString{name}, id, thread_id, timestamp,         \
-      TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
-#define TRACE_EVENT_COPY_END_WITH_ID_TID_AND_TIMESTAMP2(                 \
-    category_group, name, id, thread_id, timestamp, arg1_name, arg1_val, \
-    arg2_name, arg2_val)                                                 \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                    \
-      TRACE_EVENT_PHASE_ASYNC_END, category_group,                       \
-      ::perfetto::DynamicString{name}, id, thread_id, timestamp,         \
-      TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val, arg2_name, arg2_val)
+      TRACE_EVENT_FLAG_NONE, ::perfetto::DynamicString{arg1_name}, arg1_val)
+#define TRACE_EVENT_COPY_END_WITH_ID_TID_AND_TIMESTAMP2(                     \
+    category_group, name, id, thread_id, timestamp, arg1_name, arg1_val,     \
+    arg2_name, arg2_val)                                                     \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                        \
+      TRACE_EVENT_PHASE_ASYNC_END, category_group,                           \
+      ::perfetto::DynamicString{name}, id, thread_id, timestamp,             \
+      TRACE_EVENT_FLAG_NONE, ::perfetto::DynamicString{arg1_name}, arg1_val, \
+      ::perfetto::DynamicString{arg2_name}, arg2_val)
 
 // Counters.
 #define TRACE_COUNTER1(category_group, name, value)                         \
@@ -880,16 +911,17 @@
       ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_NONE)
 #define TRACE_EVENT_COPY_ASYNC_BEGIN1(category_group, name, id, arg1_name, \
                                       arg1_val)                            \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN,          \
-                                   category_group,                         \
-                                   ::perfetto::DynamicString{name}, id,    \
-                                   TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
-#define TRACE_EVENT_COPY_ASYNC_BEGIN2(category_group, name, id, arg1_name,   \
-                                      arg1_val, arg2_name, arg2_val)         \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                          \
-      TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group,                         \
-      ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_NONE, arg1_name, \
-      arg1_val, arg2_name, arg2_val)
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                        \
+      TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group,                       \
+      ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_NONE,          \
+      ::perfetto::DynamicString{arg1_name}, arg1_val)
+#define TRACE_EVENT_COPY_ASYNC_BEGIN2(category_group, name, id, arg1_name, \
+                                      arg1_val, arg2_name, arg2_val)       \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                        \
+      TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group,                       \
+      ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_NONE,          \
+      ::perfetto::DynamicString{arg1_name}, arg1_val,                      \
+      ::perfetto::DynamicString{arg2_name}, arg2_val)
 #define TRACE_EVENT_ASYNC_BEGIN_WITH_FLAGS0(category_group, name, id, flags) \
   INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN,            \
                                    category_group, name, id, flags)
@@ -985,16 +1017,17 @@
       ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_NONE)
 #define TRACE_EVENT_COPY_ASYNC_END1(category_group, name, id, arg1_name, \
                                     arg1_val)                            \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END,          \
-                                   category_group,                       \
-                                   ::perfetto::DynamicString{name}, id,  \
-                                   TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
-#define TRACE_EVENT_COPY_ASYNC_END2(category_group, name, id, arg1_name,     \
-                                    arg1_val, arg2_name, arg2_val)           \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                          \
-      TRACE_EVENT_PHASE_ASYNC_END, category_group,                           \
-      ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_NONE, arg1_name, \
-      arg1_val, arg2_name, arg2_val)
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                      \
+      TRACE_EVENT_PHASE_ASYNC_END, category_group,                       \
+      ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_NONE,        \
+      ::perfetto::DynamicString{arg1_name}, arg1_val)
+#define TRACE_EVENT_COPY_ASYNC_END2(category_group, name, id, arg1_name, \
+                                    arg1_val, arg2_name, arg2_val)       \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                      \
+      TRACE_EVENT_PHASE_ASYNC_END, category_group,                       \
+      ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_NONE,        \
+      ::perfetto::DynamicString{arg1_name}, arg1_val,                    \
+      ::perfetto::DynamicString{arg2_name}, arg2_val)
 #define TRACE_EVENT_ASYNC_END_WITH_FLAGS0(category_group, name, id, flags) \
   INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_END,            \
                                    category_group, name, id, flags)
@@ -1096,13 +1129,15 @@
   INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                     \
       TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, category_group,           \
       ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_ASYNC_TTS,  \
-      arg1_name, arg1_val, arg2_name, arg2_val)
+      ::perfetto::DynamicString{arg1_name}, arg1_val,                   \
+      ::perfetto::DynamicString{arg2_name}, arg2_val)
 #define TRACE_EVENT_COPY_NESTABLE_ASYNC_END_WITH_TTS2(                  \
     category_group, name, id, arg1_name, arg1_val, arg2_name, arg2_val) \
   INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                     \
       TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, category_group,             \
       ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_ASYNC_TTS,  \
-      arg1_name, arg1_val, arg2_name, arg2_val)
+      ::perfetto::DynamicString{arg1_name}, arg1_val,                   \
+      ::perfetto::DynamicString{arg2_name}, arg2_val)
 
 // Async events with explicit timestamps.
 #define TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(category_group, name, \
@@ -1142,18 +1177,19 @@
   INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                      \
       TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, category_group,            \
       ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_NONE)
-#define TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN1(category_group, name, id,   \
-                                               arg1_name, arg1_val)        \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, \
-                                   category_group,                         \
-                                   ::perfetto::DynamicString{name}, id,    \
-                                   TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
-#define TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN2(                              \
-    category_group, name, id, arg1_name, arg1_val, arg2_name, arg2_val)      \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                          \
-      TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, category_group,                \
-      ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_NONE, arg1_name, \
-      arg1_val, arg2_name, arg2_val)
+#define TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN1(category_group, name, id, \
+                                               arg1_name, arg1_val)      \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                      \
+      TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, category_group,            \
+      ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_NONE,        \
+      ::perfetto::DynamicString{arg1_name}, arg1_val)
+#define TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN2(                         \
+    category_group, name, id, arg1_name, arg1_val, arg2_name, arg2_val) \
+  INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                     \
+      TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, category_group,           \
+      ::perfetto::DynamicString{name}, id, TRACE_EVENT_FLAG_NONE,       \
+      ::perfetto::DynamicString{arg1_name}, arg1_val,                   \
+      ::perfetto::DynamicString{arg2_name}, arg2_val)
 #define TRACE_EVENT_COPY_NESTABLE_ASYNC_END0(category_group, name, id) \
   INTERNAL_TRACE_EVENT_ADD_WITH_ID(                                    \
       TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, category_group,            \
@@ -1169,20 +1205,22 @@
   INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                         \
       TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, category_group,                 \
       ::perfetto::DynamicString{name}, id, TRACE_EVENT_API_CURRENT_THREAD_ID, \
-      timestamp, TRACE_EVENT_FLAG_NONE, arg1_name, arg1_val)
+      timestamp, TRACE_EVENT_FLAG_NONE, ::perfetto::DynamicString{arg1_name}, \
+      arg1_val)
 #define TRACE_EVENT_COPY_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(                  \
     category_group, name, id, timestamp)                                      \
   INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                         \
       TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, category_group,                   \
       ::perfetto::DynamicString{name}, id, TRACE_EVENT_API_CURRENT_THREAD_ID, \
       timestamp, TRACE_EVENT_FLAG_NONE)
-#define TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP2(                    \
+#define TRACE_EVENT_COPY_NESTABLE_ASYNC_END_WITH_TIMESTAMP2(               \
     category_group, name, id, timestamp, arg1_name, arg1_val, arg2_name,   \
     arg2_val)                                                              \
   INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP(                      \
       TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, category_group, name, id,      \
       TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE, \
-      arg1_name, arg1_val, arg2_name, arg2_val)
+      ::perfetto::DynamicString{arg1_name}, arg1_val,                      \
+      ::perfetto::DynamicString{arg2_name}, arg2_val)
 
 // Metadata events.
 #define TRACE_EVENT_METADATA1(category_group, name, arg1_name, arg1_val) \
@@ -1222,16 +1260,6 @@
                                    category_group, name, id,         \
                                    TRACE_EVENT_FLAG_NONE)
 
-// Context events.
-#define TRACE_EVENT_ENTER_CONTEXT(category_group, name, context)    \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ENTER_CONTEXT, \
-                                   category_group, name, context,   \
-                                   TRACE_EVENT_FLAG_NONE)
-#define TRACE_EVENT_LEAVE_CONTEXT(category_group, name, context)    \
-  INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_LEAVE_CONTEXT, \
-                                   category_group, name, context,   \
-                                   TRACE_EVENT_FLAG_NONE)
-
 // TODO(skyostil): Implement binary-efficient trace events.
 #define TRACE_EVENT_BINARY_EFFICIENT0 TRACE_EVENT0
 #define TRACE_EVENT_BINARY_EFFICIENT1 TRACE_EVENT1
@@ -1249,7 +1277,7 @@
     static int PERFETTO_UID(prev) = -1;                              \
     int PERFETTO_UID(curr) =                                         \
         ::perfetto::internal::TrackEventInternal::GetSessionCount(); \
-    if (::PERFETTO_TRACK_EVENT_NAMESPACE::TrackEvent::IsEnabled() && \
+    if (PERFETTO_TRACK_EVENT_NAMESPACE::TrackEvent::IsEnabled() &&   \
         (PERFETTO_UID(prev) != PERFETTO_UID(curr))) {                \
       *(ret) = true;                                                 \
       PERFETTO_UID(prev) = PERFETTO_UID(curr);                       \
@@ -1284,29 +1312,29 @@
 // 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(                                                      \
-            !std::is_same<::perfetto::DynamicCategory,                      \
-                          decltype(category)>::value,                       \
-            "Enabled flag pointers are not supported for dynamic trace "    \
-            "categories.");                                                 \
-      },                                                                    \
-      PERFETTO_TRACK_EVENT_NAMESPACE::internal::kCategoryRegistry           \
-          .GetCategoryState(                                                \
-              ::PERFETTO_TRACK_EVENT_NAMESPACE::internal::kCategoryRegistry \
+#define TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(category)              \
+  reinterpret_cast<const uint8_t*>(                                       \
+      [&] {                                                               \
+        static_assert(                                                    \
+            !std::is_same<::perfetto::DynamicCategory,                    \
+                          decltype(category)>::value,                     \
+            "Enabled flag pointers are not supported for dynamic trace "  \
+            "categories.");                                               \
+      },                                                                  \
+      PERFETTO_TRACK_EVENT_NAMESPACE::internal::kCategoryRegistry         \
+          .GetCategoryState(                                              \
+              PERFETTO_TRACK_EVENT_NAMESPACE::internal::kCategoryRegistry \
                   .Find(category, /*is_dynamic=*/false)))
 
 // Given a pointer returned by TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED,
 // yields a pointer to the name of the corresponding category group.
-#define TRACE_EVENT_API_GET_CATEGORY_GROUP_NAME(category_enabled_ptr)       \
-  ::PERFETTO_TRACK_EVENT_NAMESPACE::internal::kCategoryRegistry             \
-      .GetCategory(                                                         \
-          category_enabled_ptr -                                            \
-          reinterpret_cast<const uint8_t*>(                                 \
-              ::PERFETTO_TRACK_EVENT_NAMESPACE::internal::kCategoryRegistry \
-                  .GetCategoryState(0u)))                                   \
+#define TRACE_EVENT_API_GET_CATEGORY_GROUP_NAME(category_enabled_ptr)     \
+  PERFETTO_TRACK_EVENT_NAMESPACE::internal::kCategoryRegistry             \
+      .GetCategory(                                                       \
+          category_enabled_ptr -                                          \
+          reinterpret_cast<const uint8_t*>(                               \
+              PERFETTO_TRACK_EVENT_NAMESPACE::internal::kCategoryRegistry \
+                  .GetCategoryState(0u)))                                 \
       ->name
 
 #endif  // PERFETTO_ENABLE_LEGACY_TRACE_EVENTS
diff --git a/infra/ci/config.py b/infra/ci/config.py
index 9c56f8b..7a10502 100755
--- a/infra/ci/config.py
+++ b/infra/ci/config.py
@@ -66,7 +66,9 @@
 # Only variables starting with PERFETTO_ are propagated into the sandbox.
 JOB_CONFIGS = {
     'linux-clang-x86_64-debug': {
-        'PERFETTO_TEST_GN_ARGS': 'is_debug=true is_hermetic_clang=false',
+        'PERFETTO_TEST_GN_ARGS': 'is_debug=true is_hermetic_clang=false '
+                                 'non_hermetic_clang_stdlib="libc++" '
+                                 'perfetto_cpp11_until_q1_2023=true',
         'PERFETTO_TEST_SCRIPT': 'test/ci/linux_tests.sh',
     },
     'linux-clang-x86_64-tsan': {
diff --git a/infra/ci/frontend/static/script.js b/infra/ci/frontend/static/script.js
index 18c36bf..383010d 100644
--- a/infra/ci/frontend/static/script.js
+++ b/infra/ci/frontend/static/script.js
@@ -562,7 +562,7 @@
 async function fetchGerritCLs() {
   console.log('Fetching CL list from Gerrit');
   let uri = '/gerrit/changes/?-age:7days';
-  uri += '+-is:abandoned&o=DETAILED_ACCOUNTS&o=CURRENT_REVISION';
+  uri += '+-is:abandoned+branch:master&o=DETAILED_ACCOUNTS&o=CURRENT_REVISION';
   const response = await fetch(uri);
   state.gerritCls = [];
   if (response.status !== 200) {
diff --git a/infra/ci/sandbox/Dockerfile b/infra/ci/sandbox/Dockerfile
index 79da8d4..ebd5539 100644
--- a/infra/ci/sandbox/Dockerfile
+++ b/infra/ci/sandbox/Dockerfile
@@ -26,7 +26,8 @@
     apt-get -y install python3 python3-pip git curl sudo lz4 tar ccache tini \
                        libpulse0 libgl1 libxml2 libc6-dev-i386 libtinfo5 \
                        gnupg2 pkg-config zip g++ zlib1g-dev unzip \
-                       python3-distutils clang-8 gcc-7 g++-7; \
+                       python3-distutils gcc-7 g++-7; \
+    apt-get -y install libc++-8-dev libc++abi-8-dev clang-8; \
     update-alternatives --install /usr/bin/python python /usr/bin/python3.7 1; \
     gcc-7 --version; \
     g++-7 --version; \
diff --git a/infra/perfetto.dev/BUILD.gn b/infra/perfetto.dev/BUILD.gn
new file mode 100644
index 0000000..6e514f1
--- /dev/null
+++ b/infra/perfetto.dev/BUILD.gn
@@ -0,0 +1,393 @@
+# Copyright (C) 2022 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")
+
+# Prevent that this file is accidentally included in embedder builds.
+assert(enable_perfetto_site)
+
+nodejs_bin = rebase_path("../../tools/node", root_build_dir)
+
+# The destination directory where the website will be built. GN pollutes
+# root_out_dir with all sorts of files, so we use a subdirectory.
+perfetto_website_out_dir = "$root_out_dir/site"
+
+# The directory containing all the markdown sources for the docs.
+src_doc_dir = "../../docs"
+
+group("site") {
+  deps = [
+    ":all_mdfiles",
+    ":assets",
+    ":gen_index",
+    ":gen_sql_stats_html",
+    ":gen_sql_tables_html",
+    ":gen_stdlib_docs_html",
+    ":gen_toc",
+    ":gen_trace_config_proto",
+    ":gen_trace_packet_proto",
+    ":node_assets",
+    ":readme",
+    ":style_scss",
+  ]
+}
+
+# Runs a nodejs script using the hermetic node toolchain.
+# Args:
+# * script: The .js script to execute
+# * inputs
+# * outputs
+# * deps
+# * depfile
+template("nodejs_script") {
+  assert(defined(invoker.script), "Need script in $target_name")
+
+  action(target_name) {
+    forward_variables_from(invoker,
+                           [
+                             "outputs",
+                             "depfile",
+                           ])
+    deps = [ ":node_modules" ]
+    if (defined(invoker.deps)) {
+      deps += invoker.deps
+    }
+    script = "../../gn/standalone/build_tool_wrapper.py"
+    inputs = [ invoker.script ]
+    inputs += invoker.inputs
+    args = [
+      nodejs_bin,
+      rebase_path(invoker.script, root_build_dir),
+    ]
+    args += invoker.args
+  }
+}
+
+# Installs the node modules specified in package.json
+action("node_modules") {
+  script = "../../gn/standalone/build_tool_wrapper.py"
+  stamp_file = "$target_out_dir/.$target_name.stamp"
+  cur_dir = rebase_path(".", root_build_dir)
+  args = [
+    "--stamp",
+    rebase_path(stamp_file, root_build_dir),
+    "--chdir=$cur_dir",
+    rebase_path("../../tools/npm", root_build_dir),
+    "ci",
+  ]
+  inputs = [
+    "../../tools/npm",
+    "package.json",
+    "package-lock.json",
+  ]
+  outputs = [ stamp_file ]
+}
+
+# Renders a markdown file into html.
+# Args:
+# * markdown: Optional. The source markdown file
+# * html_template: Optional. The html template to use
+# * out_html: The generated html, relative to `perfetto_website_out_dir`
+# * deps
+template("md_to_html") {
+  assert(defined(invoker.out_html), "Need out_html in $target_name")
+  assert(defined(invoker.html_template) || defined(invoker.markdown),
+         "Need html_template or markdown in $target_name")
+  nodejs_script(target_name) {
+    forward_variables_from(invoker, [ "deps" ])
+    script = "src/markdown_render.js"
+    inputs = []
+    if (defined(invoker.markdown)) {
+      inputs += [ invoker.markdown ]
+    }
+    depfile = "${target_gen_dir}/$target_name.d"
+    if (defined(invoker.html_template)) {
+      inputs += [ invoker.html_template ]
+    }
+    outputs = [ "${perfetto_website_out_dir}/${invoker.out_html}" ]
+    args = [
+      "--odir",
+      rebase_path(perfetto_website_out_dir, root_build_dir),
+      "-o",
+      rebase_path("${perfetto_website_out_dir}/${invoker.out_html}",
+                  root_build_dir),
+      "--depfile",
+      rebase_path(depfile, root_build_dir),
+    ]
+    if (defined(invoker.markdown)) {
+      args += [
+        "-i",
+        rebase_path(invoker.markdown, root_build_dir),
+      ]
+    }
+    if (defined(invoker.html_template)) {
+      args += [
+        "-t",
+        rebase_path(invoker.html_template, root_build_dir),
+      ]
+    }
+  }
+}
+
+md_to_html("gen_toc") {
+  markdown = "${src_doc_dir}/toc.md"
+  out_html = "docs/_nav.html"
+}
+
+md_to_html("gen_index") {
+  html_template = "src/template_index.html"
+  deps = [ ":gen_toc" ]
+  out_html = "index.html"
+}
+
+nodejs_script("style_scss") {
+  script = "node_modules/.bin/node-sass"
+  input = "src/assets/style.scss"
+  inputs = [ input ]
+  output = "${perfetto_website_out_dir}/assets/style.css"
+  outputs = [ output ]
+  args = [
+    "--quiet",
+    rebase_path(input, root_build_dir),
+    rebase_path(output, root_build_dir),
+  ]
+  deps = [ ":node_modules" ]
+}
+
+sql_stats_md = "${target_gen_dir}/sql-stats.md"
+
+nodejs_script("gen_sql_stats_md") {
+  script = "src/gen_stats_reference.js"
+  input = "../../src/trace_processor/storage/stats.h"
+  inputs = [ input ]
+  outputs = [ sql_stats_md ]
+  args = [
+    "-i",
+    rebase_path(input, root_build_dir),
+    "-o",
+    rebase_path(sql_stats_md, root_build_dir),
+  ]
+}
+
+md_to_html("gen_sql_stats_html") {
+  markdown = sql_stats_md
+  html_template = "src/template_markdown.html"
+  deps = [
+    ":gen_sql_stats_md",
+    ":gen_toc",
+  ]
+  out_html = "docs/analysis/sql-stats"
+}
+
+# Generates a html reference for a proto
+# Args:
+# * proto: The path to a .proto file
+# * message_name: The proto message name
+# * out_html
+template("proto_reference") {
+  sql_stats_md = "${target_gen_dir}/${target_name}.md"
+  nodejs_script("${target_name}_md") {
+    script = "src/gen_proto_reference.js"
+    inputs = [ invoker.proto ]
+    outputs = [ sql_stats_md ]
+    args = [
+      "-i",
+      rebase_path(invoker.proto, root_build_dir),
+      "-p",
+      invoker.message_name,
+      "-o",
+      rebase_path(sql_stats_md, root_build_dir),
+    ]
+  }
+
+  md_to_html(target_name) {
+    markdown = sql_stats_md
+    html_template = "src/template_markdown.html"
+    deps = [
+      ":${target_name}_md",
+      ":gen_toc",
+    ]
+    out_html = invoker.out_html
+  }
+}
+
+proto_reference("gen_trace_config_proto") {
+  proto = "../../protos/perfetto/config/trace_config.proto"
+  message_name = "perfetto.protos.TraceConfig"
+  out_html = "docs/reference/trace-config-proto"
+}
+
+proto_reference("gen_trace_packet_proto") {
+  proto = "../../protos/perfetto/trace/trace_packet.proto"
+  message_name = "perfetto.protos.TracePacket"
+  out_html = "docs/reference/trace-packet-proto"
+}
+
+# WARNING: this does globbing at generation time. Incremental builds are not
+# going to work properly if files are added/removed. `gn gen` needs to be
+# rerun.
+sql_tables =
+    exec_script("../../gn/standalone/glob.py",
+                [
+                  "--root=" + rebase_path("../../src/trace_processor/tables",
+                                          root_build_dir),
+                  "--filter=*.h",
+                ],
+                "list lines")
+
+src_sql_tables = []
+
+foreach(i, sql_tables) {
+  src_sql_tables += [ rebase_path(i, ".", root_build_dir) ]
+}
+
+sql_tables_md = "${target_gen_dir}/sql-tables.md"
+stdlib_docs_md = "${target_gen_dir}/stdlib_docs.md"
+
+action("gen_stdlib_docs_md") {
+  script = "src/gen_stdlib_docs_md.py"
+  label_info = get_label_info(
+          "../../src/trace_processor/stdlib:gen_amalgamated_stdlib_json_docs",
+          "target_gen_dir")
+  absolute_input_path = label_info + "/stdlib_docs.json"
+  deps = [
+    "../../python:trace_processor_stdlib_docs",
+    "../../src/trace_processor/stdlib:gen_amalgamated_stdlib_json_docs",
+  ]
+  outputs = [ stdlib_docs_md ]
+  args = [
+    "--input",
+    rebase_path(absolute_input_path, root_build_dir),
+    "--output",
+    rebase_path(stdlib_docs_md, root_build_dir),
+  ]
+}
+
+md_to_html("gen_stdlib_docs_html") {
+  markdown = stdlib_docs_md
+  html_template = "src/template_markdown.html"
+  deps = [
+    ":gen_stdlib_docs_md",
+    ":gen_toc",
+  ]
+  out_html = "docs/analysis/stdlib-docs"
+}
+
+nodejs_script("gen_sql_tables_md") {
+  python_label = "../../src/trace_processor/tables:tables_python_docs"
+  python_docs_json = get_label_info(python_label, "target_gen_dir") + "/" +
+                     get_label_info(python_label, "name") + ".json"
+
+  script = "src/gen_sql_tables_reference.js"
+  inputs = src_sql_tables
+  deps = [ python_label ]
+  outputs = [ sql_tables_md ]
+  args = [
+    "-o",
+    rebase_path(sql_tables_md, root_build_dir),
+  ]
+  foreach(file, src_sql_tables) {
+    args += [
+      "-i",
+      rebase_path(file, root_build_dir),
+    ]
+  }
+  args += [
+    "-j",
+    rebase_path(python_docs_json, root_build_dir),
+  ]
+}
+
+md_to_html("gen_sql_tables_html") {
+  markdown = sql_tables_md
+  html_template = "src/template_markdown.html"
+  deps = [
+    ":gen_sql_tables_md",
+    ":gen_toc",
+  ]
+  out_html = "docs/analysis/sql-tables"
+}
+
+md_to_html("readme") {
+  markdown = "${src_doc_dir}/README.md"
+  html_template = "src/template_markdown.html"
+  out_html = "docs/index.html"
+  deps = [ ":gen_toc" ]
+}
+
+# WARNING: this does globbing at generation time. Incremental builds are not
+# going to work properly if files are added/removed. `gn gen` needs to be
+# rerun.
+mdfiles = exec_script("../../gn/standalone/glob.py",
+                      [
+                        "--root=" + rebase_path(src_doc_dir, root_build_dir),
+                        "--filter=*.md",
+                      ],
+                      "list lines")
+
+mdfiles -= [
+  rebase_path("../../docs/README.md", root_build_dir),
+  rebase_path("../../docs/toc.md", root_build_dir),
+]
+
+mdtargets = []
+
+foreach(source, mdfiles) {
+  filename = rebase_path(string_replace(source, ".md", ""),
+                         rebase_path("../../docs", root_build_dir))
+
+  md_to_html("mdfile_${source}") {
+    markdown = rebase_path(source, ".", root_build_dir)
+    html_template = "src/template_markdown.html"
+    out_html = "docs/${filename}"
+    deps = [ ":gen_toc" ]
+  }
+  mdtargets += [ ":mdfile_${source}" ]
+}
+
+group("all_mdfiles") {
+  deps = mdtargets
+}
+
+copy("node_assets") {
+  sources = [
+    "node_modules/highlight.js/styles/tomorrow-night.css",
+    "node_modules/mermaid/dist/mermaid.min.js",
+  ]
+  deps = [ ":node_modules" ]
+
+  outputs = [ "${perfetto_website_out_dir}/assets/{{source_file_part}}" ]
+}
+
+# WARNING: this does globbing at generation time. Incremental builds are not
+# going to work properly if files are added/removed. `gn gen` needs to be
+# rerun.
+assets = exec_script("../../gn/standalone/glob.py",
+                     [
+                       "--root=" + rebase_path("src/assets", root_build_dir),
+                       "--filter=*.png",
+                       "--filter=*.js",
+                     ],
+                     "list lines")
+
+src_assets = []
+
+foreach(i, assets) {
+  src_assets += [ rebase_path(i, ".", root_build_dir) ]
+}
+
+copy("assets") {
+  sources = src_assets
+  outputs = [ "${perfetto_website_out_dir}/assets/{{source_file_part}}" ]
+}
diff --git a/infra/perfetto.dev/build b/infra/perfetto.dev/build
index 59a1c35..d65f7d5 100755
--- a/infra/perfetto.dev/build
+++ b/infra/perfetto.dev/build
@@ -19,10 +19,10 @@
 # other paths. the cd is required for npm install.
 cd -P ${BASH_SOURCE[0]%/*}
 
-# Ensure that `npm install` has been called and is current. Note that the set of
+# Ensure that `npm ci` has been called and is current. Note that the set of
 # packages that the docs website requires is != the UI ones.
 if ! test node_modules/.stamp -nt package-lock.json; then
-  ../../tools/npm install
+  ../../tools/npm ci
   touch node_modules/.stamp
 fi
 
diff --git a/infra/perfetto.dev/build.js b/infra/perfetto.dev/build.js
index eb73ece..2b2b07f 100644
--- a/infra/perfetto.dev/build.js
+++ b/infra/perfetto.dev/build.js
@@ -34,30 +34,6 @@
   outDir: pjoin(ROOT_DIR, 'out/perfetto.dev'),
 };
 
-const RULES = [
-  {r: /infra\/perfetto.dev\/src\/assets\/((.*)\.png)/, f: copyAssets},
-  {r: /infra\/perfetto.dev\/src\/assets\/((.*)\.js)/, f: copyAssets},
-  {r: /infra\/perfetto.dev\/node_modules\/.*\/(.*\.css|.*\.js)/, f: copyAssets},
-  {r: /infra\/perfetto.dev\/src\/assets\/.+\.scss/, f: compileScss},
-  {
-    r: /protos\/perfetto\/config\/trace_config\.proto/,
-    f: s => genProtoReference(s, 'perfetto.protos.TraceConfig')
-  },
-  {
-    r: /protos\/perfetto\/trace\/trace_packet\.proto/,
-    f: s => genProtoReference(s, 'perfetto.protos.TracePacket')
-  },
-  {r: /src\/trace_processor\/storage\/stats\.h/, f: genSqlStatsReference},
-  {r: /src\/trace_processor\/tables\/.*\.h/, f: s => sqlTables.add(s)},
-  {r: /docs\/toc[.]md/, f: genNav},
-  {r: /docs\/.*[.]md/, f: renderDoc},
-];
-
-let sqlTables = new Set();
-let tasks = [];
-let tasksTot = 0, tasksRan = 0;
-let tStart = Date.now();
-
 function main() {
   const parser = new argparse.ArgumentParser();
   parser.add_argument('--out', {help: 'Output directory'});
@@ -74,126 +50,47 @@
   // Check that deps are current before starting.
   const installBuildDeps = pjoin(ROOT_DIR, 'tools/install-build-deps');
 
-  // --filter=nodejs is to match what cloud_build_entrypoint.sh passes to
-  // install-build-deps. It doesn't bother installing the full toolchains
-  // because, unlike the Perfetto UI, it doesn't need Wasm.
-  const depsArgs = ['--check-only=/dev/null', '--ui', '--filter=nodejs'];
+  // --filter=nodejs --filter=gn --filter=ninja is to match what
+  // cloud_build_entrypoint.sh passes to install-build-deps. It doesn't bother
+  // installing the full toolchains because, unlike the Perfetto UI, it doesn't
+  // need Wasm.
+  const depsArgs = ['--check-only=/dev/null', '--ui', '--filter=nodejs',
+                    '--filter=gn', '--filter=ninja'];
   exec(installBuildDeps, depsArgs);
 
-  console.log('Entering', cfg.outDir);
-  process.chdir(cfg.outDir);
+  ninjaBuild();
 
-  scanDir('infra/perfetto.dev/src/assets');
-  scanFile(
-      'infra/perfetto.dev/node_modules/highlight.js/styles/tomorrow-night.css');
-  scanFile('infra/perfetto.dev/node_modules/mermaid/dist/mermaid.min.js');
-  scanFile('docs/toc.md');
-  genIndex();
-  scanFile('src/trace_processor/storage/stats.h');
-  scanDir('src/trace_processor/tables');
-  scanDir('protos');
-  genSqlTableReference();
-  scanDir('docs');
+  if (args.watch) {
+    watchDir('docs');
+    watchDir('infra/perfetto.dev/src/assets');
+    watchDir('protos');
+    watchDir('python');
+    watchDir('src/trace_processor/tables');
+  }
   if (args.serve) {
-    addTask(startServer);
+    startServer();
   }
 }
 
-// -----------
-// Build rules
-// -----------
-
-function copyAssets(src, dst) {
-  addTask(cp, [src, pjoin(cfg.outDir, 'assets', dst)]);
-}
-
-function compileScss() {
-  const src = pjoin(__dirname, 'src/assets/style.scss');
-  const dst = pjoin(cfg.outDir, 'assets/style.css');
-  // In watch mode, don't exit(1) if scss fails. It can easily happen by
-  // having a typo in the css. It will still print an errror.
-  const noErrCheck = !!cfg.watch;
-  addTask(
-      execNode,
-      ['node_modules/.bin/node-sass', ['--quiet', src, dst], {noErrCheck}]);
-}
-
-function md2html(src, dst, template) {
-  const script = pjoin(__dirname, 'src/markdown_render.js');
-  const args = ['-i', src, '--odir', cfg.outDir, '-o', dst];
-  ensureDir(path.dirname(dst));
-  if (template) args.push('-t', pjoin(__dirname, 'src', template));
-  execNode(script, args);
-}
-
-function proto2md(src, dst, protoRootType) {
-  const script = pjoin(__dirname, 'src/gen_proto_reference.js');
-  const args = ['-i', src, '-p', protoRootType, '-o', dst];
-  ensureDir(path.dirname(dst));
-  execNode(script, args);
-}
-
-function genNav(src) {
-  const dst = pjoin(cfg.outDir, 'docs', '_nav.html');
-  addTask(md2html, [src, dst]);
-}
-
-function genIndex() {
-  const dst = pjoin(cfg.outDir, 'index.html');
-  addTask(md2html, ['/dev/null', dst, 'template_index.html']);
-}
-
-function renderDoc(src) {
-  let dstRel = path.relative(ROOT_DIR, src);
-  dstRel = dstRel.replace('.md', '').replace(/\bREADME$/, 'index.html');
-  const dst = pjoin(cfg.outDir, dstRel);
-  addTask(md2html, [src, dst, 'template_markdown.html']);
-}
-
-function genProtoReference(src, protoRootType) {
-  const fname = path.basename(src);
-  const dstFname = fname.replace(/[._]/g, '-');
-  const dstHtml = pjoin(cfg.outDir, 'docs/reference', dstFname);
-  const dstMd = dstHtml + '.md';
-  addTask(proto2md, [src, dstMd, protoRootType]);
-  addTask(md2html, [dstMd, dstHtml, 'template_markdown.html']);
-  addTask(exec, ['rm', [dstMd]]);
-}
-
-function genSqlStatsReference(src) {
-  const dstDir = ensureDir(pjoin(cfg.outDir, 'docs/analysis'));
-  const dstHtml = pjoin(dstDir, 'sql-stats');
-  const dstMd = dstHtml + '.md';
-  const script = pjoin(__dirname, 'src/gen_stats_reference.js');
-  const args = ['-i', src, '-o', dstMd];
-  addTask(execNode, [script, args]);
-  addTask(md2html, [dstMd, dstHtml, 'template_markdown.html']);
-  addTask(exec, ['rm', [dstMd]]);
-}
-
-function genSqlTableReference() {
-  const dstDir = ensureDir(pjoin(cfg.outDir, 'docs/analysis'));
-  const dstHtml = pjoin(dstDir, 'sql-tables');
-  const dstMd = dstHtml + '.md';
-  const script = pjoin(__dirname, 'src/gen_sql_tables_reference.js');
-  const args = ['-o', dstMd];
-  sqlTables.forEach(f => args.push('-i', f));
-  addTask(execNode, [script, args]);
-  addTask(md2html, [dstMd, dstHtml, 'template_markdown.html']);
-  addTask(exec, ['rm', [dstMd]]);
+function ninjaBuild() {
+  exec(
+      pjoin(ROOT_DIR, 'tools/gn'),
+      ['gen', cfg.outDir, '--args=enable_perfetto_site=true']);
+  exec(pjoin(ROOT_DIR, 'tools/ninja'), ['-C', cfg.outDir, 'site']);
 }
 
 function startServer() {
   const port = 8082;
   console.log(`Starting HTTP server on http://localhost:${port}`)
+  const serveDir = path.join(cfg.outDir, 'site');
   http.createServer(function(req, res) {
         console.debug(req.method, req.url);
         let uri = req.url.split('?', 1)[0];
         uri += uri.endsWith('/') ? 'index.html' : '';
 
         // Disallow serving anything outside out directory.
-        const absPath = path.normalize(path.join(cfg.outDir, uri));
-        const relative = path.relative(cfg.outDir, absPath);
+        const absPath = path.normalize(path.join(serveDir, uri));
+        const relative = path.relative(serveDir, absPath);
         if (relative.startsWith('..')) {
           res.writeHead(404);
           res.end();
@@ -208,6 +105,7 @@
           }
           const mimeMap = {
             'css': 'text/css',
+            'png': 'image/png',
             'svg': 'image/svg+xml',
             'js': 'application/javascript',
           };
@@ -224,72 +122,17 @@
       .listen(port, 'localhost');
 }
 
-
-// -----------------------
-// Task chaining functions
-// -----------------------
-
-function addTask(func, args) {
-  const task = new Task(func, args);
-  for (const t of tasks) {
-    if (t.identity === task.identity) {
-      return;
-    }
-  }
-  tasks.push(task);
-  setTimeout(runTasks, 0);
-}
-
-function runTasks() {
-  const snapTasks = tasks.splice(0);  // snap = std::move(tasks).
-  tasksTot += snapTasks.length;
-  for (const task of snapTasks) {
-    const DIM = '\u001b[2m';
-    const BRT = '\u001b[37m';
-    const RST = '\u001b[0m';
-    const ms = (new Date(Date.now() - tStart)).toISOString().slice(17, -1);
-    const ts = `[${DIM}${ms}${RST}]`;
-    const descr = task.description.substr(0, 80);
-    console.log(`${ts} ${BRT}${++tasksRan}/${tasksTot}${RST}\t${descr}`);
-    task.func.apply(/*this=*/ undefined, task.args);
-  }
-}
-
-// Executes the first rule in RULES that match the given |absPath|.
-function scanFile(file) {
-  const absPath = path.isAbsolute(file) ? file : pjoin(ROOT_DIR, file);
-  console.assert(fs.existsSync(absPath));
-  const normPath = path.relative(ROOT_DIR, absPath);
-  for (const rule of RULES) {
-    const match = rule.r.exec(normPath);
-    if (!match || match[0] !== normPath) continue;
-    const captureGroup = match.length > 1 ? match[1] : undefined;
-    rule.f(absPath, captureGroup);
-    return;
-  }
-}
-
-// Walks the passed |dir| recursively and, for each file, invokes the matching
-// RULES. If --watch is used, it also installs a fswatch() and re-triggers the
-// matching RULES on each file change.
-function scanDir(dir, regex) {
-  const filterFn = regex ? absPath => regex.test(absPath) : () => true;
+function watchDir(dir) {
   const absDir = path.isAbsolute(dir) ? dir : pjoin(ROOT_DIR, dir);
   // Add a fs watch if in watch mode.
   if (cfg.watch) {
     fswatch(absDir, {recursive: true}, (_eventType, filePath) => {
-      if (!filterFn(filePath)) return;
       if (cfg.verbose) {
         console.log('File change detected', _eventType, filePath);
       }
-      if (fs.existsSync(filePath)) {
-        scanFile(filePath, filterFn);
-      }
+      ninjaBuild();
     });
   }
-  walk(absDir, f => {
-    if (filterFn(f)) scanFile(f);
-  });
 }
 
 function exec(cmd, args, opts) {
@@ -309,53 +152,6 @@
   return spawnRes;
 }
 
-function execNode(script, args, opts) {
-  const modPath = path.isAbsolute(script) ? script : pjoin(__dirname, script);
-  const nodeBin = pjoin(ROOT_DIR, 'tools/node');
-  args = [modPath].concat(args || []);
-  return exec(nodeBin, args, opts);
-}
-
-// ------------------------------------------
-// File system & subprocess utility functions
-// ------------------------------------------
-
-class Task {
-  constructor(func, args) {
-    this.func = func;
-    this.args = args || [];
-    // |identity| is used to dedupe identical tasks in the queue.
-    this.identity = JSON.stringify([this.func.name, this.args]);
-  }
-
-  get description() {
-    const ret = this.func.name.startsWith('exec') ? [] : [this.func.name];
-    const flattenedArgs = [].concat.apply([], this.args);
-    for (const arg of flattenedArgs) {
-      const argStr = `${arg}`;
-      if (argStr.startsWith('/')) {
-        ret.push(path.relative(cfg.outDir, arg));
-      } else {
-        ret.push(argStr);
-      }
-    }
-    return ret.join(' ');
-  }
-}
-
-function walk(dir, callback, skipRegex) {
-  for (const child of fs.readdirSync(dir)) {
-    const childPath = pjoin(dir, child);
-    const stat = fs.lstatSync(childPath);
-    if (skipRegex !== undefined && skipRegex.test(child)) continue;
-    if (stat.isDirectory()) {
-      walk(childPath, callback, skipRegex);
-    } else if (!stat.isSymbolicLink()) {
-      callback(childPath);
-    }
-  }
-}
-
 function ensureDir(dirPath, clean) {
   const exists = fs.existsSync(dirPath);
   if (exists && clean) {
@@ -366,13 +162,4 @@
   return dirPath;
 }
 
-function cp(src, dst) {
-  ensureDir(path.dirname(dst));
-  if (cfg.verbose) {
-    console.log(
-        'cp', path.relative(ROOT_DIR, src), '->', path.relative(ROOT_DIR, dst));
-  }
-  fs.copyFileSync(src, dst);
-}
-
 main();
diff --git a/infra/perfetto.dev/cloud_build_entrypoint.sh b/infra/perfetto.dev/cloud_build_entrypoint.sh
index 4497867..52e1a59 100755
--- a/infra/perfetto.dev/cloud_build_entrypoint.sh
+++ b/infra/perfetto.dev/cloud_build_entrypoint.sh
@@ -32,8 +32,8 @@
 cd upstream/
 git rev-parse HEAD
 
-# Install only NodeJS, no need to install the other toolchains.
-tools/install-build-deps --ui --filter=nodejs
+# Install only NodeJS, gn and ninja no need to install the other toolchains.
+tools/install-build-deps --ui --filter=nodejs --filter=gn --filter=ninja
 
 # The deploy script takes care of building by invoking ./build internally.
 infra/perfetto.dev/deploy
diff --git a/infra/perfetto.dev/deploy b/infra/perfetto.dev/deploy
index 825f9e7..e3ef780 100755
--- a/infra/perfetto.dev/deploy
+++ b/infra/perfetto.dev/deploy
@@ -29,7 +29,7 @@
 readonly ROOT_DIR=$(dirname $(dirname "$CUR_DIR"))
 
 # The directory that will contain the static website artifacts.
-readonly OUT_DIR="$ROOT_DIR/out/perfetto.dev"
+readonly OUT_DIR="$ROOT_DIR/out/perfetto.dev/site"
 
 # Build first.
 "$CUR_DIR/build"
diff --git a/infra/perfetto.dev/src/assets/script.js b/infra/perfetto.dev/src/assets/script.js
index 91c57e5..aa368bc 100644
--- a/infra/perfetto.dev/src/assets/script.js
+++ b/infra/perfetto.dev/src/assets/script.js
@@ -253,6 +253,69 @@
   document.body.appendChild(script);
 }
 
+function setupSearch() {
+  const URL =
+      'https://www.googleapis.com/customsearch/v1?key=AIzaSyBTD2XJkQkkuvDn76LSftsgWOkdBz9Gfwo&cx=007128963598137843411:8suis14kcmy&q='
+  const searchContainer = document.getElementById('search');
+  const searchBox = document.getElementById('search-box');
+  const searchRes = document.getElementById('search-res')
+  if (!searchBox || !searchRes) return;
+
+  document.body.addEventListener('keydown', (e) => {
+    if (e.key === '/' && e.target.tagName.toLowerCase() === 'body') {
+      searchBox.setSelectionRange(0, -1);
+      searchBox.focus();
+      e.preventDefault();
+    } else if (e.key === 'Escape' && searchContainer.contains(e.target)) {
+      searchBox.blur();
+
+      // Handle the case of clicking Tab and moving down to results.
+      e.target.blur();
+    }
+  });
+
+  let timerId = -1;
+  let lastSearchId = 0;
+
+  const doSearch = async () => {
+    timerId = -1;
+    searchRes.style.width = `${searchBox.offsetWidth}px`;
+
+    // `searchId` handles the case of two subsequent requests racing. This is to
+    // prevent older results, delivered in reverse order, to replace newer ones.
+    const searchId = ++lastSearchId;
+    const f = await fetch(URL + encodeURIComponent(searchBox.value));
+    const jsonRes = await f.json();
+    const results = jsonRes['items'];
+    searchRes.innerHTML = '';
+    if (results === undefined || searchId != lastSearchId) {
+      return;
+    }
+    for (const res of results) {
+      const link = document.createElement('a');
+      link.href = res.link;
+      const title = document.createElement('div');
+      title.className = 'sr-title';
+      title.innerText = res.title.replace(' - Perfetto Tracing Docs', '');
+      link.appendChild(title);
+
+      const snippet = document.createElement('div');
+      snippet.className = 'sr-snippet';
+      snippet.innerText = res.snippet;
+      link.appendChild(snippet);
+
+      const div = document.createElement('div');
+      div.appendChild(link);
+      searchRes.appendChild(div);
+    }
+  };
+
+  searchBox.addEventListener('keyup', () => {
+    if (timerId >= 0) return;
+    timerId = setTimeout(doSearch, 200);
+  });
+}
+
 window.addEventListener('DOMContentLoaded', () => {
   updateNav();
   updateTOC();
@@ -275,6 +338,7 @@
   }
 
   updateTOC();
+  setupSearch();
 
   // Enable animations only after the load event. This is to prevent glitches
   // when switching pages.
diff --git a/infra/perfetto.dev/src/assets/style.scss b/infra/perfetto.dev/src/assets/style.scss
index 7f23862..63d6fc1 100644
--- a/infra/perfetto.dev/src/assets/style.scss
+++ b/infra/perfetto.dev/src/assets/style.scss
@@ -147,8 +147,105 @@
     }
 }
 
+
+#search {
+    position: relative;
+    flex-grow: 0;
+    transition: flex-grow cubic-bezier(1, 0.01, 1, 1) var(--anim-time), background-color ease var(--anim-time);
+    padding: 0;
+    &::before {
+        visibility: hidden;
+        user-select: none;
+        content: '';
+        position: fixed;
+        left: 0;
+        right: 0;
+        top: var(--site-header-height);
+        bottom: 0;
+        z-index: -100;
+        background-color: rgba(255, 255, 255, 0.8);
+        backdrop-filter: blur(3px);
+        opacity: 0;
+        transition: opacity ease var(--anim-time), visibility 0s;
+
+    }
+    &:focus-within {
+        flex-grow: 1000;
+        &::before {
+            display: block;
+            opacity: 1;
+            visibility: visible;
+        }
+        #search-res {
+            display: block;
+        }
+
+    }
+
+    @media #{$mobile} {
+        display: none;
+    }
+
+    #search-box {
+        width: 100%;
+        height: 32px;
+        font-size: 1rem;;
+        color: #333;
+        background-color: rgba(255, 255, 255, 0.9);
+        border: 1px solid #eee;
+        border-radius: 2px;
+        background-image: url('data:image/svg+xml;utf-8,<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M39.8 41.95 26.65 28.8q-1.5 1.3-3.5 2.025-2 .725-4.25.725-5.4 0-9.15-3.75T6 18.75q0-5.3 3.75-9.05 3.75-3.75 9.1-3.75 5.3 0 9.025 3.75 3.725 3.75 3.725 9.05 0 2.15-.7 4.15-.7 2-2.1 3.75L42 39.75Zm-20.95-13.4q4.05 0 6.9-2.875Q28.6 22.8 28.6 18.75t-2.85-6.925Q22.9 8.95 18.85 8.95q-4.1 0-6.975 2.875T9 18.75q0 4.05 2.875 6.925t6.975 2.875Z"/></svg>');
+        background-repeat: no-repeat;
+        background-size: contain;
+        padding-left: 40px;
+        outline: none;
+        &:hover, &:focus {
+            background-color: rgba(255, 255, 255, 0.95);
+        }
+    }
+
+    #search-res {
+        display: none;
+        background-color: rgba(255, 255, 255, 1.0);
+        border: 1px solid #eee;
+        box-shadow: #aaa 0px 1px 5px;
+        color: #333;
+        line-height: initial;
+        margin-top: -4px;
+        overflow-x: auto;
+        position: fixed;
+        top: var(--site-header-height);
+        max-height: calc(100vh - var(--site-header-height));
+        z-index: 10;
+        >div {
+            padding: 10px;
+            margin: 0;
+            &:hover {
+                background-color: #f0f0f0;
+            }
+        }
+        .sr-title {
+            color: #333;
+            font-weight: bold;
+        }
+        .sr-snippet {
+            color: #444;
+            font-size: 0.9rem;
+         }
+
+        a { text-decoration: none; }
+        a:hover { color: initial };
+
+        &:empty {
+            visibility: hidden;
+        }
+    }
+
+}
+
+
 // -----------------------------------------------------------------------------
-// Site header
+// Site footer
 // -----------------------------------------------------------------------------
 
 // Footer in the index page.
diff --git a/infra/perfetto.dev/src/gen_sql_tables_reference.js b/infra/perfetto.dev/src/gen_sql_tables_reference.js
index f92acfc..9af1d32 100644
--- a/infra/perfetto.dev/src/gen_sql_tables_reference.js
+++ b/infra/perfetto.dev/src/gen_sql_tables_reference.js
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Generation of reference from protos
+// Generation of SQL table references from C++ headers.
 
 'use strict';
 
@@ -25,6 +25,7 @@
 function singleLineComment(comment) {
   comment = comment || '';
   comment = comment.trim();
+  comment = comment.replaceAll('|', '\\|');
   comment = comment.replace(/\.\n/g, '<br>');
   comment = comment.replace(/\n/g, ' ');
   return comment;
@@ -90,7 +91,7 @@
       if (lastColumn === undefined) {
         tableDesc.comment += `${comm}\n`;
       } else {
-        lastColumn.comment = `${lastColumn.comment}${comm}\n`;
+        lastColumn.comment = `${lastColumn.comment}\n${comm}`;
       }
       continue;
     }
@@ -185,6 +186,24 @@
   return tables;
 }
 
+function parseTablesInJson(filePath) {
+  return JSON.parse(fs.readFileSync(filePath, 'UTF8'));
+}
+
+function overrideCppTablesWithJsonTables(cpp, json) {
+  const out = [];
+  const jsonAdded = new Set();
+  for (const table of json) {
+    out.push(table);
+    jsonAdded.add(table.name);
+  }
+  for (const table of cpp) {
+    if (!jsonAdded.has(table.name)) {
+      out.push(table);
+    }
+  }
+  return out;
+}
 
 function genLink(table) {
   return `[${table.name}](#${table.name})`;
@@ -222,15 +241,22 @@
 function main() {
   const inFile = argv['i'];
   const outFile = argv['o'];
+  const jsonFile = argv['j'];
   if (!inFile) {
-    console.error('Usage: -i hdr1.h -i hdr2.h -[-o out.md]');
+    console.error('Usage: -i hdr1.h -i hdr2.h -j tbls.json -[-o out.md]');
     process.exit(1);
   }
 
   // Can be either a string (-i single) or an array (-i one -i two).
   const inFiles = (inFile instanceof Array) ? inFile : [inFile];
+  const cppTables =
+      Array.prototype.concat(...inFiles.map(parseTablesInCppFile));
 
-  const tables = Array.prototype.concat(...inFiles.map(parseTablesInCppFile));
+  // Can be either a string (-j single) or an array (-j one -j two).
+  const jsonFiles = (jsonFile instanceof Array) ? jsonFile : [jsonFile];
+  const jsonTables =
+      Array.prototype.concat(...jsonFiles.map(parseTablesInJson));
+  const tables = overrideCppTablesWithJsonTables(cppTables, jsonTables)
 
   // Resolve parents.
   const tablesIndex = {};    // 'TP_SCHED_SLICE_TABLE_DEF' -> table
@@ -264,16 +290,16 @@
   let graph = '## Tables diagram\n';
   const mkLabel = (table) => `${table.defMacro}["${table.name}"]`;
   for (const tableGroup of tableGroups) {
-    let gaphEdges = '';
-    let gaphLinks = '';
+    let graphEdges = '';
+    let graphLinks = '';
     graph += `#### ${tableGroup} tables\n`;
     graph += '```mermaid\ngraph TD\n';
     graph += `  subgraph ${tableGroup}\n`;
     for (const table of tablesByGroup[tableGroup]) {
       graph += `  ${mkLabel(table)}\n`;
-      gaphLinks += `  click ${table.defMacro} "#${table.name}"\n`
+      graphLinks += `  click ${table.defMacro} "#${table.name}"\n`
       if (table.parent) {
-        gaphEdges += ` ${mkLabel(table)} --> ${mkLabel(table.parent)}\n`
+        graphEdges += ` ${mkLabel(table)} --> ${mkLabel(table.parent)}\n`
       }
 
       for (const col of Object.values(table.cols)) {
@@ -288,14 +314,14 @@
         }
         if (!refTable)
           continue;
-        gaphEdges +=
+        graphEdges +=
             `  ${mkLabel(table)} -. ${col.name} .-> ${mkLabel(refTable)}\n`
-        gaphLinks += `  click ${refTable.defMacro} "#${refTable.name}"\n`
+        graphLinks += `  click ${refTable.defMacro} "#${refTable.name}"\n`
       }
     }
     graph += `  end\n`;
-    graph += gaphEdges;
-    graph += gaphLinks;
+    graph += graphEdges;
+    graph += graphLinks;
     graph += '\n```\n';
   }
 
diff --git a/infra/perfetto.dev/src/gen_stdlib_docs_md.py b/infra/perfetto.dev/src/gen_stdlib_docs_md.py
new file mode 100644
index 0000000..efb52a2
--- /dev/null
+++ b/infra/perfetto.dev/src/gen_stdlib_docs_md.py
@@ -0,0 +1,220 @@
+#!/usr/bin/env python3
+# Copyright (C) 2022 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
+# disibuted under the License is disibuted 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 sys
+import json
+from typing import List, Dict
+
+
+# Responsible for module level markdown generation.
+class ModuleMd:
+
+  def __init__(self, module_name: str,
+               module_files: List[Dict[str, any]]) -> None:
+    self.module_name = module_name
+    self.files_md = [
+        FileMd(module_name, file_dict) for file_dict in module_files
+    ]
+    self.summary_objs = "\n".join(
+        file.summary_objs for file in self.files_md if file.summary_objs)
+    self.summary_funs = "\n".join(
+        file.summary_funs for file in self.files_md if file.summary_funs)
+    self.summary_view_funs = "\n".join(file.summary_view_funs
+                                       for file in self.files_md
+                                       if file.summary_view_funs)
+
+  def print_description(self):
+    long_s = []
+    long_s.append(f'## Module: {self.module_name}')
+
+    for file in self.files_md:
+      long_s.append(f'### {file.import_key}')
+      if file.objs:
+        long_s.append('#### Views/Tables')
+        long_s.append("\n".join(file.objs))
+      if file.funs:
+        long_s.append('#### Functions')
+        long_s.append("\n".join(file.funs))
+      if file.view_funs:
+        long_s.append('#### View Functions')
+        long_s.append("\n".join(file.view_funs))
+
+    return '\n'.join(long_s)
+
+
+# Responsible for file level markdown generation.
+class FileMd:
+
+  def __init__(self, module_name, file_dict):
+    self.import_key = file_dict['import_key']
+    self.objs, self.funs, self.view_funs = [], [], []
+    summary_objs_list, summary_funs_list, summary_view_funs_list = [], [], []
+
+    # Add imports if in file.
+    for data in file_dict['imports']:
+      # Anchor
+      anchor = f'obj/{module_name}/{data["name"]}'
+
+      # Add summary of imported view/table
+      desc = data["desc"].split(".")[0]
+      summary_objs_list.append(f'[{data["name"]}](#{anchor})|'
+                               f'{file_dict["import_key"]}|'
+                               f'{desc}')
+
+      self.objs.append(f'\n\n<a name="{anchor}"></a>'
+                       f'**{data["name"]}**, {data["type"]}\n\n'
+                       f'{data["desc"]}\n')
+
+      self.objs.append('Column | Description\n------ | -----------')
+      for name, desc in data['cols'].items():
+        self.objs.append(f'{name} | {desc}')
+
+      self.objs.append("\n\n")
+
+    # Add functions if in file
+    for data in file_dict['functions']:
+      # Anchor
+      anchor = f'fun/{module_name}/{data["name"]}'
+
+      # Add summary of imported function
+      summary_funs_list.append(f'[{data["name"]}](#{anchor})|'
+                               f'{file_dict["import_key"]}|'
+                               f'{data["return_type"]}|'
+                               f'{data["desc"].split(".")[0]}')
+      self.funs.append(
+          f'\n\n<a name="{anchor}"></a>'
+          f'**{data["name"]}**\n'
+          f'{data["desc"]}\n\n'
+          f'Returns: {data["return_type"]}, {data["return_desc"]}\n\n')
+      if data['args']:
+        self.funs.append('Argument | Type | Description\n'
+                         '-------- | ---- | -----------')
+        for name, arg_dict in data['args'].items():
+          self.funs.append(f'{name} | {arg_dict["type"]} | {arg_dict["desc"]}')
+
+        self.funs.append("\n\n")
+
+    # Add view functions if in file
+    for data in file_dict['view_functions']:
+      # Anchor
+      anchor = rf'view_fun/{module_name}/{data["name"]}'
+      # Add summary of imported view function
+      summary_view_funs_list.append(f'[{data["name"]}](#{anchor})|'
+                                    f'{file_dict["import_key"]}|'
+                                    f'{data["desc"].split(".")[0]}')
+
+      self.view_funs.append(f'\n\n<a name="{anchor}"></a>'
+                            f'**{data["name"]}**\n'
+                            f'{data["desc"]}\n\n')
+      if data['args']:
+        self.view_funs.append('Argument | Type | Description\n'
+                              '-------- | ---- | -----------')
+        for name, arg_dict in data['args'].items():
+          self.view_funs.append(
+              f'{name} | {arg_dict["type"]} | {arg_dict["desc"]}')
+        self.view_funs.append('\n')
+      self.view_funs.append('Column | Description\n' '------ | -----------')
+      for name, desc in data['cols'].items():
+        self.view_funs.append(f'{name} | {desc}')
+
+      self.view_funs.append("\n\n")
+
+    self.summary_objs = "\n".join(summary_objs_list)
+    self.summary_funs = "\n".join(summary_funs_list)
+    self.summary_view_funs = "\n".join(summary_view_funs_list)
+
+
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--input', required=True)
+  parser.add_argument('--output', required=True)
+  args = parser.parse_args()
+
+  with open(args.input) as f:
+    modules_json_dict = json.load(f)
+
+  modules_dict = {}
+
+  for module_name, module_files in modules_json_dict.items():
+    modules_dict[module_name] = ModuleMd(module_name, module_files)
+
+  common_module = modules_dict.pop('common')
+
+  with open(args.output, 'w') as f:
+    f.write("# SQL standard library\n"
+            "To import any function, view_function, view or table simply run "
+            "`SELECT IMPORT({import key});` in your SQL query.\n"
+            "## Summary\n")
+
+    summary_objs = [common_module.summary_objs
+                   ] if common_module.summary_objs else []
+    summary_objs += [
+        module.summary_objs
+        for name, module in modules_dict.items()
+        if (module.summary_objs and name != 'experimental')
+    ]
+
+    summary_funs = [common_module.summary_funs
+                   ] if common_module.summary_funs else []
+    summary_funs += [
+        module.summary_funs
+        for name, module in modules_dict.items()
+        if (module.summary_funs and name != 'experimental')
+    ]
+    summary_view_funs = [common_module.summary_view_funs
+                        ] if common_module.summary_view_funs else []
+    summary_view_funs += [
+        module.summary_view_funs
+        for name, module in modules_dict.items()
+        if (module.summary_view_funs and name != 'experimental')
+    ]
+
+    if summary_objs:
+      f.write('### Views/tables\n\n'
+              'Name | Import | Description\n'
+              '---- | ------ | -----------\n')
+      f.write('\n'.join(summary_objs))
+      f.write("\n")
+
+    if summary_funs:
+      f.write("### Functions\n\n"
+              'Name | Import | Return type | Description\n'
+              '---- | ------ | ----------- | -----------\n')
+      f.write('\n'.join(summary_funs))
+      f.write("\n")
+
+    if summary_view_funs:
+      f.write("### View Functions\n\n"
+              'Name | Import |  Description\n'
+              '---- | ------ |  -----------\n')
+      f.write('\n'.join(summary_view_funs))
+      f.write("\n")
+
+    f.write('\n\n')
+    f.write(common_module.print_description())
+    f.write('\n')
+    f.write('\n'.join(
+        module.print_description() for module in modules_dict.values()))
+
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/infra/perfetto.dev/src/markdown_render.js b/infra/perfetto.dev/src/markdown_render.js
index 4a0b788..04b56b8 100644
--- a/infra/perfetto.dev/src/markdown_render.js
+++ b/infra/perfetto.dev/src/markdown_render.js
@@ -27,6 +27,7 @@
 let outDir = '';
 let curMdFile = '';
 let title = '';
+let depFileFd = undefined;
 
 function hrefInDocs(href) {
   if (href.match(/^(https?:)|^(mailto:)|^#/)) {
@@ -131,6 +132,9 @@
     const outParDir = path.dirname(outFile);
     fs.ensureDirSync(outParDir);
     fs.copyFileSync(ROOT_DIR + docsHref, outFile);
+    if (depFileFd) {
+      fs.write(depFileFd, ` ${ROOT_DIR + docsHref}`);
+    }
   }
   if (href.endsWith('.svg')) {
     return `<object type="image/svg+xml" data="${href}"></object>`
@@ -187,14 +191,23 @@
   const inFile = argv['i'];
   const outFile = argv['o'];
   outDir = argv['odir'];
+  depFile = argv['depfile'];
   const templateFile = argv['t'];
   if (!outFile || !outDir) {
     console.error(
-        'Usage: --odir site -o out.html [-i input.md] [-t templ.html]');
+        'Usage: --odir site -o out.html ' +
+        '[-i input.md] [-t templ.html] ' +
+        '[--depfile depfile.d]');
     process.exit(1);
   }
   curMdFile = inFile;
 
+  if (depFile) {
+    const depFileDir = path.dirname(depFile);
+    fs.ensureDirSync(depFileDir);
+    depFileFd = fs.openSync(depFile, 'w');
+    fs.write(depFileFd, `${outFile}:`);
+  }
   let markdownHtml = '';
   if (inFile) {
     markdownHtml = render(fs.readFileSync(inFile, 'utf8'));
diff --git a/infra/perfetto.dev/src/template_header.html b/infra/perfetto.dev/src/template_header.html
index 45960d9..46fc39b 100644
--- a/infra/perfetto.dev/src/template_header.html
+++ b/infra/perfetto.dev/src/template_header.html
@@ -29,6 +29,13 @@
   </div>
   <a href="#toggle" class="menu"><i class="material-icons-round">menu</i></a>
 
+  <% if (fileName.startsWith('/docs/')) { %>
+    <div id="search">
+      <input id="search-box" type="text" placeholder="Search" autocomplete="off">
+      <div id="search-res"></div>
+    </div>
+  <% } %>
+
   <a href="/docs/">Docs</a>
   <a href="/docs/contributing/getting-started#community">Community</a>
   <a href="https://ui.perfetto.dev/">Trace Viewer</a>
diff --git a/meson.build b/meson.build
index 0601514..5ef6fce 100644
--- a/meson.build
+++ b/meson.build
@@ -18,8 +18,8 @@
 
 project(
     'perfetto',
-    ['c','cpp'],
-    default_options: ['c_std=c99', 'cpp_std=c++11']
+    ['cpp'],
+    default_options: ['cpp_std=c++17']
 )
 
 fs = import('fs')
@@ -28,18 +28,32 @@
     error('sdk dir not found, please checkout a release tag, e.g. v14.0')
 endif
 
-dep_threads = dependency('threads')
+cpp = meson.get_compiler('cpp')
+
+deps_perfetto = [dependency('threads')]
+
+if host_machine.system() == 'android'
+  deps_perfetto += cpp.find_library('log')
+endif
 
 lib_perfetto = static_library(
     'perfetto',
     sources: 'sdk/perfetto.cc',
-    dependencies: dep_threads,
+    dependencies: deps_perfetto,
     install: true,
 )
 
 inc_perfetto = include_directories('sdk')
 
+dir_perfetto_trace = join_paths(meson.current_source_dir(),
+                                'protos/perfetto/trace')
+
+install_data(dir_perfetto_trace / 'perfetto_trace.proto')
+
 dep_perfetto = declare_dependency(
     link_with: lib_perfetto,
     include_directories: inc_perfetto,
+    variables: {
+        'pkgdatadir': dir_perfetto_trace,
+    }
 )
diff --git a/protos/perfetto/config/android/BUILD.gn b/protos/perfetto/config/android/BUILD.gn
index 0a0bb9a..c4f0eb2 100644
--- a/protos/perfetto/config/android/BUILD.gn
+++ b/protos/perfetto/config/android/BUILD.gn
@@ -22,6 +22,7 @@
     "android_log_config.proto",
     "android_polled_state_config.proto",
     "android_system_property_config.proto",
+    "network_trace_config.proto",
     "packages_list_config.proto",
   ]
 }
diff --git a/protos/perfetto/config/android/network_trace_config.proto b/protos/perfetto/config/android/network_trace_config.proto
new file mode 100644
index 0000000..f58f280
--- /dev/null
+++ b/protos/perfetto/config/android/network_trace_config.proto
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 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;
+
+// Network tracing data source that records details on all packets sent or
+// received by the network.
+message NetworkPacketTraceConfig {
+  // Polling frequency in milliseconds. Network tracing writes to a fixed size
+  // ring buffer. The polling interval should be such that the ring buffer is
+  // unlikely to fill in that interval (or that filling is an acceptable risk).
+  // The minimum polling rate is 100ms (values below this are ignored).
+  // Introduced in Android 14 (U).
+  optional uint32 poll_ms = 1;
+}
diff --git a/protos/perfetto/config/data_source_config.proto b/protos/perfetto/config/data_source_config.proto
index ac7a4f8..2bb9056 100644
--- a/protos/perfetto/config/data_source_config.proto
+++ b/protos/perfetto/config/data_source_config.proto
@@ -22,6 +22,7 @@
 import "protos/perfetto/config/android/android_log_config.proto";
 import "protos/perfetto/config/android/android_polled_state_config.proto";
 import "protos/perfetto/config/android/android_system_property_config.proto";
+import "protos/perfetto/config/android/network_trace_config.proto";
 import "protos/perfetto/config/android/packages_list_config.proto";
 import "protos/perfetto/config/chrome/chrome_config.proto";
 import "protos/perfetto/config/ftrace/ftrace_config.proto";
@@ -41,7 +42,7 @@
 import "protos/perfetto/config/system_info/system_info.proto";
 
 // The configuration that is passed to each data source when starting tracing.
-// Next id: 120
+// Next id: 121
 message DataSourceConfig {
   enum SessionInitiator {
     SESSION_INITIATOR_UNSPECIFIED = 0;
@@ -117,7 +118,7 @@
   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.game_intervention_list
+  // Data source name: android.game_interventions
   optional AndroidGameInterventionListConfig
       android_game_intervention_list_config = 116 [lazy = true];
   // Data source name: android.packages_list
@@ -155,6 +156,11 @@
   // traced_probes) may not support interception.
   optional InterceptorConfig interceptor_config = 115;
 
+  // Data source name: android.network_packets.
+  // Introduced in Android 14 (U).
+  optional NetworkPacketTraceConfig network_packet_trace_config = 120
+      [lazy = true];
+
   // 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
diff --git a/protos/perfetto/config/ftrace/ftrace_config.proto b/protos/perfetto/config/ftrace/ftrace_config.proto
index 4b25317..9aba9f3 100644
--- a/protos/perfetto/config/ftrace/ftrace_config.proto
+++ b/protos/perfetto/config/ftrace/ftrace_config.proto
@@ -18,7 +18,7 @@
 
 package perfetto.protos;
 
-// Next id: 18.
+// Next id: 24.
 message FtraceConfig {
   repeated string ftrace_events = 1;
   repeated string atrace_categories = 2;
@@ -37,6 +37,36 @@
   }
   optional CompactSchedConfig compact_sched = 12;
 
+  // Optional filter for "ftrace/print" events.
+  //
+  // The filter consists of multiple rules. As soon as a rule matches (the rules
+  // are processed in order), its `allow` field will be used as the outcome: if
+  // `allow` is true, the event will be included in the trace, otherwise it will
+  // be discarded. If an event does not match any rule, it will be allowed by
+  // default (a rule with an empty prefix and allow=false, disallows everything
+  // by default).
+  message PrintFilter {
+    message Rule {
+      // Matches an atrace message of the form:
+      // <type>|pid|<prefix>...
+      message AtraceMessage {
+        optional string type = 1;
+        optional string prefix = 2;
+      }
+      oneof match {
+        // This rule matches if `prefix` matches exactly with the beginning of
+        // the "ftrace/print" "buf" field.
+        string prefix = 1;
+        // This rule matches if the "buf" field contains an atrace-style print
+        // message as specified in `atrace_msg`.
+        AtraceMessage atrace_msg = 3;
+      }
+      optional bool allow = 2;
+    }
+    repeated Rule rules = 1;
+  }
+  optional PrintFilter print_filter = 22;
+
   // Enables symbol name resolution against /proc/kallsyms.
   // It requires that either traced_probes is running as root or that
   // kptr_restrict has been manually lowered.
@@ -70,7 +100,7 @@
 
   // When this boolean is true AND the ftrace_events contains "kmem/rss_stat",
   // this option causes traced_probes to enable the "kmem/rss_stat_throttled"
-  // event instad if present, and fall back to "kmem/rss_stat" if not present.
+  // event instead if present, and fall back to "kmem/rss_stat" if not present.
   // The historical context for this is the following:
   // - Up to Android S (12), the rss_stat was internally throttled in its
   //   kernel implementation.
@@ -80,8 +110,6 @@
   // - Not all Android T/13 devices will receive a new kernel though, hence we
   //   need to deal with both cases.
   // For more context: go/rss-stat-throttled.
-  // TODO(kaleshsingh): implement the logic behind this. Right now this flag
-  // does nothing.
   optional bool throttle_rss_stat = 15;
 
   // If true, avoid enabling events that aren't statically known by
@@ -92,4 +120,60 @@
   // expand to events that aren't of interest to the tracing user.
   // Introduced in: Android T.
   optional bool disable_generic_events = 16;
+
+  // The list of syscalls that should be recorded by sys_{enter,exit} ftrace
+  // events. When empty, all syscalls are recorded. If neither sys_{enter,exit}
+  // are enabled, this setting has no effect. Example: ["sys_read", "sys_open"].
+  // Introduced in: Android U.
+  repeated string syscall_events = 18;
+
+  // If true, enable the "function_graph" kernel tracer that emits events
+  // whenever a kernel function is entered and exited
+  // (funcgraph_entry/funcgraph_exit).
+  // Notes on use:
+  // * Requires |symbolize_ksyms| for function name resolution.
+  // * Use |function_filters| or |function_graph_roots| to constrain the traced
+  //   set of functions, otherwise the event bandwidth will be too high for
+  //   practical use.
+  // * The data source might be rejected if there is already a concurrent
+  //   ftrace data source that does not use function graph itself, as we do not
+  //   support switching kernel tracers mid-trace.
+  // * Requires a kernel compiled with CONFIG_FUNCTION_GRAPH_TRACER. This is
+  //   enabled if "cat /sys/kernel/tracing/available_tracers" includes
+  //   "function_graph".
+  // Android:
+  // * Available only on debuggable builds.
+  // * Introduced in: Android U.
+  optional bool enable_function_graph = 19;
+
+  // Constrains the set of functions traced when |enable_function_graph| is
+  // true. Supports globs, e.g. "sched*". You can specify multiple filters,
+  // in which case all matching functions will be traced. See kernel
+  // documentation on ftrace "set_ftrace_filter" file for more details.
+  // Android:
+  // * Available only on debuggable builds.
+  // * Introduced in: Android U.
+  repeated string function_filters = 20;
+
+  // If |enable_function_graph| is true, trace this set of functions *and* all
+  // of its callees. Supports globs. Can be set together with
+  // |function_filters|, in which case only callees matching the filter will be
+  // traced. If setting both, you most likely want all roots to also be
+  // included in |function_filters|.
+  // Android:
+  // * Available only on debuggable builds.
+  // * Introduced in: Android U.
+  repeated string function_graph_roots = 21;
+
+  // If true, does not clear ftrace buffers before the start of the program.
+  // This makes sense only if this is the first ftrace data source instance
+  // created after the daemon has been started. Can be useful for gathering boot
+  // traces, if ftrace has been separately configured (e.g. via kernel
+  // commandline).
+  optional bool preserve_ftrace_buffer = 23;
+
+  // If true, overrides the default timestamp clock and uses a raw hardware
+  // based monotonic clock for getting timestamps.
+  // * Introduced in: Android U.
+  optional bool use_monotonic_raw_clock = 24;
 }
diff --git a/protos/perfetto/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index a6dcdce..585a592 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -419,6 +419,21 @@
 
 // End of protos/perfetto/config/android/android_system_property_config.proto
 
+// Begin of protos/perfetto/config/android/network_trace_config.proto
+
+// Network tracing data source that records details on all packets sent or
+// received by the network.
+message NetworkPacketTraceConfig {
+  // Polling frequency in milliseconds. Network tracing writes to a fixed size
+  // ring buffer. The polling interval should be such that the ring buffer is
+  // unlikely to fill in that interval (or that filling is an acceptable risk).
+  // The minimum polling rate is 100ms (values below this are ignored).
+  // Introduced in Android 14 (U).
+  optional uint32 poll_ms = 1;
+}
+
+// End of protos/perfetto/config/android/network_trace_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
@@ -466,7 +481,7 @@
 
 // Begin of protos/perfetto/config/ftrace/ftrace_config.proto
 
-// Next id: 18.
+// Next id: 24.
 message FtraceConfig {
   repeated string ftrace_events = 1;
   repeated string atrace_categories = 2;
@@ -485,6 +500,36 @@
   }
   optional CompactSchedConfig compact_sched = 12;
 
+  // Optional filter for "ftrace/print" events.
+  //
+  // The filter consists of multiple rules. As soon as a rule matches (the rules
+  // are processed in order), its `allow` field will be used as the outcome: if
+  // `allow` is true, the event will be included in the trace, otherwise it will
+  // be discarded. If an event does not match any rule, it will be allowed by
+  // default (a rule with an empty prefix and allow=false, disallows everything
+  // by default).
+  message PrintFilter {
+    message Rule {
+      // Matches an atrace message of the form:
+      // <type>|pid|<prefix>...
+      message AtraceMessage {
+        optional string type = 1;
+        optional string prefix = 2;
+      }
+      oneof match {
+        // This rule matches if `prefix` matches exactly with the beginning of
+        // the "ftrace/print" "buf" field.
+        string prefix = 1;
+        // This rule matches if the "buf" field contains an atrace-style print
+        // message as specified in `atrace_msg`.
+        AtraceMessage atrace_msg = 3;
+      }
+      optional bool allow = 2;
+    }
+    repeated Rule rules = 1;
+  }
+  optional PrintFilter print_filter = 22;
+
   // Enables symbol name resolution against /proc/kallsyms.
   // It requires that either traced_probes is running as root or that
   // kptr_restrict has been manually lowered.
@@ -518,7 +563,7 @@
 
   // When this boolean is true AND the ftrace_events contains "kmem/rss_stat",
   // this option causes traced_probes to enable the "kmem/rss_stat_throttled"
-  // event instad if present, and fall back to "kmem/rss_stat" if not present.
+  // event instead if present, and fall back to "kmem/rss_stat" if not present.
   // The historical context for this is the following:
   // - Up to Android S (12), the rss_stat was internally throttled in its
   //   kernel implementation.
@@ -528,8 +573,6 @@
   // - Not all Android T/13 devices will receive a new kernel though, hence we
   //   need to deal with both cases.
   // For more context: go/rss-stat-throttled.
-  // TODO(kaleshsingh): implement the logic behind this. Right now this flag
-  // does nothing.
   optional bool throttle_rss_stat = 15;
 
   // If true, avoid enabling events that aren't statically known by
@@ -540,6 +583,62 @@
   // expand to events that aren't of interest to the tracing user.
   // Introduced in: Android T.
   optional bool disable_generic_events = 16;
+
+  // The list of syscalls that should be recorded by sys_{enter,exit} ftrace
+  // events. When empty, all syscalls are recorded. If neither sys_{enter,exit}
+  // are enabled, this setting has no effect. Example: ["sys_read", "sys_open"].
+  // Introduced in: Android U.
+  repeated string syscall_events = 18;
+
+  // If true, enable the "function_graph" kernel tracer that emits events
+  // whenever a kernel function is entered and exited
+  // (funcgraph_entry/funcgraph_exit).
+  // Notes on use:
+  // * Requires |symbolize_ksyms| for function name resolution.
+  // * Use |function_filters| or |function_graph_roots| to constrain the traced
+  //   set of functions, otherwise the event bandwidth will be too high for
+  //   practical use.
+  // * The data source might be rejected if there is already a concurrent
+  //   ftrace data source that does not use function graph itself, as we do not
+  //   support switching kernel tracers mid-trace.
+  // * Requires a kernel compiled with CONFIG_FUNCTION_GRAPH_TRACER. This is
+  //   enabled if "cat /sys/kernel/tracing/available_tracers" includes
+  //   "function_graph".
+  // Android:
+  // * Available only on debuggable builds.
+  // * Introduced in: Android U.
+  optional bool enable_function_graph = 19;
+
+  // Constrains the set of functions traced when |enable_function_graph| is
+  // true. Supports globs, e.g. "sched*". You can specify multiple filters,
+  // in which case all matching functions will be traced. See kernel
+  // documentation on ftrace "set_ftrace_filter" file for more details.
+  // Android:
+  // * Available only on debuggable builds.
+  // * Introduced in: Android U.
+  repeated string function_filters = 20;
+
+  // If |enable_function_graph| is true, trace this set of functions *and* all
+  // of its callees. Supports globs. Can be set together with
+  // |function_filters|, in which case only callees matching the filter will be
+  // traced. If setting both, you most likely want all roots to also be
+  // included in |function_filters|.
+  // Android:
+  // * Available only on debuggable builds.
+  // * Introduced in: Android U.
+  repeated string function_graph_roots = 21;
+
+  // If true, does not clear ftrace buffers before the start of the program.
+  // This makes sense only if this is the first ftrace data source instance
+  // created after the daemon has been started. Can be useful for gathering boot
+  // traces, if ftrace has been separately configured (e.g. via kernel
+  // commandline).
+  optional bool preserve_ftrace_buffer = 23;
+
+  // If true, overrides the default timestamp clock and uses a raw hardware
+  // based monotonic clock for getting timestamps.
+  // * Introduced in: Android U.
+  optional bool use_monotonic_raw_clock = 24;
 }
 
 // End of protos/perfetto/config/ftrace/ftrace_config.proto
@@ -711,6 +810,13 @@
 
   // DEPRECATED thread_time_in_state_cache_size
   reserved 8;
+
+  // If true this will resolve filedescriptors for each process so these
+  // can be mapped to their actual device or file.
+  // Requires raw_syscalls/sys_exit ftrace events to be enabled or
+  // new fds opened after initially scanning a process will not be
+  // recognized.
+  optional bool resolve_process_fds = 9;
 }
 
 // End of protos/perfetto/config/process_stats/process_stats_config.proto
@@ -2118,6 +2224,10 @@
   // Polls /proc/buddyinfo every X ms, if non-zero.
   // This is required to be > 10ms to avoid excessive CPU usage.
   optional uint32 buddyinfo_period_ms = 9;
+
+  // Polls /proc/diskstats every X ms, if non-zero.
+  // This is required to be > 10ms to avoid excessive CPU usage.
+  optional uint32 diskstat_period_ms = 10;
 }
 
 // End of protos/perfetto/config/sys_stats/sys_stats_config.proto
@@ -2268,7 +2378,7 @@
 // Begin of protos/perfetto/config/data_source_config.proto
 
 // The configuration that is passed to each data source when starting tracing.
-// Next id: 120
+// Next id: 121
 message DataSourceConfig {
   enum SessionInitiator {
     SESSION_INITIATOR_UNSPECIFIED = 0;
@@ -2344,7 +2454,7 @@
   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.game_intervention_list
+  // Data source name: android.game_interventions
   optional AndroidGameInterventionListConfig
       android_game_intervention_list_config = 116 [lazy = true];
   // Data source name: android.packages_list
@@ -2382,6 +2492,11 @@
   // traced_probes) may not support interception.
   optional InterceptorConfig interceptor_config = 115;
 
+  // Data source name: android.network_packets.
+  // Introduced in Android 14 (U).
+  optional NetworkPacketTraceConfig network_packet_trace_config = 120
+      [lazy = true];
+
   // 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
@@ -2589,6 +2704,10 @@
     // On R-, this override only affected userdebug builds. Since S, it also
     // affects user builds.
     optional uint64 max_upload_per_day_bytes = 1;
+
+    // Overrides the guardrail for maximum trace buffer size.
+    // Available on U+
+    optional uint32 max_tracing_buffer_size_kb = 2;
   }
   optional GuardrailOverrides guardrail_overrides = 11;
 
@@ -2798,10 +2917,14 @@
 
   // 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;
+  // DEPRECATED as per v32. See TracePacket.trace_uuid for the authoritative
+  // Trace UUID. If this field is set, the tracing service will respect the
+  // requested UUID (i.e. TracePacket.trace_uuid == this field) but only if
+  // gap-less snapshotting is not used.
+  optional int64 trace_uuid_msb = 27 [deprecated = true];
+  optional int64 trace_uuid_lsb = 28 [deprecated = true];
 
   // When set applies a post-filter to the trace contents using the filter
   // provided. The filter is applied at ReadBuffers() time and works both in the
diff --git a/protos/perfetto/config/process_stats/process_stats_config.proto b/protos/perfetto/config/process_stats/process_stats_config.proto
index 4a0848d..d71e7d3 100644
--- a/protos/perfetto/config/process_stats/process_stats_config.proto
+++ b/protos/perfetto/config/process_stats/process_stats_config.proto
@@ -62,4 +62,11 @@
 
   // DEPRECATED thread_time_in_state_cache_size
   reserved 8;
+
+  // If true this will resolve filedescriptors for each process so these
+  // can be mapped to their actual device or file.
+  // Requires raw_syscalls/sys_exit ftrace events to be enabled or
+  // new fds opened after initially scanning a process will not be
+  // recognized.
+  optional bool resolve_process_fds = 9;
 }
diff --git a/protos/perfetto/config/sys_stats/sys_stats_config.proto b/protos/perfetto/config/sys_stats/sys_stats_config.proto
index d505771..c4ac227 100644
--- a/protos/perfetto/config/sys_stats/sys_stats_config.proto
+++ b/protos/perfetto/config/sys_stats/sys_stats_config.proto
@@ -71,4 +71,8 @@
   // Polls /proc/buddyinfo every X ms, if non-zero.
   // This is required to be > 10ms to avoid excessive CPU usage.
   optional uint32 buddyinfo_period_ms = 9;
+
+  // Polls /proc/diskstats every X ms, if non-zero.
+  // This is required to be > 10ms to avoid excessive CPU usage.
+  optional uint32 diskstat_period_ms = 10;
 }
diff --git a/protos/perfetto/config/trace_config.proto b/protos/perfetto/config/trace_config.proto
index be1ba82..4338154 100644
--- a/protos/perfetto/config/trace_config.proto
+++ b/protos/perfetto/config/trace_config.proto
@@ -208,6 +208,10 @@
     // On R-, this override only affected userdebug builds. Since S, it also
     // affects user builds.
     optional uint64 max_upload_per_day_bytes = 1;
+
+    // Overrides the guardrail for maximum trace buffer size.
+    // Available on U+
+    optional uint32 max_tracing_buffer_size_kb = 2;
   }
   optional GuardrailOverrides guardrail_overrides = 11;
 
@@ -417,10 +421,14 @@
 
   // 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;
+  // DEPRECATED as per v32. See TracePacket.trace_uuid for the authoritative
+  // Trace UUID. If this field is set, the tracing service will respect the
+  // requested UUID (i.e. TracePacket.trace_uuid == this field) but only if
+  // gap-less snapshotting is not used.
+  optional int64 trace_uuid_msb = 27 [deprecated = true];
+  optional int64 trace_uuid_lsb = 28 [deprecated = true];
 
   // When set applies a post-filter to the trace contents using the filter
   // provided. The filter is applied at ReadBuffers() time and works both in the
diff --git a/protos/perfetto/ipc/consumer_port.proto b/protos/perfetto/ipc/consumer_port.proto
index 9f6d533..aa7fc93 100644
--- a/protos/perfetto/ipc/consumer_port.proto
+++ b/protos/perfetto/ipc/consumer_port.proto
@@ -26,13 +26,6 @@
 
 // IPC interface definition for the consumer port of the tracing service.
 service ConsumerPort {
-  // Creates the ring buffers that will be used for the tracing session.
-  // TODO(primiano): not implemented yet. EnableTracing will implicitly create
-  // the required buffer. This is to allow Enabling/Disabling tracing with
-  // different configs without losing the contents of the buffers for the
-  // previous tracing session.
-  // rpc CreateBuffers(CreateBuffersRequest) returns (CreateBuffersResponse) {}
-
   // Enables tracing for one or more data sources. At least one buffer must have
   // been previously created. The EnableTracingResponse is sent when tracing is
   // disabled (either explicitly or because of the |duration_ms| expired).
@@ -116,8 +109,24 @@
   // ----------------------------------------------------
   // All methods below have been introduced in Android S.
   // ----------------------------------------------------
+
+  // This method has been deprecated and removed in Android U in favour of
+  // CloneSession.
   rpc SaveTraceForBugreport(SaveTraceForBugreportRequest)
       returns (SaveTraceForBugreportResponse) {}
+
+  // ----------------------------------------------------
+  // All methods below have been introduced in Android U.
+  // ----------------------------------------------------
+
+  // Clones an existing tracing session and binds the consumer to it (as if
+  // the session was created via EnableTracing), copying over all the tracing
+  // data (including metadata and stats).
+  // The cloned session is stopped and read-only (as if DisableTracing was
+  // invoked).
+  // A consumer can clone a session only if the uid of the consumer matches the
+  // uid of the source session or if the consumer uid is 0 (root).
+  rpc CloneSession(CloneSessionRequest) returns (CloneSessionResponse) {}
 }
 
 // Arguments for rpc EnableTracing().
@@ -267,3 +276,15 @@
   optional bool success = 1;
   optional string msg = 2;
 }
+
+// Arguments for rpc CloneSession.
+message CloneSessionRequest {
+  optional uint64 session_id = 1;
+}
+
+message CloneSessionResponse {
+  // If true, the clone was successful. If false it failed and |error| contains
+  // the details about the failure.
+  optional bool success = 1;
+  optional string error = 2;
+}
diff --git a/protos/perfetto/metrics/android/BUILD.gn b/protos/perfetto/metrics/android/BUILD.gn
index 5edbf90..4fef3c0 100644
--- a/protos/perfetto/metrics/android/BUILD.gn
+++ b/protos/perfetto/metrics/android/BUILD.gn
@@ -55,7 +55,6 @@
     "simpleperf.proto",
     "startup_metric.proto",
     "surfaceflinger.proto",
-    "sysui_cuj_metrics.proto",
     "task_names.proto",
     "thread_time_in_state_metric.proto",
     "trace_quality.proto",
diff --git a/protos/perfetto/metrics/android/display_metrics.proto b/protos/perfetto/metrics/android/display_metrics.proto
index 0c6f163..daec268 100644
--- a/protos/perfetto/metrics/android/display_metrics.proto
+++ b/protos/perfetto/metrics/android/display_metrics.proto
@@ -51,4 +51,15 @@
 
   // The statistics for each refresh rate value
   repeated RefreshRateStat refresh_rate_stats = 5;
+
+  // Stats to measure the runtime of updating the power state in
+  // DisplayPowerController
+  message UpdatePowerState {
+    optional uint32 avg_runtime_micro_secs = 2;
+
+    // Removed: avg_runtime_ms
+    reserved 1;
+  }
+
+  optional UpdatePowerState update_power_state = 6;
 }
diff --git a/protos/perfetto/metrics/android/startup_metric.proto b/protos/perfetto/metrics/android/startup_metric.proto
index 833e708..d8aa954 100644
--- a/protos/perfetto/metrics/android/startup_metric.proto
+++ b/protos/perfetto/metrics/android/startup_metric.proto
@@ -29,6 +29,8 @@
     optional int64 runnable_dur_ns = 2;
     optional int64 uninterruptible_sleep_dur_ns = 3;
     optional int64 interruptible_sleep_dur_ns = 4;
+    optional int64 uninterruptible_io_sleep_dur_ns = 5;
+    optional int64 uninterruptible_non_io_sleep_dur_ns = 6;
   }
 
   message McyclesByCoreType {
@@ -178,10 +180,12 @@
   // Useful to put the startup in context.
   message SystemState {
     // Whether the dex2oat64 process was running concurrent to the startup.
-    optional bool dex2oat_running = 1;
+    // Deprecated as of 10/2022.
+    optional bool dex2oat_running = 1 [deprecated=true];
 
     // Whether the installd process was running concurrent to the startup.
-    optional bool installd_running = 2;
+    // Deprecated as of 10/2022.
+    optional bool installd_running = 2 [deprecated=true];
 
     // The number of broadcasts dispatched by the system during the app
     // launch.
@@ -198,9 +202,14 @@
     // Note: the exact number of returned is an implementation detail and
     // will likely change over time.
     repeated string most_active_non_launch_processes = 5;
+
+    // Duration the installd process was running concurrent to the startup.
+    optional int64 installd_dur_ns = 6;
+    // Duration the dex2oat64 process was running concurrent to the startup.
+    optional int64 dex2oat_dur_ns = 7;
   }
 
-  // Next id: 18
+  // Next id: 19
   message Startup {
     // Random id uniquely identifying an app startup in this trace.
     optional uint32 startup_id = 1;
@@ -253,6 +262,9 @@
     // Contains information about the status of odex files.
     repeated OptimizationStatus optimization_status = 12;
 
+    // Package name of startups running concurrent to the launch.
+    repeated string startup_concurrent_to_launch = 18;
+
     // Contains information about the state of the rest of the system during the
     // startup. This is useful for getting context about why a startup might
     // be slow beyond just what the app is doing.
diff --git a/protos/perfetto/metrics/android/sysui_cuj_metrics.proto b/protos/perfetto/metrics/android/sysui_cuj_metrics.proto
deleted file mode 100644
index 41708ce..0000000
--- a/protos/perfetto/metrics/android/sysui_cuj_metrics.proto
+++ /dev/null
@@ -1,47 +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.
- */
-
-syntax = "proto2";
-
-package perfetto.protos;
-
-import "protos/perfetto/metrics/android/process_metadata.proto";
-
-// Metric that stores frame information and potential jank root causes
-// for a single Android system UI interaction/user journey.
-message AndroidSysUiCujMetrics {
-  // A list of all frames within the SysUi user journey.
-  repeated Frame frames = 1;
-
-  optional string cuj_name = 2;
-  optional int64 cuj_start = 3;
-  optional int64 cuj_dur = 4;
-
-  // Details about the process (uid, version, etc)
-  optional AndroidProcessMetadata process = 5;
-
-  message Frame {
-    // Index of the frame within the single user journey.
-    optional int64 number = 1;
-    optional int64 vsync = 5;
-    optional int64 ts = 2;
-    optional int64 dur = 3;
-
-    // A list of identified potential causes for jank.
-    // Optional.
-    repeated string jank_cause = 4;
-  }
-}
diff --git a/protos/perfetto/metrics/chrome/BUILD.gn b/protos/perfetto/metrics/chrome/BUILD.gn
index 3a77c75..1bba0f4 100644
--- a/protos/perfetto/metrics/chrome/BUILD.gn
+++ b/protos/perfetto/metrics/chrome/BUILD.gn
@@ -22,6 +22,7 @@
   ]
   sources = [
     "all_chrome_metrics.proto",
+    "args_class_names.proto",
     "blink_gc_metric.proto",
     "dropped_frames.proto",
     "frame_times.proto",
diff --git a/protos/perfetto/metrics/chrome/all_chrome_metrics.proto b/protos/perfetto/metrics/chrome/all_chrome_metrics.proto
index 1965df3..2428824 100644
--- a/protos/perfetto/metrics/chrome/all_chrome_metrics.proto
+++ b/protos/perfetto/metrics/chrome/all_chrome_metrics.proto
@@ -19,6 +19,7 @@
 package perfetto.protos;
 
 import "protos/perfetto/metrics/metrics.proto";
+import "protos/perfetto/metrics/chrome/args_class_names.proto";
 import "protos/perfetto/metrics/chrome/blink_gc_metric.proto";
 import "protos/perfetto/metrics/chrome/dropped_frames.proto";
 import "protos/perfetto/metrics/chrome/frame_times.proto";
@@ -50,4 +51,5 @@
   optional ChromePerformanceMarkHashes chrome_performance_mark_hashes = 1012;
   optional ChromeSliceNames chrome_slice_names = 1013;
   optional ChromeUnsymbolizedArgs chrome_unsymbolized_args = 1014;
+  optional ChromeArgsClassNames chrome_args_class_names = 1015;
 }
diff --git a/protos/perfetto/metrics/chrome/args_class_names.proto b/protos/perfetto/metrics/chrome/args_class_names.proto
new file mode 100644
index 0000000..daa0e67
--- /dev/null
+++ b/protos/perfetto/metrics/chrome/args_class_names.proto
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 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;
+
+// The list of class names from the "args" table per |package_name| &
+// |version_code|.
+message ChromeArgsClassNames {
+  message ChromeArgsClassNamesPerVersion {
+    optional string package_name = 1;
+    optional int64 version_code = 2;
+    repeated string class_name = 3;
+  }
+
+  repeated ChromeArgsClassNamesPerVersion class_names_per_version = 1;
+}
diff --git a/protos/perfetto/metrics/metrics.proto b/protos/perfetto/metrics/metrics.proto
index b76ea0c..db94258 100644
--- a/protos/perfetto/metrics/metrics.proto
+++ b/protos/perfetto/metrics/metrics.proto
@@ -50,7 +50,6 @@
 import "protos/perfetto/metrics/android/simpleperf.proto";
 import "protos/perfetto/metrics/android/startup_metric.proto";
 import "protos/perfetto/metrics/android/surfaceflinger.proto";
-import "protos/perfetto/metrics/android/sysui_cuj_metrics.proto";
 import "protos/perfetto/metrics/android/task_names.proto";
 import "protos/perfetto/metrics/android/trace_quality.proto";
 import "protos/perfetto/metrics/android/android_trusty_workqueues.proto";
@@ -171,8 +170,8 @@
   // GPU metrics on Android.
   optional AndroidGpuMetric android_gpu = 26;
 
-  // Frame timing and jank root causes for system UI interactions.
-  optional AndroidSysUiCujMetrics android_sysui_cuj = 27;
+  // Deprecated AndroidSysUiCujMetrics.
+  reserved 27;
 
   // Interaction and frame timings for CUJs (important UI transitions).
   optional AndroidJankCujMetric android_jank_cuj = 48;
diff --git a/protos/perfetto/metrics/perfetto_merged_metrics.proto b/protos/perfetto/metrics/perfetto_merged_metrics.proto
index d889dc7..d4507af 100644
--- a/protos/perfetto/metrics/perfetto_merged_metrics.proto
+++ b/protos/perfetto/metrics/perfetto_merged_metrics.proto
@@ -298,6 +298,17 @@
 
   // The statistics for each refresh rate value
   repeated RefreshRateStat refresh_rate_stats = 5;
+
+  // Stats to measure the runtime of updating the power state in
+  // DisplayPowerController
+  message UpdatePowerState {
+    optional uint32 avg_runtime_micro_secs = 2;
+
+    // Removed: avg_runtime_ms
+    reserved 1;
+  }
+
+  optional UpdatePowerState update_power_state = 6;
 }
 
 // End of protos/perfetto/metrics/android/display_metrics.proto
@@ -1331,6 +1342,8 @@
     optional int64 runnable_dur_ns = 2;
     optional int64 uninterruptible_sleep_dur_ns = 3;
     optional int64 interruptible_sleep_dur_ns = 4;
+    optional int64 uninterruptible_io_sleep_dur_ns = 5;
+    optional int64 uninterruptible_non_io_sleep_dur_ns = 6;
   }
 
   message McyclesByCoreType {
@@ -1480,10 +1493,12 @@
   // Useful to put the startup in context.
   message SystemState {
     // Whether the dex2oat64 process was running concurrent to the startup.
-    optional bool dex2oat_running = 1;
+    // Deprecated as of 10/2022.
+    optional bool dex2oat_running = 1 [deprecated=true];
 
     // Whether the installd process was running concurrent to the startup.
-    optional bool installd_running = 2;
+    // Deprecated as of 10/2022.
+    optional bool installd_running = 2 [deprecated=true];
 
     // The number of broadcasts dispatched by the system during the app
     // launch.
@@ -1500,9 +1515,14 @@
     // Note: the exact number of returned is an implementation detail and
     // will likely change over time.
     repeated string most_active_non_launch_processes = 5;
+
+    // Duration the installd process was running concurrent to the startup.
+    optional int64 installd_dur_ns = 6;
+    // Duration the dex2oat64 process was running concurrent to the startup.
+    optional int64 dex2oat_dur_ns = 7;
   }
 
-  // Next id: 18
+  // Next id: 19
   message Startup {
     // Random id uniquely identifying an app startup in this trace.
     optional uint32 startup_id = 1;
@@ -1555,6 +1575,9 @@
     // Contains information about the status of odex files.
     repeated OptimizationStatus optimization_status = 12;
 
+    // Package name of startups running concurrent to the launch.
+    repeated string startup_concurrent_to_launch = 18;
+
     // Contains information about the state of the rest of the system during the
     // startup. This is useful for getting context about why a startup might
     // be slow beyond just what the app is doing.
@@ -1614,36 +1637,6 @@
 
 // End of protos/perfetto/metrics/android/surfaceflinger.proto
 
-// Begin of protos/perfetto/metrics/android/sysui_cuj_metrics.proto
-
-// Metric that stores frame information and potential jank root causes
-// for a single Android system UI interaction/user journey.
-message AndroidSysUiCujMetrics {
-  // A list of all frames within the SysUi user journey.
-  repeated Frame frames = 1;
-
-  optional string cuj_name = 2;
-  optional int64 cuj_start = 3;
-  optional int64 cuj_dur = 4;
-
-  // Details about the process (uid, version, etc)
-  optional AndroidProcessMetadata process = 5;
-
-  message Frame {
-    // Index of the frame within the single user journey.
-    optional int64 number = 1;
-    optional int64 vsync = 5;
-    optional int64 ts = 2;
-    optional int64 dur = 3;
-
-    // A list of identified potential causes for jank.
-    // Optional.
-    repeated string jank_cause = 4;
-  }
-}
-
-// End of protos/perfetto/metrics/android/sysui_cuj_metrics.proto
-
 // Begin of protos/perfetto/metrics/android/task_names.proto
 
 message AndroidTaskNames {
@@ -1821,8 +1814,8 @@
   // GPU metrics on Android.
   optional AndroidGpuMetric android_gpu = 26;
 
-  // Frame timing and jank root causes for system UI interactions.
-  optional AndroidSysUiCujMetrics android_sysui_cuj = 27;
+  // Deprecated AndroidSysUiCujMetrics.
+  reserved 27;
 
   // Interaction and frame timings for CUJs (important UI transitions).
   optional AndroidJankCujMetric android_jank_cuj = 48;
diff --git a/protos/perfetto/trace/BUILD.gn b/protos/perfetto/trace/BUILD.gn
index 90c1880..f81b05c 100644
--- a/protos/perfetto/trace/BUILD.gn
+++ b/protos/perfetto/trace/BUILD.gn
@@ -39,6 +39,7 @@
 
 proto_sources_minimal = [
   "clock_snapshot.proto",
+  "trace_uuid.proto",
   "trigger.proto",
   "system_info.proto",
 ]
diff --git a/protos/perfetto/trace/android/BUILD.gn b/protos/perfetto/trace/android/BUILD.gn
index 12d69c1..cea77c1 100644
--- a/protos/perfetto/trace/android/BUILD.gn
+++ b/protos/perfetto/trace/android/BUILD.gn
@@ -26,6 +26,7 @@
     "gpu_mem_event.proto",
     "graphics_frame_event.proto",
     "initial_display_state.proto",
+    "network_trace.proto",
     "packages_list.proto",
   ]
 }
diff --git a/protos/perfetto/trace/android/network_trace.proto b/protos/perfetto/trace/android/network_trace.proto
new file mode 100644
index 0000000..d1c43a2
--- /dev/null
+++ b/protos/perfetto/trace/android/network_trace.proto
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 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;
+
+enum TrafficDirection {
+  DIR_UNSPECIFIED = 0;
+  DIR_INGRESS = 1;
+  DIR_EGRESS = 2;
+}
+
+// NetworkPacketEvent records the details of a single packet sent or received
+// on the network (in Linux kernel terminology, one sk_buff struct).
+message NetworkPacketEvent {
+  // The direction traffic is flowing for this event.
+  optional TrafficDirection direction = 1;
+
+  // The name of the interface if available (e.g. 'rmnet0').
+  optional string interface = 2;
+
+  // The length of the packet in bytes (wire_size - L2_header_size).
+  optional uint32 length = 3;
+
+  // The Linux user id associated with the packet's socket.
+  optional uint32 uid = 4;
+
+  // The Android network tag associated with the packet's socket.
+  optional uint32 tag = 5;
+
+  // The packet's IP protocol (TCP=6, UDP=17, etc).
+  optional uint32 ip_proto = 6;
+
+  // The packet's TCP flags as a bitmask (FIN=0x1, SYN=0x2, RST=0x4, etc).
+  optional uint32 tcp_flags = 7;
+
+  // The local udp/tcp port of the packet.
+  optional uint32 local_port = 8;
+
+  // The remote udp/tcp port of the packet.
+  optional uint32 remote_port = 9;
+}
diff --git a/protos/perfetto/trace/chrome/chrome_metadata.proto b/protos/perfetto/trace/chrome/chrome_metadata.proto
index 8882593..d2c46e4 100644
--- a/protos/perfetto/trace/chrome/chrome_metadata.proto
+++ b/protos/perfetto/trace/chrome/chrome_metadata.proto
@@ -85,4 +85,7 @@
 
   // List of all active triggers in current session, when trace was triggered.
   repeated TriggerRule active_rules = 2;
+
+  // Hash of the scenario name.
+  optional fixed32 scenario_name_hash = 3;
 }
diff --git a/protos/perfetto/trace/ftrace/all_protos.gni b/protos/perfetto/trace/ftrace/all_protos.gni
index aa14c18..15af1e3 100644
--- a/protos/perfetto/trace/ftrace/all_protos.gni
+++ b/protos/perfetto/trace/ftrace/all_protos.gni
@@ -20,10 +20,12 @@
   "ftrace_stats.proto",
   "test_bundle_wrapper.proto",
   "generic.proto",
+  "android_fs.proto",
   "binder.proto",
   "block.proto",
   "cgroup.proto",
   "clk.proto",
+  "cma.proto",
   "compaction.proto",
   "cpuhp.proto",
   "cros_ec.proto",
@@ -47,11 +49,13 @@
   "kmem.proto",
   "kvm.proto",
   "lowmemorykiller.proto",
+  "lwis.proto",
   "mali.proto",
   "mdss.proto",
   "mm_event.proto",
   "net.proto",
   "oom.proto",
+  "panel.proto",
   "power.proto",
   "printk.proto",
   "raw_syscalls.proto",
@@ -68,8 +72,11 @@
   "task.proto",
   "tcp.proto",
   "thermal.proto",
+  "trusty.proto",
   "ufs.proto",
   "v4l2.proto",
+  "virtio_gpu.proto",
+  "virtio_video.proto",
   "vmscan.proto",
   "workqueue.proto",
 ]
diff --git a/protos/perfetto/trace/ftrace/android_fs.proto b/protos/perfetto/trace/ftrace/android_fs.proto
new file mode 100644
index 0000000..76d7216
--- /dev/null
+++ b/protos/perfetto/trace/ftrace/android_fs.proto
@@ -0,0 +1,47 @@
+// Autogenerated by:
+// ../../src/tools/ftrace_proto_gen/ftrace_proto_gen.cc
+// Do not edit.
+
+syntax = "proto2";
+package perfetto.protos;
+
+message AndroidFsDatareadEndFtraceEvent {
+  optional int32 bytes = 1;
+  optional uint64 ino = 2;
+  optional int64 offset = 3;
+}
+message AndroidFsDatareadStartFtraceEvent {
+  optional int32 bytes = 1;
+  optional string cmdline = 2;
+  optional int64 i_size = 3;
+  optional uint64 ino = 4;
+  optional int64 offset = 5;
+  optional string pathbuf = 6;
+  optional int32 pid = 7;
+}
+message AndroidFsDatawriteEndFtraceEvent {
+  optional int32 bytes = 1;
+  optional uint64 ino = 2;
+  optional int64 offset = 3;
+}
+message AndroidFsDatawriteStartFtraceEvent {
+  optional int32 bytes = 1;
+  optional string cmdline = 2;
+  optional int64 i_size = 3;
+  optional uint64 ino = 4;
+  optional int64 offset = 5;
+  optional string pathbuf = 6;
+  optional int32 pid = 7;
+}
+message AndroidFsFsyncEndFtraceEvent {
+  optional int32 bytes = 1;
+  optional uint64 ino = 2;
+  optional int64 offset = 3;
+}
+message AndroidFsFsyncStartFtraceEvent {
+  optional string cmdline = 1;
+  optional int64 i_size = 2;
+  optional uint64 ino = 3;
+  optional string pathbuf = 4;
+  optional int32 pid = 5;
+}
diff --git a/protos/perfetto/trace/ftrace/cma.proto b/protos/perfetto/trace/ftrace/cma.proto
new file mode 100644
index 0000000..20f7a7f
--- /dev/null
+++ b/protos/perfetto/trace/ftrace/cma.proto
@@ -0,0 +1,24 @@
+// Autogenerated by:
+// ../../src/tools/ftrace_proto_gen/ftrace_proto_gen.cc
+// Do not edit.
+
+syntax = "proto2";
+package perfetto.protos;
+
+message CmaAllocStartFtraceEvent {
+  optional uint32 align = 1;
+  optional uint32 count = 2;
+  optional string name = 3;
+}
+message CmaAllocInfoFtraceEvent {
+  optional uint32 align = 1;
+  optional uint32 count = 2;
+  optional uint32 err_iso = 3;
+  optional uint32 err_mig = 4;
+  optional uint32 err_test = 5;
+  optional string name = 6;
+  optional uint64 nr_mapped = 7;
+  optional uint64 nr_migrated = 8;
+  optional uint64 nr_reclaimed = 9;
+  optional uint64 pfn = 10;
+}
diff --git a/protos/perfetto/trace/ftrace/ftrace.proto b/protos/perfetto/trace/ftrace/ftrace.proto
index eedc6e9..d114f64 100644
--- a/protos/perfetto/trace/ftrace/ftrace.proto
+++ b/protos/perfetto/trace/ftrace/ftrace.proto
@@ -9,3 +9,14 @@
   optional uint64 ip = 1;
   optional string buf = 2;
 }
+message FuncgraphEntryFtraceEvent {
+  optional int32 depth = 1;
+  optional uint64 func = 2;
+}
+message FuncgraphExitFtraceEvent {
+  optional uint64 calltime = 1;
+  optional int32 depth = 2;
+  optional uint64 func = 3;
+  optional uint64 overrun = 4;
+  optional uint64 rettime = 5;
+}
diff --git a/protos/perfetto/trace/ftrace/ftrace_event.proto b/protos/perfetto/trace/ftrace/ftrace_event.proto
index 8d79b12..f32c8e1 100644
--- a/protos/perfetto/trace/ftrace/ftrace_event.proto
+++ b/protos/perfetto/trace/ftrace/ftrace_event.proto
@@ -20,10 +20,12 @@
 
 syntax = "proto2";
 
+import "protos/perfetto/trace/ftrace/android_fs.proto";
 import "protos/perfetto/trace/ftrace/binder.proto";
 import "protos/perfetto/trace/ftrace/block.proto";
 import "protos/perfetto/trace/ftrace/cgroup.proto";
 import "protos/perfetto/trace/ftrace/clk.proto";
+import "protos/perfetto/trace/ftrace/cma.proto";
 import "protos/perfetto/trace/ftrace/compaction.proto";
 import "protos/perfetto/trace/ftrace/cpuhp.proto";
 import "protos/perfetto/trace/ftrace/cros_ec.proto";
@@ -47,11 +49,13 @@
 import "protos/perfetto/trace/ftrace/kmem.proto";
 import "protos/perfetto/trace/ftrace/kvm.proto";
 import "protos/perfetto/trace/ftrace/lowmemorykiller.proto";
+import "protos/perfetto/trace/ftrace/lwis.proto";
 import "protos/perfetto/trace/ftrace/mali.proto";
 import "protos/perfetto/trace/ftrace/mdss.proto";
 import "protos/perfetto/trace/ftrace/mm_event.proto";
 import "protos/perfetto/trace/ftrace/net.proto";
 import "protos/perfetto/trace/ftrace/oom.proto";
+import "protos/perfetto/trace/ftrace/panel.proto";
 import "protos/perfetto/trace/ftrace/power.proto";
 import "protos/perfetto/trace/ftrace/printk.proto";
 import "protos/perfetto/trace/ftrace/raw_syscalls.proto";
@@ -68,8 +72,11 @@
 import "protos/perfetto/trace/ftrace/task.proto";
 import "protos/perfetto/trace/ftrace/tcp.proto";
 import "protos/perfetto/trace/ftrace/thermal.proto";
+import "protos/perfetto/trace/ftrace/trusty.proto";
 import "protos/perfetto/trace/ftrace/ufs.proto";
 import "protos/perfetto/trace/ftrace/v4l2.proto";
+import "protos/perfetto/trace/ftrace/virtio_gpu.proto";
+import "protos/perfetto/trace/ftrace/virtio_video.proto";
 import "protos/perfetto/trace/ftrace/vmscan.proto";
 import "protos/perfetto/trace/ftrace/workqueue.proto";
 import "protos/perfetto/trace/ftrace/generic.proto";
@@ -529,5 +536,54 @@
     Vb2V4l2BufDoneFtraceEvent vb2_v4l2_buf_done = 425;
     Vb2V4l2QbufFtraceEvent vb2_v4l2_qbuf = 426;
     Vb2V4l2DqbufFtraceEvent vb2_v4l2_dqbuf = 427;
+    DsiCmdFifoStatusFtraceEvent dsi_cmd_fifo_status = 428;
+    DsiRxFtraceEvent dsi_rx = 429;
+    DsiTxFtraceEvent dsi_tx = 430;
+    AndroidFsDatareadEndFtraceEvent android_fs_dataread_end = 431;
+    AndroidFsDatareadStartFtraceEvent android_fs_dataread_start = 432;
+    AndroidFsDatawriteEndFtraceEvent android_fs_datawrite_end = 433;
+    AndroidFsDatawriteStartFtraceEvent android_fs_datawrite_start = 434;
+    AndroidFsFsyncEndFtraceEvent android_fs_fsync_end = 435;
+    AndroidFsFsyncStartFtraceEvent android_fs_fsync_start = 436;
+    FuncgraphEntryFtraceEvent funcgraph_entry = 437;
+    FuncgraphExitFtraceEvent funcgraph_exit = 438;
+    VirtioVideoCmdFtraceEvent virtio_video_cmd = 439;
+    VirtioVideoCmdDoneFtraceEvent virtio_video_cmd_done = 440;
+    VirtioVideoResourceQueueFtraceEvent virtio_video_resource_queue = 441;
+    VirtioVideoResourceQueueDoneFtraceEvent virtio_video_resource_queue_done =
+        442;
+    MmShrinkSlabStartFtraceEvent mm_shrink_slab_start = 443;
+    MmShrinkSlabEndFtraceEvent mm_shrink_slab_end = 444;
+    TrustySmcFtraceEvent trusty_smc = 445;
+    TrustySmcDoneFtraceEvent trusty_smc_done = 446;
+    TrustyStdCall32FtraceEvent trusty_std_call32 = 447;
+    TrustyStdCall32DoneFtraceEvent trusty_std_call32_done = 448;
+    TrustyShareMemoryFtraceEvent trusty_share_memory = 449;
+    TrustyShareMemoryDoneFtraceEvent trusty_share_memory_done = 450;
+    TrustyReclaimMemoryFtraceEvent trusty_reclaim_memory = 451;
+    TrustyReclaimMemoryDoneFtraceEvent trusty_reclaim_memory_done = 452;
+    TrustyIrqFtraceEvent trusty_irq = 453;
+    TrustyIpcHandleEventFtraceEvent trusty_ipc_handle_event = 454;
+    TrustyIpcConnectFtraceEvent trusty_ipc_connect = 455;
+    TrustyIpcConnectEndFtraceEvent trusty_ipc_connect_end = 456;
+    TrustyIpcWriteFtraceEvent trusty_ipc_write = 457;
+    TrustyIpcPollFtraceEvent trusty_ipc_poll = 458;
+    // removed field with id 459;
+    TrustyIpcReadFtraceEvent trusty_ipc_read = 460;
+    TrustyIpcReadEndFtraceEvent trusty_ipc_read_end = 461;
+    TrustyIpcRxFtraceEvent trusty_ipc_rx = 462;
+    // removed field with id 463;
+    TrustyEnqueueNopFtraceEvent trusty_enqueue_nop = 464;
+    CmaAllocStartFtraceEvent cma_alloc_start = 465;
+    CmaAllocInfoFtraceEvent cma_alloc_info = 466;
+    LwisTracingMarkWriteFtraceEvent lwis_tracing_mark_write = 467;
+    VirtioGpuCmdQueueFtraceEvent virtio_gpu_cmd_queue = 468;
+    VirtioGpuCmdResponseFtraceEvent virtio_gpu_cmd_response = 469;
+    MaliMaliKCPUCQSSETFtraceEvent mali_mali_KCPU_CQS_SET = 470;
+    MaliMaliKCPUCQSWAITSTARTFtraceEvent mali_mali_KCPU_CQS_WAIT_START = 471;
+    MaliMaliKCPUCQSWAITENDFtraceEvent mali_mali_KCPU_CQS_WAIT_END = 472;
+    MaliMaliKCPUFENCESIGNALFtraceEvent mali_mali_KCPU_FENCE_SIGNAL = 473;
+    MaliMaliKCPUFENCEWAITSTARTFtraceEvent mali_mali_KCPU_FENCE_WAIT_START = 474;
+    MaliMaliKCPUFENCEWAITENDFtraceEvent mali_mali_KCPU_FENCE_WAIT_END = 475;
   }
 }
diff --git a/protos/perfetto/trace/ftrace/ftrace_event_bundle.proto b/protos/perfetto/trace/ftrace/ftrace_event_bundle.proto
index 5e531f6..57c3ce7 100644
--- a/protos/perfetto/trace/ftrace/ftrace_event_bundle.proto
+++ b/protos/perfetto/trace/ftrace/ftrace_event_bundle.proto
@@ -104,4 +104,5 @@
   FTRACE_CLOCK_UNKNOWN = 1;
   FTRACE_CLOCK_GLOBAL = 2;
   FTRACE_CLOCK_LOCAL = 3;
+  FTRACE_CLOCK_MONO_RAW = 4;
 }
diff --git a/protos/perfetto/trace/ftrace/ftrace_stats.proto b/protos/perfetto/trace/ftrace/ftrace_stats.proto
index 9f26fa3..fac68b0 100644
--- a/protos/perfetto/trace/ftrace/ftrace_stats.proto
+++ b/protos/perfetto/trace/ftrace/ftrace_stats.proto
@@ -92,4 +92,8 @@
   // failed to enable due to permissions, or due to a conflicting option
   // (currently FtraceConfig.disable_generic_events).
   repeated string failed_ftrace_events = 7;
+
+  // The data source was configured to preserve existing events in the ftrace
+  // buffer before the start of the trace.
+  optional bool preserve_ftrace_buffer = 8;
 }
diff --git a/protos/perfetto/trace/ftrace/lwis.proto b/protos/perfetto/trace/ftrace/lwis.proto
new file mode 100644
index 0000000..d15fbb2
--- /dev/null
+++ b/protos/perfetto/trace/ftrace/lwis.proto
@@ -0,0 +1,14 @@
+// Autogenerated by:
+// ../../src/tools/ftrace_proto_gen/ftrace_proto_gen.cc
+// Do not edit.
+
+syntax = "proto2";
+package perfetto.protos;
+
+message LwisTracingMarkWriteFtraceEvent {
+  optional string lwis_name = 1;
+  optional uint32 type = 2;
+  optional int32 pid = 3;
+  optional string func_name = 4;
+  optional int64 value = 5;
+}
diff --git a/protos/perfetto/trace/ftrace/mali.proto b/protos/perfetto/trace/ftrace/mali.proto
index 62c4777..0c59cd4 100644
--- a/protos/perfetto/trace/ftrace/mali.proto
+++ b/protos/perfetto/trace/ftrace/mali.proto
@@ -11,3 +11,45 @@
   optional uint32 type = 3;
   optional int32 value = 4;
 }
+message MaliMaliKCPUCQSSETFtraceEvent {
+  optional uint32 id = 1;
+  optional uint64 info_val1 = 2;
+  optional uint64 info_val2 = 3;
+  optional uint32 kctx_id = 4;
+  optional int32 kctx_tgid = 5;
+}
+message MaliMaliKCPUCQSWAITSTARTFtraceEvent {
+  optional uint32 id = 1;
+  optional uint64 info_val1 = 2;
+  optional uint64 info_val2 = 3;
+  optional uint32 kctx_id = 4;
+  optional int32 kctx_tgid = 5;
+}
+message MaliMaliKCPUCQSWAITENDFtraceEvent {
+  optional uint32 id = 1;
+  optional uint64 info_val1 = 2;
+  optional uint64 info_val2 = 3;
+  optional uint32 kctx_id = 4;
+  optional int32 kctx_tgid = 5;
+}
+message MaliMaliKCPUFENCESIGNALFtraceEvent {
+  optional uint64 info_val1 = 1;
+  optional uint64 info_val2 = 2;
+  optional int32 kctx_tgid = 3;
+  optional uint32 kctx_id = 4;
+  optional uint32 id = 5;
+}
+message MaliMaliKCPUFENCEWAITSTARTFtraceEvent {
+  optional uint64 info_val1 = 1;
+  optional uint64 info_val2 = 2;
+  optional int32 kctx_tgid = 3;
+  optional uint32 kctx_id = 4;
+  optional uint32 id = 5;
+}
+message MaliMaliKCPUFENCEWAITENDFtraceEvent {
+  optional uint64 info_val1 = 1;
+  optional uint64 info_val2 = 2;
+  optional int32 kctx_tgid = 3;
+  optional uint32 kctx_id = 4;
+  optional uint32 id = 5;
+}
diff --git a/protos/perfetto/trace/ftrace/panel.proto b/protos/perfetto/trace/ftrace/panel.proto
new file mode 100644
index 0000000..5b84b3b
--- /dev/null
+++ b/protos/perfetto/trace/ftrace/panel.proto
@@ -0,0 +1,20 @@
+// Autogenerated by:
+// ../../src/tools/ftrace_proto_gen/ftrace_proto_gen.cc
+// Do not edit.
+
+syntax = "proto2";
+package perfetto.protos;
+
+message DsiCmdFifoStatusFtraceEvent {
+  optional uint32 header = 1;
+  optional uint32 payload = 2;
+}
+message DsiRxFtraceEvent {
+  optional uint32 cmd = 1;
+  optional uint32 rx_buf = 2;
+}
+message DsiTxFtraceEvent {
+  optional uint32 last = 1;
+  optional uint32 tx_buf = 2;
+  optional uint32 type = 3;
+}
diff --git a/protos/perfetto/trace/ftrace/raw_syscalls.proto b/protos/perfetto/trace/ftrace/raw_syscalls.proto
index 3b330d1..475ab31 100644
--- a/protos/perfetto/trace/ftrace/raw_syscalls.proto
+++ b/protos/perfetto/trace/ftrace/raw_syscalls.proto
@@ -7,6 +7,7 @@
 
 message SysEnterFtraceEvent {
   optional int64 id = 1;
+  repeated uint64 args = 2;
 }
 message SysExitFtraceEvent {
   optional int64 id = 1;
diff --git a/protos/perfetto/trace/ftrace/trusty.proto b/protos/perfetto/trace/ftrace/trusty.proto
new file mode 100644
index 0000000..6237e7f
--- /dev/null
+++ b/protos/perfetto/trace/ftrace/trusty.proto
@@ -0,0 +1,96 @@
+// Autogenerated by:
+// ../../src/tools/ftrace_proto_gen/ftrace_proto_gen.cc
+// Do not edit.
+
+syntax = "proto2";
+package perfetto.protos;
+
+message TrustySmcFtraceEvent {
+  optional uint64 r0 = 1;
+  optional uint64 r1 = 2;
+  optional uint64 r2 = 3;
+  optional uint64 r3 = 4;
+}
+message TrustySmcDoneFtraceEvent {
+  optional uint64 ret = 1;
+}
+message TrustyStdCall32FtraceEvent {
+  optional uint64 r0 = 1;
+  optional uint64 r1 = 2;
+  optional uint64 r2 = 3;
+  optional uint64 r3 = 4;
+}
+message TrustyStdCall32DoneFtraceEvent {
+  optional int64 ret = 1;
+}
+message TrustyShareMemoryFtraceEvent {
+  optional uint64 len = 1;
+  optional uint32 lend = 2;
+  optional uint32 nents = 3;
+}
+message TrustyShareMemoryDoneFtraceEvent {
+  optional uint64 handle = 1;
+  optional uint64 len = 2;
+  optional uint32 lend = 3;
+  optional uint32 nents = 4;
+  optional int32 ret = 5;
+}
+message TrustyReclaimMemoryFtraceEvent {
+  optional uint64 id = 1;
+}
+message TrustyReclaimMemoryDoneFtraceEvent {
+  optional uint64 id = 1;
+  optional int32 ret = 2;
+}
+message TrustyIrqFtraceEvent {
+  optional int32 irq = 1;
+}
+message TrustyIpcHandleEventFtraceEvent {
+  optional uint32 chan = 1;
+  optional uint32 event_id = 2;
+  optional string srv_name = 3;
+}
+message TrustyIpcConnectFtraceEvent {
+  optional uint32 chan = 1;
+  optional string port = 2;
+  optional int32 state = 3;
+}
+message TrustyIpcConnectEndFtraceEvent {
+  optional uint32 chan = 1;
+  optional int32 err = 2;
+  optional int32 state = 3;
+}
+message TrustyIpcWriteFtraceEvent {
+  optional uint64 buf_id = 1;
+  optional uint32 chan = 2;
+  optional int32 kind_shm = 3;
+  optional int32 len_or_err = 4;
+  optional uint64 shm_cnt = 5;
+  optional string srv_name = 6;
+}
+message TrustyIpcPollFtraceEvent {
+  optional uint32 chan = 1;
+  optional uint32 poll_mask = 2;
+  optional string srv_name = 3;
+}
+message TrustyIpcReadFtraceEvent {
+  optional uint32 chan = 1;
+  optional string srv_name = 2;
+}
+message TrustyIpcReadEndFtraceEvent {
+  optional uint64 buf_id = 1;
+  optional uint32 chan = 2;
+  optional int32 len_or_err = 3;
+  optional uint64 shm_cnt = 4;
+  optional string srv_name = 5;
+}
+message TrustyIpcRxFtraceEvent {
+  optional uint64 buf_id = 1;
+  optional uint32 chan = 2;
+  optional string srv_name = 3;
+}
+message TrustyEnqueueNopFtraceEvent {
+  optional uint32 arg1 = 1;
+  optional uint32 arg2 = 2;
+  optional uint32 arg3 = 3;
+}
diff --git a/protos/perfetto/trace/ftrace/virtio_gpu.proto b/protos/perfetto/trace/ftrace/virtio_gpu.proto
new file mode 100644
index 0000000..eca7272
--- /dev/null
+++ b/protos/perfetto/trace/ftrace/virtio_gpu.proto
@@ -0,0 +1,29 @@
+// Autogenerated by:
+// ../../src/tools/ftrace_proto_gen/ftrace_proto_gen.cc
+// Do not edit.
+
+syntax = "proto2";
+package perfetto.protos;
+
+message VirtioGpuCmdQueueFtraceEvent {
+  optional uint32 ctx_id = 1;
+  optional int32 dev = 2;
+  optional uint64 fence_id = 3;
+  optional uint32 flags = 4;
+  optional string name = 5;
+  optional uint32 num_free = 6;
+  optional uint32 seqno = 7;
+  optional uint32 type = 8;
+  optional uint32 vq = 9;
+}
+message VirtioGpuCmdResponseFtraceEvent {
+  optional uint32 ctx_id = 1;
+  optional int32 dev = 2;
+  optional uint64 fence_id = 3;
+  optional uint32 flags = 4;
+  optional string name = 5;
+  optional uint32 num_free = 6;
+  optional uint32 seqno = 7;
+  optional uint32 type = 8;
+  optional uint32 vq = 9;
+}
diff --git a/protos/perfetto/trace/ftrace/virtio_video.proto b/protos/perfetto/trace/ftrace/virtio_video.proto
new file mode 100644
index 0000000..8c1b9f2
--- /dev/null
+++ b/protos/perfetto/trace/ftrace/virtio_video.proto
@@ -0,0 +1,35 @@
+// Autogenerated by:
+// ../../src/tools/ftrace_proto_gen/ftrace_proto_gen.cc
+// Do not edit.
+
+syntax = "proto2";
+package perfetto.protos;
+
+message VirtioVideoCmdFtraceEvent {
+  optional uint32 stream_id = 1;
+  optional uint32 type = 2;
+}
+message VirtioVideoCmdDoneFtraceEvent {
+  optional uint32 stream_id = 1;
+  optional uint32 type = 2;
+}
+message VirtioVideoResourceQueueFtraceEvent {
+  optional uint32 data_size0 = 1;
+  optional uint32 data_size1 = 2;
+  optional uint32 data_size2 = 3;
+  optional uint32 data_size3 = 4;
+  optional uint32 queue_type = 5;
+  optional int32 resource_id = 6;
+  optional int32 stream_id = 7;
+  optional uint64 timestamp = 8;
+}
+message VirtioVideoResourceQueueDoneFtraceEvent {
+  optional uint32 data_size0 = 1;
+  optional uint32 data_size1 = 2;
+  optional uint32 data_size2 = 3;
+  optional uint32 data_size3 = 4;
+  optional uint32 queue_type = 5;
+  optional int32 resource_id = 6;
+  optional int32 stream_id = 7;
+  optional uint64 timestamp = 8;
+}
diff --git a/protos/perfetto/trace/ftrace/vmscan.proto b/protos/perfetto/trace/ftrace/vmscan.proto
index 1ba8f0d..b0fc88d 100644
--- a/protos/perfetto/trace/ftrace/vmscan.proto
+++ b/protos/perfetto/trace/ftrace/vmscan.proto
@@ -21,3 +21,25 @@
 message MmVmscanKswapdSleepFtraceEvent {
   optional int32 nid = 1;
 }
+message MmShrinkSlabStartFtraceEvent {
+  optional uint64 cache_items = 1;
+  optional uint64 delta = 2;
+  optional uint32 gfp_flags = 3;
+  optional uint64 lru_pgs = 4;
+  optional int64 nr_objects_to_shrink = 5;
+  optional uint64 pgs_scanned = 6;
+  optional uint64 shr = 7;
+  optional uint64 shrink = 8;
+  optional uint64 total_scan = 9;
+  optional int32 nid = 10;
+  optional int32 priority = 11;
+}
+message MmShrinkSlabEndFtraceEvent {
+  optional int64 new_scan = 1;
+  optional int32 retval = 2;
+  optional uint64 shr = 3;
+  optional uint64 shrink = 4;
+  optional int64 total_scan = 5;
+  optional int64 unused_scan = 6;
+  optional int32 nid = 7;
+}
diff --git a/protos/perfetto/trace/perfetto/perfetto_metatrace.proto b/protos/perfetto/trace/perfetto/perfetto_metatrace.proto
index df33b13..56caf9f 100644
--- a/protos/perfetto/trace/perfetto/perfetto_metatrace.proto
+++ b/protos/perfetto/trace/perfetto/perfetto_metatrace.proto
@@ -27,11 +27,19 @@
 
     // For trace processor metatracing.
     string event_name = 8;
+    uint64 event_name_iid = 11;
+
     string counter_name = 9;
   }
   message Arg {
-    optional string key = 1;
-    optional string value = 2;
+    oneof key_or_interned_key {
+      string key = 1;
+      uint64 key_iid = 3;
+    }
+    oneof value_or_interned_value {
+      string value = 2;
+      uint64 value_iid = 4;
+    }
   }
 
   // Only when using |event_id|.
@@ -49,4 +57,12 @@
 
   // Args for the event.
   repeated Arg args = 7;
+
+  // Interned strings corresponding to the |event_name_iid|, |key_iid| and
+  // |value_iid| above.
+  message InternedString {
+    optional uint64 iid = 1;
+    optional string value = 2;
+  };
+  repeated InternedString interned_strings = 10;
 }
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 42c7f85..a215592 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -419,6 +419,21 @@
 
 // End of protos/perfetto/config/android/android_system_property_config.proto
 
+// Begin of protos/perfetto/config/android/network_trace_config.proto
+
+// Network tracing data source that records details on all packets sent or
+// received by the network.
+message NetworkPacketTraceConfig {
+  // Polling frequency in milliseconds. Network tracing writes to a fixed size
+  // ring buffer. The polling interval should be such that the ring buffer is
+  // unlikely to fill in that interval (or that filling is an acceptable risk).
+  // The minimum polling rate is 100ms (values below this are ignored).
+  // Introduced in Android 14 (U).
+  optional uint32 poll_ms = 1;
+}
+
+// End of protos/perfetto/config/android/network_trace_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
@@ -466,7 +481,7 @@
 
 // Begin of protos/perfetto/config/ftrace/ftrace_config.proto
 
-// Next id: 18.
+// Next id: 24.
 message FtraceConfig {
   repeated string ftrace_events = 1;
   repeated string atrace_categories = 2;
@@ -485,6 +500,36 @@
   }
   optional CompactSchedConfig compact_sched = 12;
 
+  // Optional filter for "ftrace/print" events.
+  //
+  // The filter consists of multiple rules. As soon as a rule matches (the rules
+  // are processed in order), its `allow` field will be used as the outcome: if
+  // `allow` is true, the event will be included in the trace, otherwise it will
+  // be discarded. If an event does not match any rule, it will be allowed by
+  // default (a rule with an empty prefix and allow=false, disallows everything
+  // by default).
+  message PrintFilter {
+    message Rule {
+      // Matches an atrace message of the form:
+      // <type>|pid|<prefix>...
+      message AtraceMessage {
+        optional string type = 1;
+        optional string prefix = 2;
+      }
+      oneof match {
+        // This rule matches if `prefix` matches exactly with the beginning of
+        // the "ftrace/print" "buf" field.
+        string prefix = 1;
+        // This rule matches if the "buf" field contains an atrace-style print
+        // message as specified in `atrace_msg`.
+        AtraceMessage atrace_msg = 3;
+      }
+      optional bool allow = 2;
+    }
+    repeated Rule rules = 1;
+  }
+  optional PrintFilter print_filter = 22;
+
   // Enables symbol name resolution against /proc/kallsyms.
   // It requires that either traced_probes is running as root or that
   // kptr_restrict has been manually lowered.
@@ -518,7 +563,7 @@
 
   // When this boolean is true AND the ftrace_events contains "kmem/rss_stat",
   // this option causes traced_probes to enable the "kmem/rss_stat_throttled"
-  // event instad if present, and fall back to "kmem/rss_stat" if not present.
+  // event instead if present, and fall back to "kmem/rss_stat" if not present.
   // The historical context for this is the following:
   // - Up to Android S (12), the rss_stat was internally throttled in its
   //   kernel implementation.
@@ -528,8 +573,6 @@
   // - Not all Android T/13 devices will receive a new kernel though, hence we
   //   need to deal with both cases.
   // For more context: go/rss-stat-throttled.
-  // TODO(kaleshsingh): implement the logic behind this. Right now this flag
-  // does nothing.
   optional bool throttle_rss_stat = 15;
 
   // If true, avoid enabling events that aren't statically known by
@@ -540,6 +583,62 @@
   // expand to events that aren't of interest to the tracing user.
   // Introduced in: Android T.
   optional bool disable_generic_events = 16;
+
+  // The list of syscalls that should be recorded by sys_{enter,exit} ftrace
+  // events. When empty, all syscalls are recorded. If neither sys_{enter,exit}
+  // are enabled, this setting has no effect. Example: ["sys_read", "sys_open"].
+  // Introduced in: Android U.
+  repeated string syscall_events = 18;
+
+  // If true, enable the "function_graph" kernel tracer that emits events
+  // whenever a kernel function is entered and exited
+  // (funcgraph_entry/funcgraph_exit).
+  // Notes on use:
+  // * Requires |symbolize_ksyms| for function name resolution.
+  // * Use |function_filters| or |function_graph_roots| to constrain the traced
+  //   set of functions, otherwise the event bandwidth will be too high for
+  //   practical use.
+  // * The data source might be rejected if there is already a concurrent
+  //   ftrace data source that does not use function graph itself, as we do not
+  //   support switching kernel tracers mid-trace.
+  // * Requires a kernel compiled with CONFIG_FUNCTION_GRAPH_TRACER. This is
+  //   enabled if "cat /sys/kernel/tracing/available_tracers" includes
+  //   "function_graph".
+  // Android:
+  // * Available only on debuggable builds.
+  // * Introduced in: Android U.
+  optional bool enable_function_graph = 19;
+
+  // Constrains the set of functions traced when |enable_function_graph| is
+  // true. Supports globs, e.g. "sched*". You can specify multiple filters,
+  // in which case all matching functions will be traced. See kernel
+  // documentation on ftrace "set_ftrace_filter" file for more details.
+  // Android:
+  // * Available only on debuggable builds.
+  // * Introduced in: Android U.
+  repeated string function_filters = 20;
+
+  // If |enable_function_graph| is true, trace this set of functions *and* all
+  // of its callees. Supports globs. Can be set together with
+  // |function_filters|, in which case only callees matching the filter will be
+  // traced. If setting both, you most likely want all roots to also be
+  // included in |function_filters|.
+  // Android:
+  // * Available only on debuggable builds.
+  // * Introduced in: Android U.
+  repeated string function_graph_roots = 21;
+
+  // If true, does not clear ftrace buffers before the start of the program.
+  // This makes sense only if this is the first ftrace data source instance
+  // created after the daemon has been started. Can be useful for gathering boot
+  // traces, if ftrace has been separately configured (e.g. via kernel
+  // commandline).
+  optional bool preserve_ftrace_buffer = 23;
+
+  // If true, overrides the default timestamp clock and uses a raw hardware
+  // based monotonic clock for getting timestamps.
+  // * Introduced in: Android U.
+  optional bool use_monotonic_raw_clock = 24;
 }
 
 // End of protos/perfetto/config/ftrace/ftrace_config.proto
@@ -711,6 +810,13 @@
 
   // DEPRECATED thread_time_in_state_cache_size
   reserved 8;
+
+  // If true this will resolve filedescriptors for each process so these
+  // can be mapped to their actual device or file.
+  // Requires raw_syscalls/sys_exit ftrace events to be enabled or
+  // new fds opened after initially scanning a process will not be
+  // recognized.
+  optional bool resolve_process_fds = 9;
 }
 
 // End of protos/perfetto/config/process_stats/process_stats_config.proto
@@ -2118,6 +2224,10 @@
   // Polls /proc/buddyinfo every X ms, if non-zero.
   // This is required to be > 10ms to avoid excessive CPU usage.
   optional uint32 buddyinfo_period_ms = 9;
+
+  // Polls /proc/diskstats every X ms, if non-zero.
+  // This is required to be > 10ms to avoid excessive CPU usage.
+  optional uint32 diskstat_period_ms = 10;
 }
 
 // End of protos/perfetto/config/sys_stats/sys_stats_config.proto
@@ -2268,7 +2378,7 @@
 // Begin of protos/perfetto/config/data_source_config.proto
 
 // The configuration that is passed to each data source when starting tracing.
-// Next id: 120
+// Next id: 121
 message DataSourceConfig {
   enum SessionInitiator {
     SESSION_INITIATOR_UNSPECIFIED = 0;
@@ -2344,7 +2454,7 @@
   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.game_intervention_list
+  // Data source name: android.game_interventions
   optional AndroidGameInterventionListConfig
       android_game_intervention_list_config = 116 [lazy = true];
   // Data source name: android.packages_list
@@ -2382,6 +2492,11 @@
   // traced_probes) may not support interception.
   optional InterceptorConfig interceptor_config = 115;
 
+  // Data source name: android.network_packets.
+  // Introduced in Android 14 (U).
+  optional NetworkPacketTraceConfig network_packet_trace_config = 120
+      [lazy = true];
+
   // 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
@@ -2589,6 +2704,10 @@
     // On R-, this override only affected userdebug builds. Since S, it also
     // affects user builds.
     optional uint64 max_upload_per_day_bytes = 1;
+
+    // Overrides the guardrail for maximum trace buffer size.
+    // Available on U+
+    optional uint32 max_tracing_buffer_size_kb = 2;
   }
   optional GuardrailOverrides guardrail_overrides = 11;
 
@@ -2798,10 +2917,14 @@
 
   // 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;
+  // DEPRECATED as per v32. See TracePacket.trace_uuid for the authoritative
+  // Trace UUID. If this field is set, the tracing service will respect the
+  // requested UUID (i.e. TracePacket.trace_uuid == this field) but only if
+  // gap-less snapshotting is not used.
+  optional int64 trace_uuid_msb = 27 [deprecated = true];
+  optional int64 trace_uuid_lsb = 28 [deprecated = true];
 
   // When set applies a post-filter to the trace contents using the filter
   // provided. The filter is applied at ReadBuffers() time and works both in the
@@ -3532,6 +3655,47 @@
 
 // End of protos/perfetto/trace/android/initial_display_state.proto
 
+// Begin of protos/perfetto/trace/android/network_trace.proto
+
+enum TrafficDirection {
+  DIR_UNSPECIFIED = 0;
+  DIR_INGRESS = 1;
+  DIR_EGRESS = 2;
+}
+
+// NetworkPacketEvent records the details of a single packet sent or received
+// on the network (in Linux kernel terminology, one sk_buff struct).
+message NetworkPacketEvent {
+  // The direction traffic is flowing for this event.
+  optional TrafficDirection direction = 1;
+
+  // The name of the interface if available (e.g. 'rmnet0').
+  optional string interface = 2;
+
+  // The length of the packet in bytes (wire_size - L2_header_size).
+  optional uint32 length = 3;
+
+  // The Linux user id associated with the packet's socket.
+  optional uint32 uid = 4;
+
+  // The Android network tag associated with the packet's socket.
+  optional uint32 tag = 5;
+
+  // The packet's IP protocol (TCP=6, UDP=17, etc).
+  optional uint32 ip_proto = 6;
+
+  // The packet's TCP flags as a bitmask (FIN=0x1, SYN=0x2, RST=0x4, etc).
+  optional uint32 tcp_flags = 7;
+
+  // The local udp/tcp port of the packet.
+  optional uint32 local_port = 8;
+
+  // The remote udp/tcp port of the packet.
+  optional uint32 remote_port = 9;
+}
+
+// End of protos/perfetto/trace/android/network_trace.proto
+
 // Begin of protos/perfetto/trace/android/packages_list.proto
 
 message PackagesList {
@@ -3659,6 +3823,9 @@
 
   // List of all active triggers in current session, when trace was triggered.
   repeated TriggerRule active_rules = 2;
+
+  // Hash of the scenario name.
+  optional fixed32 scenario_name_hash = 3;
 }
 
 // End of protos/perfetto/trace/chrome/chrome_metadata.proto
@@ -4061,6 +4228,51 @@
 
 // End of protos/perfetto/trace/filesystem/inode_file_map.proto
 
+// Begin of protos/perfetto/trace/ftrace/android_fs.proto
+
+message AndroidFsDatareadEndFtraceEvent {
+  optional int32 bytes = 1;
+  optional uint64 ino = 2;
+  optional int64 offset = 3;
+}
+message AndroidFsDatareadStartFtraceEvent {
+  optional int32 bytes = 1;
+  optional string cmdline = 2;
+  optional int64 i_size = 3;
+  optional uint64 ino = 4;
+  optional int64 offset = 5;
+  optional string pathbuf = 6;
+  optional int32 pid = 7;
+}
+message AndroidFsDatawriteEndFtraceEvent {
+  optional int32 bytes = 1;
+  optional uint64 ino = 2;
+  optional int64 offset = 3;
+}
+message AndroidFsDatawriteStartFtraceEvent {
+  optional int32 bytes = 1;
+  optional string cmdline = 2;
+  optional int64 i_size = 3;
+  optional uint64 ino = 4;
+  optional int64 offset = 5;
+  optional string pathbuf = 6;
+  optional int32 pid = 7;
+}
+message AndroidFsFsyncEndFtraceEvent {
+  optional int32 bytes = 1;
+  optional uint64 ino = 2;
+  optional int64 offset = 3;
+}
+message AndroidFsFsyncStartFtraceEvent {
+  optional string cmdline = 1;
+  optional int64 i_size = 2;
+  optional uint64 ino = 3;
+  optional string pathbuf = 4;
+  optional int32 pid = 5;
+}
+
+// End of protos/perfetto/trace/ftrace/android_fs.proto
+
 // Begin of protos/perfetto/trace/ftrace/binder.proto
 
 message BinderTransactionFtraceEvent {
@@ -4319,6 +4531,28 @@
 
 // End of protos/perfetto/trace/ftrace/clk.proto
 
+// Begin of protos/perfetto/trace/ftrace/cma.proto
+
+message CmaAllocStartFtraceEvent {
+  optional uint32 align = 1;
+  optional uint32 count = 2;
+  optional string name = 3;
+}
+message CmaAllocInfoFtraceEvent {
+  optional uint32 align = 1;
+  optional uint32 count = 2;
+  optional uint32 err_iso = 3;
+  optional uint32 err_mig = 4;
+  optional uint32 err_test = 5;
+  optional string name = 6;
+  optional uint64 nr_mapped = 7;
+  optional uint64 nr_migrated = 8;
+  optional uint64 nr_reclaimed = 9;
+  optional uint64 pfn = 10;
+}
+
+// End of protos/perfetto/trace/ftrace/cma.proto
+
 // Begin of protos/perfetto/trace/ftrace/compaction.proto
 
 message MmCompactionBeginFtraceEvent {
@@ -5580,6 +5814,17 @@
   optional uint64 ip = 1;
   optional string buf = 2;
 }
+message FuncgraphEntryFtraceEvent {
+  optional int32 depth = 1;
+  optional uint64 func = 2;
+}
+message FuncgraphExitFtraceEvent {
+  optional uint64 calltime = 1;
+  optional int32 depth = 2;
+  optional uint64 func = 3;
+  optional uint64 overrun = 4;
+  optional uint64 rettime = 5;
+}
 
 // End of protos/perfetto/trace/ftrace/ftrace.proto
 
@@ -6180,6 +6425,18 @@
 
 // End of protos/perfetto/trace/ftrace/lowmemorykiller.proto
 
+// Begin of protos/perfetto/trace/ftrace/lwis.proto
+
+message LwisTracingMarkWriteFtraceEvent {
+  optional string lwis_name = 1;
+  optional uint32 type = 2;
+  optional int32 pid = 3;
+  optional string func_name = 4;
+  optional int64 value = 5;
+}
+
+// End of protos/perfetto/trace/ftrace/lwis.proto
+
 // Begin of protos/perfetto/trace/ftrace/mali.proto
 
 message MaliTracingMarkWriteFtraceEvent {
@@ -6188,6 +6445,48 @@
   optional uint32 type = 3;
   optional int32 value = 4;
 }
+message MaliMaliKCPUCQSSETFtraceEvent {
+  optional uint32 id = 1;
+  optional uint64 info_val1 = 2;
+  optional uint64 info_val2 = 3;
+  optional uint32 kctx_id = 4;
+  optional int32 kctx_tgid = 5;
+}
+message MaliMaliKCPUCQSWAITSTARTFtraceEvent {
+  optional uint32 id = 1;
+  optional uint64 info_val1 = 2;
+  optional uint64 info_val2 = 3;
+  optional uint32 kctx_id = 4;
+  optional int32 kctx_tgid = 5;
+}
+message MaliMaliKCPUCQSWAITENDFtraceEvent {
+  optional uint32 id = 1;
+  optional uint64 info_val1 = 2;
+  optional uint64 info_val2 = 3;
+  optional uint32 kctx_id = 4;
+  optional int32 kctx_tgid = 5;
+}
+message MaliMaliKCPUFENCESIGNALFtraceEvent {
+  optional uint64 info_val1 = 1;
+  optional uint64 info_val2 = 2;
+  optional int32 kctx_tgid = 3;
+  optional uint32 kctx_id = 4;
+  optional uint32 id = 5;
+}
+message MaliMaliKCPUFENCEWAITSTARTFtraceEvent {
+  optional uint64 info_val1 = 1;
+  optional uint64 info_val2 = 2;
+  optional int32 kctx_tgid = 3;
+  optional uint32 kctx_id = 4;
+  optional uint32 id = 5;
+}
+message MaliMaliKCPUFENCEWAITENDFtraceEvent {
+  optional uint64 info_val1 = 1;
+  optional uint64 info_val2 = 2;
+  optional int32 kctx_tgid = 3;
+  optional uint32 kctx_id = 4;
+  optional uint32 id = 5;
+}
 
 // End of protos/perfetto/trace/ftrace/mali.proto
 
@@ -6407,6 +6706,24 @@
 
 // End of protos/perfetto/trace/ftrace/oom.proto
 
+// Begin of protos/perfetto/trace/ftrace/panel.proto
+
+message DsiCmdFifoStatusFtraceEvent {
+  optional uint32 header = 1;
+  optional uint32 payload = 2;
+}
+message DsiRxFtraceEvent {
+  optional uint32 cmd = 1;
+  optional uint32 rx_buf = 2;
+}
+message DsiTxFtraceEvent {
+  optional uint32 last = 1;
+  optional uint32 tx_buf = 2;
+  optional uint32 type = 3;
+}
+
+// End of protos/perfetto/trace/ftrace/panel.proto
+
 // Begin of protos/perfetto/trace/ftrace/power.proto
 
 message CpuFrequencyFtraceEvent {
@@ -6469,6 +6786,7 @@
 
 message SysEnterFtraceEvent {
   optional int64 id = 1;
+  repeated uint64 args = 2;
 }
 message SysExitFtraceEvent {
   optional int64 id = 1;
@@ -6802,6 +7120,100 @@
 
 // End of protos/perfetto/trace/ftrace/thermal.proto
 
+// Begin of protos/perfetto/trace/ftrace/trusty.proto
+
+message TrustySmcFtraceEvent {
+  optional uint64 r0 = 1;
+  optional uint64 r1 = 2;
+  optional uint64 r2 = 3;
+  optional uint64 r3 = 4;
+}
+message TrustySmcDoneFtraceEvent {
+  optional uint64 ret = 1;
+}
+message TrustyStdCall32FtraceEvent {
+  optional uint64 r0 = 1;
+  optional uint64 r1 = 2;
+  optional uint64 r2 = 3;
+  optional uint64 r3 = 4;
+}
+message TrustyStdCall32DoneFtraceEvent {
+  optional int64 ret = 1;
+}
+message TrustyShareMemoryFtraceEvent {
+  optional uint64 len = 1;
+  optional uint32 lend = 2;
+  optional uint32 nents = 3;
+}
+message TrustyShareMemoryDoneFtraceEvent {
+  optional uint64 handle = 1;
+  optional uint64 len = 2;
+  optional uint32 lend = 3;
+  optional uint32 nents = 4;
+  optional int32 ret = 5;
+}
+message TrustyReclaimMemoryFtraceEvent {
+  optional uint64 id = 1;
+}
+message TrustyReclaimMemoryDoneFtraceEvent {
+  optional uint64 id = 1;
+  optional int32 ret = 2;
+}
+message TrustyIrqFtraceEvent {
+  optional int32 irq = 1;
+}
+message TrustyIpcHandleEventFtraceEvent {
+  optional uint32 chan = 1;
+  optional uint32 event_id = 2;
+  optional string srv_name = 3;
+}
+message TrustyIpcConnectFtraceEvent {
+  optional uint32 chan = 1;
+  optional string port = 2;
+  optional int32 state = 3;
+}
+message TrustyIpcConnectEndFtraceEvent {
+  optional uint32 chan = 1;
+  optional int32 err = 2;
+  optional int32 state = 3;
+}
+message TrustyIpcWriteFtraceEvent {
+  optional uint64 buf_id = 1;
+  optional uint32 chan = 2;
+  optional int32 kind_shm = 3;
+  optional int32 len_or_err = 4;
+  optional uint64 shm_cnt = 5;
+  optional string srv_name = 6;
+}
+message TrustyIpcPollFtraceEvent {
+  optional uint32 chan = 1;
+  optional uint32 poll_mask = 2;
+  optional string srv_name = 3;
+}
+message TrustyIpcReadFtraceEvent {
+  optional uint32 chan = 1;
+  optional string srv_name = 2;
+}
+message TrustyIpcReadEndFtraceEvent {
+  optional uint64 buf_id = 1;
+  optional uint32 chan = 2;
+  optional int32 len_or_err = 3;
+  optional uint64 shm_cnt = 4;
+  optional string srv_name = 5;
+}
+message TrustyIpcRxFtraceEvent {
+  optional uint64 buf_id = 1;
+  optional uint32 chan = 2;
+  optional string srv_name = 3;
+}
+message TrustyEnqueueNopFtraceEvent {
+  optional uint32 arg1 = 1;
+  optional uint32 arg2 = 2;
+  optional uint32 arg3 = 3;
+}
+
+// End of protos/perfetto/trace/ftrace/trusty.proto
+
 // Begin of protos/perfetto/trace/ftrace/ufs.proto
 
 message UfshcdCommandFtraceEvent {
@@ -6936,6 +7348,66 @@
 
 // End of protos/perfetto/trace/ftrace/v4l2.proto
 
+// Begin of protos/perfetto/trace/ftrace/virtio_gpu.proto
+
+message VirtioGpuCmdQueueFtraceEvent {
+  optional uint32 ctx_id = 1;
+  optional int32 dev = 2;
+  optional uint64 fence_id = 3;
+  optional uint32 flags = 4;
+  optional string name = 5;
+  optional uint32 num_free = 6;
+  optional uint32 seqno = 7;
+  optional uint32 type = 8;
+  optional uint32 vq = 9;
+}
+message VirtioGpuCmdResponseFtraceEvent {
+  optional uint32 ctx_id = 1;
+  optional int32 dev = 2;
+  optional uint64 fence_id = 3;
+  optional uint32 flags = 4;
+  optional string name = 5;
+  optional uint32 num_free = 6;
+  optional uint32 seqno = 7;
+  optional uint32 type = 8;
+  optional uint32 vq = 9;
+}
+
+// End of protos/perfetto/trace/ftrace/virtio_gpu.proto
+
+// Begin of protos/perfetto/trace/ftrace/virtio_video.proto
+
+message VirtioVideoCmdFtraceEvent {
+  optional uint32 stream_id = 1;
+  optional uint32 type = 2;
+}
+message VirtioVideoCmdDoneFtraceEvent {
+  optional uint32 stream_id = 1;
+  optional uint32 type = 2;
+}
+message VirtioVideoResourceQueueFtraceEvent {
+  optional uint32 data_size0 = 1;
+  optional uint32 data_size1 = 2;
+  optional uint32 data_size2 = 3;
+  optional uint32 data_size3 = 4;
+  optional uint32 queue_type = 5;
+  optional int32 resource_id = 6;
+  optional int32 stream_id = 7;
+  optional uint64 timestamp = 8;
+}
+message VirtioVideoResourceQueueDoneFtraceEvent {
+  optional uint32 data_size0 = 1;
+  optional uint32 data_size1 = 2;
+  optional uint32 data_size2 = 3;
+  optional uint32 data_size3 = 4;
+  optional uint32 queue_type = 5;
+  optional int32 resource_id = 6;
+  optional int32 stream_id = 7;
+  optional uint64 timestamp = 8;
+}
+
+// End of protos/perfetto/trace/ftrace/virtio_video.proto
+
 // Begin of protos/perfetto/trace/ftrace/vmscan.proto
 
 message MmVmscanDirectReclaimBeginFtraceEvent {
@@ -6954,6 +7426,28 @@
 message MmVmscanKswapdSleepFtraceEvent {
   optional int32 nid = 1;
 }
+message MmShrinkSlabStartFtraceEvent {
+  optional uint64 cache_items = 1;
+  optional uint64 delta = 2;
+  optional uint32 gfp_flags = 3;
+  optional uint64 lru_pgs = 4;
+  optional int64 nr_objects_to_shrink = 5;
+  optional uint64 pgs_scanned = 6;
+  optional uint64 shr = 7;
+  optional uint64 shrink = 8;
+  optional uint64 total_scan = 9;
+  optional int32 nid = 10;
+  optional int32 priority = 11;
+}
+message MmShrinkSlabEndFtraceEvent {
+  optional int64 new_scan = 1;
+  optional int32 retval = 2;
+  optional uint64 shr = 3;
+  optional uint64 shrink = 4;
+  optional int64 total_scan = 5;
+  optional int64 unused_scan = 6;
+  optional int32 nid = 7;
+}
 
 // End of protos/perfetto/trace/ftrace/vmscan.proto
 
@@ -7435,6 +7929,55 @@
     Vb2V4l2BufDoneFtraceEvent vb2_v4l2_buf_done = 425;
     Vb2V4l2QbufFtraceEvent vb2_v4l2_qbuf = 426;
     Vb2V4l2DqbufFtraceEvent vb2_v4l2_dqbuf = 427;
+    DsiCmdFifoStatusFtraceEvent dsi_cmd_fifo_status = 428;
+    DsiRxFtraceEvent dsi_rx = 429;
+    DsiTxFtraceEvent dsi_tx = 430;
+    AndroidFsDatareadEndFtraceEvent android_fs_dataread_end = 431;
+    AndroidFsDatareadStartFtraceEvent android_fs_dataread_start = 432;
+    AndroidFsDatawriteEndFtraceEvent android_fs_datawrite_end = 433;
+    AndroidFsDatawriteStartFtraceEvent android_fs_datawrite_start = 434;
+    AndroidFsFsyncEndFtraceEvent android_fs_fsync_end = 435;
+    AndroidFsFsyncStartFtraceEvent android_fs_fsync_start = 436;
+    FuncgraphEntryFtraceEvent funcgraph_entry = 437;
+    FuncgraphExitFtraceEvent funcgraph_exit = 438;
+    VirtioVideoCmdFtraceEvent virtio_video_cmd = 439;
+    VirtioVideoCmdDoneFtraceEvent virtio_video_cmd_done = 440;
+    VirtioVideoResourceQueueFtraceEvent virtio_video_resource_queue = 441;
+    VirtioVideoResourceQueueDoneFtraceEvent virtio_video_resource_queue_done =
+        442;
+    MmShrinkSlabStartFtraceEvent mm_shrink_slab_start = 443;
+    MmShrinkSlabEndFtraceEvent mm_shrink_slab_end = 444;
+    TrustySmcFtraceEvent trusty_smc = 445;
+    TrustySmcDoneFtraceEvent trusty_smc_done = 446;
+    TrustyStdCall32FtraceEvent trusty_std_call32 = 447;
+    TrustyStdCall32DoneFtraceEvent trusty_std_call32_done = 448;
+    TrustyShareMemoryFtraceEvent trusty_share_memory = 449;
+    TrustyShareMemoryDoneFtraceEvent trusty_share_memory_done = 450;
+    TrustyReclaimMemoryFtraceEvent trusty_reclaim_memory = 451;
+    TrustyReclaimMemoryDoneFtraceEvent trusty_reclaim_memory_done = 452;
+    TrustyIrqFtraceEvent trusty_irq = 453;
+    TrustyIpcHandleEventFtraceEvent trusty_ipc_handle_event = 454;
+    TrustyIpcConnectFtraceEvent trusty_ipc_connect = 455;
+    TrustyIpcConnectEndFtraceEvent trusty_ipc_connect_end = 456;
+    TrustyIpcWriteFtraceEvent trusty_ipc_write = 457;
+    TrustyIpcPollFtraceEvent trusty_ipc_poll = 458;
+    // removed field with id 459;
+    TrustyIpcReadFtraceEvent trusty_ipc_read = 460;
+    TrustyIpcReadEndFtraceEvent trusty_ipc_read_end = 461;
+    TrustyIpcRxFtraceEvent trusty_ipc_rx = 462;
+    // removed field with id 463;
+    TrustyEnqueueNopFtraceEvent trusty_enqueue_nop = 464;
+    CmaAllocStartFtraceEvent cma_alloc_start = 465;
+    CmaAllocInfoFtraceEvent cma_alloc_info = 466;
+    LwisTracingMarkWriteFtraceEvent lwis_tracing_mark_write = 467;
+    VirtioGpuCmdQueueFtraceEvent virtio_gpu_cmd_queue = 468;
+    VirtioGpuCmdResponseFtraceEvent virtio_gpu_cmd_response = 469;
+    MaliMaliKCPUCQSSETFtraceEvent mali_mali_KCPU_CQS_SET = 470;
+    MaliMaliKCPUCQSWAITSTARTFtraceEvent mali_mali_KCPU_CQS_WAIT_START = 471;
+    MaliMaliKCPUCQSWAITENDFtraceEvent mali_mali_KCPU_CQS_WAIT_END = 472;
+    MaliMaliKCPUFENCESIGNALFtraceEvent mali_mali_KCPU_FENCE_SIGNAL = 473;
+    MaliMaliKCPUFENCEWAITSTARTFtraceEvent mali_mali_KCPU_FENCE_WAIT_START = 474;
+    MaliMaliKCPUFENCEWAITENDFtraceEvent mali_mali_KCPU_FENCE_WAIT_END = 475;
   }
 }
 
@@ -7526,6 +8069,7 @@
   FTRACE_CLOCK_UNKNOWN = 1;
   FTRACE_CLOCK_GLOBAL = 2;
   FTRACE_CLOCK_LOCAL = 3;
+  FTRACE_CLOCK_MONO_RAW = 4;
 }
 
 // End of protos/perfetto/trace/ftrace/ftrace_event_bundle.proto
@@ -7606,6 +8150,10 @@
   // failed to enable due to permissions, or due to a conflicting option
   // (currently FtraceConfig.disable_generic_events).
   repeated string failed_ftrace_events = 7;
+
+  // The data source was configured to preserve existing events in the ftrace
+  // buffer before the start of the trace.
+  optional bool preserve_ftrace_buffer = 8;
 }
 
 // End of protos/perfetto/trace/ftrace/ftrace_stats.proto
@@ -8235,6 +8783,14 @@
 
 // End of protos/perfetto/trace/track_event/source_location.proto
 
+// Begin of protos/perfetto/trace/track_event/chrome_active_processes.proto
+
+// A list of processes connected to the tracing service.
+message ChromeActiveProcesses {
+  repeated int32 pid = 1;
+}
+// End of protos/perfetto/trace/track_event/chrome_active_processes.proto
+
 // Begin of protos/perfetto/trace/track_event/chrome_application_state_info.proto
 
 
@@ -8798,6 +9354,15 @@
   // trace requires storing them in the binary, which causes a significant
   // binary size bloat for Chromium.
   optional uint64 mojo_interface_method_iid = 4;
+
+  // Indicate whether this is a message or reply.
+  optional bool is_reply = 5;
+
+  // The payload size of the message being sent through mojo messages.
+  optional uint64 payload_size = 6;
+
+  // Represents the size of the message. Includes all headers and user payload.
+  optional uint64 data_num_bytes = 7;
 }
 
 // End of protos/perfetto/trace/track_event/chrome_mojo_event_info.proto
@@ -8931,7 +9496,7 @@
 // their default track association) can be emitted as part of a
 // TrackEventDefaults message.
 //
-// Next reserved id: 13 (up to 15). Next id: 49.
+// Next reserved id: 13 (up to 15). Next id: 50.
 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.
@@ -9083,6 +9648,7 @@
   optional ChromeWindowHandleEventInfo chrome_window_handle_event_info = 41;
   optional ChromeContentSettingsEventInfo chrome_content_settings_event_info =
       43;
+  optional ChromeActiveProcesses chrome_active_processes = 49;
 
   // This field is used only if the source location represents the function that
   // executes during this event.
@@ -9465,11 +10031,19 @@
 
     // For trace processor metatracing.
     string event_name = 8;
+    uint64 event_name_iid = 11;
+
     string counter_name = 9;
   }
   message Arg {
-    optional string key = 1;
-    optional string value = 2;
+    oneof key_or_interned_key {
+      string key = 1;
+      uint64 key_iid = 3;
+    }
+    oneof value_or_interned_value {
+      string value = 2;
+      uint64 value_iid = 4;
+    }
   }
 
   // Only when using |event_id|.
@@ -9487,6 +10061,14 @@
 
   // Args for the event.
   repeated Arg args = 7;
+
+  // Interned strings corresponding to the |event_name_iid|, |key_iid| and
+  // |value_iid| above.
+  message InternedString {
+    optional uint64 iid = 1;
+    optional string value = 2;
+  };
+  repeated InternedString interned_strings = 10;
 }
 
 // End of protos/perfetto/trace/perfetto/perfetto_metatrace.proto
@@ -9619,6 +10201,9 @@
 
   // Instantaneous battery current in microamperes(µA).
   optional int64 current_avg_ua = 4;
+
+  // Battery name, emitted only on multiple batteries.
+  optional string name = 5;
 }
 
 // End of protos/perfetto/trace/power/battery_counters.proto
@@ -10070,6 +10655,7 @@
     UNWIND_ERROR_BAD_ARCH = 12;
     UNWIND_ERROR_MAPS_PARSE = 13;
     UNWIND_ERROR_INVALID_PARAMETER = 14;
+    UNWIND_ERROR_PTRACE_CALL = 15;
   }
 }
 
@@ -10216,6 +10802,11 @@
     reserved 4;
   }
 
+  message FDInfo {
+    optional uint64 fd = 1;
+    optional string path = 2;
+  }
+
   message Process {
     optional int32 pid = 1;
 
@@ -10245,6 +10836,8 @@
     // https://docs.google.com/document/d/1_WmgE1F5WUrhwkPqJis3dWyOiUmQKvpXp5cd4w86TvA
     optional uint32 chrome_private_footprint_kb = 13;
     optional uint32 chrome_peak_resident_set_kb = 14;
+
+    repeated FDInfo fds = 15;
   }
   repeated Process processes = 1;
 
@@ -10443,6 +11036,21 @@
   }
   // One entry per each node's zones.
   repeated BuddyInfo buddy_info = 12;
+
+  // Counters from /proc/diskstats.
+  message DiskStat {
+    optional string device_name = 1;
+    optional uint64 read_sectors = 2;
+    optional uint64 read_time_ms = 3;
+    optional uint64 write_sectors = 4;
+    optional uint64 write_time_ms = 5;
+    optional uint64 discard_sectors = 6;
+    optional uint64 discard_time_ms = 7;
+    optional uint64 flush_count = 8;
+    optional uint64 flush_time_ms = 9;
+  }
+  // One entry per disk device.
+  repeated DiskStat disk_stat = 13;
 }
 
 // End of protos/perfetto/trace/sys_stats/sys_stats.proto
@@ -10556,6 +11164,22 @@
 }
 // End of protos/perfetto/trace/trace_packet_defaults.proto
 
+// Begin of protos/perfetto/trace/trace_uuid.proto
+
+// A random unique ID that identifies the trace.
+// This message has been introduced in v32. Prior to that, the UUID was
+// only (optionally) present in the TraceConfig.trace_uuid_msb/lsb fields.
+// This has been moved to a standalone packet to deal with new use-cases for
+// go/gapless-aot, where the same tracing session can be serialized several
+// times, in which case the UUID is changed on each snapshot and does not match
+// the one in the TraceConfig.
+message TraceUuid {
+  optional int64 msb = 1;
+  optional int64 lsb = 2;
+}
+
+// End of protos/perfetto/trace/trace_uuid.proto
+
 // Begin of protos/perfetto/trace/track_event/process_descriptor.proto
 
 // Describes a process's attributes. Emitted as part of a TrackDescriptor,
@@ -10602,6 +11226,16 @@
 
 // End of protos/perfetto/trace/track_event/process_descriptor.proto
 
+// Begin of protos/perfetto/trace/track_event/range_of_interest.proto
+
+// This message specifies the "range of interest" for track events. With the
+// `drop_track_event_data_before` option set to `kTrackEventRangeOfInterest`,
+// Trace Processor drops track events outside of this range.
+message TrackEventRangeOfInterest {
+  optional int64 start_us = 1;
+}
+// End of protos/perfetto/trace/track_event/range_of_interest.proto
+
 // Begin of protos/perfetto/trace/track_event/thread_descriptor.proto
 
 // Describes a thread's attributes. Emitted as part of a TrackDescriptor,
@@ -11050,7 +11684,7 @@
 
 // Begin of protos/perfetto/trace/trace_packet.proto
 
-// TracePacket is the root object of a Perfeto trace.
+// TracePacket is the root object of a Perfetto trace.
 // A Perfetto trace is a linear sequence of TracePacket(s).
 //
 // The tracing service guarantees that all TracePacket(s) written by a given
@@ -11070,7 +11704,7 @@
 // See the [Buffers and Dataflow](/docs/concepts/buffers.md) doc for details.
 //
 // Next reserved id: 14 (up to 15).
-// Next id: 86.
+// Next id: 91.
 message TracePacket {
   // The timestamp of the TracePacket.
   // By default this timestamps refers to the trace clock (CLOCK_BOOTTIME on
@@ -11099,6 +11733,7 @@
     // IDs up to 15 are reserved. They take only one byte to encode their
     // preamble so should be used for frequent events.
 
+    TraceUuid trace_uuid = 89;
     TraceConfig trace_config = 33;
     FtraceStats ftrace_stats = 34;
     TraceStats trace_stats = 35;
@@ -11174,6 +11809,13 @@
     // even if the extension proto is not checked in the Perfetto repo.
     ExtensionDescriptor extension_descriptor = 72;
 
+    // Represents a single packet sent or received by the network.
+    NetworkPacketEvent network_packet = 88;
+
+    // The "range of interest" for track events. See the message definition
+    // comments for more details.
+    TrackEventRangeOfInterest track_event_range_of_interest = 90;
+
     // This field is only used for testing.
     // In previous versions of this proto this field had the id 268435455
     // This caused many problems:
@@ -11262,6 +11904,21 @@
   // 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;
+
+  // Flag set by a producer (starting from SDK v29) if, for the current packet
+  // sequence (see |trusted_packet_sequence_id|), this is the first packet.
+  //
+  // This flag can be used for distinguishing the two situations when
+  // processing the trace:
+  // 1. There are no prior events for the sequence because of data loss, e.g.
+  //    due to ring buffer wrapping.
+  // 2. There are no prior events for the sequence because it didn't start
+  //    before this packet (= there's definitely no preceeding data loss).
+  //
+  // Given that older SDK versions do not support this flag, this flag not
+  // being present for a particular sequence does not necessarily imply data
+  // loss.
+  optional bool first_packet_on_sequence = 87;
 }
 
 // End of protos/perfetto/trace/trace_packet.proto
diff --git a/protos/perfetto/trace/power/battery_counters.proto b/protos/perfetto/trace/power/battery_counters.proto
index 719f9a8..ab1aaa8 100644
--- a/protos/perfetto/trace/power/battery_counters.proto
+++ b/protos/perfetto/trace/power/battery_counters.proto
@@ -32,4 +32,7 @@
 
   // Instantaneous battery current in microamperes(µA).
   optional int64 current_avg_ua = 4;
+
+  // Battery name, emitted only on multiple batteries.
+  optional string name = 5;
 }
diff --git a/protos/perfetto/trace/profiling/profile_packet.proto b/protos/perfetto/trace/profiling/profile_packet.proto
index 08c1f7c..df466f7 100644
--- a/protos/perfetto/trace/profiling/profile_packet.proto
+++ b/protos/perfetto/trace/profiling/profile_packet.proto
@@ -274,6 +274,7 @@
     UNWIND_ERROR_BAD_ARCH = 12;
     UNWIND_ERROR_MAPS_PARSE = 13;
     UNWIND_ERROR_INVALID_PARAMETER = 14;
+    UNWIND_ERROR_PTRACE_CALL = 15;
   }
 }
 
diff --git a/protos/perfetto/trace/ps/process_stats.proto b/protos/perfetto/trace/ps/process_stats.proto
index d647de2..12c8636 100644
--- a/protos/perfetto/trace/ps/process_stats.proto
+++ b/protos/perfetto/trace/ps/process_stats.proto
@@ -40,6 +40,11 @@
     reserved 4;
   }
 
+  message FDInfo {
+    optional uint64 fd = 1;
+    optional string path = 2;
+  }
+
   message Process {
     optional int32 pid = 1;
 
@@ -69,6 +74,8 @@
     // https://docs.google.com/document/d/1_WmgE1F5WUrhwkPqJis3dWyOiUmQKvpXp5cd4w86TvA
     optional uint32 chrome_private_footprint_kb = 13;
     optional uint32 chrome_peak_resident_set_kb = 14;
+
+    repeated FDInfo fds = 15;
   }
   repeated Process processes = 1;
 
diff --git a/protos/perfetto/trace/sys_stats/sys_stats.proto b/protos/perfetto/trace/sys_stats/sys_stats.proto
index 13a733a..2aad34c 100644
--- a/protos/perfetto/trace/sys_stats/sys_stats.proto
+++ b/protos/perfetto/trace/sys_stats/sys_stats.proto
@@ -116,4 +116,19 @@
   }
   // One entry per each node's zones.
   repeated BuddyInfo buddy_info = 12;
+
+  // Counters from /proc/diskstats.
+  message DiskStat {
+    optional string device_name = 1;
+    optional uint64 read_sectors = 2;
+    optional uint64 read_time_ms = 3;
+    optional uint64 write_sectors = 4;
+    optional uint64 write_time_ms = 5;
+    optional uint64 discard_sectors = 6;
+    optional uint64 discard_time_ms = 7;
+    optional uint64 flush_count = 8;
+    optional uint64 flush_time_ms = 9;
+  }
+  // One entry per disk device.
+  repeated DiskStat disk_stat = 13;
 }
diff --git a/protos/perfetto/trace/trace_packet.proto b/protos/perfetto/trace/trace_packet.proto
index 53732c8..1df67bd 100644
--- a/protos/perfetto/trace/trace_packet.proto
+++ b/protos/perfetto/trace/trace_packet.proto
@@ -27,6 +27,7 @@
 import "protos/perfetto/trace/android/gpu_mem_event.proto";
 import "protos/perfetto/trace/android/graphics_frame_event.proto";
 import "protos/perfetto/trace/android/initial_display_state.proto";
+import "protos/perfetto/trace/android/network_trace.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";
@@ -60,17 +61,19 @@
 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/range_of_interest.proto";
 import "protos/perfetto/trace/track_event/thread_descriptor.proto";
 import "protos/perfetto/trace/track_event/track_descriptor.proto";
 import "protos/perfetto/trace/track_event/track_event.proto";
 import "protos/perfetto/trace/translation/translation_table.proto";
+import "protos/perfetto/trace/trace_uuid.proto";
 import "protos/perfetto/trace/trigger.proto";
 import "protos/perfetto/trace/test_event.proto";
 import "protos/perfetto/trace/ui_state.proto";
 
 package perfetto.protos;
 
-// TracePacket is the root object of a Perfeto trace.
+// TracePacket is the root object of a Perfetto trace.
 // A Perfetto trace is a linear sequence of TracePacket(s).
 //
 // The tracing service guarantees that all TracePacket(s) written by a given
@@ -90,7 +93,7 @@
 // See the [Buffers and Dataflow](/docs/concepts/buffers.md) doc for details.
 //
 // Next reserved id: 14 (up to 15).
-// Next id: 86.
+// Next id: 91.
 message TracePacket {
   // The timestamp of the TracePacket.
   // By default this timestamps refers to the trace clock (CLOCK_BOOTTIME on
@@ -119,6 +122,7 @@
     // IDs up to 15 are reserved. They take only one byte to encode their
     // preamble so should be used for frequent events.
 
+    TraceUuid trace_uuid = 89;
     TraceConfig trace_config = 33;
     FtraceStats ftrace_stats = 34;
     TraceStats trace_stats = 35;
@@ -194,6 +198,13 @@
     // even if the extension proto is not checked in the Perfetto repo.
     ExtensionDescriptor extension_descriptor = 72;
 
+    // Represents a single packet sent or received by the network.
+    NetworkPacketEvent network_packet = 88;
+
+    // The "range of interest" for track events. See the message definition
+    // comments for more details.
+    TrackEventRangeOfInterest track_event_range_of_interest = 90;
+
     // This field is only used for testing.
     // In previous versions of this proto this field had the id 268435455
     // This caused many problems:
@@ -282,4 +293,19 @@
   // 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;
+
+  // Flag set by a producer (starting from SDK v29) if, for the current packet
+  // sequence (see |trusted_packet_sequence_id|), this is the first packet.
+  //
+  // This flag can be used for distinguishing the two situations when
+  // processing the trace:
+  // 1. There are no prior events for the sequence because of data loss, e.g.
+  //    due to ring buffer wrapping.
+  // 2. There are no prior events for the sequence because it didn't start
+  //    before this packet (= there's definitely no preceeding data loss).
+  //
+  // Given that older SDK versions do not support this flag, this flag not
+  // being present for a particular sequence does not necessarily imply data
+  // loss.
+  optional bool first_packet_on_sequence = 87;
 }
diff --git a/protos/perfetto/trace/trace_uuid.proto b/protos/perfetto/trace/trace_uuid.proto
new file mode 100644
index 0000000..1e6657c
--- /dev/null
+++ b/protos/perfetto/trace/trace_uuid.proto
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2022 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;
+
+// A random unique ID that identifies the trace.
+// This message has been introduced in v32. Prior to that, the UUID was
+// only (optionally) present in the TraceConfig.trace_uuid_msb/lsb fields.
+// This has been moved to a standalone packet to deal with new use-cases for
+// go/gapless-aot, where the same tracing session can be serialized several
+// times, in which case the UUID is changed on each snapshot and does not match
+// the one in the TraceConfig.
+message TraceUuid {
+  optional int64 msb = 1;
+  optional int64 lsb = 2;
+}
diff --git a/protos/perfetto/trace/track_event/BUILD.gn b/protos/perfetto/trace/track_event/BUILD.gn
index ab9d273..7dc1aad 100644
--- a/protos/perfetto/trace/track_event/BUILD.gn
+++ b/protos/perfetto/trace/track_event/BUILD.gn
@@ -16,6 +16,7 @@
 
 perfetto_proto_library("@TYPE@") {
   sources = [
+    "chrome_active_processes.proto",
     "chrome_application_state_info.proto",
     "chrome_compositor_scheduler_state.proto",
     "chrome_content_settings_event_info.proto",
@@ -35,6 +36,7 @@
     "debug_annotation.proto",
     "log_message.proto",
     "process_descriptor.proto",
+    "range_of_interest.proto",
     "source_location.proto",
     "task_execution.proto",
     "thread_descriptor.proto",
diff --git a/protos/perfetto/trace/track_event/chrome_active_processes.proto b/protos/perfetto/trace/track_event/chrome_active_processes.proto
new file mode 100644
index 0000000..e229d77
--- /dev/null
+++ b/protos/perfetto/trace/track_event/chrome_active_processes.proto
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 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;
+
+// A list of processes connected to the tracing service.
+message ChromeActiveProcesses {
+  repeated int32 pid = 1;
+}
\ No newline at end of file
diff --git a/protos/perfetto/trace/track_event/chrome_mojo_event_info.proto b/protos/perfetto/trace/track_event/chrome_mojo_event_info.proto
index 6c37136..4ce0849 100644
--- a/protos/perfetto/trace/track_event/chrome_mojo_event_info.proto
+++ b/protos/perfetto/trace/track_event/chrome_mojo_event_info.proto
@@ -43,4 +43,13 @@
   // trace requires storing them in the binary, which causes a significant
   // binary size bloat for Chromium.
   optional uint64 mojo_interface_method_iid = 4;
+
+  // Indicate whether this is a message or reply.
+  optional bool is_reply = 5;
+
+  // The payload size of the message being sent through mojo messages.
+  optional uint64 payload_size = 6;
+
+  // Represents the size of the message. Includes all headers and user payload.
+  optional uint64 data_num_bytes = 7;
 }
diff --git a/protos/perfetto/trace/track_event/range_of_interest.proto b/protos/perfetto/trace/track_event/range_of_interest.proto
new file mode 100644
index 0000000..2955f97
--- /dev/null
+++ b/protos/perfetto/trace/track_event/range_of_interest.proto
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 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;
+
+// This message specifies the "range of interest" for track events. With the
+// `drop_track_event_data_before` option set to `kTrackEventRangeOfInterest`,
+// Trace Processor drops track events outside of this range.
+message TrackEventRangeOfInterest {
+  optional int64 start_us = 1;
+}
\ No newline at end of file
diff --git a/protos/perfetto/trace/track_event/track_event.proto b/protos/perfetto/trace/track_event/track_event.proto
index 66ebb4a..e57e014 100644
--- a/protos/perfetto/trace/track_event/track_event.proto
+++ b/protos/perfetto/trace/track_event/track_event.proto
@@ -19,6 +19,7 @@
 import "protos/perfetto/trace/track_event/debug_annotation.proto";
 import "protos/perfetto/trace/track_event/log_message.proto";
 import "protos/perfetto/trace/track_event/task_execution.proto";
+import "protos/perfetto/trace/track_event/chrome_active_processes.proto";
 import "protos/perfetto/trace/track_event/chrome_application_state_info.proto";
 import "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.proto";
 import "protos/perfetto/trace/track_event/chrome_content_settings_event_info.proto";
@@ -101,7 +102,7 @@
 // their default track association) can be emitted as part of a
 // TrackEventDefaults message.
 //
-// Next reserved id: 13 (up to 15). Next id: 49.
+// Next reserved id: 13 (up to 15). Next id: 50.
 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.
@@ -253,6 +254,7 @@
   optional ChromeWindowHandleEventInfo chrome_window_handle_event_info = 41;
   optional ChromeContentSettingsEventInfo chrome_content_settings_event_info =
       43;
+  optional ChromeActiveProcesses chrome_active_processes = 49;
 
   // This field is used only if the source location represents the function that
   // executes during this event.
diff --git a/protos/perfetto/trace_processor/metatrace_categories.proto b/protos/perfetto/trace_processor/metatrace_categories.proto
new file mode 100644
index 0000000..7708962
--- /dev/null
+++ b/protos/perfetto/trace_processor/metatrace_categories.proto
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2022 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;
+
+// Bitmask of metatrace categories which can be enabled.
+// Keep in sync with TraceProcessor::MetatraceCategories.
+enum MetatraceCategories {
+  // 1 << 0.
+  TOPLEVEL = 1;
+  // 1 << 1.
+  QUERY = 2;
+  // 1 << 2.
+  FUNCTION = 4;
+
+  // Aliases for common subsets.
+  NONE = 0;
+  // TOPLEVEL | QUERY | FUNCTION
+  ALL = 7;
+}
diff --git a/protos/perfetto/trace_processor/proto_files.gni b/protos/perfetto/trace_processor/proto_files.gni
index 246b803..b46da47 100644
--- a/protos/perfetto/trace_processor/proto_files.gni
+++ b/protos/perfetto/trace_processor/proto_files.gni
@@ -14,4 +14,7 @@
 
 # This variable is used both by ./BUILD.gn (for the C++ proto codegen) and by
 # //ui/BUIlD.gn (for the TypeScript/JS proto codegen).
-trace_processor_protos = [ "trace_processor" ]
+trace_processor_protos = [
+  "trace_processor",
+  "metatrace_categories",
+]
diff --git a/protos/perfetto/trace_processor/trace_processor.proto b/protos/perfetto/trace_processor/trace_processor.proto
index 4af7161..667cab0 100644
--- a/protos/perfetto/trace_processor/trace_processor.proto
+++ b/protos/perfetto/trace_processor/trace_processor.proto
@@ -19,6 +19,7 @@
 package perfetto.protos;
 
 import "protos/perfetto/common/descriptor.proto";
+import "protos/perfetto/trace_processor/metatrace_categories.proto";
 
 // This file defines the schema for {,un}marshalling arguments and return values
 // when interfacing to the trace processor binary interface.
@@ -41,7 +42,7 @@
   // every time a new feature that the UI depends on is being introduced (e.g.
   // new tables, new SQL operators, metrics that are required by the UI).
   // See also StatusResult.api_version (below).
-  TRACE_PROCESSOR_CURRENT_API_VERSION = 5;
+  TRACE_PROCESSOR_CURRENT_API_VERSION = 6;
 }
 
 // At lowest level, the wire-format of the RPC procol is a linear sequence of
@@ -82,6 +83,7 @@
     TPM_ENABLE_METATRACE = 8;
     TPM_DISABLE_AND_READ_METATRACE = 9;
     TPM_GET_STATUS = 10;
+    TPM_RESET_TRACE_PROCESSOR = 11;
   }
 
   oneof type {
@@ -109,6 +111,10 @@
     QueryArgs query_args = 103;
     // For TPM_COMPUTE_METRIC.
     ComputeMetricArgs compute_metric_args = 105;
+    // For TPM_ENABLE_METATRACE.
+    EnableMetatraceArgs enable_metatrace_args = 106;
+    // For TPM_RESET_TRACE_PROCESSOR.
+    ResetTraceProcessorArgs reset_trace_processor_args = 107;
 
     // TraceProcessorMethod response args.
     // For TPM_APPEND_TRACE_DATA.
@@ -138,9 +144,10 @@
 
 message QueryArgs {
   optional string sql_query = 1;
-
   // Was time_queued_ns
   reserved 2;
+  // Optional string to tag this query with for performance diagnostic purposes.
+  optional string tag = 3;
 }
 
 // Output for the /query endpoint.
@@ -250,7 +257,9 @@
 }
 
 // Input for the /enable_metatrace endpoint.
-message EnableMetatraceArgs {}
+message EnableMetatraceArgs {
+  optional MetatraceCategories categories = 1;
+}
 
 // Output for the /enable_metatrace endpoint.
 message EnableMetatraceResult {}
@@ -271,3 +280,17 @@
 message DescriptorSet {
   repeated DescriptorProto descriptors = 1;
 }
+
+// Input for setting Trace Processor config. This method works only in the WASM
+// mode. trace_processor_shell supports configuration through command line
+// flags.
+message ResetTraceProcessorArgs {
+  enum DropTrackEventDataBefore {
+    NO_DROP = 0;
+    TRACK_EVENT_RANGE_OF_INTEREST = 1;
+  }
+  // Mirror of the corresponding perfetto::trace_processor::Config fields.
+  optional DropTrackEventDataBefore drop_track_event_data_before = 1;
+  optional bool ingest_ftrace_in_raw_table = 2;
+  optional bool analyze_trace_proto_content = 3;
+}
diff --git a/protos/third_party/chromium/chrome_track_event.proto b/protos/third_party/chromium/chrome_track_event.proto
index 58007f4..fb35740 100644
--- a/protos/third_party/chromium/chrome_track_event.proto
+++ b/protos/third_party/chromium/chrome_track_event.proto
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -27,6 +27,22 @@
   optional uint64 creation_location_iid = 2;
 }
 
+message BlinkTaskScope {
+  enum TaskScopeType {
+    TASK_SCOPE_UNKNOWN = 0;
+    TASK_SCOPE_CALLBACK = 1;
+    TASK_SCOPE_SCHEDULED_ACTION = 2;
+    TASK_SCOPE_SCRIPT_EXECUTION = 3;
+    TASK_SCOPE_POST_MESSAGE = 4;
+    TASK_SCOPE_POP_STATE = 5;
+  }
+  optional TaskScopeType type = 1;
+  optional int64 scope_task_id = 2;
+  optional int64 running_task_id_to_be_restored = 3;
+  optional int64 continuation_task_id_to_be_restored = 4;
+  optional int64 parent_task_id = 5;
+}
+
 message ChromeTaskAnnotator {
   optional uint32 ipc_hash = 1;
   // The delay in microseconds that was specified, if any, when this task was
@@ -129,6 +145,7 @@
   SHOULD_SWAP_BROWSING_INSTANCE_NO_RELOAD = 18;
   SHOULD_SWAP_BROWSING_INSTANCE_NO_GUEST = 19;
   SHOULD_SWAP_BROWSING_INSTANCE_NO_HAS_NOT_COMMITTED_ANY_NAVIGATION = 20;
+  // The following reason was deprecated from https://crrev.com/c/3858766
   SHOULD_SWAP_BROWSING_INSTANCE_NO_UNLOAD_HANDLER_EXISTS_ON_SAME_SITE_NAVIGATION =
       21;
   SHOULD_SWAP_BROWSING_INSTANCE_NO_NOT_PRIMARY_MAIN_FRAME = 22;
@@ -155,6 +172,18 @@
   optional RenderFrameHost current_frame_host = 4;
   optional RenderFrameHost speculative_frame_host = 5;
 
+  // NOTE: this proto must be kept consistent with
+  // content::FrameType.
+  enum FrameType {
+    UNSPECIFIED_FRAME_TYPE = 0;
+    SUBFRAME = 1;
+    PRIMARY_MAIN_FRAME = 2;
+    PRERENDER_MAIN_FRAME = 3;
+    FENCED_FRAME_ROOT = 4;
+  }
+
+  optional FrameType frame_type = 6;
+
   // Additional untyped debug information associated with this
   // FrameTreeNode, populated via TracedProto::AddDebugAnnotations API.
   repeated DebugAnnotation debug_annotations = 99;
@@ -202,6 +231,7 @@
 
   optional bool has_valid_header = 8;
   optional bool has_valid_schema = 9;
+  optional string error_message = 10;
 }
 
 message ChromeWebAppBadNavigate {
@@ -508,6 +538,7 @@
   optional RenderFrameHost outer_document = 9;
   optional RenderFrameHost embedder = 10;
   optional BrowsingContextState browsing_context_state = 11;
+  optional FrameTreeNodeInfo.FrameType frame_type = 12;
 
   // Additional untyped debug information associated with this
   // RenderViewHost, populated via TracedProto::AddDebugAnnotations API.
@@ -696,6 +727,7 @@
     TASK_TYPE_WEB_GPU = 78;
     TASK_TYPE_INTERNAL_POST_MESSAGE_FORWARDING = 79;
     TASK_TYPE_INTERNAL_NAVIGATION_CANCELLATION = 80;
+    TASK_TYPE_LOW_PRIORITY_SCRIPT_EXECUTION = 81;
   }
 
   enum FrameType {
@@ -810,9 +842,236 @@
   optional int64 dur_ms = 2;
 }
 
+message SequenceManagerTask {
+  enum Priority {
+    UNKNOWN = 0;
+    CONTROL_PRIORITY = 1;
+    HIGHEST_PRIORITY = 2;
+    VERY_HIGH_PRIORITY = 3;
+    HIGH_PRIORITY = 4;
+    NORMAL_PRIORITY = 5;
+    LOW_PRIORITY = 6;
+    BEST_EFFORT_PRIORITY = 7;
+  }
+
+  enum QueueName {
+    UNKNOWN_TQ = 0;
+    DEFAULT_TQ = 1;
+    TASK_ENVIRONMENT_DEFAULT_TQ = 2;
+    TEST2_TQ = 3;
+    TEST_TQ = 4;
+    CONTROL_TQ = 5;
+
+    SUBTHREAD_CONTROL_TQ = 6;
+    SUBTHREAD_DEFAULT_TQ = 7;
+    SUBTHREAD_INPUT_TQ = 8;
+
+    UI_BEST_EFFORT_TQ = 9;
+    UI_BOOTSTRAP_TQ = 10;
+    UI_CONTROL_TQ = 11;
+    UI_DEFAULT_TQ = 12;
+    UI_NAVIGATION_NETWORK_RESPONSE_TQ = 13;
+    UI_RUN_ALL_PENDING_TQ = 14;
+    UI_SERVICE_WORKER_STORAGE_CONTROL_RESPONSE_TQ = 15;
+    UI_THREAD_TQ = 16;
+    UI_USER_BLOCKING_TQ = 17;
+    UI_USER_INPUT_TQ = 18;
+    UI_USER_VISIBLE_TQ = 19;
+
+    IO_BEST_EFFORT_TQ = 20;
+    IO_BOOTSTRAP_TQ = 21;
+    IO_CONTROL_TQ = 22;
+    IO_DEFAULT_TQ = 23;
+    IO_NAVIGATION_NETWORK_RESPONSE_TQ = 24;
+    IO_RUN_ALL_PENDING_TQ = 25;
+    IO_SERVICE_WORKER_STORAGE_CONTROL_RESPONSE_TQ = 26;
+    IO_THREAD_TQ = 27;
+    IO_USER_BLOCKING_TQ = 28;
+    IO_USER_INPUT_TQ = 29;
+    IO_USER_VISIBLE_TQ = 30;
+
+    COMPOSITOR_TQ = 31;
+    DETACHED_TQ = 32;
+    FRAME_DEFERRABLE_TQ = 33;
+    FRAME_LOADING_CONTROL_TQ = 34;
+    FRAME_LOADING_TQ = 35;
+    FRAME_PAUSABLE_TQ = 36;
+    FRAME_THROTTLEABLE_TQ = 37;
+    FRAME_UNPAUSABLE_TQ = 38;
+    IDLE_TQ = 39;
+    INPUT_TQ = 40;
+    IPC_TRACKING_FOR_CACHED_PAGES_TQ = 41;
+    NON_WAKING_TQ = 42;
+    OTHER_TQ = 43;
+    V8_TQ = 44;
+    WEB_SCHEDULING_TQ = 45;
+
+    WORKER_IDLE_TQ = 46;
+    WORKER_PAUSABLE_TQ = 47;
+    WORKER_THREAD_INTERNAL_TQ = 48;
+    WORKER_THROTTLEABLE_TQ = 49;
+    WORKER_UNPAUSABLE_TQ = 50;
+    WORKER_WEB_SCHEDULING_TQ = 51;
+
+    UI_USER_BLOCKING_DEFERRABLE_TQ = 52;
+    IO_USER_BLOCKING_DEFERRABLE_TQ = 53;
+  }
+
+  optional Priority priority = 1;
+  optional QueueName queue_name = 2;
+}
+
+message AndroidToolbar {
+  enum BlockCaptureReason {
+    BLOCKED_UNKNOWN = 0;
+    BLOCKED_TOOLBAR_OR_RESULT_NULL = 1;
+    BLOCKED_VIEW_NOT_DIRTY = 2;
+    BLOCKED_SNAPSHOT_SAME = 3;
+    BLOCKED_URL_BAR_HAS_FOCUS = 4;
+    BLOCKED_URL_BAR_FOCUS_IN_PROGRESS = 5;
+    BLOCKED_OPTIONAL_BUTTON_ANIMATION_IN_PROGRESS = 6;
+    BLOCKED_STATUS_ICON_ANIMATION_IN_PROGRESS = 7;
+    BLOCKED_SCROLL_ABLATION = 8;
+    BLOCKED_BROWSER_CONTROLS_LOCKED = 9;
+    BLOCKED_TAB_SWITCHER_MODE = 10;
+    BLOCKED_COMPOSITOR_IN_MOTION = 11;
+    // TODO(https://crbug.com/1324678): NATIVE_PAGE.
+  }
+  enum AllowCaptureReason {
+    ALLOWED_UNKNOWN = 0;
+    ALLOWED_FORCE_CAPTURE = 1;
+    ALLOWED_SNAPSHOT_DIFFERENCE = 2;
+  }
+  enum SnapshotDifference {
+    DIFF_NONE = 0;
+    DIFF_NULL = 1;
+    DIFF_TINT = 2;
+    DIFF_TAB_COUNT = 3;
+    DIFF_OPTIONAL_BUTTON_DATA = 4;
+    DIFF_VISUAL_STATE = 5;
+    DIFF_SECURITY_ICON = 6;
+    DIFF_SHOWING_UPDATE_BADGE = 7;
+    DIFF_PAINT_PREVIEW = 8;
+    DIFF_PROGRESS = 9;
+    DIFF_LOCATION_BAR_WIDTH = 10;
+    DIFF_URL_TEXT = 11;
+    DIFF_HOME_BUTTON_COLOR = 12;
+    DIFF_TITLE_TEXT = 13;
+    DIFF_CCT_ANIMATION = 14;
+  }
+
+  optional BlockCaptureReason block_capture_reason = 1;
+  optional AllowCaptureReason allow_capture_reason = 2;
+  optional SnapshotDifference snapshot_difference = 3;
+}
+
+message ActiveProcesses {
+  repeated int32 pid = 1;
+}
+
+message UkmPageLoadTimingUpdate {
+  // This can be used to uniquely identify a navigation from the point of view
+  // of UKM.
+  optional int64 ukm_source_id = 1;
+
+  // The URL of a page can change throughout its lifetime. This is the current
+  // url when this timing update was dispatched.
+  optional string latest_url = 2;
+
+  // Latest fully aggregated value of Cumulative Layout Shift.
+  optional float latest_cumulative_layout_shift = 3;
+
+  // Latest fully aggregated value of Largest Contentful Paint.
+  optional double latest_largest_contentful_paint_ms = 4;
+
+  optional double first_contentful_paint_ms = 5;
+}
+
+// A serialisation of v8StackFrame class.
+message V8StackFrame {
+  // Code location (path to the script and line/column number)
+  message ScriptLocation {
+    optional string source_url = 1;
+    optional int64 line_number = 2;
+    optional int64 column_number = 3;
+  }
+
+  // The name of the function that was called
+  optional string function_name = 1;
+
+  // If the function was defined in a script, contains the location within the
+  // script.
+  optional ScriptLocation script_location = 2;
+}
+
+// Serializes the blink::ExecutionContext object.
+message BlinkExecutionContext {
+  // Definition of different context types.
+  enum ContextType {
+    UNKNOWN_CONTEXT = 0;
+    WINDOW = 1;
+    WORKLET = 2;
+    DEDICATED_WORKER = 3;
+    SHARED_WORKER = 4;
+    SERVICE_WORKER = 5;
+  }
+
+  optional ContextType type = 1;
+  // Contains url of frame or worker.
+  optional string url = 2;
+  // The origin of the execution context.
+  optional string origin = 3;
+}
+
+// Serializes the blink::SourceLocation object.
+message BlinkSourceLocation {
+  optional string function_name = 1;
+  optional int32 script_id = 2;
+  optional string url = 3;
+  optional int32 line_number = 4;
+  optional int32 column_number = 5;
+  optional string stack_trace = 6;
+  repeated V8StackFrame stack_frames = 7;
+}
+// Contains the meta information for high entropy events (like api calls)
+// that are to be traced for debugging in the context of identifiability study.
+message BlinkHighEntropyAPI {
+  // Serialization of a parameter passed to a javascript function.
+  // Contains the stringified type of the object and some string representation
+  // of its value.
+  message JSFunctionArgument {
+    // Definition of different types of function parameters.
+    enum ArgumentType {
+      UNKNOWN_TYPE = 0;
+      NULL_TYPE = 1;
+      UNDEFINED = 2;
+      BIGINT = 3;
+      BOOLEAN = 4;
+      FUNCTION = 5;
+      NUMBER = 6;
+      STRING = 7;
+      SYMBOL = 8;
+      OBJECT = 9;
+    }
+    optional ArgumentType type = 1;
+    optional string value = 2;
+  }
+
+  // Describes a Javascript API call.
+  message CalledJsApi {
+    // Contains class and function name of api called
+    // similar to "Navigator.languages.get".
+    optional string identifier = 1;
+    repeated JSFunctionArgument func_arguments = 2;
+    optional BlinkSourceLocation source_location = 3;
+  }
+  optional BlinkExecutionContext execution_context = 1;
+  optional CalledJsApi called_api = 2;
+}
+
 message ChromeTrackEvent {
   // Extension range for Chrome: 1000-1999
-  // Next ID: 1040
+  // Next ID: 1046
   extend TrackEvent {
     optional ChromeAppState chrome_app_state = 1000;
 
@@ -898,5 +1157,17 @@
     optional AndroidIPC android_ipc = 1038;
 
     optional ChromeSqlDiagnostics sql_diagnostics = 1039;
+
+    optional SequenceManagerTask sequence_manager_task = 1040;
+
+    optional AndroidToolbar android_toolbar = 1041;
+
+    optional ActiveProcesses active_processes = 1042;
+
+    optional BlinkTaskScope blink_task_scope = 1043;
+
+    optional UkmPageLoadTimingUpdate ukm_page_load_timing_update = 1044;
+
+    optional BlinkHighEntropyAPI high_entropy_api = 1045;
   }
 }
diff --git a/protos/third_party/chromium/sources.gni b/protos/third_party/chromium/sources.gni
index cef4a8e..115ec4b 100644
--- a/protos/third_party/chromium/sources.gni
+++ b/protos/third_party/chromium/sources.gni
@@ -1,4 +1,4 @@
-# Copyright (c) 2020 The Chromium Authors. All rights reserved.
+# Copyright 2020 The Chromium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
diff --git a/python/BUILD b/python/BUILD
index 6f98301..4700d19 100644
--- a/python/BUILD
+++ b/python/BUILD
@@ -11,6 +11,8 @@
 # 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 is automatically generated by tools/gen_bazel. Do not edit.
 
 load("@perfetto_cfg//:perfetto_cfg.bzl", "PERFETTO_CONFIG")
 load(
@@ -21,87 +23,109 @@
 
 licenses(["notice"])
 
-package(default_visibility = ["//visibility:private"])
+package(default_visibility = [PERFETTO_CONFIG.root + ":__subpackages__"])
 
-perfetto_py_binary(
-    name = "trace_processor_py_example",
-    srcs = ["example.py"],
-    deps = [":trace_processor_py"] + PERFETTO_CONFIG.deps.pandas_py,
-    main = "example.py",
-    python_version = "PY3",
-)
-
+# GN target: //python:batch_trace_processor
 perfetto_py_library(
-    name = "trace_processor_py",
-    srcs = glob(["perfetto/trace_processor/*.py"]),
-    data = [
-        ":trace_uri_resolver",
-        "perfetto/trace_processor/trace_processor.descriptor",
-        "perfetto/trace_processor/metrics.descriptor",
-        PERFETTO_CONFIG.root + ":trace_processor_shell",
+    name = "batch_trace_processor",
+    srcs = [
+        "perfetto/batch_trace_processor/__init__.py",
+        "perfetto/batch_trace_processor/api.py",
+        "perfetto/batch_trace_processor/platform.py",
     ],
-    deps = PERFETTO_CONFIG.deps.tp_vendor_py +
-        PERFETTO_CONFIG.deps.protobuf_py +
-        PERFETTO_CONFIG.deps.pandas_py,
-    imports = [
-        ".",
+    visibility = [
+        "//visibility:public",
     ],
-    visibility = PERFETTO_CONFIG.public_visibility,
-)
-
-perfetto_py_library(
-    name = "trace_uri_resolver",
-    srcs = glob(["perfetto/trace_uri_resolver/*.py"]),
-    imports = [
-        ".",
-    ],
-)
-
-perfetto_py_library(
-    name = "experimental_slice_breakdown_lib",
-    srcs = glob(["perfetto/experimental/slice_breakdown/*.py"]),
     deps = [
-        ":trace_processor_py",
-    ],
-    imports = [
-        "tools/slice_breakdown",
-    ],
+               ":trace_processor_py",
+           ] + PERFETTO_CONFIG.deps.pandas_py +
+           PERFETTO_CONFIG.deps.tp_vendor_py,
 )
 
+# GN target: //python:experimental_slice_breakdown_bin
 perfetto_py_binary(
     name = "experimental_slice_breakdown_bin",
-    srcs = ["tools/slice_breakdown.py"],
-    main = "tools/slice_breakdown.py",
+    srcs = [
+        "tools/slice_breakdown.py",
+    ],
     deps = [
         ":experimental_slice_breakdown_lib",
         ":trace_processor_py",
     ] + PERFETTO_CONFIG.deps.pandas_py,
+    main = "tools/slice_breakdown.py",
     python_version = "PY3",
-    legacy_create_init = 0,
 )
 
+# GN target: //python:trace_processor_table_generator
 perfetto_py_library(
-    name = "batch_trace_processor",
-    srcs = glob([
-      "perfetto/batch_trace_processor/*.py"
-    ]),
-    deps = [
-        ":trace_processor_py",
-    ] + PERFETTO_CONFIG.deps.pandas_py +
-        PERFETTO_CONFIG.deps.tp_vendor_py,
-    imports = [
-        ".",
+    name = "trace_processor_table_generator",
+    srcs = [
+        "generators/trace_processor_table/public.py",
+        "generators/trace_processor_table/serialize.py",
+        "generators/trace_processor_table/util.py",
     ],
 )
 
+# GN target: //python:trace_processor_py_example
 perfetto_py_binary(
-    name = "batch_trace_processor_shell",
-    srcs = ["tools/batch_trace_processor_shell.py"],
-    main = "tools/batch_trace_processor_shell.py",
+    name = "trace_processor_py_example",
+    srcs = [
+        "example.py",
+    ],
     deps = [
         ":trace_processor_py",
-        ":batch_trace_processor",
     ] + PERFETTO_CONFIG.deps.pandas_py,
+    main = "example.py",
     python_version = "PY3",
-    legacy_create_init = 0,
 )
+
+# GN target: //python:experimental_slice_breakdown_lib
+perfetto_py_library(
+    name = "experimental_slice_breakdown_lib",
+    srcs = [
+        "perfetto/experimental/slice_breakdown/__init__.py",
+        "perfetto/experimental/slice_breakdown/breakdown.py",
+    ],
+    deps = [
+        ":trace_processor_py",
+    ],
+)
+
+# GN target: //python:trace_processor_py
+perfetto_py_library(
+    name = "trace_processor_py",
+    srcs = [
+        "perfetto/trace_processor/__init__.py",
+        "perfetto/trace_processor/api.py",
+        "perfetto/trace_processor/http.py",
+        "perfetto/trace_processor/platform.py",
+        "perfetto/trace_processor/protos.py",
+        "perfetto/trace_processor/shell.py",
+    ],
+    visibility = [
+        "//visibility:public",
+    ],
+    data = [
+        PERFETTO_CONFIG.root + ":trace_processor_shell",
+        "perfetto/trace_processor/metrics.descriptor",
+        "perfetto/trace_processor/trace_processor.descriptor",
+    ],
+    deps = [
+               ":trace_uri_resolver",
+           ] + PERFETTO_CONFIG.deps.pandas_py +
+           PERFETTO_CONFIG.deps.protobuf_py +
+           PERFETTO_CONFIG.deps.tp_vendor_py,
+)
+
+# GN target: //python:trace_uri_resolver
+perfetto_py_library(
+    name = "trace_uri_resolver",
+    srcs = [
+        "perfetto/trace_uri_resolver/__init__.py",
+        "perfetto/trace_uri_resolver/path.py",
+        "perfetto/trace_uri_resolver/registry.py",
+        "perfetto/trace_uri_resolver/resolver.py",
+        "perfetto/trace_uri_resolver/util.py",
+    ],
+)
+
diff --git a/python/BUILD.gn b/python/BUILD.gn
new file mode 100644
index 0000000..06ebd9c
--- /dev/null
+++ b/python/BUILD.gn
@@ -0,0 +1,112 @@
+# Copyright (C) 2022 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_python.gni")
+
+perfetto_py_library("trace_processor_table_generator") {
+  sources = [
+    "generators/trace_processor_table/public.py",
+    "generators/trace_processor_table/serialize.py",
+    "generators/trace_processor_table/util.py",
+  ]
+}
+
+perfetto_py_library("trace_processor_stdlib_docs") {
+  sources = [
+    "generators/stdlib_docs/parse.py",
+    "generators/stdlib_docs/stdlib.py",
+    "generators/stdlib_docs/utils.py",
+    "generators/stdlib_docs/validate.py",
+  ]
+}
+
+perfetto_py_library("trace_processor_diff_tests") {
+  sources = [
+    "generators/diff_tests/runner.py",
+    "generators/diff_tests/testing.py",
+    "generators/diff_tests/utils.py",
+  ]
+}
+
+perfetto_py_binary("trace_processor_py_example") {
+  sources = [ "example.py" ]
+  deps = [
+    ":trace_processor_py",
+    "../gn:pandas_py",
+  ]
+  main = "example.py"
+}
+
+perfetto_py_library("trace_processor_py") {
+  sources = [
+    "perfetto/trace_processor/__init__.py",
+    "perfetto/trace_processor/api.py",
+    "perfetto/trace_processor/http.py",
+    "perfetto/trace_processor/platform.py",
+    "perfetto/trace_processor/protos.py",
+    "perfetto/trace_processor/shell.py",
+  ]
+  data = [
+    "perfetto/trace_processor/trace_processor.descriptor",
+    "perfetto/trace_processor/metrics.descriptor",
+    "..:trace_processor_shell",
+  ]
+  deps = [
+    ":trace_uri_resolver",
+    "../gn:pandas_py",
+    "../gn:protobuf_py",
+    "../gn:tp_vendor_py",
+  ]
+}
+
+perfetto_py_library("trace_uri_resolver") {
+  sources = [
+    "perfetto/trace_uri_resolver/__init__.py",
+    "perfetto/trace_uri_resolver/path.py",
+    "perfetto/trace_uri_resolver/registry.py",
+    "perfetto/trace_uri_resolver/resolver.py",
+    "perfetto/trace_uri_resolver/util.py",
+  ]
+}
+
+perfetto_py_library("experimental_slice_breakdown_lib") {
+  sources = [
+    "perfetto/experimental/slice_breakdown/__init__.py",
+    "perfetto/experimental/slice_breakdown/breakdown.py",
+  ]
+  deps = [ ":trace_processor_py" ]
+}
+
+perfetto_py_binary("experimental_slice_breakdown_bin") {
+  sources = [ "tools/slice_breakdown.py" ]
+  main = "tools/slice_breakdown.py"
+  deps = [
+    ":experimental_slice_breakdown_lib",
+    ":trace_processor_py",
+    "../gn:pandas_py",
+  ]
+}
+
+perfetto_py_library("batch_trace_processor") {
+  sources = [
+    "perfetto/batch_trace_processor/__init__.py",
+    "perfetto/batch_trace_processor/api.py",
+    "perfetto/batch_trace_processor/platform.py",
+  ]
+  deps = [
+    ":trace_processor_py",
+    "../gn:pandas_py",
+    "../gn:tp_vendor_py",
+  ]
+}
diff --git a/python/generators/diff_tests/runner.py b/python/generators/diff_tests/runner.py
new file mode 100644
index 0000000..dab06d1
--- /dev/null
+++ b/python/generators/diff_tests/runner.py
@@ -0,0 +1,421 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 concurrent.futures
+import datetime
+import difflib
+import os
+import subprocess
+import sys
+import tempfile
+from dataclasses import dataclass
+from typing import Dict, List, Tuple
+
+from google.protobuf import text_format
+from python.generators.diff_tests.testing import TestCase, TestType
+from python.generators.diff_tests.utils import (
+    ColorFormatter, create_message_factory, get_env, get_trace_descriptor_path,
+    read_all_tests, serialize_python_trace, serialize_textproto_trace)
+
+ROOT_DIR = os.path.dirname(
+    os.path.dirname(
+        os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
+
+
+# Performance result of running the test.
+@dataclass
+class PerfResult:
+  test: TestCase
+  ingest_time_ns: int
+  real_time_ns: int
+
+  def __init__(self, test: TestCase, perf_lines: List[str]):
+    self.test = test
+
+    assert len(perf_lines) == 1
+    perf_numbers = perf_lines[0].split(',')
+
+    assert len(perf_numbers) == 2
+    self.ingest_time_ns = int(perf_numbers[0])
+    self.real_time_ns = int(perf_numbers[1])
+
+
+# Data gathered from running the test.
+@dataclass
+class TestResult:
+  test: TestCase
+  trace: str
+  cmd: List[str]
+  expected: str
+  actual: str
+  passed: bool
+  stderr: str
+  exit_code: int
+  perf_result: PerfResult
+
+  def __init__(self, test: TestCase, gen_trace_path: str, cmd: List[str],
+               expected_text: str, actual_text: str, stderr: str,
+               exit_code: int, perf_lines: List[str]) -> None:
+    self.test = test
+    self.trace = gen_trace_path
+    self.cmd = cmd
+    self.stderr = stderr
+    self.exit_code = exit_code
+
+    # For better string formatting we often add whitespaces, which has to now
+    # be removed.
+    def strip_whitespaces(text: str):
+      no_front_new_line_text = text.lstrip('\n')
+      return '\n'.join(s.strip() for s in no_front_new_line_text.split('\n'))
+
+    self.expected = strip_whitespaces(expected_text)
+    self.actual = strip_whitespaces(actual_text)
+
+    expected_content = self.expected.replace('\r\n', '\n')
+
+    actual_content = self.actual.replace('\r\n', '\n')
+    self.passed = (expected_content == actual_content)
+
+    self.perf_result = PerfResult(self.test, perf_lines)
+
+  def write_diff(self):
+    expected_lines = self.expected.splitlines(True)
+    actual_lines = self.actual.splitlines(True)
+    diff = difflib.unified_diff(
+        expected_lines, actual_lines, fromfile='expected', tofile='actual')
+    return "".join(list(diff))
+
+  def rebase(self, rebase) -> str:
+    if not rebase or self.passed:
+      return ""
+    if not self.test.blueprint.is_out_file():
+      return f"Can't rebase expected results passed as strings.\n"
+    if self.exit_code != 0:
+      return f"Rebase failed for {self.test.name} as query failed\n"
+
+    with open(self.test.expected_path, 'w') as f:
+      f.write(self.actual)
+    return f"Rebasing {self.test.name}\n"
+
+
+# Results of running the test suite. Mostly used for printing aggregated
+# results.
+@dataclass
+class TestResults:
+  test_failures: List[str]
+  perf_data: List[PerfResult]
+  rebased: List[str]
+  test_time_ms: int
+
+  def str(self, no_colors: bool, tests_no: int):
+    c = ColorFormatter(no_colors)
+    res = (
+        f"[==========] {tests_no} tests ran. ({self.test_time_ms} ms total)\n"
+        f"{c.green('[  PASSED  ]')} "
+        f"{tests_no - len(self.test_failures)} tests.\n")
+    if len(self.test_failures) > 0:
+      res += (f"{c.red('[  FAILED  ]')} " f"{len(self.test_failures)} tests.\n")
+      for failure in self.test_failures:
+        res += f"{c.red('[  FAILED  ]')} {failure}\n"
+    return res
+
+  def rebase_str(self):
+    res = f"\n[  REBASED  ] {len(self.rebased)} tests.\n"
+    for name in self.rebased:
+      res += f"[  REBASED  ] {name}\n"
+    return res
+
+
+# Responsible for executing singular diff test.
+@dataclass
+class TestCaseRunner:
+  test: TestCase
+  trace_processor_path: str
+  trace_descriptor_path: str
+  colors: ColorFormatter
+
+  def __run_metrics_test(self, trace_path: str,
+                         metrics_message_factory) -> TestResult:
+
+    if self.test.blueprint.is_out_file():
+      with open(self.test.expected_path, 'r') as expected_file:
+        expected = expected_file.read()
+    else:
+      expected = self.test.blueprint.out.contents
+
+    tmp_perf_file = tempfile.NamedTemporaryFile(delete=False)
+    is_json_output_file = self.test.blueprint.is_out_file(
+    ) and os.path.basename(self.test.expected_path).endswith('.json.out')
+    is_json_output = is_json_output_file or self.test.blueprint.is_out_json()
+    cmd = [
+        self.trace_processor_path,
+        '--analyze-trace-proto-content',
+        '--crop-track-events',
+        '--run-metrics',
+        self.test.blueprint.query.name,
+        '--metrics-output=%s' % ('json' if is_json_output else 'binary'),
+        '--perf-file',
+        tmp_perf_file.name,
+        trace_path,
+    ]
+    tp = subprocess.Popen(
+        cmd,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        env=get_env(ROOT_DIR))
+    (stdout, stderr) = tp.communicate()
+
+    if is_json_output:
+      expected_text = expected
+      actual_text = stdout.decode('utf8')
+    else:
+      # Expected will be in text proto format and we'll need to parse it to
+      # a real proto.
+      expected_message = metrics_message_factory()
+      text_format.Merge(expected, expected_message)
+
+      # Actual will be the raw bytes of the proto and we'll need to parse it
+      # into a message.
+      actual_message = metrics_message_factory()
+      actual_message.ParseFromString(stdout)
+
+      # Convert both back to text format.
+      expected_text = text_format.MessageToString(expected_message)
+      actual_text = text_format.MessageToString(actual_message)
+
+    perf_lines = [line.decode('utf8') for line in tmp_perf_file.readlines()]
+    tmp_perf_file.close()
+    os.remove(tmp_perf_file.name)
+    return TestResult(self.test, trace_path, cmd, expected_text, actual_text,
+                      stderr.decode('utf8'), tp.returncode, perf_lines)
+
+  # Run a query based Diff Test.
+  def __run_query_test(self, trace_path: str) -> TestResult:
+    # Fetch expected text.
+    if self.test.expected_path:
+      with open(self.test.expected_path, 'r') as expected_file:
+        expected = expected_file.read()
+    else:
+      expected = self.test.blueprint.out.contents
+
+    # Fetch query.
+    if self.test.blueprint.is_query_file():
+      query = self.test.query_path
+    else:
+      tmp_query_file = tempfile.NamedTemporaryFile(delete=False)
+      with open(tmp_query_file.name, 'w') as query_file:
+        query_file.write(self.test.blueprint.query)
+      query = tmp_query_file.name
+
+    tmp_perf_file = tempfile.NamedTemporaryFile(delete=False)
+    cmd = [
+        self.trace_processor_path,
+        '--analyze-trace-proto-content',
+        '--crop-track-events',
+        '-q',
+        query,
+        '--perf-file',
+        tmp_perf_file.name,
+        trace_path,
+    ]
+    tp = subprocess.Popen(
+        cmd,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        env=get_env(ROOT_DIR))
+    (stdout, stderr) = tp.communicate()
+
+    if not self.test.blueprint.is_query_file():
+      tmp_query_file.close()
+      os.remove(tmp_query_file.name)
+    perf_lines = [line.decode('utf8') for line in tmp_perf_file.readlines()]
+    tmp_perf_file.close()
+    os.remove(tmp_perf_file.name)
+
+    return TestResult(self.test,
+                      trace_path, cmd, expected, stdout.decode('utf8'),
+                      stderr.decode('utf8'), tp.returncode, perf_lines)
+
+  def __run(self, metrics_descriptor_paths: List[str],
+            extension_descriptor_paths: List[str], keep_input,
+            rebase) -> Tuple[TestResult, str]:
+    # We can't use delete=True here. When using that on Windows, the
+    # resulting file is opened in exclusive mode (in turn that's a subtle
+    # side-effect of the underlying CreateFile(FILE_ATTRIBUTE_TEMPORARY))
+    # and TP fails to open the passed path.
+    gen_trace_file = None
+    if self.test.blueprint.is_trace_file():
+      if self.test.trace_path.endswith('.py'):
+        gen_trace_file = tempfile.NamedTemporaryFile(delete=False)
+        serialize_python_trace(ROOT_DIR, self.trace_descriptor_path,
+                               self.test.trace_path, gen_trace_file)
+
+      elif self.test.trace_path.endswith('.textproto'):
+        gen_trace_file = tempfile.NamedTemporaryFile(delete=False)
+        serialize_textproto_trace(self.trace_descriptor_path,
+                                  extension_descriptor_paths,
+                                  self.test.trace_path, gen_trace_file)
+
+    elif self.test.blueprint.is_trace_textproto():
+      gen_trace_file = tempfile.NamedTemporaryFile(delete=False)
+      proto = create_message_factory([self.trace_descriptor_path] +
+                                     extension_descriptor_paths,
+                                     'perfetto.protos.Trace')()
+      text_format.Merge(self.test.blueprint.trace.contents, proto)
+      gen_trace_file.write(proto.SerializeToString())
+      gen_trace_file.flush()
+
+    else:
+      gen_trace_file = tempfile.NamedTemporaryFile(delete=False)
+      with open(gen_trace_file.name, 'w') as trace_file:
+        trace_file.write(self.test.blueprint.trace.contents)
+
+    if gen_trace_file:
+      trace_path = os.path.realpath(gen_trace_file.name)
+    else:
+      trace_path = self.test.trace_path
+
+    str = f"{self.colors.yellow('[ RUN      ]')} {self.test.name}\n"
+
+    if self.test.type == TestType.QUERY:
+      result = self.__run_query_test(trace_path)
+    elif self.test.type == TestType.METRIC:
+      result = self.__run_metrics_test(
+          trace_path,
+          create_message_factory(metrics_descriptor_paths,
+                                 'perfetto.protos.TraceMetrics'))
+    else:
+      assert False
+
+    if gen_trace_file:
+      if keep_input:
+        str += f"Saving generated input trace: {trace_path}\n"
+      else:
+        gen_trace_file.close()
+        os.remove(trace_path)
+
+    def write_cmdlines():
+      res = ""
+      if not gen_trace_file:
+        res += 'Command to generate trace:\n'
+        res += 'tools/serialize_test_trace.py '
+        res += '--descriptor {} {} > {}\n'.format(
+            os.path.relpath(self.trace_descriptor_path, ROOT_DIR),
+            os.path.relpath(self.test.trace_path, ROOT_DIR),
+            os.path.relpath(trace_path, ROOT_DIR))
+      res += f"Command line:\n{' '.join(result.cmd)}\n"
+      return res
+
+    if result.exit_code != 0 or not result.passed:
+      str += result.stderr
+
+      if result.exit_code == 0:
+        str += f"Expected did not match actual for test {self.test.name}.\n"
+        str += write_cmdlines()
+        str += result.write_diff()
+      else:
+        str += write_cmdlines()
+
+      str += (f"{self.colors.red('[  FAILED  ]')} {self.test.name}\n")
+      str += result.rebase(rebase)
+
+      return result, str
+    else:
+      str += (f"{self.colors.green('[       OK ]')} {self.test.name} "
+              f"(ingest: {result.perf_result.ingest_time_ns / 1000000:.2f} ms "
+              f"query: {result.perf_result.real_time_ns / 1000000:.2f} ms)\n")
+    return result, str
+
+  # Run a TestCase.
+  def execute(self, extension_descriptor_paths: List[str],
+              metrics_descriptor: str, keep_input: bool,
+              rebase: bool) -> Tuple[str, str, TestResult]:
+    if metrics_descriptor:
+      metrics_descriptor_paths = [metrics_descriptor]
+    else:
+      out_path = os.path.dirname(self.trace_processor_path)
+      metrics_protos_path = os.path.join(out_path, 'gen', 'protos', 'perfetto',
+                                         'metrics')
+      metrics_descriptor_paths = [
+          os.path.join(metrics_protos_path, 'metrics.descriptor'),
+          os.path.join(metrics_protos_path, 'chrome',
+                       'all_chrome_metrics.descriptor')
+      ]
+    result_str = ""
+
+    result, run_str = self.__run(metrics_descriptor_paths,
+                                 extension_descriptor_paths, keep_input, rebase)
+    result_str += run_str
+    if not result:
+      return self.test.name, result_str, None
+
+    return self.test.name, result_str, result
+
+
+# Fetches and executes all diff viable tests.
+@dataclass
+class DiffTestsRunner:
+  tests: List[TestCase]
+  trace_processor_path: str
+  trace_descriptor_path: str
+  test_runners: List[TestCaseRunner]
+
+  def __init__(self, name_filter: str, trace_processor_path: str,
+               trace_descriptor: str, no_colors: bool):
+    self.tests = read_all_tests(name_filter, ROOT_DIR)
+    self.trace_processor_path = trace_processor_path
+
+    out_path = os.path.dirname(self.trace_processor_path)
+    self.trace_descriptor_path = get_trace_descriptor_path(
+        out_path, trace_descriptor)
+    self.test_runners = []
+    color_formatter = ColorFormatter(no_colors)
+    for test in self.tests:
+      self.test_runners.append(
+          TestCaseRunner(test, self.trace_processor_path,
+                         self.trace_descriptor_path, color_formatter))
+
+  def run_all_tests(self, metrics_descriptor: str, keep_input: bool,
+                    rebase: bool) -> TestResults:
+    perf_results = []
+    failures = []
+    rebased = []
+    test_run_start = datetime.datetime.now()
+
+    out_path = os.path.dirname(self.trace_processor_path)
+    chrome_extensions = os.path.join(out_path, 'gen', 'protos', 'third_party',
+                                     'chromium',
+                                     'chrome_track_event.descriptor')
+    test_extensions = os.path.join(out_path, 'gen', 'protos', 'perfetto',
+                                   'trace', 'test_extensions.descriptor')
+
+    with concurrent.futures.ProcessPoolExecutor() as e:
+      fut = [
+          e.submit(test.execute, [chrome_extensions, test_extensions],
+                   metrics_descriptor, keep_input, rebase)
+          for test in self.test_runners
+      ]
+      for res in concurrent.futures.as_completed(fut):
+        test_name, res_str, result = res.result()
+        sys.stderr.write(res_str)
+        if not result or not result.passed:
+          if rebase:
+            rebased.append(test_name)
+          failures.append(test_name)
+        else:
+          perf_results.append(result.perf_result)
+    test_time_ms = int(
+        (datetime.datetime.now() - test_run_start).total_seconds() * 1000)
+    return TestResults(failures, perf_results, rebased, test_time_ms)
diff --git a/python/generators/diff_tests/testing.py b/python/generators/diff_tests/testing.py
new file mode 100644
index 0000000..30f69bd
--- /dev/null
+++ b/python/generators/diff_tests/testing.py
@@ -0,0 +1,204 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 inspect
+import os
+from dataclasses import dataclass
+from typing import Dict, List, Union
+from enum import Enum
+import re
+
+TestName = str
+
+
+@dataclass
+class Path:
+  filename: str
+
+
+@dataclass
+class DataPath(Path):
+  filename: str
+
+
+@dataclass
+class Metric:
+  name: str
+
+
+@dataclass
+class Json:
+  contents: str
+
+
+@dataclass
+class Csv:
+  contents: str
+
+
+@dataclass
+class TextProto:
+  contents: str
+
+
+@dataclass
+class Systrace:
+  contents: str
+
+
+class TestType(Enum):
+  QUERY = 1
+  METRIC = 2
+
+
+# Blueprint for running the diff test. 'query' is being run over data from the
+# 'trace 'and result will be compared to the 'out. Each test (function in class
+# inheriting from TestSuite) returns a DiffTestBlueprint.
+@dataclass
+class DiffTestBlueprint:
+
+  trace: Union[Path, DataPath, Json, Systrace, TextProto]
+  query: Union[str, Path, DataPath, Metric]
+  out: Union[Path, DataPath, Json, Csv, TextProto]
+
+  def is_trace_file(self):
+    return isinstance(self.trace, Path)
+
+  def is_trace_textproto(self):
+    return isinstance(self.trace, TextProto)
+
+  def is_trace_json(self):
+    return isinstance(self.trace, Json)
+
+  def is_trace_systrace(self):
+    return isinstance(self.trace, Systrace)
+
+  def is_query_file(self):
+    return isinstance(self.query, Path)
+
+  def is_metric(self):
+    return isinstance(self.query, Metric)
+
+  def is_out_file(self):
+    return isinstance(self.out, Path)
+
+  def is_out_json(self):
+    return isinstance(self.out, Json)
+
+  def is_out_texproto(self):
+    return isinstance(self.out, TextProto)
+
+  def is_out_csv(self):
+    return isinstance(self.out, Csv)
+
+
+# Description of a diff test. Created in `fetch_diff_tests()` in
+# TestSuite: each test (function starting with `test_`) returns
+# DiffTestBlueprint and function name is a TestCase name. Used by diff test
+# script.
+class TestCase:
+
+  def __get_query_path(self) -> str:
+    if not self.blueprint.is_query_file():
+      return None
+
+    if isinstance(self.blueprint.query, DataPath):
+      path = os.path.join(self.test_dir, 'data', self.blueprint.query.filename)
+    else:
+      path = os.path.abspath(
+          os.path.join(self.index_dir, self.blueprint.query.filename))
+
+    if not os.path.exists(path):
+      raise AssertionError(
+          f"Query file ({path}) for test '{self.name}' does not exist.")
+    return path
+
+  def __get_trace_path(self) -> str:
+    if not self.blueprint.is_trace_file():
+      return None
+
+    if isinstance(self.blueprint.trace, DataPath):
+      path = os.path.join(self.test_dir, 'data', self.blueprint.trace.filename)
+    else:
+      path = os.path.abspath(
+          os.path.join(self.index_dir, self.blueprint.trace.filename))
+
+    if not os.path.exists(path):
+      raise AssertionError(
+          f"Trace file ({path}) for test '{self.name}' does not exist.")
+    return path
+
+  def __get_out_path(self) -> str:
+    if not self.blueprint.is_out_file():
+      return None
+
+    if isinstance(self.blueprint.out, DataPath):
+      path = os.path.join(self.test_dir, 'data', self.blueprint.out.filename)
+    else:
+      path = os.path.abspath(
+          os.path.join(self.index_dir, self.blueprint.out.filename))
+
+    if not os.path.exists(path):
+      raise AssertionError(
+          f"Out file ({path}) for test '{self.name}' does not exist.")
+    return path
+
+  def __init__(self, name: str, blueprint: DiffTestBlueprint,
+               index_dir: str) -> None:
+    self.name = name
+    self.blueprint = blueprint
+    self.index_dir = index_dir
+    self.test_dir = os.path.dirname(os.path.dirname(os.path.dirname(index_dir)))
+
+    if blueprint.is_metric():
+      self.type = TestType.METRIC
+    else:
+      self.type = TestType.QUERY
+
+    self.query_path = self.__get_query_path()
+    self.trace_path = self.__get_trace_path()
+    self.expected_path = self.__get_out_path()
+
+  # Verifies that the test should be in test suite. If False, test will not be
+  # executed.
+  def validate(self, name_filter: str):
+    query_metric_pattern = re.compile(name_filter)
+    return bool(query_metric_pattern.match(os.path.basename(self.name)))
+
+
+# Virtual class responsible for fetching diff tests.
+# All functions with name starting with `test_` have to return
+# DiffTestBlueprint and function name is a test name. All DiffTestModules have
+# to be included in `test/diff_tests/trace_processor/include_index.py`.
+# `fetch_diff_test` function should not be overwritten.
+class TestSuite:
+
+  def __init__(self, include_index_dir: str, dir_name: str,
+               class_name: str) -> None:
+    self.dir_name = dir_name
+    self.index_dir = os.path.join(include_index_dir, dir_name)
+    self.class_name = class_name
+
+  def __test_name(self, method_name):
+    return f"{self.class_name}:{method_name.split('test_',1)[1]}"
+
+  def fetch(self) -> List['TestCase']:
+    attrs = (getattr(self, name) for name in dir(self))
+    methods = [attr for attr in attrs if inspect.ismethod(attr)]
+    return [
+        TestCase(self.__test_name(method.__name__), method(), self.index_dir)
+        for method in methods
+        if method.__name__.startswith('test_')
+    ]
diff --git a/python/generators/diff_tests/utils.py b/python/generators/diff_tests/utils.py
new file mode 100644
index 0000000..cb447fd
--- /dev/null
+++ b/python/generators/diff_tests/utils.py
@@ -0,0 +1,160 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 sys
+import os
+import signal
+from typing import List
+import subprocess
+
+from google.protobuf import descriptor_pb2, message_factory, text_format
+
+from python.generators.diff_tests import testing
+
+USE_COLOR_CODES = sys.stderr.isatty()
+
+
+class ColorFormatter:
+
+  def __init__(self, no_colors: bool):
+    self.no_colors = no_colors
+
+  def __red_str(self) -> str:
+    return "\u001b[31m" if USE_COLOR_CODES and not self.no_colors else ""
+
+  def __green_str(self) -> str:
+    return "\u001b[32m" if USE_COLOR_CODES and not self.no_colors else ""
+
+  def __yellow_str(self) -> str:
+    return "\u001b[33m" if USE_COLOR_CODES and not self.no_colors else ""
+
+  def __end_color(self) -> str:
+    return "\u001b[0m" if USE_COLOR_CODES and not self.no_colors else ""
+
+  def red(self, s: str) -> str:
+    return self.__red_str() + s + self.__end_color()
+
+  def green(self, s: str) -> str:
+    return self.__green_str() + s + self.__end_color()
+
+  def yellow(self, s: str) -> str:
+    return self.__yellow_str() + s + self.__end_color()
+
+
+def get_env(root_dir):
+  env = {
+      'PERFETTO_BINARY_PATH': os.path.join(root_dir, 'test', 'data'),
+  }
+  if sys.platform.startswith('linux'):
+    env['PATH'] = os.path.join(root_dir, 'buildtools', 'linux64', 'clang',
+                               'bin')
+  elif sys.platform.startswith('darwin'):
+    # Sadly, on macOS we need to check out the Android deps to get
+    # llvm symbolizer.
+    env['PATH'] = os.path.join(root_dir, 'buildtools', 'ndk', 'toolchains',
+                               'llvm', 'prebuilt', 'darwin-x86_64', 'bin')
+  elif sys.platform.startswith('win32'):
+    env['PATH'] = os.path.join(root_dir, 'buildtools', 'win', 'clang', 'bin')
+  return env
+
+
+def ctrl_c_handler(_num, _frame):
+  # Send a sigkill to the whole process group. Our process group looks like:
+  # - Main python interpreter running the main()
+  #   - N python interpreters coming from ProcessPoolExecutor workers.
+  #     - 1 trace_processor_shell subprocess coming from the subprocess.Popen().
+  # We don't need any graceful termination as the diff tests are stateless and
+  # don't write any file. Just kill them all immediately.
+  os.killpg(os.getpid(), signal.SIGKILL)
+
+
+def create_message_factory(descriptor_file_paths, proto_type):
+  files = []
+  for file_path in descriptor_file_paths:
+    files.extend(read_descriptor(file_path).file)
+
+  # We use this method rather than working directly with DescriptorPool
+  # because, when the pure-Python protobuf runtime is used, extensions
+  # need to be explicitly registered with the message type. See
+  # https://github.com/protocolbuffers/protobuf/blob/9e09343a49e9e75be576b31ed7402bf8502b080c/python/google/protobuf/message_factory.py#L145
+  return message_factory.GetMessages(files)[proto_type]
+
+
+def create_metrics_message_factory(metrics_descriptor_paths):
+  return create_message_factory(metrics_descriptor_paths,
+                                'perfetto.protos.TraceMetrics')
+
+
+def read_descriptor(file_name):
+  with open(file_name, 'rb') as f:
+    contents = f.read()
+
+  descriptor = descriptor_pb2.FileDescriptorSet()
+  descriptor.MergeFromString(contents)
+
+  return descriptor
+
+
+def serialize_textproto_trace(trace_descriptor_path, extension_descriptor_paths,
+                              text_proto_path, out_stream):
+  proto = create_message_factory([trace_descriptor_path] +
+                                 extension_descriptor_paths,
+                                 'perfetto.protos.Trace')()
+
+  with open(text_proto_path, 'r') as text_proto_file:
+    text_format.Merge(text_proto_file.read(), proto)
+  out_stream.write(proto.SerializeToString())
+  out_stream.flush()
+
+
+def serialize_python_trace(root_dir, trace_descriptor_path, python_trace_path,
+                           out_stream):
+  python_cmd = [
+      'python3',
+      python_trace_path,
+      trace_descriptor_path,
+  ]
+
+  # Add the test dir to the PYTHONPATH to allow synth_common to be found.
+  env = os.environ.copy()
+  if 'PYTHONPATH' in env:
+    env['PYTHONPATH'] = "{}:{}".format(
+        os.path.join(root_dir, 'test'), env['PYTHONPATH'])
+  else:
+    env['PYTHONPATH'] = os.path.join(root_dir, 'test')
+  subprocess.check_call(python_cmd, env=env, stdout=out_stream)
+
+
+def get_trace_descriptor_path(out_path: str, trace_descriptor: str):
+  if trace_descriptor:
+    return trace_descriptor
+
+  path = ['gen', 'protos', 'perfetto', 'trace', 'trace.descriptor']
+  trace_descriptor_path = os.path.join(out_path, *path)
+  if not os.path.exists(trace_descriptor_path):
+    trace_descriptor_path = os.path.join(out_path, 'gcc_like_host', *path)
+
+  return trace_descriptor_path
+
+
+def read_all_tests(name_filter: str, root_dir: str) -> List[testing.TestCase]:
+  # Import
+  INCLUDE_PATH = os.path.join(root_dir, 'test', 'trace_processor', 'diff_tests')
+  sys.path.append(INCLUDE_PATH)
+  from include_index import fetch_all_diff_tests
+  sys.path.pop()
+  diff_tests = fetch_all_diff_tests(INCLUDE_PATH)
+
+  return [test for test in diff_tests if test.validate(name_filter)]
diff --git a/python/generators/stdlib_docs/parse.py b/python/generators/stdlib_docs/parse.py
new file mode 100644
index 0000000..79f4cfe
--- /dev/null
+++ b/python/generators/stdlib_docs/parse.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python3
+# Copyright (C) 2022 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 re
+from typing import Union, List, Tuple
+
+from python.generators.stdlib_docs import stdlib
+from python.generators.stdlib_docs.utils import Errors, Pattern, get_text, fetch_comment, match_pattern
+
+
+def parse_desc(docs: 'stdlib.AnyDocs') -> str:
+  desc_lines = [get_text(line, False) for line in docs.desc]
+  return ' '.join(desc_lines).strip('\n').strip()
+
+
+# Whether comment segment about columns contain proper schema. Can be matched
+# against parsed SQL data by setting `use_data_from_sql`.
+def parse_columns(docs: Union['stdlib.TableViewDocs', 'stdlib.ViewFunctionDocs']
+                 ) -> dict:
+  cols = {}
+  last_col = None
+  last_desc = []
+  for line in docs.columns:
+    # Ignore only '--' line.
+    if line == "--" or not line.startswith("-- @column"):
+      last_desc.append(get_text(line))
+      continue
+
+    # Look for '-- @column' line as a column description
+    m = re.match(Pattern['column'], line)
+    if last_col:
+      cols[last_col] = ' '.join(last_desc)
+    last_col, last_desc = m.group(1), [m.group(2)]
+
+  cols[last_col] = ' '.join(last_desc)
+  return cols
+
+
+def parse_args(docs: "stdlib.FunctionDocs") -> dict:
+  if not docs.args:
+    return {}
+
+  args = {}
+  last_arg, last_desc, last_type = None, [], None
+  for line in docs.args:
+    # Ignore only '--' line.
+    if line == "--" or not line.startswith("-- @arg"):
+      last_desc.append(get_text(line))
+      continue
+
+    m = re.match(Pattern['args'], line)
+    if last_arg:
+      args[last_arg] = {'type': last_type, 'desc': ' '.join(last_desc)}
+    last_arg, last_type, last_desc = m.group(1), m.group(2), [m.group(3)]
+
+  args[last_arg] = {'type': last_type, 'desc': ' '.join(last_desc)}
+  return args
+
+
+# Whether comment segment about return contain proper schema. Matches against
+# parsed SQL data.
+def parse_ret(docs: "stdlib.FunctionDocs") -> Tuple[str, str]:
+  desc = []
+  for line in docs.ret:
+    # Ignore only '--' line.
+    if line == "--" or not line.startswith("-- @ret"):
+      desc.append(get_text(line))
+
+    m = re.match(Pattern['return_arg'], line)
+    ret_type, desc = m.group(1), [m.group(2)]
+  return (ret_type, ' '.join(desc))
+
+
+# After matching file to Pattern, fetches and validates related documentation.
+def parse_typed_docs(path: str, module: str, sql: str, Pattern: str,
+                     docs_object: type
+                    ) -> Tuple[List['stdlib.AnyDocs'], Errors]:
+  errors = []
+  line_id_to_match = match_pattern(Pattern, sql)
+  lines = sql.split("\n")
+  all_typed_docs = []
+  for line_id, matches in line_id_to_match.items():
+    # Fetch comment by looking at lines over beginning of match in reverse
+    # order.
+    comment = fetch_comment(lines[line_id - 1::-1])
+    typed_docs, obj_errors = docs_object.create_from_comment(
+        path, comment, module, matches)
+    errors += obj_errors
+
+    if not typed_docs:
+      continue
+
+    errors += typed_docs.check_comment()
+
+    if not errors:
+      all_typed_docs.append(typed_docs)
+
+  return all_typed_docs, errors
diff --git a/python/generators/stdlib_docs/stdlib.py b/python/generators/stdlib_docs/stdlib.py
new file mode 100644
index 0000000..bf35d55
--- /dev/null
+++ b/python/generators/stdlib_docs/stdlib.py
@@ -0,0 +1,343 @@
+#!/usr/bin/env python3
+# Copyright (C) 2022 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 tool checks that every SQL object created without prefix
+# 'internal_' is documented with proper schema.
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import os
+import re
+import sys
+from typing import Union, List, Tuple, Dict
+from dataclasses import dataclass
+
+from python.generators.stdlib_docs.utils import *
+from python.generators.stdlib_docs.validate import *
+from python.generators.stdlib_docs.parse import *
+
+CommentLines = List[str]
+AnyDocs = Union['TableViewDocs', 'FunctionDocs', 'ViewFunctionDocs']
+
+
+# Stores documentation for CREATE {TABLE|VIEW} with comment split into
+# segments.
+@dataclass
+class TableViewDocs:
+  name: str
+  obj_type: str
+  desc: CommentLines
+  columns: CommentLines
+  path: str
+
+  # Contructs new TableViewDocs from the entire comment, by splitting it on
+  # typed lines. Returns None for improperly structured schemas.
+  @staticmethod
+  def create_from_comment(path: str, comment: CommentLines, module: str,
+                          matches: Tuple) -> Tuple['TableViewDocs', Errors]:
+    obj_type, name = matches[:2]
+
+    # Ignore internal tables and views.
+    if re.match(r"^internal_.*", name):
+      return None, []
+
+    errors = validate_name(name, module)
+    col_start = None
+    has_desc = False
+
+    # Splits code into segments by finding beginning of column segment.
+    for i, line in enumerate(comment):
+      # Ignore only '--' line.
+      if line == "--":
+        continue
+
+      m = re.match(Pattern['typed_line'], line)
+
+      # Ignore untyped lines
+      if not m:
+        if not col_start:
+          has_desc = True
+        continue
+
+      line_type = m.group(1)
+      if line_type == "column" and not col_start:
+        col_start = i
+        continue
+
+    if not has_desc:
+      errors.append(f"No description for {obj_type}: '{name}' in {path}'\n")
+      return None, errors
+
+    if not col_start:
+      errors.append(f"No columns for {obj_type}: '{name}' in {path}'\n")
+      return None, errors
+
+    return (
+        TableViewDocs(name, obj_type, comment[:col_start], comment[col_start:],
+                      path),
+        errors,
+    )
+
+  def check_comment(self) -> Errors:
+    return validate_columns(self)
+
+  def parse_comment(self) -> dict:
+    return {
+        'name': self.name,
+        'type': self.obj_type,
+        'desc': parse_desc(self),
+        'cols': parse_columns(self)
+    }
+
+
+# Stores documentation for create_function with comment split into segments.
+class FunctionDocs:
+
+  def __init__(
+      self,
+      path: str,
+      data_from_sql: dict,
+      module: str,
+      name: str,
+      desc: str,
+      args: CommentLines,
+      ret: CommentLines,
+  ):
+    self.path = path
+    self.data_from_sql = data_from_sql
+    self.module = module
+    self.name = name
+    self.desc = desc
+    self.args = args
+    self.ret = ret
+
+  # Contructs new FunctionDocs from whole comment, by splitting it on typed
+  # lines. Returns None for improperly structured schemas.
+  @staticmethod
+  def create_from_comment(path: str, comment: CommentLines, module: str,
+                          matches: Tuple) -> Tuple['FunctionDocs', Errors]:
+    name, args, ret, sql = matches
+
+    # Ignore internal functions.
+    if re.match(r"^INTERNAL_.*", name):
+      return None, []
+
+    errors = validate_name(name, module, upper=True)
+    has_desc, start_args, start_ret = False, None, None
+
+    args_dict, parse_errors = parse_args_str(args)
+    errors += parse_errors
+
+    # Splits code into segments by finding beginning of args and ret segments.
+    for i, line in enumerate(comment):
+      # Ignore only '--' line.
+      if line == "--":
+        continue
+
+      m = re.match(Pattern['typed_line'], line)
+
+      # Ignore untyped lines
+      if not m:
+        if not start_args:
+          has_desc = True
+        continue
+
+      line_type = m.group(1)
+      if line_type == "arg" and not start_args:
+        start_args = i
+        continue
+
+      if line_type == "ret" and not start_ret:
+        start_ret = i
+        continue
+
+    if not has_desc:
+      errors.append(f"No description for '{name}' in {path}'\n")
+      return None, errors
+
+    if not start_ret or (args_dict and not start_args):
+      errors.append(f"Function requires 'arg' and 'ret' comments.\n"
+                    f"'{name}' in {path}\n")
+      return None, errors
+
+    if not args_dict:
+      start_args = start_ret
+
+    data_from_sql = {'name': name, 'args': args_dict, 'ret': ret, 'sql': sql}
+    return (
+        FunctionDocs(
+            path,
+            data_from_sql,
+            module,
+            name,
+            comment[:start_args],
+            comment[start_args:start_ret] if args_dict else None,
+            comment[start_ret:],
+        ),
+        errors,
+    )
+
+  def check_comment(self) -> Errors:
+    errors = validate_args(self)
+    errors += validate_ret(self)
+    return errors
+
+  def parse_comment(self) -> dict:
+    ret_type, ret_desc = parse_ret(self)
+    return {
+        'name': self.name,
+        'desc': parse_desc(self),
+        'args': parse_args(self),
+        'return_type': ret_type,
+        'return_desc': ret_desc
+    }
+
+
+# Stores documentation for create_view_function with comment split into
+# segments.
+class ViewFunctionDocs:
+
+  def __init__(
+      self,
+      path: str,
+      data_from_sql: str,
+      module: str,
+      name: str,
+      desc: CommentLines,
+      args: CommentLines,
+      columns: CommentLines,
+  ):
+    self.path = path
+    self.data_from_sql = data_from_sql
+    self.module = module
+    self.name = name
+    self.desc = desc
+    self.args = args
+    self.columns = columns
+
+  # Contructs new ViewFunctionDocs from whole comment, by splitting it on typed
+  # lines. Returns None for improperly structured schemas.
+  @staticmethod
+  def create_from_comment(path: str, comment: CommentLines, module: str,
+                          matches: Tuple) -> Tuple['ViewFunctionDocs', Errors]:
+    name, args, columns, sql = matches
+
+    # Ignore internal functions.
+    if re.match(r"^INTERNAL_.*", name):
+      return None, []
+
+    errors = validate_name(name, module, upper=True)
+    args_dict, parse_errors = parse_args_str(args)
+    errors += parse_errors
+    has_desc, start_args, start_cols = False, None, None
+
+    # Splits code into segments by finding beginning of args and cols segments.
+    for i, line in enumerate(comment):
+      # Ignore only '--' line.
+      if line == "--":
+        continue
+
+      m = re.match(Pattern['typed_line'], line)
+
+      # Ignore untyped lines
+      if not m:
+        if not start_args:
+          has_desc = True
+        continue
+
+      line_type = m.group(1)
+      if line_type == "arg" and not start_args:
+        start_args = i
+        continue
+
+      if line_type == "column" and not start_cols:
+        start_cols = i
+        continue
+
+    if not has_desc:
+      errors.append(f"No description for '{name}' in {path}'\n")
+      return None, errors
+
+    if not start_cols or (args_dict and not start_args):
+      errors.append(f"Function requires 'arg' and 'column' comments.\n"
+                    f"'{name}' in {path}\n")
+      return None, errors
+
+    if not args_dict:
+      start_args = start_cols
+
+    cols_dict, parse_errors = parse_args_str(columns)
+    errors += parse_errors
+
+    data_from_sql = dict(name=name, args=args_dict, columns=cols_dict, sql=sql)
+    return (
+        ViewFunctionDocs(
+            path,
+            data_from_sql,
+            module,
+            name,
+            comment[:start_args],
+            comment[start_args:start_cols] if args_dict else None,
+            comment[start_cols:],
+        ),
+        errors,
+    )
+
+  def check_comment(self) -> Errors:
+    errors = validate_args(self)
+    errors += validate_columns(self, use_data_from_sql=True)
+    return errors
+
+  def parse_comment(self) -> dict:
+    return {
+        'name': self.name,
+        'desc': parse_desc(self),
+        'args': parse_args(self),
+        'cols': parse_columns(self)
+    }
+
+
+# Reads the provided SQL and, if possible, generates a dictionary with data
+# from documentation together with errors from validation of the schema.
+def parse_file_to_dict(path: str, sql: str) -> Tuple[Dict[str, any], Errors]:
+  if sys.platform.startswith('win'):
+    path = path.replace("\\", "/")
+
+  # Get module name
+  module_name = path.split("/stdlib/")[-1].split("/")[0]
+
+  imports, import_errors = parse_typed_docs(path, module_name, sql,
+                                            Pattern['create_table_view'],
+                                            TableViewDocs)
+  functions, function_errors = parse_typed_docs(path, module_name, sql,
+                                                Pattern['create_function'],
+                                                FunctionDocs)
+  view_functions, view_function_errors = parse_typed_docs(
+      path, module_name, sql, Pattern['create_view_function'], ViewFunctionDocs)
+
+  errors = import_errors + function_errors + view_function_errors
+
+  if errors:
+    sys.stderr.write("\n\n".join(errors))
+
+  return ({
+      'imports': [imp.parse_comment() for imp in imports if imp],
+      'functions': [fun.parse_comment() for fun in functions if fun],
+      'view_functions': [
+          view_fun.parse_comment() for view_fun in view_functions if view_fun
+      ]
+  }, errors)
diff --git a/python/generators/stdlib_docs/utils.py b/python/generators/stdlib_docs/utils.py
new file mode 100644
index 0000000..d23b414
--- /dev/null
+++ b/python/generators/stdlib_docs/utils.py
@@ -0,0 +1,128 @@
+#!/usr/bin/env python3
+# Copyright (C) 2022 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 re
+from typing import List, Tuple
+
+Errors = List[str]
+CommentLines = List[str]
+
+LOWER_NAME = r'[a-z_\d]*'
+UPPER_NAME = r'[A-Z_\d]*'
+ANY_WORDS = r'[A-Za-z_\d, \n]*'
+TYPE = r'[A-Z]*'
+SQL = r'[\s\S]*?'
+
+Pattern = {
+    'create_table_view': (
+        # Match create table/view and catch type
+        r'CREATE (?:VIRTUAL )?(TABLE|VIEW)?(?:IF NOT EXISTS)?\s*'
+        # Catch the name
+        fr'({LOWER_NAME})\s*(?:AS|USING)?.*'),
+    'create_function': (
+        r"SELECT\s*CREATE_FUNCTION\(\s*"
+        # Function name: we are matching everything [A-Z]* between ' and ).
+        fr"'\s*({UPPER_NAME})\s*\("
+        # Args: anything before closing bracket with '.
+        fr"({ANY_WORDS})\)',\s*"
+        # Type: [A-Z]* between two '.
+        fr"'({TYPE})',\s*"
+        # Sql: Anything between ' and ');. We are catching \'.
+        fr"'({SQL})'\s*\);"),
+    'create_view_function': (
+        r"SELECT\s*CREATE_VIEW_FUNCTION\(\s*"
+        # Function name: we are matching everything [A-Z]* between ' and ).
+        fr"'({UPPER_NAME})\s*\("
+        # Args: anything before closing bracket with '.
+        fr"({ANY_WORDS})\)',\s*"
+        # Return columns: anything between two '.
+        fr"'\s*({ANY_WORDS})',\s*"
+        # Sql: Anything between ' and ');. We are catching \'.
+        fr"'({SQL})'\s*\);"),
+    'column': fr'^-- @column\s*({LOWER_NAME})\s*({ANY_WORDS})',
+    'arg_str': fr"\s*({LOWER_NAME})\s*({TYPE})\s*",
+    'args': fr'^-- @arg\s*({LOWER_NAME})\s*({TYPE})\s*(.*)',
+    'return_arg': fr"^-- @ret ({TYPE})\s*(.*)",
+    'typed_line': fr'^-- @([a-z]*)'
+}
+
+
+def fetch_comment(lines_reversed: CommentLines) -> CommentLines:
+  comment_reversed = []
+  for line in lines_reversed:
+    # Break on empty line, as that suggests it is no longer a part of
+    # this comment.
+    if not line or not line.startswith('--'):
+      break
+
+    # The only  option left is a description, but it has to be after
+    # schema columns.
+    comment_reversed.append(line)
+
+  comment_reversed.reverse()
+  return comment_reversed
+
+
+def match_pattern(pattern: str, file_str: str) -> dict:
+  objects = {}
+  for match in re.finditer(pattern, file_str):
+    line_id = file_str[:match.start()].count('\n')
+    objects[line_id] = match.groups()
+  return dict(sorted(objects.items()))
+
+
+# Whether the name starts with module_name.
+def validate_name(name: str, module: str, upper: bool = False) -> Errors:
+  module_pattern = f"^{module}_.*"
+  if upper:
+    module_pattern = module_pattern.upper()
+  starts_with_module_name = re.match(module_pattern, name)
+  if module == "common":
+    if starts_with_module_name:
+      return [(f"Invalid name in module {name}. "
+               f"In module 'common' the name shouldn't "
+               f"start with '{module_pattern}'.\n")]
+  else:
+    if not starts_with_module_name:
+      return [(f"Invalid name in module {name}. "
+               f"Name has to begin with '{module_pattern}'.\n")]
+  return []
+
+
+# Parses string with multiple arguments with type separated by comma into dict.
+def parse_args_str(args_str: str) -> Tuple[dict, Errors]:
+  if not args_str.strip():
+    return None, []
+
+  errors = []
+  args = {}
+  for arg_str in args_str.split(","):
+    m = re.match(Pattern['arg_str'], arg_str)
+    if m is None:
+      errors.append(f"Wrong arguments formatting for '{arg_str}'\n")
+      continue
+    args[m.group(1)] = m.group(2)
+  return args, errors
+
+
+def get_text(line: str, no_break_line: bool = True) -> str:
+  line = line.lstrip('--').strip()
+  if not line:
+    return '' if no_break_line else '\n'
+  return line
diff --git a/python/generators/stdlib_docs/validate.py b/python/generators/stdlib_docs/validate.py
new file mode 100644
index 0000000..fb419e2
--- /dev/null
+++ b/python/generators/stdlib_docs/validate.py
@@ -0,0 +1,175 @@
+#!/usr/bin/env python3
+# Copyright (C) 2022 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 re
+from typing import Union, List
+
+from python.generators.stdlib_docs import stdlib
+from python.generators.stdlib_docs.utils import Pattern, Errors
+
+
+# Whether the only typed comment in provided comment segment is of type
+# `comment_type`.
+def validate_typed_comment(
+    comment_segment: str,
+    comment_type: str,
+    docs: 'stdlib.AnyDocs',
+) -> Errors:
+  for line in comment_segment:
+    # Ignore only '--' line.
+    if line == "--":
+      continue
+
+    m = re.match(Pattern['typed_line'], line)
+
+    # Ignore untyped lines
+    if not m:
+      continue
+
+    line_type = m.group(1)
+
+    if line_type != comment_type:
+      return [(
+          f"Wrong comment type. Expected '{comment_type}', got '{line_type}'.\n"
+          f"'{docs.name}' in {docs.path}:\n'{line}'\n")]
+  return []
+
+
+# Whether comment segment about columns contain proper schema. Can be matched
+# against parsed SQL data by setting `use_data_from_sql`.
+def validate_columns(
+    docs: Union['stdlib.TableViewDocs', 'stdlib.ViewFunctionDocs'],
+    use_data_from_sql=False) -> Errors:
+  errors = validate_typed_comment(docs.columns, "column", docs)
+
+  if errors:
+    return errors
+
+  if use_data_from_sql:
+    cols_from_sql = docs.data_from_sql["columns"]
+
+  for line in docs.columns:
+    # Ignore only '--' line.
+    if line == "--" or not line.startswith("-- @column"):
+      continue
+
+    # Look for '-- @column' line as a column description
+    m = re.match(Pattern['column'], line)
+    if not m:
+      errors.append(f"Wrong column description.\n"
+                    f"'{docs.name}' in {docs.path}:\n'{line}'\n")
+      continue
+
+    if not m.group(2).strip():
+      errors.append(f"No description for column '{m.group(1)}'.\n"
+                    f"'{docs.name}' in {docs.path}:\n'{line}'\n")
+      continue
+
+    if not use_data_from_sql:
+      return errors
+
+    col_name = m.group(1)
+    if col_name not in cols_from_sql:
+      errors.append(f"There is no argument '{col_name}' specified in code.\n"
+                    f"'{docs.name}' in {docs.path}:\n'{line}'\n")
+      continue
+
+    cols_from_sql.pop(col_name)
+
+  if not use_data_from_sql:
+    errors.append(f"Missing columns for {docs.name}\n{docs.path}\n")
+    return errors
+
+  if not cols_from_sql:
+    return errors
+
+  errors.append(
+      f"Missing documentation of columns: {list(cols_from_sql.keys())}.\n"
+      f"'{docs.name}' in {docs.path}:\n")
+  return errors
+
+
+# Whether comment segment about columns contain proper schema. Matches against
+# parsed SQL data.
+def validate_args(docs: Union['stdlib.FunctionDocs', 'stdlib.ViewFunctionDocs']
+                 ) -> Errors:
+  if not docs.args:
+    return []
+
+  errors = validate_typed_comment(docs.args, "arg", docs)
+
+  if errors:
+    return errors
+
+  args_from_sql = docs.data_from_sql["args"]
+  for line in docs.args:
+    # Ignore only '--' line.
+    if line == "--" or not line.startswith("-- @"):
+      continue
+
+    m = re.match(Pattern['args'], line)
+    if m is None:
+      errors.append("The arg docs formatting is wrong. It should be:\n"
+                    "-- @arg [a-z_]* [A-Z]* {desc}\n"
+                    f"'{docs.name}' in {docs.path}:\n'{line}'\n")
+      return errors
+
+    arg_name, arg_type = m.group(1), m.group(2)
+    if arg_name not in args_from_sql:
+      errors.append(f"There is not argument '{arg_name}' specified in code.\n"
+                    f"'{docs.name}' in {docs.path}:\n'{line}'\n")
+      continue
+
+    arg_type_from_sql = args_from_sql.pop(arg_name)
+    if arg_type != arg_type_from_sql:
+      errors.append(f"In the code, the type of '{arg_name}' is "
+                    f"'{arg_type_from_sql}', but according to the docs "
+                    f"it is '{arg_type}'.\n"
+                    f"'{docs.name}' in {docs.path}:\n'{line}'\n")
+
+  if not args_from_sql:
+    return errors
+
+  errors.append(
+      f"Missing documentation of args: {list(args_from_sql.keys())}.\n"
+      f"'{docs.name}' in {docs.path}\n")
+  return errors
+
+
+# Whether comment segment about return contain proper schema. Matches against
+# parsed SQL data.
+def validate_ret(docs: "stdlib.FunctionDocs") -> Errors:
+  errors = validate_typed_comment(docs.ret, "ret", docs)
+  if errors:
+    return errors
+
+  ret_type_from_sql = docs.data_from_sql["ret"]
+
+  for line in docs.ret:
+    # Ignore only '--' line.
+    if line == "--" or not line.startswith("-- @ret"):
+      continue
+
+    m = re.match(Pattern['return_arg'], line)
+    if m is None:
+      return [("The return docs formatting is wrong. It should be:\n"
+               "-- @ret [A-Z]* {desc}\n"
+               f"'{docs.name}' in {docs.path}:\n'{line}'\n")]
+    docs_ret_type = m.group(1)
+    if ret_type_from_sql != docs_ret_type:
+      return [(f"The return type in docs is '{docs_ret_type}', "
+               f"but it is {ret_type_from_sql} in code.\n"
+               f"'{docs.name}' in {docs.path}:\n'{line}'\n")]
+    return []
diff --git a/python/generators/trace_processor_table/public.py b/python/generators/trace_processor_table/public.py
new file mode 100644
index 0000000..6b326e8
--- /dev/null
+++ b/python/generators/trace_processor_table/public.py
@@ -0,0 +1,183 @@
+# Copyright (C) 2022 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.
+"""Contains the public API for generating C++ tables."""
+
+from dataclasses import dataclass
+from enum import auto
+from enum import Flag as enum_Flag
+from typing import Dict
+from typing import List
+from typing import Optional
+from typing import Union
+
+
+@dataclass
+class CppColumnType:
+  """
+  The type of a column on a C++ table.
+
+  See below for subclasses of this class which can be used.
+  """
+
+
+class ColumnFlag(enum_Flag):
+  """
+  Flags which should be associated to the C++ table column.
+
+  For more information on each option here, see the Column::Flag
+  enum. Keep this in sync with Column::Flag.
+  """
+  NONE = 0
+  SORTED = auto()
+  SET_ID = auto()
+
+
+@dataclass
+class Column:
+  """
+  Representation of a column of a C++ table.
+
+  Attributes:
+    name: The name of the column.
+    type: The type of the column.
+    flags: Flags for the column, ColumnFlag.NONE by default.
+  """
+  name: str
+  type: CppColumnType
+  flags: ColumnFlag = ColumnFlag.NONE
+
+  # Private fields used by the generator. Do not set these manually.
+  _is_auto_added_id: bool = False
+  _is_auto_added_type: bool = False
+
+
+@dataclass
+class ColumnDoc:
+  """
+  Documentation for the C++ table column.
+
+  Used to generate reference pages on the docs website.
+
+  Attributes:
+    doc: Freeform docstring for the column.
+    joinable: Indicates this column is joinable with a column
+    from another table. Should have the format "table.column".
+  """
+  doc: str
+  joinable: Optional[str] = None
+
+
+@dataclass
+class TableDoc:
+  """
+  Documentation for the C++ table.
+
+  Used to generate reference pages on the docs website.
+
+  Attributes:
+    doc: Freeform docstring for the table.
+    group: The group of tables this table belongs to. Examples include "Tracks",
+    "Events", "ART Heap Graphs" etc: see the docs page for all the existing
+    groups.
+    columns: Documentation for each table column.
+    skip_id_and_type: Skips publishing these columns in the documentation.
+    Should only be used when these columns
+    are not meaningful or are aliased to something better.
+  """
+  doc: str
+  group: str
+  columns: Dict[str, Union[ColumnDoc, str]]
+  skip_id_and_type: bool = False
+
+
+@dataclass
+class WrappingSqlView:
+  """
+  Specifies information about SQL view wrapping a table.
+
+  Useful for tables which are not exposed directly to
+  SQL but instead are wrapped with a SQL view.
+
+  Attributes:
+    view_name: The name of the SQL view exposed to SQL.
+  """
+  view_name: str
+
+
+@dataclass
+class Table:
+  """
+  Representation of of a C++ table.
+
+  Attributes:
+    class_name: Name of the C++ table class.
+    sql_name: Name of the table in SQL.
+    columns: The columns in this table.
+    wrapping_sql_view: See |WrappingSqlView|.
+    tabledoc: Documentation for this table. Can include
+    documentation overrides for auto-added columns (i.e.
+    id and type) and aliases added in |wrapping_sql_view|.
+  """
+  class_name: str
+  sql_name: str
+  columns: List[Column]
+  tabledoc: TableDoc
+  wrapping_sql_view: Optional[WrappingSqlView] = None
+
+
+@dataclass
+class CppInt64(CppColumnType):
+  """Represents the int64_t C++ type."""
+
+
+@dataclass
+class CppUint32(CppColumnType):
+  """Represents the uint32_t C++ type."""
+
+
+@dataclass
+class CppInt32(CppColumnType):
+  """Represents the int32_t C++ type."""
+
+
+@dataclass
+class CppString(CppColumnType):
+  """Represents the StringPool::Id C++ type."""
+
+
+@dataclass
+class CppOptional(CppColumnType):
+  """Represents the base::Optional C++ type."""
+  inner: CppColumnType
+
+
+@dataclass
+class CppTableId(CppColumnType):
+  """Represents the Table::Id C++ type."""
+  table: Table
+
+
+@dataclass
+class CppSelfTableId(CppColumnType):
+  """Represents the Id C++ type."""
+
+
+@dataclass
+class Alias(CppColumnType):
+  """Represents a column which aliases another column.
+
+  Aliasing refers to re-exporting a column with a different name. This is useful
+  especially for exporting "id" columns which names which associate it to the
+  table name: e.g. exporting thread.id as thread.utid"""
+  underlying_column: str
diff --git a/python/generators/trace_processor_table/serialize.py b/python/generators/trace_processor_table/serialize.py
new file mode 100644
index 0000000..43ca5cd
--- /dev/null
+++ b/python/generators/trace_processor_table/serialize.py
@@ -0,0 +1,249 @@
+# Copyright (C) 2022 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 typing import List
+from typing import Optional
+
+from python.generators.trace_processor_table.public import Alias
+from python.generators.trace_processor_table.public import Column
+from python.generators.trace_processor_table.public import ColumnFlag
+from python.generators.trace_processor_table.public import Table
+from python.generators.trace_processor_table.util import parse_type
+from python.generators.trace_processor_table.util import typed_column_type
+from python.generators.trace_processor_table.util import to_cpp_flags
+
+
+class ColumnSerializer:
+  """Functions for serializing a single Column in a table into C++."""
+
+  def __init__(self, table: Table, col_index: int):
+    self.col_index = col_index
+    self.col = table.columns[col_index]
+    self.name = self.col.name
+    self.flags = self.col.flags
+    self.typed_column_type = typed_column_type(table, self.col)
+    self.cpp_type = parse_type(table, self.col.type).cpp_type_with_optionality()
+
+  def colindex(self) -> str:
+    return f'    static constexpr uint32_t {self.name} = {self.col_index};'
+
+  def coltype_enum(self) -> str:
+    return f'    using {self.name} = {self.typed_column_type};'
+
+  def row_field(self) -> Optional[str]:
+    if self.col._is_auto_added_id or self.col._is_auto_added_type:
+      return None
+    return f'    {self.cpp_type} {self.name};'
+
+  def row_param(self) -> Optional[str]:
+    if self.col._is_auto_added_id or self.col._is_auto_added_type:
+      return None
+    return f'{self.cpp_type} in_{self.name} = {{}}'
+
+  def row_initializer(self) -> Optional[str]:
+    if self.col._is_auto_added_id or self.col._is_auto_added_type:
+      return None
+    return f'{self.name}(std::move(in_{self.name}))'
+
+  def flag(self) -> Optional[str]:
+    if self.col._is_auto_added_id or self.col._is_auto_added_type:
+      return None
+    default = f'ColumnType::{self.name}::default_flags()'
+    if self.flags == ColumnFlag.NONE:
+      flags = default
+    else:
+      flags = f'static_cast<uint32_t>({to_cpp_flags(self.flags)}) | {default}'
+    return f'''
+      static constexpr uint32_t {self.name} = {flags};
+    '''
+
+  def storage_init(self) -> Optional[str]:
+    if self.col._is_auto_added_id or self.col._is_auto_added_type:
+      return None
+
+    storage = f'ColumnStorage<ColumnType::{self.name}::stored_type>'
+    # TODO(lalitm): add support for dense columns.
+    return f'''{self.name}_({storage}::Create<false>())'''
+
+  def column_init(self) -> Optional[str]:
+    if self.col._is_auto_added_id or self.col._is_auto_added_type:
+      return None
+    return f'''
+    columns_.emplace_back("{self.name}", &{self.name}_, ColumnFlag::{self.name},
+                          this, static_cast<uint32_t>(columns_.size()),
+                          overlay_count);
+    '''
+
+  def shrink_to_fit(self) -> Optional[str]:
+    if self.col._is_auto_added_id:
+      return None
+    return f'    {self.name}_.ShrinkToFit();'
+
+  def append(self) -> Optional[str]:
+    if self.col._is_auto_added_id or self.col._is_auto_added_type:
+      return None
+    return f'    mutable_{self.name}()->Append(std::move(row.{self.name}));'
+
+  def accessor(self) -> Optional[str]:
+    inner = f'columns_[ColumnIndex::{self.name}]'
+    return f'''
+  const {self.typed_column_type}& {self.name}() const {{
+    return static_cast<const ColumnType::{self.name}&>({inner});
+  }}
+  '''
+
+  def mutable_accessor(self) -> Optional[str]:
+    if self.col._is_auto_added_id or self.col._is_auto_added_type:
+      return None
+    return f'''
+  {self.typed_column_type}* mutable_{self.name}() {{
+    return static_cast<ColumnType::{self.name}*>(
+        &columns_[ColumnIndex::{self.name}]);
+  }}
+  '''
+
+  def storage(self) -> Optional[str]:
+    if self.col._is_auto_added_id or self.col._is_auto_added_type:
+      return None
+    name = self.name
+    return f'  ColumnStorage<ColumnType::{name}::stored_type> {name}_;'
+
+
+class TableSerializer(object):
+  """Functions for seralizing a single Table into C++."""
+
+  def __init__(self, table: Table):
+    self.table = table
+    self.table_name = table.class_name
+    self.column_serializers = [
+        ColumnSerializer(table, i) for i in range(len(table.columns))
+    ]
+
+  def foreach_col(self, serialize_fn, delimiter='\n') -> str:
+    lines = []
+    for c in self.column_serializers:
+      serialized = serialize_fn(c)
+      if serialized:
+        lines.append(serialized.lstrip('\n').rstrip())
+    return delimiter.join(lines).strip()
+
+  def id_defn(self) -> str:
+    return '''
+  struct Id : public BaseId {
+    Id() = default;
+    explicit constexpr Id(uint32_t v) : BaseId(v) {}
+  };
+  static_assert(std::is_trivially_destructible<Id>::value,
+                "Inheritance used without trivial destruction");
+    '''
+
+  def row_struct(self) -> str:
+    param = self.foreach_col(
+        ColumnSerializer.row_param, delimiter=',\n        ')
+    row_init = self.foreach_col(
+        ColumnSerializer.row_initializer, delimiter=',\n          ')
+    return f'''
+  struct Row : public macros_internal::RootParentTable::Row {{
+    Row({param})
+        : macros_internal::RootParentTable::Row(nullptr),
+          {row_init} {{
+      type_ = "{self.table.sql_name}";
+    }}
+    {self.foreach_col(ColumnSerializer.row_field)}
+  }};
+    '''
+
+  def constructor(self) -> str:
+    col_init = self.foreach_col(
+        ColumnSerializer.storage_init, delimiter=',\n        ')
+    return f'''
+  explicit {self.table_name}(StringPool* pool)
+      : macros_internal::MacroTable(pool, nullptr),
+        {col_init} {{
+    uint32_t overlay_count = static_cast<uint32_t>(overlays_.size()) - 1;
+    {self.foreach_col(ColumnSerializer.column_init)}
+  }}
+    '''
+
+  def serialize(self) -> str:
+    return f'''
+class {self.table_name} : public macros_internal::MacroTable {{
+ public:
+  {self.id_defn().lstrip()}
+  struct ColumnIndex {{
+    {self.foreach_col(ColumnSerializer.colindex)}
+  }};
+  struct ColumnType {{
+    {self.foreach_col(ColumnSerializer.coltype_enum)}
+  }};
+  {self.row_struct().strip()}
+  struct IdAndRow {{
+    uint32_t row;
+  }};
+  struct ColumnFlag {{
+    {self.foreach_col(ColumnSerializer.flag)}
+  }};
+
+  {self.constructor().strip()}
+  ~{self.table_name}() override;
+
+  static const char* Name() {{ return "{self.table.sql_name}"; }}
+
+  void ShrinkToFit() {{
+    {self.foreach_col(ColumnSerializer.shrink_to_fit)}
+  }}
+
+  IdAndRow Insert(const Row& row) {{
+    uint32_t row_number = row_count();
+    type_.Append(string_pool_->InternString(row.type()));
+    {self.foreach_col(ColumnSerializer.append)}
+    UpdateSelfOverlayAfterInsert();
+    return IdAndRow{{row_number}};
+  }}
+
+  {self.foreach_col(ColumnSerializer.accessor)}
+
+  {self.foreach_col(ColumnSerializer.mutable_accessor)}
+
+ private:
+  {self.foreach_col(ColumnSerializer.storage)}
+}};
+  '''.strip('\n')
+
+
+def serialize_header(ifdef_guard: str, tables: List[Table],
+                     include_paths: List[str]) -> str:
+  """Serializes a table header file containing the given set of tables."""
+  include_paths_str = '\n'.join([f'#include "{i}"' for i in include_paths])
+  tables_str = '\n\n'.join([TableSerializer(t).serialize() for t in tables])
+  return f'''
+#ifndef {ifdef_guard}
+#define {ifdef_guard}
+
+#include "src/trace_processor/tables/macros.h"
+
+{include_paths_str}
+
+namespace perfetto {{
+namespace trace_processor {{
+namespace tables {{
+
+{tables_str.strip()}
+
+}}  // namespace tables
+}}  // namespace trace_processor
+}}  // namespace perfetto
+
+#endif  // {ifdef_guard}
+  '''.strip()
diff --git a/python/generators/trace_processor_table/util.py b/python/generators/trace_processor_table/util.py
new file mode 100644
index 0000000..7acfea7
--- /dev/null
+++ b/python/generators/trace_processor_table/util.py
@@ -0,0 +1,197 @@
+# Copyright (C) 2022 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 dataclasses
+from dataclasses import dataclass
+from typing import Dict
+from typing import List
+from typing import Set
+from typing import Optional
+from typing import Union
+
+from python.generators.trace_processor_table.public import Alias
+from python.generators.trace_processor_table.public import Column
+from python.generators.trace_processor_table.public import ColumnDoc
+from python.generators.trace_processor_table.public import ColumnFlag
+from python.generators.trace_processor_table.public import CppColumnType
+from python.generators.trace_processor_table.public import CppInt32
+from python.generators.trace_processor_table.public import CppInt64
+from python.generators.trace_processor_table.public import CppOptional
+from python.generators.trace_processor_table.public import CppSelfTableId
+from python.generators.trace_processor_table.public import CppString
+from python.generators.trace_processor_table.public import CppTableId
+from python.generators.trace_processor_table.public import CppUint32
+from python.generators.trace_processor_table.public import Table
+
+
+@dataclass
+class ParsedType:
+  """Result of parsing a CppColumnType into its parts."""
+  cpp_type: str
+  is_optional: bool = False
+  is_alias: bool = False
+  alias_underlying_name: Optional[str] = None
+  is_self_id: bool = False
+  id_table: Optional[Table] = None
+
+  def cpp_type_with_optionality(self) -> str:
+    """Returns the C++ type wrapping with base::Optional if necessary."""
+
+    # ThreadTable and ProcessTable are special for legacy reasons as they were
+    # around even before the advent of C++ macro tables. Because of this a lot
+    # of code was written assuming that upid and utid were uint32 (e.g. indexing
+    # directly into vectors using them) and it was decided this behaviour was
+    # too expensive in engineering cost to fix given the trivial benefit. For
+    # this reason, continue to maintain this illusion.
+    if self.id_table and (self.id_table.class_name == 'ThreadTable' or
+                          self.id_table.class_name == 'ProcessTable'):
+      cpp_type = 'uint32_t'
+    else:
+      cpp_type = self.cpp_type
+    if self.is_optional:
+      return f'base::Optional<{cpp_type}>'
+    return cpp_type
+
+
+def public_sql_name_for_table(table: Table) -> str:
+  """Extracts SQL name for the table which should be publicised."""
+
+  wrapping_view = table.wrapping_sql_view
+  return wrapping_view.view_name if wrapping_view else table.sql_name
+
+
+def parse_type(table: Table, col_type: CppColumnType) -> ParsedType:
+  """Parses a CppColumnType into its constiuient parts."""
+
+  if isinstance(col_type, CppInt64):
+    return ParsedType('int64_t')
+  if isinstance(col_type, CppInt32):
+    return ParsedType('int32_t')
+  if isinstance(col_type, CppUint32):
+    return ParsedType('uint32_t')
+  if isinstance(col_type, CppString):
+    return ParsedType('StringPool::Id')
+
+  if isinstance(col_type, Alias):
+    col = next(c for c in table.columns if c.name == col_type.underlying_column)
+    return ParsedType(
+        parse_type(table, col.type).cpp_type,
+        is_alias=True,
+        alias_underlying_name=col.name)
+
+  if isinstance(col_type, CppTableId):
+    return ParsedType(
+        f'{col_type.table.class_name}::Id', id_table=col_type.table)
+
+  if isinstance(col_type, CppSelfTableId):
+    return ParsedType(
+        f'{table.class_name}::Id', is_self_id=True, id_table=table)
+
+  if isinstance(col_type, CppOptional):
+    inner = parse_type(table, col_type.inner)
+    assert not inner.is_optional, 'Nested optional not allowed'
+    return dataclasses.replace(inner, is_optional=True)
+
+  raise Exception(f'Unknown type {col_type}')
+
+
+def augment_table_with_auto_cols(table: Table) -> Table:
+  """Adds auto-added columns (i.e. id and type) to the user defined table."""
+
+  auto_cols = [
+      Column('id', CppSelfTableId(), ColumnFlag.SORTED, _is_auto_added_id=True),
+      Column('type', CppString(), ColumnFlag.NONE, _is_auto_added_type=True),
+  ]
+  public_sql_name = public_sql_name_for_table(table)
+  new_cols_doc: Dict[str, Union[ColumnDoc, str]] = {
+      'id':
+          ColumnDoc(doc=f'Unique idenitifier for this {public_sql_name}.'),
+      'type':
+          ColumnDoc(doc='''
+                The name of the "most-specific" child table containing this row.
+              '''),
+  }
+  new_cols_doc.update(table.tabledoc.columns)
+  return dataclasses.replace(
+      table,
+      columns=auto_cols + table.columns,
+      tabledoc=dataclasses.replace(table.tabledoc, columns=new_cols_doc))
+
+
+def find_table_deps(table: Table) -> Set[str]:
+  """Finds all the other table class names this table depends on.
+
+  By "depends", we mean this table in C++ would need the dependency to be
+  defined (or included) before this table is defined."""
+  deps: Set[str] = set()
+  for c in table.columns:
+    id_table = parse_type(table, c.type).id_table
+    if id_table:
+      deps.add(id_table.class_name)
+  return deps
+
+
+def topological_sort_tables(tables: List[Table]) -> List[Table]:
+  """Topologically sorts a list of tables (i.e. dependenices appear earlier).
+
+  See [1] for information on a topological sort. We do this to allow
+  dependencies to be processed and appear ealier than their dependents.
+
+  [1] https://en.wikipedia.org/wiki/Topological_sorting"""
+  tables_by_name: dict[str, Table] = dict((t.class_name, t) for t in tables)
+  visited: Set[str] = set()
+  result: List[Table] = []
+
+  # Topological sorting is really just a DFS where we put the nodes in the list
+  # after any dependencies.
+  def dfs(table_class_name: str):
+    table = tables_by_name.get(table_class_name)
+    # If the table is not found, that might be because it's not in this list of
+    # tables. Just ignore this as its up to the caller to make sure any external
+    # deps are handled correctly.
+    if not table or table.class_name in visited:
+      return
+    visited.add(table.class_name)
+
+    for dep in find_table_deps(table):
+      dfs(dep)
+    result.append(table)
+
+  for table in tables:
+    dfs(table.class_name)
+  return result
+
+
+def to_cpp_flags(raw_flag: ColumnFlag) -> str:
+  """Converts a ColumnFlag to the C++ flags which it represents
+
+  It is not valid to call this function with ColumnFlag.NONE as in this case
+  defaults for that column should be implicitly used."""
+
+  assert raw_flag != ColumnFlag.NONE
+  flags = []
+  if ColumnFlag.SORTED in raw_flag:
+    flags.append('Column::Flag::kSorted')
+  if ColumnFlag.SET_ID in raw_flag:
+    flags.append('Column::Flag::kSetId')
+  return ' | '.join(flags)
+
+
+def typed_column_type(table: Table, col: Column) -> str:
+  """Returns the TypedColumn/IdColumn C++ type for a given column."""
+
+  parsed = parse_type(table, col.type)
+  if col._is_auto_added_id:
+    return f'IdColumn<{parsed.cpp_type}>'
+  return f'TypedColumn<{parsed.cpp_type_with_optionality()}>'
diff --git a/python/perfetto/batch_trace_processor/api.py b/python/perfetto/batch_trace_processor/api.py
index c26a8e6..f6f353a 100644
--- a/python/perfetto/batch_trace_processor/api.py
+++ b/python/perfetto/batch_trace_processor/api.py
@@ -13,9 +13,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 """Contains classes for BatchTraceProcessor API."""
-
+import abc
 import concurrent.futures as cf
 import dataclasses as dc
+import time
 from enum import Enum
 import multiprocessing
 from typing import Any, Callable, Dict, Tuple, List, Optional
@@ -98,9 +99,28 @@
         print(df)
   """
 
+  class Observer(abc.ABC):
+    """Observer that can be used to provide side-channel information about
+    processed traces.
+    """
+
+    @abc.abstractmethod
+    def trace_processed(self, metadata: Metadata,
+                        execution_time_seconds: float):
+      """Invoked every time query has been executed on a trace.
+
+      Args:
+        metadata: Metadata provided by trace resolver, can be used to identify
+          the trace.
+
+        execution_time_seconds: Query execution time, in seconds.
+      """
+      raise NotImplementedError
+
   def __init__(self,
                traces: TraceListReference,
-               config: BatchTraceProcessorConfig = BatchTraceProcessorConfig()):
+               config: BatchTraceProcessorConfig = BatchTraceProcessorConfig(),
+               observer: Optional[Observer] = None):
     """Creates a batch trace processor instance.
 
     BatchTraceProcessor is the blessed way of running ad-hoc queries in
@@ -130,6 +150,8 @@
         |config.resolver_registry|.
       config: configuration options which customize functionality of batch
         trace processor and underlying trace processors.
+      observer: an optional observer for side-channel information, e.g.
+        running time of queries.
     """
 
     self.tps_and_metadata = None
@@ -140,6 +162,8 @@
     self.tp_platform_delegate = TP_PLATFORM_DELEGATE()
     self.config = config
 
+    self.observer = observer
+
     # Make sure the descendent trace processors are using the same resolver
     # registry (even though they won't actually use it as we will resolve
     # everything fully in this class).
@@ -212,13 +236,13 @@
       value in the dataframe).
 
       For example:
-        class CustomResolver(TraceResolver):
+        class CustomResolver(TraceUriResolver):
           def resolve(self):
-            return [TraceResolver.Result(trace='/tmp/path',
-                                        metadata={
-                                          'path': '/tmp/path'
-                                          'foo': 'bar'
-                                        })]
+            return [TraceUriResolver.Result(trace='/tmp/path',
+                                            metadata={
+                                              'path': '/tmp/path'
+                                              'foo': 'bar'
+                                            })]
 
         with BatchTraceProcessor(CustomResolver()) as btp:
           df = btp.query_and_flatten('select count(1) as cnt from slice')
@@ -302,7 +326,11 @@
 
     def wrapped(pair: Tuple[TraceProcessor, Metadata]):
       (tp, metadata) = pair
+      start = time.time()
       df = self._execute_handling_failure(fn, tp, metadata)
+      end = time.time()
+      if self.observer:
+        self.observer.trace_processed(metadata, end - start)
       for key, value in metadata.items():
         df[key] = value
       return df
diff --git a/python/perfetto/experimental/slice_breakdown/breakdown.py b/python/perfetto/experimental/slice_breakdown/breakdown.py
index 5debd2d..13dd665 100644
--- a/python/perfetto/experimental/slice_breakdown/breakdown.py
+++ b/python/perfetto/experimental/slice_breakdown/breakdown.py
@@ -186,14 +186,15 @@
     The same as |compute_breakdown| but only containing slices which happened
     during app startup.
   """
-  tp.metric(['android_startup'])
 
   # Verify there was only one startup in the trace matching the package
   # name.
   filter = "WHERE package = '{}'".format(package_name) if package_name else ''
   launches = tp.query(f'''
+    SELECT IMPORT('android.startup.startups');
+
     SELECT ts, ts_end, dur
-    FROM launches
+    FROM android_startups
     {filter}
   ''').as_pandas_dataframe()
   if len(launches) == 0:
diff --git a/python/perfetto/prebuilts/manifests/trace_processor_shell.py b/python/perfetto/prebuilts/manifests/trace_processor_shell.py
index 1c2ebd4..8510090 100755
--- a/python/perfetto/prebuilts/manifests/trace_processor_shell.py
+++ b/python/perfetto/prebuilts/manifests/trace_processor_shell.py
@@ -1,15 +1,15 @@
-# This file has been generated by: /s/perfetto/tools/roll-prebuilts v28.0
+# This file has been generated by: /usr/local/google/home/lalitm/perfetto/tools/roll-prebuilts v31.0
 TRACE_PROCESSOR_SHELL_MANIFEST = [{
     'arch':
         'mac-amd64',
     'file_name':
         'trace_processor_shell',
     'file_size':
-        7991568,
+        8516960,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-amd64/trace_processor_shell',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/mac-amd64/trace_processor_shell',
     'sha256':
-        '4704eb8c51946ae288b74ad9f4386019a2b146c310c7717aeeaada73d6eef8fc',
+        'e15e409929d3c140ce82f69ad3f0b0634a4d9b765a1a61f03d8ff29d87cb8f7f',
     'platform':
         'darwin',
     'machine': ['x86_64']
@@ -19,11 +19,11 @@
     'file_name':
         'trace_processor_shell',
     'file_size':
-        6757608,
+        7270264,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-arm64/trace_processor_shell',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/mac-arm64/trace_processor_shell',
     'sha256':
-        '2aa7ddebedb6d5cf6300b3dc3e6ea8e294f645f67c2dbd727d497ab4a17d6599',
+        'd500226e1d53c3e8db4f29f44df463139f8fbc81ba2364934f6a69e76d9d065d',
     'platform':
         'darwin',
     'machine': ['arm64']
@@ -33,11 +33,11 @@
     'file_name':
         'trace_processor_shell',
     'file_size':
-        8568248,
+        8944032,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-amd64/trace_processor_shell',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-amd64/trace_processor_shell',
     'sha256':
-        '67deabb7e215cc66d3694c0e0367f441cd48a3456d2dd5877bab6eb07117887f',
+        '5a2303c36e852f044962c502b9e24d1f5ffb468f25c4672cbe8e5dd9c345906c',
     'platform':
         'linux',
     'machine': ['x86_64']
@@ -47,11 +47,11 @@
     'file_name':
         'trace_processor_shell',
     'file_size':
-        6279736,
+        7110964,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm/trace_processor_shell',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-arm/trace_processor_shell',
     'sha256':
-        '172ac15b010b1e9a28e1efb8991e2e9195dc732969b33e04ebd5b5f801a99218',
+        '4646a44ccab1f28c29c045edda338ebbde2880d5431cc151a6003daf8efe8ddb',
     'platform':
         'linux',
     'machine': ['armv6l', 'armv7l', 'armv8l']
@@ -61,11 +61,11 @@
     'file_name':
         'trace_processor_shell',
     'file_size':
-        7703256,
+        8380416,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm64/trace_processor_shell',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-arm64/trace_processor_shell',
     'sha256':
-        'd70d621ca9aedff0b29a24984ff896e7069cadee607ada1ccfca58634e1774a1',
+        '130c60bebbbb338d8e9c250a5fbd526296d4cab49af08680fef5134cb6831130',
     'platform':
         'linux',
     'machine': ['aarch64']
@@ -75,55 +75,55 @@
     'file_name':
         'trace_processor_shell',
     'file_size':
-        5357880,
+        5807408,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm/trace_processor_shell',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-arm/trace_processor_shell',
     'sha256':
-        '75285ac8964e93890b936bfacd2679ee67ae4cd029be74e675253f92746f9904'
+        '5d13bc0d37f3379a6fd0d66d8f98c36ec8bf80c133b7660ae8b7cbcfe41284f4'
 }, {
     'arch':
         'android-arm64',
     'file_name':
         'trace_processor_shell',
     'file_size':
-        6940832,
+        7458752,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm64/trace_processor_shell',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-arm64/trace_processor_shell',
     'sha256':
-        '75118859c9a1b2ed2bcffc664eb98884dab26a9d2ad7788d05d90ef22b24a8d5'
+        '3e038732d0ee77875e6376ddf0f804271c4e5337b76977e939c91a553621587a'
 }, {
     'arch':
         'android-x86',
     'file_name':
         'trace_processor_shell',
     'file_size':
-        7856396,
+        8404228,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x86/trace_processor_shell',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-x86/trace_processor_shell',
     'sha256':
-        'c3d4da7aa12a2eedd59df98b5c8143658f39ebd9a4ba3c8180be33ab3484e824'
+        '1a04a4d888dfa16a5baa36073df50f28ea9f5ed688fb59fe8ef24a9f3341ad77'
 }, {
     'arch':
         'android-x64',
     'file_name':
         'trace_processor_shell',
     'file_size':
-        8194240,
+        8744960,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x64/trace_processor_shell',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-x64/trace_processor_shell',
     'sha256':
-        '105c70a3908bf6997b428ee7f74bf7ee8a48907b860480c87e62a62cf92e1a8a'
+        '45fe2744b60baac964c636304b4b83853405f7f4f7329b2764cf921d3d90a2b1'
 }, {
     'arch':
         'windows-amd64',
     'file_name':
         'trace_processor_shell.exe',
     'file_size':
-        7857152,
+        8152064,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/windows-amd64/trace_processor_shell.exe',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/windows-amd64/trace_processor_shell.exe',
     'sha256':
-        'eda1e38cb7490a99ba31285a977a3a564c53f25eae2b301152b2578de62fcc5d',
+        '6d5e873b65ea68ec5ddf51a02a049f350e9da4f5f78cc7294d0bbf95f7673ea9',
     'platform':
         'win32',
     'machine': ['amd64']
diff --git a/python/perfetto/prebuilts/manifests/tracebox.py b/python/perfetto/prebuilts/manifests/tracebox.py
index 46779cb..50a74de 100755
--- a/python/perfetto/prebuilts/manifests/tracebox.py
+++ b/python/perfetto/prebuilts/manifests/tracebox.py
@@ -1,15 +1,15 @@
-# This file has been generated by: /s/perfetto/tools/roll-prebuilts v28.0
+# This file has been generated by: /usr/local/google/home/lalitm/perfetto/tools/roll-prebuilts v31.0
 TRACEBOX_MANIFEST = [{
     'arch':
         'mac-amd64',
     'file_name':
         'tracebox',
     'file_size':
-        1399080,
+        1431944,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-amd64/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/mac-amd64/tracebox',
     'sha256':
-        '05674f9872a14bf92d4215a180c883ab54c58376d7e08b95e3f83d03efdeba21',
+        '9ba2bd56eac2464060158fc6a972709663102fd79e210036558a79e0450a39a7',
     'platform':
         'darwin',
     'machine': ['x86_64']
@@ -19,11 +19,11 @@
     'file_name':
         'tracebox',
     'file_size':
-        1292504,
+        1325592,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-arm64/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/mac-arm64/tracebox',
     'sha256':
-        '48008e6aeb7680c4f31b1147d5c26a504368a696c793aec1895bb5fb1f597f64',
+        '2bc59074dc169bd3ca0b58fc8e6536b5a8d35f99ef6d44ce467719b888f5f65e',
     'platform':
         'darwin',
     'machine': ['arm64']
@@ -33,11 +33,11 @@
     'file_name':
         'tracebox',
     'file_size':
-        2243632,
+        2140080,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-amd64/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-amd64/tracebox',
     'sha256':
-        '12c87ba1d03e39d4c07792e09f0be9e240677cec2d9bc1638b0a676bc664d724',
+        'ee12c839586f19d024c6de78825b416fcb7d9a6d4fe6e9266e239812ec63e188',
     'platform':
         'linux',
     'machine': ['x86_64']
@@ -47,11 +47,11 @@
     'file_name':
         'tracebox',
     'file_size':
-        1315252,
+        1286400,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-arm/tracebox',
     'sha256':
-        '3d026fe982e90fb5bc410528f210458f9bcafea30d3e1b9728772fd32148ee9a',
+        'e4b76213b71d192a6155f576ccf265b9f2605090eff01444216fa49e3f06c40c',
     'platform':
         'linux',
     'machine': ['armv6l', 'armv7l', 'armv8l']
@@ -61,11 +61,11 @@
     'file_name':
         'tracebox',
     'file_size':
-        2089160,
+        2070760,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm64/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-arm64/tracebox',
     'sha256':
-        '9f524f943a1c12dcb6b273e78d5c46952d4a7528514639cd2537686d5c530c89',
+        '653469e497c7f021c51220b2b3f0a70efce35dcb4ca897bbf09e24a89af92aea',
     'platform':
         'linux',
     'machine': ['aarch64']
@@ -75,42 +75,42 @@
     'file_name':
         'tracebox',
     'file_size':
-        1099732,
+        1157076,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-arm/tracebox',
     'sha256':
-        '53a1a65e2e409a552cd75f003c755b5903075e37e06841b3a514889306fb9616'
+        '6f610486d0ee1d052a21d6c16491a2c373634bbfa5374fed01026eb2767c05f7'
 }, {
     'arch':
         'android-arm64',
     'file_name':
         'tracebox',
     'file_size':
-        1653416,
+        1768104,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm64/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-arm64/tracebox',
     'sha256':
-        'df911aa2e5d80d1fdda5860e1d18dca7ba13c835aada090bb07e8f253562129a'
+        'b30ca03fa0cb1261386b5d6b01161c275bf68026e894bf5d74781abf8f78acef'
 }, {
     'arch':
         'android-x86',
     'file_name':
         'tracebox',
     'file_size':
-        1677228,
+        1755052,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x86/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-x86/tracebox',
     'sha256':
-        '8d1a296a084f488f826dac837530a40f2ca38af76c70a10966e11f9fec91a2f3'
+        '781ed489fe91aad98fc72f9d0faf69c62323feaac8ae0ce813fbfe0274789144'
 }, {
     'arch':
         'android-x64',
     'file_name':
         'tracebox',
     'file_size':
-        1911464,
+        2038440,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x64/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-x64/tracebox',
     'sha256':
-        '8f844f4d8263d0ff904d120b5858a02d7a24efd87f49c3fa4fd8682d534c738f'
+        'e58167092e4ec060efc152ace675175f04af57c564ec2ca38704fa65af8a7b25'
 }]
diff --git a/python/perfetto/prebuilts/manifests/traceconv.py b/python/perfetto/prebuilts/manifests/traceconv.py
index 3b7334a..e17cd91 100755
--- a/python/perfetto/prebuilts/manifests/traceconv.py
+++ b/python/perfetto/prebuilts/manifests/traceconv.py
@@ -1,15 +1,15 @@
-# This file has been generated by: /s/perfetto/tools/roll-prebuilts v28.0
+# This file has been generated by: /usr/local/google/home/lalitm/perfetto/tools/roll-prebuilts v31.0
 TRACECONV_MANIFEST = [{
     'arch':
         'mac-amd64',
     'file_name':
         'traceconv',
     'file_size':
-        7181712,
+        7772872,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-amd64/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/mac-amd64/traceconv',
     'sha256':
-        '6b398ad9539ddf8208536c0412db198d4627daa97efc7e0850f3e7ec0e115510',
+        'a987cfd2e722994a5911032d046ec2d0b912845a83b093226c3fccd5e316ed01',
     'platform':
         'darwin',
     'machine': ['x86_64']
@@ -19,11 +19,11 @@
     'file_name':
         'traceconv',
     'file_size':
-        6025176,
+        6554552,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-arm64/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/mac-arm64/traceconv',
     'sha256':
-        '407e2988e795a593158304061c547093ad74419f826dd03c2a66911b5a29d065',
+        '972610d249990d4b03de50bece4a7adf03ebd6b3cc3c2c10feb7bb6561c1fce1',
     'platform':
         'darwin',
     'machine': ['arm64']
@@ -33,11 +33,11 @@
     'file_name':
         'traceconv',
     'file_size':
-        7668600,
+        8073432,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-amd64/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-amd64/traceconv',
     'sha256':
-        '1bebc0dd7b2b18fd4abeeb5f811d6d4c7f431d212efd5469c7e5d8b18b19e0c7',
+        'e34f6ddd91ad0de62bfbef76303f2a94136470fe5a4f679af2f5b8898548ac13',
     'platform':
         'linux',
     'machine': ['x86_64']
@@ -47,11 +47,11 @@
     'file_name':
         'traceconv',
     'file_size':
-        5827680,
+        6677612,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-arm/traceconv',
     'sha256':
-        'a19780145f965838e334a57a52230bc67b0db207365746360314fbbbe4e1d12f',
+        '55c9cd6e5c5bc65210068a173b0c16ad3323faaba0fccec20247dca5bd3b913d',
     'platform':
         'linux',
     'machine': ['armv6l', 'armv7l', 'armv8l']
@@ -61,11 +61,11 @@
     'file_name':
         'traceconv',
     'file_size':
-        6876384,
+        7543648,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm64/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-arm64/traceconv',
     'sha256':
-        '2a5e5fcf340070ed6a30204c79b7e76787c5f26181bc8377587547f3eb5df685',
+        '2477857470f613410f6151acb06b6b6a067ef8612d619eee86dd2a3b8aac2a3e',
     'platform':
         'linux',
     'machine': ['aarch64']
@@ -75,55 +75,55 @@
     'file_name':
         'traceconv',
     'file_size':
-        4881820,
+        5360020,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-arm/traceconv',
     'sha256':
-        '73827b82d941a9650580fbd48c3d4ff2323eb8d4ff9d3fffd3e0cac1bc853f34'
+        '60cecbd8d9b6357bb89929fa5afa1eba6c1d46d621e62ee558509424d88bd53d'
 }, {
     'arch':
         'android-arm64',
     'file_name':
         'traceconv',
     'file_size':
-        6222592,
+        6769184,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm64/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-arm64/traceconv',
     'sha256':
-        '72d46258645d486f40ee463052b609d1fd7c4cc64f70c0ba2ef811a9924be98e'
+        'e373cbed4502977290abe3e246132ced538fca33881058e6049676ef9c613dd9'
 }, {
     'arch':
         'android-x86',
     'file_name':
         'traceconv',
     'file_size':
-        7089524,
+        7682412,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x86/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-x86/traceconv',
     'sha256':
-        '689d0b48f91624585285b3833362cdcfdf0de1ff5dedcb97bb9851c729b4a15e'
+        'dcaa5d156247433537d59ad33238feb23dc0f97ba64c51581360f25ac0fc16a8'
 }, {
     'arch':
         'android-x64',
     'file_name':
         'traceconv',
     'file_size':
-        7316248,
+        7903832,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x64/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-x64/traceconv',
     'sha256':
-        '785ec3f0da302ed52521febc5ed5e2cef57ae8840ff241037c51b8d94464f6a2'
+        'b73b8d398019b2a349d3748b4fe4096a069c6362697375d3c2668cf79ef1bb38'
 }, {
     'arch':
         'windows-amd64',
     'file_name':
         'traceconv.exe',
     'file_size':
-        6850048,
+        7188992,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/windows-amd64/traceconv.exe',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/windows-amd64/traceconv.exe',
     'sha256':
-        '19cdec3824d369be3bb053b40b3cfe9f62c2e57e71a5e2ee17ca15b6e7463683',
+        '9cbdfcad3b5f2164d0c9e79d4f8b749db693814ae97fba038385d2e6f0111f72',
     'platform':
         'win32',
     'machine': ['amd64']
diff --git a/python/perfetto/prebuilts/perfetto_prebuilts.py b/python/perfetto/prebuilts/perfetto_prebuilts.py
index 723e796..087a337 100644
--- a/python/perfetto/prebuilts/perfetto_prebuilts.py
+++ b/python/perfetto/prebuilts/perfetto_prebuilts.py
@@ -52,8 +52,8 @@
   """ Downloads a prebuilt or returns a cached version
 
   The first time this is invoked, it downloads the |url| and caches it into
-  ~/.perfetto/prebuilts/$tool_name. On subsequent invocations it just runs the
-  cached version.
+  ~/.local/share/perfetto/prebuilts/$tool_name. On subsequent invocations it
+  just runs the cached version.
   """
   dir = os.path.join(
       os.path.expanduser('~'), '.local', 'share', 'perfetto', 'prebuilts')
@@ -81,7 +81,7 @@
       raise Exception('Checksum mismatch for %s (actual: %s, expected: %s)' %
                       (url, actual_sha256, sha256))
     os.chmod(tmp_path, 0o755)
-    os.rename(tmp_path, bin_path)
+    os.replace(tmp_path, bin_path)
     with open(sha256_path, 'w') as f:
       f.write(sha256)
   return bin_path
diff --git a/python/perfetto/trace_processor/api.py b/python/perfetto/trace_processor/api.py
index 604f19b..d884759 100644
--- a/python/perfetto/trace_processor/api.py
+++ b/python/perfetto/trace_processor/api.py
@@ -46,6 +46,7 @@
   unique_port: bool
   verbose: bool
   ingest_ftrace_in_raw: bool
+  enable_dev_features: bool
   resolver_registry: Optional[ResolverRegistry]
 
   def __init__(self,
@@ -53,11 +54,13 @@
                unique_port: bool = True,
                verbose: bool = False,
                ingest_ftrace_in_raw: bool = False,
+               enable_dev_features=False,
                resolver_registry: Optional[ResolverRegistry] = None):
     self.bin_path = bin_path
     self.unique_port = unique_port
     self.verbose = verbose
     self.ingest_ftrace_in_raw = ingest_ftrace_in_raw
+    self.enable_dev_features = enable_dev_features
     self.resolver_registry = resolver_registry
 
 
@@ -328,6 +331,7 @@
                                       self.config.unique_port,
                                       self.config.verbose,
                                       self.config.ingest_ftrace_in_raw,
+                                      self.config.enable_dev_features,
                                       self.platform_delegate)
     return TraceProcessorHttp(url, protos=self.protos)
 
@@ -364,3 +368,6 @@
       self.subprocess.kill()
       self.subprocess.wait()
     self.http.conn.close()
+
+  def __del__(self):
+    self.close()
diff --git a/python/perfetto/trace_processor/metrics.descriptor b/python/perfetto/trace_processor/metrics.descriptor
index 5de2463..bafcbbc 100644
--- a/python/perfetto/trace_processor/metrics.descriptor
+++ b/python/perfetto/trace_processor/metrics.descriptor
Binary files differ
diff --git a/python/perfetto/trace_processor/metrics.descriptor.sha1 b/python/perfetto/trace_processor/metrics.descriptor.sha1
index 026813d..aff5d76 100644
--- a/python/perfetto/trace_processor/metrics.descriptor.sha1
+++ b/python/perfetto/trace_processor/metrics.descriptor.sha1
@@ -2,5 +2,5 @@
 // SHA1(tools/gen_binary_descriptors)
 // 6886b319e65925c037179e71a803b8473d06dc7d
 // SHA1(protos/perfetto/metrics/metrics.proto)
-// f0a8addcb2ad208e6aee70249ccd28264a5bdcd5
+// 493e9c04a68437008b69b5b78cae53802b33c624
   
\ No newline at end of file
diff --git a/python/perfetto/trace_processor/shell.py b/python/perfetto/trace_processor/shell.py
index d000044..40cf964 100644
--- a/python/perfetto/trace_processor/shell.py
+++ b/python/perfetto/trace_processor/shell.py
@@ -26,7 +26,8 @@
 
 
 def load_shell(bin_path: str, unique_port: bool, verbose: bool,
-               ingest_ftrace_in_raw: bool, platform_delegate: PlatformDelegate):
+               ingest_ftrace_in_raw: bool, enable_dev_features: bool,
+               platform_delegate: PlatformDelegate):
   addr, port = platform_delegate.get_bind_addr(
       port=0 if unique_port else TP_PORT)
   url = f'{addr}:{str(port)}'
@@ -41,13 +42,17 @@
   if not ingest_ftrace_in_raw:
     args.append('--no-ftrace-raw')
 
+  if enable_dev_features:
+    args.append('--dev')
+
   p = subprocess.Popen(
       tp_exec + args,
+      stdin=subprocess.DEVNULL,
       stdout=subprocess.DEVNULL,
       stderr=None if verbose else subprocess.DEVNULL)
 
   success = False
-  for i in range(3):
+  for _ in range(3):
     try:
       if p.poll() is None:
         _ = request.urlretrieve(f'http://{url}/status')
diff --git a/python/perfetto/trace_processor/trace_processor.descriptor b/python/perfetto/trace_processor/trace_processor.descriptor
index 6a3ae7a..29fe8c8 100644
--- a/python/perfetto/trace_processor/trace_processor.descriptor
+++ b/python/perfetto/trace_processor/trace_processor.descriptor
Binary files differ
diff --git a/python/perfetto/trace_processor/trace_processor.descriptor.sha1 b/python/perfetto/trace_processor/trace_processor.descriptor.sha1
index ce1e9bb..7f7827f 100644
--- a/python/perfetto/trace_processor/trace_processor.descriptor.sha1
+++ b/python/perfetto/trace_processor/trace_processor.descriptor.sha1
@@ -2,5 +2,5 @@
 // SHA1(tools/gen_binary_descriptors)
 // 6886b319e65925c037179e71a803b8473d06dc7d
 // SHA1(protos/perfetto/trace_processor/trace_processor.proto)
-// 6d81e358a9db005e8bed1685412434560d172183
+// 59f86a32aa28f29e290d8ce3f97461725aa8b9f8
   
\ No newline at end of file
diff --git a/python/perfetto/trace_uri_resolver/resolver.py b/python/perfetto/trace_uri_resolver/resolver.py
index 7344dd4..bcdbf93 100644
--- a/python/perfetto/trace_uri_resolver/resolver.py
+++ b/python/perfetto/trace_uri_resolver/resolver.py
@@ -54,7 +54,7 @@
   up traces using URI strings.
 
   For example:
-    class CustomTraceResolver(TraceResolver):
+    class CustomTraceResolver(TraceUriResolver):
       PREFIX = 'custom'
 
       def __init__(self, build_branch: List[str] = None, id: str = None):
@@ -66,7 +66,7 @@
         traces = self.db.lookup(
           id=self.id, build_branch=self.build_branch)['path']
         return [
-          TraceResolver.Result(
+          TraceUriResolver.Result(
             trace=t['path'],
             args={'iteration': t['iteration'], 'device': t['device']}
           )
diff --git a/python/run_tests.py b/python/run_tests.py
index 9c1dbf9..245b7f6 100755
--- a/python/run_tests.py
+++ b/python/run_tests.py
@@ -21,6 +21,7 @@
 from test import api_unittest
 from test import api_integrationtest
 from test import resolver_unittest
+from test import stdlib_unittest
 
 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 
@@ -47,6 +48,7 @@
   suite.addTests(loader.loadTestsFromModule(api_unittest))
   suite.addTests(loader.loadTestsFromModule(resolver_unittest))
   suite.addTests(loader.loadTestsFromModule(api_integrationtest))
+  suite.addTests(loader.loadTestsFromModule(stdlib_unittest))
 
   # Initialise runner to run all tests in suite
   runner = unittest.TextTestRunner(verbosity=3)
diff --git a/python/setup.py b/python/setup.py
index 15ccfd1..db49cac 100644
--- a/python/setup.py
+++ b/python/setup.py
@@ -8,13 +8,13 @@
     ],
     package_data={'perfetto.trace_processor': ['*.descriptor']},
     include_package_data=True,
-    version='0.5.0',
+    version='0.6.0',
     license='apache-2.0',
     description='Python API for Perfetto\'s Trace Processor',
     author='Perfetto',
     author_email='perfetto-pypi@google.com',
     url='https://perfetto.dev/',
-    download_url='https://github.com/google/perfetto/archive/refs/tags/v20.1.tar.gz',
+    download_url='https://github.com/google/perfetto/archive/refs/tags/v30.0.tar.gz',
     keywords=['trace processor', 'tracing', 'perfetto'],
     install_requires=[
         'protobuf',
diff --git a/python/test/api_integrationtest.py b/python/test/api_integrationtest.py
index 090c464..bc86231 100644
--- a/python/test/api_integrationtest.py
+++ b/python/test/api_integrationtest.py
@@ -16,12 +16,14 @@
 import io
 import os
 import unittest
+from typing import Optional
 
 import pandas as pd
 
 from perfetto.batch_trace_processor.api import BatchTraceProcessor
 from perfetto.batch_trace_processor.api import BatchTraceProcessorConfig
 from perfetto.batch_trace_processor.api import FailureHandling
+from perfetto.batch_trace_processor.api import Metadata
 from perfetto.batch_trace_processor.api import TraceListReference
 from perfetto.trace_processor.api import PLATFORM_DELEGATE
 from perfetto.trace_processor.api import TraceProcessor
@@ -88,11 +90,20 @@
     ]
 
 
+class SimpleObserver(BatchTraceProcessor.Observer):
+
+  def __init__(self):
+    self.execution_times = []
+
+  def trace_processed(self, metadata: Metadata, execution_time_seconds: float):
+    self.execution_times.append(execution_time_seconds)
+
+
 def create_batch_tp(
     traces: TraceListReference,
     load_failure_handling: FailureHandling = FailureHandling.RAISE_EXCEPTION,
     execute_failure_handling: FailureHandling = FailureHandling.RAISE_EXCEPTION,
-):
+    observer: Optional[BatchTraceProcessor.Observer] = None):
   registry = PLATFORM_DELEGATE().default_resolver_registry()
   registry.register(SimpleResolver)
   registry.register(RecursiveResolver)
@@ -101,7 +112,7 @@
       execute_failure_handling=execute_failure_handling,
       tp_config=TraceProcessorConfig(
           bin_path=os.environ["SHELL_PATH"], resolver_registry=registry))
-  return BatchTraceProcessor(traces=traces, config=config)
+  return BatchTraceProcessor(traces=traces, config=config, observer=observer)
 
 
 def create_tp(trace: TraceReference):
@@ -205,6 +216,16 @@
       df = btp.query_and_flatten('select dur from slice limit 1')
       pd.testing.assert_frame_equal(df, expected, check_dtype=False)
 
+  def test_query_timing(self):
+    observer = SimpleObserver()
+    with create_batch_tp(
+        traces='simple:path={}'.format(example_android_trace_path()),
+        observer=observer) as btp:
+      btp.query_and_flatten('select dur from slice limit 1')
+      self.assertTrue(
+          all([x > 0 for x in observer.execution_times]),
+          'Running time should be positive')
+
   def test_recursive_resolver(self):
     dur = [
         178646, 178646, 178646, 178646, 178646, 178646, 178646, 178646, 178646
diff --git a/python/test/stdlib_unittest.py b/python/test/stdlib_unittest.py
new file mode 100644
index 0000000..3d64be7
--- /dev/null
+++ b/python/test/stdlib_unittest.py
@@ -0,0 +1,211 @@
+# Copyright (C) 2022 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 unittest
+import os
+import sys
+
+ROOT_DIR = os.path.dirname(
+    os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+sys.path.append(os.path.join(ROOT_DIR))
+
+from generators.stdlib_docs.stdlib import FunctionDocs, ViewFunctionDocs, TableViewDocs
+
+DESC = """--
+-- First line.
+-- Second line."""
+
+COLS_STR = """--
+-- @column slice_id           Id of slice.
+-- @column slice_name         Name of slice."""
+
+COLS_SQL_STR = "slice_id INT, slice_name STRING"
+
+ARGS_STR = """--
+-- @arg utid INT              Utid of thread.
+-- @arg name STRING           String name.
+"""
+
+ARGS_SQL_STR = "utid INT, name STRING"
+
+RET_STR = """--
+-- @ret BOOL                  Exists.
+"""
+
+RET_SQL_STR = "BOOL"
+
+SQL_STR = "SELECT * FROM slice"
+
+
+class TestStdlib(unittest.TestCase):
+
+  # Valid schemas
+
+  def test_valid_table(self):
+    valid_table_comment = f"{DESC}\n{COLS_STR}".split('\n')
+
+    docs, create_errors = TableViewDocs.create_from_comment(
+        "", valid_table_comment, 'common', ('table', 'tab_name', 'to_ignore'))
+    self.assertFalse(create_errors)
+
+    validation_errors = docs.check_comment()
+    self.assertFalse(validation_errors)
+
+  def test_valid_function(self):
+    valid_function = f"{DESC}\n{ARGS_STR}\n{RET_STR}".split('\n')
+    valid_regex_matches = ('fun_name', ARGS_SQL_STR, RET_SQL_STR, SQL_STR)
+    docs, create_errors = FunctionDocs.create_from_comment(
+        "", valid_function, 'common', valid_regex_matches)
+    self.assertFalse(create_errors)
+
+    validation_errors = docs.check_comment()
+    self.assertFalse(validation_errors)
+
+  def test_valid_view_function(self):
+    valid_view_function = f"{DESC}\n{ARGS_STR}\n{COLS_STR}".split('\n')
+    valid_regex_matches = ('fun_name', ARGS_SQL_STR, COLS_SQL_STR, SQL_STR)
+    docs, create_errors = ViewFunctionDocs.create_from_comment(
+        "", valid_view_function, 'common', valid_regex_matches)
+    self.assertFalse(create_errors)
+
+    validation_errors = docs.check_comment()
+    self.assertFalse(validation_errors)
+
+  # Missing modules in names
+
+  def test_missing_module_in_table_name(self):
+    valid_table_comment = f"{DESC}\n{COLS_STR}".split('\n')
+
+    _, create_errors = TableViewDocs.create_from_comment(
+        "", valid_table_comment, 'android', ('table', 'tab_name', 'to_ignore'))
+    self.assertTrue(create_errors)
+
+  def test_missing_module_in_function_name(self):
+    valid_function = f"{DESC}\n{ARGS_STR}\n{RET_STR}".split('\n')
+    valid_regex_matches = ('fun_name', ARGS_SQL_STR, RET_SQL_STR, SQL_STR)
+    _, create_errors = FunctionDocs.create_from_comment("", valid_function,
+                                                        'android',
+                                                        valid_regex_matches)
+    self.assertTrue(create_errors)
+
+  def test_missing_module_in_view_function_name(self):
+    valid_view_function = f"{DESC}\n{ARGS_STR}\n{COLS_STR}".split('\n')
+    valid_regex_matches = ('fun_name', ARGS_SQL_STR, COLS_SQL_STR, SQL_STR)
+    _, create_errors = ViewFunctionDocs.create_from_comment(
+        "", valid_view_function, 'android', valid_regex_matches)
+    self.assertTrue(create_errors)
+
+  # Missing part of schemas
+
+  def test_missing_desc_in_table_name(self):
+    comment = f"{COLS_STR}".split('\n')
+
+    _, create_errors = TableViewDocs.create_from_comment(
+        "", comment, 'common', ('table', 'tab_name', 'to_ignore'))
+    self.assertTrue(create_errors)
+
+  def test_missing_cols_in_table(self):
+    comment = f"{DESC}".split('\n')
+
+    _, create_errors = TableViewDocs.create_from_comment(
+        "", comment, 'common', ('table', 'tab_name', 'to_ignore'))
+    self.assertTrue(create_errors)
+
+  def test_missing_desc_in_function(self):
+    comment = f"{ARGS_STR}\n{RET_STR}".split('\n')
+    valid_regex_matches = ('fun_name', ARGS_SQL_STR, RET_SQL_STR, SQL_STR)
+    _, create_errors = FunctionDocs.create_from_comment("", comment, 'common',
+                                                        valid_regex_matches)
+    self.assertTrue(create_errors)
+
+  def test_missing_args_in_function(self):
+    comment = f"{DESC}\n{RET_STR}".split('\n')
+    valid_regex_matches = ('fun_name', ARGS_SQL_STR, RET_SQL_STR, SQL_STR)
+    _, create_errors = FunctionDocs.create_from_comment("", comment, 'common',
+                                                        valid_regex_matches)
+    self.assertTrue(create_errors)
+
+  def test_missing_ret_in_function(self):
+    comment = f"{DESC}\n{ARGS_STR}".split('\n')
+    valid_regex_matches = ('fun_name', ARGS_SQL_STR, RET_SQL_STR, SQL_STR)
+    _, create_errors = FunctionDocs.create_from_comment("", comment, 'common',
+                                                        valid_regex_matches)
+    self.assertTrue(create_errors)
+
+  def test_missing_desc_in_view_function(self):
+    comment = f"{ARGS_STR}\n{COLS_STR}".split('\n')
+    valid_regex_matches = ('fun_name', ARGS_SQL_STR, COLS_SQL_STR, SQL_STR)
+    _, create_errors = ViewFunctionDocs.create_from_comment(
+        "", comment, 'common', valid_regex_matches)
+    self.assertTrue(create_errors)
+
+  def test_missing_args_in_view_function(self):
+    comment = f"{DESC}\n{COLS_STR}".split('\n')
+    valid_regex_matches = ('fun_name', ARGS_SQL_STR, COLS_SQL_STR, SQL_STR)
+    _, create_errors = ViewFunctionDocs.create_from_comment(
+        "", comment, 'common', valid_regex_matches)
+    self.assertTrue(create_errors)
+
+  def test_missing_cols_in_view_function(self):
+    comment = f"{DESC}\n{ARGS_STR}".split('\n')
+    valid_regex_matches = ('fun_name', ARGS_SQL_STR, COLS_SQL_STR, SQL_STR)
+    _, create_errors = ViewFunctionDocs.create_from_comment(
+        "", comment, 'common', valid_regex_matches)
+    self.assertTrue(create_errors)
+
+  # Validate elements
+
+  def test_invalid_table_columns(self):
+    invalid_cols = "-- @column slice_id"
+    comment = f"{DESC}\n{invalid_cols}".split('\n')
+
+    docs, create_errors = TableViewDocs.create_from_comment(
+        "", comment, 'common', ('table', 'tab_name', 'to_ignore'))
+    self.assertFalse(create_errors)
+
+    validation_errors = docs.check_comment()
+    self.assertTrue(validation_errors)
+
+  def test_invalid_view_function_columns(self):
+    comment = f"{DESC}\n{ARGS_STR}\n{COLS_STR}".split('\n')
+    cols_sql_str = "slice_id INT"
+    valid_regex_matches = ('fun_name', ARGS_SQL_STR, cols_sql_str, SQL_STR)
+    docs, create_errors = ViewFunctionDocs.create_from_comment(
+        "", comment, 'common', valid_regex_matches)
+    self.assertFalse(create_errors)
+
+    validation_errors = docs.check_comment()
+    self.assertTrue(validation_errors)
+
+  def test_invalid_arguments(self):
+    valid_function = f"{DESC}\n{ARGS_STR}\n{RET_STR}".split('\n')
+    args_sql_str = "utid BOOL"
+    valid_regex_matches = ('fun_name', args_sql_str, RET_SQL_STR, SQL_STR)
+    docs, create_errors = FunctionDocs.create_from_comment(
+        "", valid_function, 'common', valid_regex_matches)
+    self.assertFalse(create_errors)
+
+    validation_errors = docs.check_comment()
+    self.assertTrue(validation_errors)
+
+  def test_invalid_ret(self):
+    valid_function = f"{DESC}\n{ARGS_STR}\n{RET_STR}".split('\n')
+    ret_sql_str = "utid BOOL"
+    valid_regex_matches = ('fun_name', ARGS_SQL_STR, ret_sql_str, SQL_STR)
+    docs, create_errors = FunctionDocs.create_from_comment(
+        "", valid_function, 'common', valid_regex_matches)
+    self.assertFalse(create_errors)
+
+    validation_errors = docs.check_comment()
+    self.assertTrue(validation_errors)
diff --git a/python/tools/cpu_profile.py b/python/tools/cpu_profile.py
index 4c33f2b..5a142c0 100644
--- a/python/tools/cpu_profile.py
+++ b/python/tools/cpu_profile.py
@@ -119,6 +119,11 @@
       metavar="CONFIG",
       default=None)
   parser.add_argument(
+      "--no-annotations",
+      help="Do not suffix the pprof function names with Android ART mode "
+      "annotations such as [jit].",
+      action="store_true")
+  parser.add_argument(
       "--print-config",
       action="store_true",
       help="Print config instead of running. For debugging.")
@@ -394,7 +399,7 @@
   return trace_file
 
 
-def generate_pprof_profiles(traceconv, trace_file):
+def generate_pprof_profiles(traceconv, trace_file, args):
   """Generates pprof profiles from the recorded trace.
 
   Args:
@@ -405,8 +410,9 @@
     The directory where pprof profiles are output.
   """
   try:
-    traceconv_output = subprocess.check_output(
-        [traceconv, 'profile', '--perf', trace_file])
+    conversion_args = [traceconv, 'profile', '--perf'] + (
+        ['--no-annotations'] if args.no_annotations else []) + [trace_file]
+    traceconv_output = subprocess.check_output(conversion_args)
   except Exception as error:
     exit_with_bug_report(
         "Unable to extract profiles from trace: {}".format(error))
@@ -445,8 +451,8 @@
   record_trace(trace_config, profile_target)
   traceconv = get_traceconv()
   trace_file = symbolize_trace(traceconv, profile_target)
-  copy_profiles_to_destination(profile_target,
-                               generate_pprof_profiles(traceconv, trace_file))
+  copy_profiles_to_destination(
+      profile_target, generate_pprof_profiles(traceconv, trace_file, args))
   return 0
 
 
diff --git a/python/tools/heap_profile.py b/python/tools/heap_profile.py
index 38325de..06e7043 100644
--- a/python/tools/heap_profile.py
+++ b/python/tools/heap_profile.py
@@ -273,6 +273,11 @@
   parser.add_argument(
       "--traceconv-binary", help="Path to local trace to text. For debugging.")
   parser.add_argument(
+      "--no-annotations",
+      help="Do not suffix the pprof function names with Android ART mode "
+      "annotations such as [jit].",
+      action="store_true")
+  parser.add_argument(
       "--print-config",
       action="store_true",
       help="Print config instead of running. For debugging.")
@@ -389,7 +394,8 @@
                   'perfetto --txt -c - -o ' + profile_device_path + ' -d')
 
   if args.disable_selinux:
-    enforcing = subprocess.check_output(['adb', 'shell', 'getenforce'])
+    enforcing = subprocess.check_output(['adb', 'shell',
+                                         'getenforce']).decode('utf-8').strip()
     atexit.register(
         subprocess.check_call,
         ['adb', 'shell', 'su root setenforce %s' % enforcing])
@@ -532,8 +538,9 @@
             out.write(buf)
     trace_file = os.path.join(profile_target, 'symbolized-trace')
 
-  traceconv_output = subprocess.check_output(
-      [traceconv_binary, 'profile', trace_file])
+  conversion_args = [traceconv_binary, 'profile'] + (
+      ['--no-annotations'] if args.no_annotations else []) + [trace_file]
+  traceconv_output = subprocess.check_output(conversion_args)
   profile_path = None
   for word in traceconv_output.decode('utf-8').split():
     if 'heap_profile-' in word:
diff --git a/python/tools/record_android_trace.py b/python/tools/record_android_trace.py
index ed62c7e..9256bd8 100644
--- a/python/tools/record_android_trace.py
+++ b/python/tools/record_android_trace.py
@@ -33,7 +33,7 @@
 
 # This is not required. It's only used as a fallback if no adb is found on the
 # PATH. It's fine if it doesn't exist so this script can be copied elsewhere.
-HERMETIC_ADB_PATH = repo_dir('/buildtools/android_sdk/platform-tools/adb')
+HERMETIC_ADB_PATH = repo_dir('buildtools/android_sdk/platform-tools/adb')
 
 # Translates the Android ro.product.cpu.abi into the GN's target_cpu.
 ABI_TO_ARCH = {
diff --git a/python/tools/slice_breakdown.py b/python/tools/slice_breakdown.py
index 9c481ba..633050b 100644
--- a/python/tools/slice_breakdown.py
+++ b/python/tools/slice_breakdown.py
@@ -18,6 +18,12 @@
 
 import argparse
 import sys
+import os
+
+PYTHON_DIR = os.path.join(
+    os.path.dirname(
+        os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "python")
+sys.path.append(os.path.join(PYTHON_DIR))
 
 from perfetto.experimental.slice_breakdown import compute_breakdown
 from perfetto.experimental.slice_breakdown import compute_breakdown_for_startup
diff --git a/python/tools/update_permalink.py b/python/tools/update_permalink.py
new file mode 100755
index 0000000..a6e8fab
--- /dev/null
+++ b/python/tools/update_permalink.py
@@ -0,0 +1,157 @@
+#!/usr/bin/env python3
+
+import sys
+import argparse
+import urllib
+import json
+import urllib.request
+import urllib.parse
+import ssl
+import hashlib
+import copy
+
+BUCKET_NAME = "perfetto-ui-data"
+CURRENT_STATE_VERSION = 28
+
+
+def upgrade_22(old):
+  new = copy.deepcopy(old)
+  new["version"] = 23
+  new["logFilteringCriteria"] = {
+      "minimumLevel": 2,
+  }
+  return new
+
+
+def upgrade_23(old):
+  new = copy.deepcopy(old)
+  new["version"] = 24
+  current_engine_id = new["currentEngineId"]
+  new["engine"] = new["engines"][
+      current_engine_id] if current_engine_id else None
+  del new["currentEngineId"]
+  del new["engines"]
+  return new
+
+
+def upgrade_24(old):
+  new = copy.deepcopy(old)
+  new["version"] = 25
+  new["omniboxState"] = new["frontendLocalState"]["omniboxState"]
+  del new["frontendLocalState"]["omniboxState"]
+  return new
+
+
+def upgrade_25(old):
+  new = copy.deepcopy(old)
+  new["version"] = 26
+  new["logFilteringCriteria"]["tags"] = []
+  return new
+
+
+def upgrade_26(old):
+  new = copy.deepcopy(old)
+  new["version"] = 27
+  new["logFilteringCriteria"]["textEntry"] = ""
+  return new
+
+
+def upgrade_27(old):
+  new = copy.deepcopy(old)
+  new["version"] = 28
+  new["logFilteringCriteria"]["hideNonMatching"] = False
+  return new
+
+
+def bug_compatible_hash_mangling(hash):
+  pairs = [hash[i:i + 2] for i in range(0, len(hash), 2)]
+  return ''.join([pair.removeprefix("0") for pair in pairs])
+
+
+def upload_state(state):
+  data = state.encode("utf-8")
+  hash = bug_compatible_hash_mangling(hashlib.sha256(data).hexdigest())
+
+  try:
+    get(make_state_url(hash))
+  except:
+    pass
+  else:
+    return hash
+
+  url = f"https://www.googleapis.com/upload/storage/v1/b/{BUCKET_NAME}/o?uploadType=media&name={hash}&predefinedAcl=publicRead"
+  request = urllib.request.Request(url, data=data)
+  request.add_header("Content-Type", "application/json; charset=utf-8")
+  response = urllib.request.urlopen(request)
+  return hash
+
+
+def make_state_url(id):
+  return f"https://storage.googleapis.com/{BUCKET_NAME}/{id}"
+
+
+def make_ui_url(id):
+  return f"https://ui.perfetto.dev/#!/?s={id}"
+
+
+def extract_state_uuid(url):
+  fragment = urllib.parse.urlparse(url).fragment
+  fragment = fragment.removeprefix("!/?")
+  return urllib.parse.parse_qs(fragment)["s"][0]
+
+
+def get(url):
+  context = ssl._create_unverified_context()
+  response = urllib.request.urlopen(url, context=context)
+  contents = response.read().decode()
+  return contents
+
+
+def post(url):
+  context = ssl._create_unverified_context()
+  response = urllib.request.urlopen(url, context=context)
+  contents = response.read().decode()
+  return contents
+
+
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument("permalink", help="Permalink you wish to update")
+  parser.add_argument(
+      "--target-version",
+      help=f"Target state version (default: {CURRENT_STATE_VERSION})",
+      default=CURRENT_STATE_VERSION)
+  args = parser.parse_args()
+
+  permalink_url = args.permalink
+  old_uuid = extract_state_uuid(permalink_url)
+  old_state_url = make_state_url(old_uuid)
+  old_state = get(old_state_url)
+  old_json = json.loads(old_state)
+
+  old_state_version = old_json["version"]
+  new_state_version = args.target_version
+
+  UPGRADE = {
+      22: upgrade_22,
+      23: upgrade_23,
+      24: upgrade_24,
+      25: upgrade_25,
+      26: upgrade_26,
+      27: upgrade_27,
+  }
+
+  new_json = old_json
+  for i in range(old_state_version, new_state_version):
+    new_json = UPGRADE[i](new_json)
+
+  new_state = json.dumps(new_json)
+  new_uuid = upload_state(new_state)
+  new_url = make_ui_url(new_uuid)
+  print(f"Your new permalink is accessible at:")
+  print(f"{new_url}")
+  return 0
+
+
+if __name__ == "__main__":
+  sys.exit(main())
diff --git a/src/android_internal/BUILD.gn b/src/android_internal/BUILD.gn
index ed01121..5843748 100644
--- a/src/android_internal/BUILD.gn
+++ b/src/android_internal/BUILD.gn
@@ -37,7 +37,7 @@
     ]
     libs = [
       "android.hardware.health@2.0",
-      "android.hardware.health-V1-ndk",
+      "android.hardware.health-V2-ndk",
       "android.hardware.power.stats@1.0",
       "android.hardware.power.stats-V1-cpp",
       "android.hardware.atrace@1.0",
diff --git a/src/base/BUILD.gn b/src/base/BUILD.gn
index 34bdb64..6a58f0b 100644
--- a/src/base/BUILD.gn
+++ b/src/base/BUILD.gn
@@ -25,7 +25,10 @@
     is_linux || is_chromeos || is_android || is_mac || is_win
 
 perfetto_component("base") {
-  deps = [ "../../gn:default_deps" ]
+  deps = [
+    "../../gn:base_platform",
+    "../../gn:default_deps",
+  ]
   public_deps = [
     "../../include/perfetto/base",
     "../../include/perfetto/ext/base",
@@ -50,6 +53,7 @@
     "string_view.cc",
     "temp_file.cc",
     "thread_checker.cc",
+    "thread_utils.cc",
     "time.cc",
     "utils.cc",
     "uuid.cc",
@@ -78,6 +82,16 @@
   }
 }
 
+# This target needs to be named as such because it's exposed directly in Bazel
+# and Android.bp.
+perfetto_component("perfetto_base_default_platform") {
+  deps = [
+    "../../gn:default_deps",
+    "../../include/perfetto/ext/base",
+  ]
+  sources = [ "default_platform.cc" ]
+}
+
 perfetto_component("version") {
   deps = [
     ":base",
@@ -185,6 +199,7 @@
     "periodic_task_unittest.cc",
     "scoped_file_unittest.cc",
     "small_vector_unittest.cc",
+    "status_or_unittest.cc",
     "string_splitter_unittest.cc",
     "string_utils_unittest.cc",
     "string_view_unittest.cc",
@@ -219,8 +234,10 @@
     if (is_linux || is_android) {
       sources += [ "watchdog_unittest.cc" ]
     }
-    sources += [ "unix_socket_unittest.cc" ]
-    deps += [ ":unix_socket" ]
+    if (!is_win) {
+      sources += [ "unix_socket_unittest.cc" ]
+      deps += [ ":unix_socket" ]
+    }
   }
 }
 
diff --git a/src/base/ctrl_c_handler.cc b/src/base/ctrl_c_handler.cc
index 080f0cf..bdb3f8a 100644
--- a/src/base/ctrl_c_handler.cc
+++ b/src/base/ctrl_c_handler.cc
@@ -35,7 +35,7 @@
 CtrlCHandlerFunction g_handler = nullptr;
 }
 
-void InstallCtrCHandler(CtrlCHandlerFunction handler) {
+void InstallCtrlCHandler(CtrlCHandlerFunction handler) {
   PERFETTO_CHECK(g_handler == nullptr);
   g_handler = handler;
 
diff --git a/src/base/default_platform.cc b/src/base/default_platform.cc
new file mode 100644
index 0000000..acc3cf3
--- /dev/null
+++ b/src/base/default_platform.cc
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 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/platform.h"
+
+namespace perfetto {
+namespace base {
+namespace platform {
+
+// This is a no-op outside of Google3 where we have some custom logic to deal
+// with the userspace scheduler.
+void BeforeMaybeBlockingSyscall() {}
+
+// This is a no-op outside of Google3 where we have some custom logic to deal
+// with the userspace scheduler.
+void AfterMaybeBlockingSyscall() {}
+
+}  // namespace platform
+}  // namespace base
+}  // namespace perfetto
diff --git a/src/base/file_utils.cc b/src/base/file_utils.cc
index ce4584c..66ab5f6 100644
--- a/src/base/file_utils.cc
+++ b/src/base/file_utils.cc
@@ -28,6 +28,8 @@
 #include "perfetto/base/logging.h"
 #include "perfetto/base/platform_handle.h"
 #include "perfetto/base/status.h"
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/platform.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/utils.h"
 
@@ -35,6 +37,7 @@
 #include <Windows.h>
 #include <direct.h>
 #include <io.h>
+#include <stringapiset.h>
 #else
 #include <dirent.h>
 #include <unistd.h>
@@ -50,16 +53,40 @@
 int CloseFindHandle(HANDLE h) {
   return FindClose(h) ? 0 : -1;
 }
+
+Optional<std::wstring> ToUtf16(const std::string str) {
+  int len = MultiByteToWideChar(CP_UTF8, 0, str.data(),
+                                static_cast<int>(str.size()), nullptr, 0);
+  if (len < 0) {
+    return base::nullopt;
+  }
+  std::vector<wchar_t> tmp;
+  tmp.resize(static_cast<std::vector<wchar_t>::size_type>(len));
+  len =
+      MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast<int>(str.size()),
+                          tmp.data(), static_cast<int>(tmp.size()));
+  if (len < 0) {
+    return base::nullopt;
+  }
+  PERFETTO_CHECK(static_cast<std::vector<wchar_t>::size_type>(len) ==
+                 tmp.size());
+  return std::wstring(tmp.data(), tmp.size());
+}
+
 #endif
 
 }  // namespace
 
 ssize_t Read(int fd, void* dst, size_t dst_size) {
+  ssize_t ret;
+  platform::BeforeMaybeBlockingSyscall();
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  return _read(fd, dst, static_cast<unsigned>(dst_size));
+  ret = _read(fd, dst, static_cast<unsigned>(dst_size));
 #else
-  return PERFETTO_EINTR(read(fd, dst, dst_size));
+  ret = PERFETTO_EINTR(read(fd, dst, dst_size));
 #endif
+  platform::AfterMaybeBlockingSyscall();
+  return ret;
 }
 
 bool ReadFileDescriptor(int fd, std::string* out) {
@@ -134,8 +161,10 @@
     // write() on windows takes an unsigned int size.
     uint32_t bytes_left = static_cast<uint32_t>(
         std::min(count - written, static_cast<size_t>(UINT32_MAX)));
+    platform::BeforeMaybeBlockingSyscall();
     ssize_t wr = PERFETTO_EINTR(
         write(fd, static_cast<const char*>(buf) + written, bytes_left));
+    platform::AfterMaybeBlockingSyscall();
     if (wr == 0)
       break;
     if (wr < 0)
@@ -191,7 +220,9 @@
 }
 
 ScopedFile OpenFile(const std::string& path, int flags, FileOpenMode mode) {
-  PERFETTO_DCHECK((flags & O_CREAT) == 0 || mode != kFileModeInvalid);
+  // If a new file might be created, ensure that the permissions for the new
+  // file are explicitly specified.
+  PERFETTO_CHECK((flags & O_CREAT) == 0 || mode != kFileModeInvalid);
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
   // Always use O_BINARY on Windows, to avoid silly EOL translations.
   ScopedFile fd(_open(path.c_str(), flags | O_BINARY, mode));
@@ -202,6 +233,23 @@
   return fd;
 }
 
+ScopedFstream OpenFstream(const char* path, const char* mode) {
+  ScopedFstream file;
+// On Windows fopen interprets filename using the ANSI or OEM codepage but
+// sqlite3_value_text returns a UTF-8 string. To make sure we interpret the
+// filename correctly we use _wfopen and a UTF-16 string on windows.
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  auto w_path = ToUtf16(path);
+  auto w_mode = ToUtf16(mode);
+  if (w_path && w_mode) {
+    file.reset(_wfopen(w_path->c_str(), w_mode->c_str()));
+  }
+#else
+  file.reset(fopen(path, mode));
+#endif
+  return file;
+}
+
 bool FileExists(const std::string& path) {
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
   return _access(path.c_str(), 0) == 0;
diff --git a/src/base/flat_hash_map_benchmark.cc b/src/base/flat_hash_map_benchmark.cc
index 33da669..23fbb3a 100644
--- a/src/base/flat_hash_map_benchmark.cc
+++ b/src/base/flat_hash_map_benchmark.cc
@@ -116,7 +116,7 @@
   }
   char line[4096];
   while (fgets(line, sizeof(line), *f)) {
-    base::Hash hasher;
+    base::Hasher hasher;
     hasher.Update(line, strlen(line));
     str_hashes.emplace_back(hasher.digest());
   }
diff --git a/src/base/hash_unittest.cc b/src/base/hash_unittest.cc
index 29ba13f..ee92edb 100644
--- a/src/base/hash_unittest.cc
+++ b/src/base/hash_unittest.cc
@@ -26,7 +26,7 @@
 TEST(HashTest, StringView) {
   base::StringView a = "abc";
   base::StringView b = "def";
-  EXPECT_NE(Hash::Combine(a), Hash::Combine(b));
+  EXPECT_NE(Hasher::Combine(a), Hasher::Combine(b));
 }
 
 }  // namespace
diff --git a/src/base/metatrace.cc b/src/base/metatrace.cc
index c676d80..1464ed3 100644
--- a/src/base/metatrace.cc
+++ b/src/base/metatrace.cc
@@ -29,7 +29,6 @@
 std::atomic<uint64_t> g_enabled_timestamp{0};
 
 // static members
-constexpr size_t RingBuffer::kCapacity;
 std::array<Record, RingBuffer::kCapacity> RingBuffer::records_;
 std::atomic<bool> RingBuffer::read_task_queued_;
 std::atomic<uint64_t> RingBuffer::wr_index_;
@@ -37,9 +36,12 @@
 std::atomic<bool> RingBuffer::has_overruns_;
 Record RingBuffer::bankruptcy_record_;
 
+#if !PERFETTO_IS_AT_LEAST_CPP17()
+constexpr size_t RingBuffer::kCapacity;
 constexpr uint16_t Record::kTypeMask;
 constexpr uint16_t Record::kTypeCounter;
 constexpr uint16_t Record::kTypeEvent;
+#endif
 
 namespace {
 
diff --git a/src/base/status_or_unittest.cc b/src/base/status_or_unittest.cc
new file mode 100644
index 0000000..ada5790
--- /dev/null
+++ b/src/base/status_or_unittest.cc
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 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/status_or.h"
+
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace base {
+
+TEST(StatusOrTest, IntOk) {
+  base::StatusOr<int> int_or = 1;
+  ASSERT_TRUE(int_or.ok());
+  ASSERT_TRUE(int_or.status().ok());
+  ASSERT_EQ(int_or.value(), 1);
+  ASSERT_EQ(*int_or, 1);
+}
+
+TEST(StatusOrTest, VecOk) {
+  base::StatusOr<std::vector<int>> vec_or({0, 1, 100});
+  ASSERT_TRUE(vec_or.ok());
+  ASSERT_TRUE(vec_or.status().ok());
+
+  ASSERT_EQ(vec_or.value()[0], 0);
+  ASSERT_EQ(vec_or.value()[2], 100);
+
+  ASSERT_EQ((*vec_or)[0], 0);
+  ASSERT_EQ((*vec_or)[2], 100);
+
+  ASSERT_EQ(vec_or->at(0), 0);
+  ASSERT_EQ(vec_or->at(2), 100);
+}
+
+TEST(StatusOrTest, ErrStatus) {
+  base::StatusOr<std::vector<int>> err(base::ErrStatus("Bad error"));
+  ASSERT_FALSE(err.ok());
+  ASSERT_FALSE(err.status().ok());
+}
+
+}  // namespace base
+}  // namespace perfetto
diff --git a/src/base/string_splitter.cc b/src/base/string_splitter.cc
index adf9fc8..845a0ef 100644
--- a/src/base/string_splitter.cc
+++ b/src/base/string_splitter.cc
@@ -23,20 +23,29 @@
 namespace perfetto {
 namespace base {
 
-StringSplitter::StringSplitter(std::string str, char delimiter)
-    : str_(std::move(str)), delimiter_(delimiter) {
+StringSplitter::StringSplitter(std::string str,
+                               char delimiter,
+                               EmptyTokenMode empty_token_mode)
+    : str_(std::move(str)),
+      delimiter_(delimiter),
+      empty_token_mode_(empty_token_mode) {
   // It's legal to access str[str.size()] in C++11 (it always returns \0),
   // hence the +1 (which becomes just size() after the -1 in Initialize()).
   Initialize(&str_[0], str_.size() + 1);
 }
 
-StringSplitter::StringSplitter(char* str, size_t size, char delimiter)
-    : delimiter_(delimiter) {
+StringSplitter::StringSplitter(char* str,
+                               size_t size,
+                               char delimiter,
+                               EmptyTokenMode empty_token_mode)
+    : delimiter_(delimiter), empty_token_mode_(empty_token_mode) {
   Initialize(str, size);
 }
 
-StringSplitter::StringSplitter(StringSplitter* outer, char delimiter)
-    : delimiter_(delimiter) {
+StringSplitter::StringSplitter(StringSplitter* outer,
+                               char delimiter,
+                               EmptyTokenMode empty_token_mode)
+    : delimiter_(delimiter), empty_token_mode_(empty_token_mode) {
   Initialize(outer->cur_token(), outer->cur_token_size() + 1);
 }
 
@@ -52,8 +61,11 @@
 
 bool StringSplitter::Next() {
   for (; next_ < end_; next_++) {
-    if (*next_ == delimiter_)
+    if (*next_ == delimiter_ &&
+        empty_token_mode_ == EmptyTokenMode::DISALLOW_EMPTY_TOKENS) {
+      // If empty tokens are disallowed, find fist non-delimiter character.
       continue;
+    }
     cur_ = next_;
     for (;; next_++) {
       if (*next_ == delimiter_) {
@@ -67,7 +79,7 @@
         break;
       }
     }
-    if (*cur_)
+    if (*cur_ || empty_token_mode_ == EmptyTokenMode::ALLOW_EMPTY_TOKENS)
       return true;
     break;
   }
diff --git a/src/base/string_splitter_unittest.cc b/src/base/string_splitter_unittest.cc
index eb80d35..8b0bbd7 100644
--- a/src/base/string_splitter_unittest.cc
+++ b/src/base/string_splitter_unittest.cc
@@ -364,6 +364,17 @@
                                             "w", "1", "l", "2", "w", "2"}));
 }  // namespace
 
+TEST(StringSplitterTest, EmptyTokens) {
+  char text[] = "a,,b";
+  std::vector<std::string> tokens;
+  for (StringSplitter lines(text, sizeof(text), ',',
+                            StringSplitter::EmptyTokenMode::ALLOW_EMPTY_TOKENS);
+       lines.Next();) {
+    tokens.push_back(lines.cur_token());
+  }
+  EXPECT_THAT(tokens, testing::ElementsAre("a", "", "b"));
+}  // namespace
+
 }  // namespace
 }  // namespace base
 }  // namespace perfetto
diff --git a/src/base/string_utils.cc b/src/base/string_utils.cc
index 7e98ecd..851b60c 100644
--- a/src/base/string_utils.cc
+++ b/src/base/string_utils.cc
@@ -120,6 +120,18 @@
   return output;
 }
 
+std::string TrimWhitespace(const std::string& str) {
+  std::string whitespaces = "\t\n ";
+
+  size_t front_idx = str.find_first_not_of(whitespaces);
+  std::string front_trimmed =
+      front_idx == std::string::npos ? "" : str.substr(front_idx);
+
+  size_t end_idx = front_trimmed.find_last_not_of(whitespaces);
+  return end_idx == std::string::npos ? ""
+                                      : front_trimmed.substr(0, end_idx + 1);
+}
+
 std::string StripPrefix(const std::string& str, const std::string& prefix) {
   return StartsWith(str, prefix) ? str.substr(prefix.size()) : str;
 }
@@ -233,5 +245,28 @@
   return res;
 }
 
+base::Optional<LineWithOffset> FindLineWithOffset(base::StringView str,
+                                                  uint32_t offset) {
+  static constexpr char kNewLine = '\n';
+  uint32_t line_offset = 0;
+  uint32_t line_count = 1;
+  for (uint32_t i = 0; i < str.size(); ++i) {
+    if (str.at(i) == kNewLine) {
+      line_offset = i + 1;
+      line_count++;
+      continue;
+    }
+    if (i == offset) {
+      size_t end_offset = str.find(kNewLine, i);
+      if (end_offset == std::string::npos) {
+        end_offset = str.size();
+      }
+      base::StringView line = str.substr(line_offset, end_offset - line_offset);
+      return LineWithOffset{line, offset - line_offset, line_count};
+    }
+  }
+  return base::nullopt;
+}
+
 }  // namespace base
 }  // namespace perfetto
diff --git a/src/base/string_utils_unittest.cc b/src/base/string_utils_unittest.cc
index 9a885d1..2cc4b06 100644
--- a/src/base/string_utils_unittest.cc
+++ b/src/base/string_utils_unittest.cc
@@ -251,6 +251,16 @@
   EXPECT_EQ(StripChars("foobar", "froab", '_'), "______");
 }
 
+TEST(StringUtilsTest, TrimWhitespace) {
+  EXPECT_EQ(TrimWhitespace(""), "");
+  EXPECT_EQ(TrimWhitespace(" "), "");
+  EXPECT_EQ(TrimWhitespace("\t\n"), "");
+
+  EXPECT_EQ(TrimWhitespace("\tx\n\n"), "x");
+  EXPECT_EQ(TrimWhitespace("\tx\n"), "x");
+  EXPECT_EQ(TrimWhitespace("\tx\nx\n"), "x\nx");
+}
+
 TEST(StringUtilsTest, Contains) {
   EXPECT_TRUE(Contains("", ""));
   EXPECT_TRUE(Contains("abc", ""));
@@ -416,6 +426,72 @@
   }
 }
 
+TEST(FindLineTest, InvalidOffset1) {
+  std::string str = "abc\ndef\n\nghi";
+  uint32_t offset = 3;
+
+  auto error = FindLineWithOffset(base::StringView(str), offset);
+
+  EXPECT_FALSE(error.has_value());
+}
+
+TEST(FindLineTest, InvalidOffset2) {
+  std::string str = "abc\ndef\n\nghi";
+  uint32_t offset = 8;
+
+  auto error = FindLineWithOffset(base::StringView(str), offset);
+
+  EXPECT_FALSE(error.has_value());
+}
+
+TEST(FindLineTest, FirstCharacter) {
+  std::string str = "abc\ndef\n\nghi";
+  uint32_t offset = 0;
+
+  auto error = FindLineWithOffset(base::StringView(str), offset);
+
+  EXPECT_TRUE(error.has_value());
+  ASSERT_EQ(error.value().line_num, 1ul);
+  ASSERT_EQ(error.value().line_offset, 0ul);
+  ASSERT_EQ(error.value().line, "abc");
+}
+
+TEST(FindLineTest, StandardCheck) {
+  std::string str = "abc\ndef\n\nghi";
+  uint32_t offset = 5;
+
+  auto error = FindLineWithOffset(base::StringView(str), offset);
+
+  EXPECT_TRUE(error.has_value());
+  ASSERT_EQ(error.value().line_num, 2ul);
+  ASSERT_EQ(error.value().line_offset, 1ul);
+  ASSERT_EQ(error.value().line, "def");
+}
+
+TEST(FindLineTest, TwoBreakLines) {
+  std::string str = "abc\ndef\n\nghi";
+  uint32_t offset = 10;
+
+  auto error = FindLineWithOffset(base::StringView(str), offset);
+
+  EXPECT_TRUE(error.has_value());
+  ASSERT_EQ(error.value().line_num, 4ul);
+  ASSERT_EQ(error.value().line_offset, 1ul);
+  ASSERT_EQ(error.value().line, "ghi");
+}
+
+TEST(FindLineTest, EndsWithBreakLine) {
+  std::string str = "abc\ndef\n\nghi\n";
+  uint32_t offset = 10;
+
+  auto error = FindLineWithOffset(base::StringView(str), offset);
+
+  EXPECT_TRUE(error.has_value());
+  ASSERT_EQ(error.value().line_num, 4ul);
+  ASSERT_EQ(error.value().line_offset, 1ul);
+  ASSERT_EQ(error.value().line, "ghi");
+}
+
 }  // namespace
 }  // namespace base
 }  // namespace perfetto
diff --git a/src/base/string_view.cc b/src/base/string_view.cc
index 7292546..ccd6025 100644
--- a/src/base/string_view.cc
+++ b/src/base/string_view.cc
@@ -19,8 +19,21 @@
 namespace perfetto {
 namespace base {
 
+// Without ignoring this warning we get the message:
+//   error: out-of-line definition of constexpr static data member is redundant
+//   in C++17 and is deprecated
+// when using clang-cl in Windows.
+#if defined(__GNUC__)  // GCC & clang
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated"
+#endif  // __GNUC__
+
 // static
 constexpr size_t StringView::npos;
 
+#if defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
+
 }  // namespace base
 }  // namespace perfetto
diff --git a/src/base/string_view_unittest.cc b/src/base/string_view_unittest.cc
index 848e51d..3b1bac7 100644
--- a/src/base/string_view_unittest.cc
+++ b/src/base/string_view_unittest.cc
@@ -169,6 +169,20 @@
   EXPECT_TRUE(StringView("foorbar").StartsWith("foo"));
   EXPECT_FALSE(StringView("foorbar").StartsWith("bar"));
   EXPECT_FALSE(StringView("foo").StartsWith("fooo"));
+
+  // Test EndsWith.
+  EXPECT_TRUE(StringView().EndsWith(StringView()));
+  EXPECT_TRUE(StringView().EndsWith(StringView("")));
+  EXPECT_TRUE(StringView("").EndsWith(StringView("")));
+  EXPECT_TRUE(StringView("").EndsWith(StringView()));
+  EXPECT_TRUE(StringView("foo").EndsWith(StringView()));
+  EXPECT_TRUE(StringView("foo").EndsWith(StringView("")));
+  EXPECT_FALSE(StringView().EndsWith("foo"));
+  EXPECT_FALSE(StringView("").EndsWith("foo"));
+  EXPECT_TRUE(StringView("foo").EndsWith("foo"));
+  EXPECT_FALSE(StringView("foorbar").EndsWith("foo"));
+  EXPECT_TRUE(StringView("foorbar").EndsWith("bar"));
+  EXPECT_FALSE(StringView("foo").EndsWith("fooo"));
 }
 
 TEST(StringViewTest, HashCollisions) {
diff --git a/src/base/test/tmp_dir_tree.cc b/src/base/test/tmp_dir_tree.cc
index 9ed785f..8b70ec0 100644
--- a/src/base/test/tmp_dir_tree.cc
+++ b/src/base/test/tmp_dir_tree.cc
@@ -44,12 +44,16 @@
 
 void TmpDirTree::AddFile(const std::string& relative_path,
                          const std::string& content) {
-  files_to_remove_.push(relative_path);
+  TrackFile(relative_path);
   base::ScopedFile fd(base::OpenFile(AbsolutePath(relative_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 TmpDirTree::TrackFile(const std::string& relative_path) {
+  files_to_remove_.push(relative_path);
+}
+
 }  // namespace base
 }  // namespace perfetto
diff --git a/src/base/test/tmp_dir_tree.h b/src/base/test/tmp_dir_tree.h
index 6bb730c..9b282f8 100644
--- a/src/base/test/tmp_dir_tree.h
+++ b/src/base/test/tmp_dir_tree.h
@@ -47,6 +47,11 @@
   // succeeds.
   void AddFile(const std::string& relative_path, const std::string& content);
 
+  // Tells this object to remove `relative_path` on destruction. This is used
+  // for files created by the caller that still need to be removed on cleanup by
+  // this object.
+  void TrackFile(const std::string& relative_path);
+
  private:
   TmpDirTree(const TmpDirTree&) = delete;
   TmpDirTree& operator=(const TmpDirTree&) = delete;
diff --git a/src/base/thread_task_runner_unittest.cc b/src/base/thread_task_runner_unittest.cc
index 0e7f2af..ae7dacd 100644
--- a/src/base/thread_task_runner_unittest.cc
+++ b/src/base/thread_task_runner_unittest.cc
@@ -18,6 +18,7 @@
 
 #include <thread>
 
+#include "perfetto/ext/base/no_destructor.h"
 #include "perfetto/ext/base/thread_checker.h"
 #include "test/gtest_and_gmock.h"
 
@@ -61,7 +62,10 @@
 }
 
 TEST_F(ThreadTaskRunnerTest, MovableOwnership) {
-  ThreadTaskRunner task_runner = ThreadTaskRunner::CreateAndStart();
+  // Will destroy manually.
+  NoDestructor<ThreadTaskRunner> ttr{ThreadTaskRunner::CreateAndStart()};
+  ThreadTaskRunner& task_runner = ttr.ref();
+
   UnixTaskRunner* runner_ptr = task_runner.get();
   EXPECT_NE(runner_ptr, nullptr);
 
diff --git a/src/base/thread_utils.cc b/src/base/thread_utils.cc
new file mode 100644
index 0000000..bc28f0f
--- /dev/null
+++ b/src/base/thread_utils.cc
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 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/base/thread_utils.h"
+
+#include "perfetto/base/build_config.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
+#include <zircon/process.h>
+#include <zircon/syscalls.h>
+#include <zircon/types.h>
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
+
+namespace perfetto {
+namespace base {
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
+static PlatformThreadId ResolveThreadId() {
+  zx_info_handle_basic_t basic;
+  return (zx_object_get_info(zx_thread_self(), ZX_INFO_HANDLE_BASIC, &basic,
+                             sizeof(basic), nullptr, nullptr) == ZX_OK)
+             ? basic.koid
+             : ZX_KOID_INVALID;
+}
+PlatformThreadId GetThreadId() {
+  thread_local static PlatformThreadId thread_id = ResolveThreadId();
+  return thread_id;
+}
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
+
+}  // namespace base
+}  // namespace perfetto
diff --git a/src/base/threading/BUILD.gn b/src/base/threading/BUILD.gn
new file mode 100644
index 0000000..ba709e7
--- /dev/null
+++ b/src/base/threading/BUILD.gn
@@ -0,0 +1,33 @@
+# Copyright (C) 2023 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("threading") {
+  deps = [ "../../../gn:default_deps" ]
+  public_deps = [ "../../../include/perfetto/ext/base/threading" ]
+  sources = [ "thread_pool.cc" ]
+}
+
+perfetto_unittest_source_set("unittests") {
+  testonly = true
+  deps = [
+    ":threading",
+    "..:base",
+    "../../../gn:default_deps",
+    "../../../gn:gtest_and_gmock",
+  ]
+
+  sources = [ "thread_pool_unittest.cc" ]
+}
diff --git a/src/base/threading/thread_pool.cc b/src/base/threading/thread_pool.cc
new file mode 100644
index 0000000..30843b3
--- /dev/null
+++ b/src/base/threading/thread_pool.cc
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023 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/threading/thread_pool.h"
+#include <mutex>
+#include <thread>
+
+namespace perfetto {
+namespace base {
+
+ThreadPool::ThreadPool(uint32_t thread_count) {
+  for (uint32_t i = 0; i < thread_count; ++i) {
+    threads_.emplace_back(std::bind(&ThreadPool::RunThreadLoop, this));
+  }
+}
+
+ThreadPool::~ThreadPool() {
+  {
+    std::lock_guard<std::mutex> guard(mutex_);
+    quit_ = true;
+  }
+  thread_waiter_.notify_all();
+  for (auto& thread : threads_) {
+    thread.join();
+  }
+}
+
+void ThreadPool::PostTask(std::function<void()> fn) {
+  std::lock_guard<std::mutex> guard(mutex_);
+  pending_tasks_.emplace_back(std::move(fn));
+  if (thread_waiting_count_ == 0) {
+    return;
+  }
+  thread_waiter_.notify_one();
+}
+
+void ThreadPool::RunThreadLoop() {
+  for (;;) {
+    std::function<void()> fn;
+    {
+      std::unique_lock<std::mutex> guard(mutex_);
+      if (quit_) {
+        return;
+      }
+      if (pending_tasks_.empty()) {
+        thread_waiting_count_++;
+        thread_waiter_.wait(
+            guard, [this]() { return quit_ || !pending_tasks_.empty(); });
+        thread_waiting_count_--;
+        continue;
+      }
+      fn = std::move(pending_tasks_.front());
+      pending_tasks_.pop_front();
+    }
+    fn();
+  }
+}
+
+}  // namespace base
+}  // namespace perfetto
diff --git a/src/base/threading/thread_pool_unittest.cc b/src/base/threading/thread_pool_unittest.cc
new file mode 100644
index 0000000..dd70258
--- /dev/null
+++ b/src/base/threading/thread_pool_unittest.cc
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2023 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//threading/thread_pool.h"
+#include <atomic>
+#include <condition_variable>
+#include <mutex>
+
+#include "perfetto/ext/base/waitable_event.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace base {
+namespace {
+
+struct ThreadLatch {
+  base::WaitableEvent notify;
+  base::WaitableEvent wait;
+  bool task_started = false;
+};
+
+TEST(ThreadPoolTest, SequentialQueueing) {
+  ThreadLatch first;
+  ThreadLatch second;
+  base::ThreadPool pool(1);
+
+  pool.PostTask([&first] {
+    first.task_started = true;
+    first.notify.Notify();
+    first.wait.Wait();
+  });
+
+  pool.PostTask([&second] {
+    second.task_started = true;
+    second.notify.Notify();
+    second.wait.Wait();
+  });
+
+  first.notify.Wait();
+  ASSERT_TRUE(first.task_started);
+  ASSERT_FALSE(second.task_started);
+  first.wait.Notify();
+
+  second.notify.Wait();
+  ASSERT_TRUE(second.task_started);
+  second.wait.Notify();
+}
+
+TEST(ThreadPoolTest, ParallelSecondFinishFirst) {
+  base::ThreadPool pool(2);
+
+  ThreadLatch first;
+  pool.PostTask([&first] {
+    first.wait.Wait();
+    first.task_started = true;
+    first.notify.Notify();
+  });
+
+  ThreadLatch second;
+  pool.PostTask([&second] {
+    second.wait.Wait();
+    second.task_started = true;
+    second.notify.Notify();
+  });
+
+  second.wait.Notify();
+  second.notify.Wait();
+  ASSERT_TRUE(second.task_started);
+
+  first.wait.Notify();
+  first.notify.Wait();
+  ASSERT_TRUE(first.task_started);
+}
+
+TEST(ThreadPoolTest, StressTest) {
+  std::mutex mu;
+  std::condition_variable cv;
+  uint32_t count = 0;
+  base::ThreadPool pool(128);
+  for (uint32_t i = 0; i < 1024; ++i) {
+    pool.PostTask([&mu, &count, &cv] {
+      std::lock_guard<std::mutex> guard(mu);
+      if (++count == 1024) {
+        cv.notify_one();
+      }
+    });
+  }
+
+  std::unique_lock<std::mutex> lock(mu);
+  cv.wait(lock, [&count]() { return count == 1024u; });
+}
+
+}  // namespace
+}  // namespace base
+}  // namespace perfetto
diff --git a/src/base/unix_socket.cc b/src/base/unix_socket.cc
index 1ebae83..be3955a 100644
--- a/src/base/unix_socket.cc
+++ b/src/base/unix_socket.cc
@@ -341,6 +341,8 @@
   }
   int fcntl_res = fcntl(*fd_, F_SETFD, flags);
   PERFETTO_CHECK(fcntl_res == 0);
+#else
+  ignore_result(retain);
 #endif
 }
 
diff --git a/src/base/unix_socket_unittest.cc b/src/base/unix_socket_unittest.cc
index a803fa8..c902033 100644
--- a/src/base/unix_socket_unittest.cc
+++ b/src/base/unix_socket_unittest.cc
@@ -430,7 +430,8 @@
   // Try listening on a random port. Some ports might be taken by other syste
   // services. Do a bunch of attempts on different ports before giving up.
   do {
-    sprintf(host_and_port, "127.0.0.1:%d", 10000 + (rand() % 10000));
+    base::SprintfTrunc(host_and_port, sizeof(host_and_port), "127.0.0.1:%d",
+                       10000 + (rand() % 10000));
     srv = UnixSocket::Listen(host_and_port, &event_listener_, &task_runner_,
                              SockFamily::kInet, SockType::kStream);
   } while ((!srv || !srv->is_listening()) && attempt++ < 10);
diff --git a/src/base/unix_task_runner.cc b/src/base/unix_task_runner.cc
index f6b6c19..9fc8f3c 100644
--- a/src/base/unix_task_runner.cc
+++ b/src/base/unix_task_runner.cc
@@ -16,6 +16,7 @@
 
 #include "perfetto/base/build_config.h"
 
+#include "perfetto/ext/base/platform.h"
 #include "perfetto/ext/base/unix_task_runner.h"
 
 #include <errno.h>
@@ -77,8 +78,10 @@
     // returned.
     PostFileDescriptorWatches(ret);
 #else
+    platform::BeforeMaybeBlockingSyscall();
     int ret = PERFETTO_EINTR(poll(
         &poll_fds_[0], static_cast<nfds_t>(poll_fds_.size()), poll_timeout_ms));
+    platform::AfterMaybeBlockingSyscall();
     PERFETTO_CHECK(ret >= 0);
     PostFileDescriptorWatches(0 /*ignored*/);
 #endif
diff --git a/src/base/utils.cc b/src/base/utils.cc
index cb24d25..198daa9 100644
--- a/src/base/utils.cc
+++ b/src/base/utils.cc
@@ -187,6 +187,14 @@
 #endif
 }
 
+void UnsetEnv(const std::string& key) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  PERFETTO_CHECK(::_putenv_s(key.c_str(), "") == 0);
+#else
+  PERFETTO_CHECK(::unsetenv(key.c_str()) == 0);
+#endif
+}
+
 void Daemonize(std::function<int()> parent_cb) {
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
     PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
diff --git a/src/base/uuid_unittest.cc b/src/base/uuid_unittest.cc
index 76f645e..9f5e52f 100644
--- a/src/base/uuid_unittest.cc
+++ b/src/base/uuid_unittest.cc
@@ -24,7 +24,7 @@
 namespace base {
 namespace {
 
-TEST(Uuid, DefaultConstructorIsBlank) {
+TEST(UuidTest, DefaultConstructorIsBlank) {
   Uuid a;
   Uuid b;
   EXPECT_EQ(a, b);
@@ -32,7 +32,7 @@
   EXPECT_EQ(a.lsb(), 0);
 }
 
-TEST(Uuid, TwoUuidsShouldBeDifferent) {
+TEST(UuidTest, TwoUuidsShouldBeDifferent) {
   Uuid a = Uuidv4();
   Uuid b = Uuidv4();
   EXPECT_NE(a, b);
@@ -40,29 +40,49 @@
   EXPECT_EQ(b, b);
 }
 
-TEST(Uuid, CanRoundTripUuid) {
+TEST(UuidTest, CanRoundTripUuid) {
   Uuid uuid = Uuidv4();
   EXPECT_EQ(Uuid(uuid.ToString()), uuid);
 }
 
-TEST(Uuid, SetGet) {
+TEST(UuidTest, SetGet) {
   Uuid a = Uuidv4();
   Uuid b;
   b.set_lsb_msb(a.lsb(), a.msb());
   EXPECT_EQ(a, b);
 }
 
-TEST(Uuid, LsbMsbConstructor) {
+TEST(UuidTest, LsbMsbConstructor) {
   Uuid uuid(-6605018796207623390, 1314564453825188563);
   EXPECT_EQ(uuid.ToPrettyString(), "123e4567-e89b-12d3-a456-426655443322");
 }
 
-TEST(Uuid, UuidToPrettyString) {
+TEST(UuidTest, UuidToPrettyString) {
   Uuid uuid;
   uuid.set_lsb_msb(-6605018796207623390, 1314564453825188563);
   EXPECT_EQ(uuid.ToPrettyString(), "123e4567-e89b-12d3-a456-426655443322");
 }
 
+TEST(UuidTest, BoolOperator) {
+  Uuid uuid;
+  EXPECT_FALSE(uuid);
+
+  uuid.set_lsb(1);
+  EXPECT_TRUE(uuid);
+
+  uuid.set_lsb(0);
+  EXPECT_FALSE(uuid);
+
+  uuid.set_msb(0x80000000);
+  EXPECT_TRUE(uuid);
+
+  uuid = Uuid();
+  EXPECT_FALSE(uuid);
+
+  uuid = Uuidv4();
+  EXPECT_TRUE(uuid);
+}
+
 }  // namespace
 }  // namespace base
 }  // namespace perfetto
diff --git a/src/base/watchdog_posix.cc b/src/base/watchdog_posix.cc
index b92e9ce..fcbf713 100644
--- a/src/base/watchdog_posix.cc
+++ b/src/base/watchdog_posix.cc
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "perfetto/ext/base/platform.h"
 #include "perfetto/ext/base/watchdog.h"
 
 #if PERFETTO_BUILDFLAG(PERFETTO_WATCHDOG)
@@ -231,7 +232,9 @@
     // We use the poll() timeout to drive the periodic ticks for the cpu/memory
     // checks. The only other case when the poll() unblocks is when we crash
     // (or have to quit via enabled_ == false, but that happens only in tests).
+    platform::BeforeMaybeBlockingSyscall();
     auto ret = poll(fds, kFdCount, static_cast<int>(polling_interval_ms_));
+    platform::AfterMaybeBlockingSyscall();
     if (!enabled_)
       return;
     if (ret < 0) {
diff --git a/src/ipc/host_impl.cc b/src/ipc/host_impl.cc
index 62e3f0f..45030ed 100644
--- a/src/ipc/host_impl.cc
+++ b/src/ipc/host_impl.cc
@@ -129,7 +129,8 @@
 }
 
 void HostImpl::AdoptConnectedSocket_Fuchsia(
-    base::ScopedSocketHandle connected_socket) {
+    base::ScopedSocketHandle connected_socket,
+    std::function<bool(int)> send_fd_cb) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
   PERFETTO_DCHECK(connected_socket);
   // Should not be used in conjunction with listen sockets.
@@ -139,7 +140,11 @@
       std::move(connected_socket), this, task_runner_, kHostSockFamily,
       base::SockType::kStream);
 
+  auto* unix_socket_ptr = unix_socket.get();
   OnNewIncomingConnection(nullptr, std::move(unix_socket));
+  ClientConnection* client_connection = clients_by_socket_[unix_socket_ptr];
+  client_connection->send_fd_cb_fuchsia = std::move(send_fd_cb);
+  PERFETTO_DCHECK(client_connection->send_fd_cb_fuchsia);
 }
 
 void HostImpl::OnNewIncomingConnection(
@@ -302,6 +307,18 @@
 
   std::string buf = BufferedFrameDeserializer::Serialize(frame);
 
+  // On Fuchsia, |send_fd_cb_fuchsia_| is used to send the FD to the client
+  // and therefore must be set.
+  PERFETTO_DCHECK(!PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA) ||
+                  client->send_fd_cb_fuchsia);
+  if (client->send_fd_cb_fuchsia && fd != base::ScopedFile::kInvalid) {
+    if (!client->send_fd_cb_fuchsia(fd)) {
+      client->sock->Shutdown(true);
+      return;
+    }
+    fd = base::ScopedFile::kInvalid;
+  }
+
   // When a new Client connects in OnNewClientConnection we set a timeout on
   // Send (see call to SetTxTimeout).
   //
diff --git a/src/ipc/host_impl.h b/src/ipc/host_impl.h
index c2c60ba..2f0a4f8 100644
--- a/src/ipc/host_impl.h
+++ b/src/ipc/host_impl.h
@@ -23,6 +23,7 @@
 #include <vector>
 
 #include "perfetto/base/task_runner.h"
+#include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/thread_checker.h"
 #include "perfetto/ext/base/unix_socket.h"
 #include "perfetto/ext/ipc/deferred.h"
@@ -41,7 +42,9 @@
 
   // Host implementation.
   bool ExposeService(std::unique_ptr<Service>) override;
-  void AdoptConnectedSocket_Fuchsia(base::ScopedSocketHandle) override;
+  void AdoptConnectedSocket_Fuchsia(
+      base::ScopedSocketHandle,
+      std::function<bool(int)> send_fd_cb) override;
 
   // base::UnixSocket::EventListener implementation.
   void OnNewIncomingConnection(base::UnixSocket*,
@@ -59,6 +62,7 @@
     std::unique_ptr<base::UnixSocket> sock;
     BufferedFrameDeserializer frame_deserializer;
     base::ScopedFile received_fd;
+    std::function<bool(int)> send_fd_cb_fuchsia;
   };
   struct ExposedService {
     ExposedService(ServiceID, const std::string&, std::unique_ptr<Service>);
diff --git a/src/ipc/host_impl_unittest.cc b/src/ipc/host_impl_unittest.cc
index 15a0004..9c23421 100644
--- a/src/ipc/host_impl_unittest.cc
+++ b/src/ipc/host_impl_unittest.cc
@@ -183,7 +183,8 @@
     auto socket_pair = base::UnixSocketRaw::CreatePairPosix(
         base::SockFamily::kUnix, base::SockType::kStream);
     host->AdoptConnectedSocket_Fuchsia(
-        base::ScopedSocketHandle(socket_pair.first.ReleaseFd()));
+        base::ScopedSocketHandle(socket_pair.first.ReleaseFd()),
+        [](int) { return false; });
     cli_.reset(
         new FakeClient(base::ScopedSocketHandle(socket_pair.second.ReleaseFd()),
                        task_runner_.get()));
diff --git a/src/ipc/test/ipc_integrationtest.cc b/src/ipc/test/ipc_integrationtest.cc
index 1e2b196..de824ac 100644
--- a/src/ipc/test/ipc_integrationtest.cc
+++ b/src/ipc/test/ipc_integrationtest.cc
@@ -95,7 +95,8 @@
           perfetto::base::ScopedSocketHandle(socket_pair.first.ReleaseFd())),
       &task_runner_);
   host->AdoptConnectedSocket_Fuchsia(
-      perfetto::base::ScopedSocketHandle(socket_pair.second.ReleaseFd()));
+      perfetto::base::ScopedSocketHandle(socket_pair.second.ReleaseFd()),
+      [](int) { return false; });
 #else
   std::unique_ptr<Client> cli = Client::CreateInstance(
       {kTestSocket.name(), /*retry=*/false}, &task_runner_);
diff --git a/src/kallsyms/kernel_symbol_map_unittest.cc b/src/kallsyms/kernel_symbol_map_unittest.cc
index 9cecdda..b0ac1d4 100644
--- a/src/kallsyms/kernel_symbol_map_unittest.cc
+++ b/src/kallsyms/kernel_symbol_map_unittest.cc
@@ -21,6 +21,7 @@
 #include <unordered_map>
 
 #include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/ext/base/temp_file.h"
 
@@ -143,10 +144,9 @@
     for (size_t i = 0; i < sym_name_len; i++)
       sym_name[i] = kCharset[rng() % strlen(kCharset)];
     sym_name[sym_name_len] = '\0';
-    char line[kNameMax + 40];
-    sprintf(line, "%" PRIx64 " t %s\n", addr, sym_name);
+    base::StackString<128> line("%" PRIx64 " t %s\n", addr, sym_name);
     symbols[addr] = sym_name;
-    fake_kallsyms += line;
+    fake_kallsyms += line.ToStdString();
   }
   base::TempFile tmp = base::TempFile::Create();
   base::WriteAll(tmp.fd(), fake_kallsyms.data(), fake_kallsyms.size());
diff --git a/src/kernel_utils/BUILD.gn b/src/kernel_utils/BUILD.gn
index 5470a76..9f136ac 100644
--- a/src/kernel_utils/BUILD.gn
+++ b/src/kernel_utils/BUILD.gn
@@ -18,8 +18,8 @@
     "../base",
   ]
   sources = [
-    "syscall_table.h",
     "syscall_table.cc",
+    "syscall_table.h",
     "syscalls_aarch32.h",
     "syscalls_aarch64.h",
     "syscalls_armeabi.h",
diff --git a/src/kernel_utils/syscall_table.h b/src/kernel_utils/syscall_table.h
index 6889282..3e88e0a 100644
--- a/src/kernel_utils/syscall_table.h
+++ b/src/kernel_utils/syscall_table.h
@@ -40,6 +40,10 @@
  public:
   explicit SyscallTable(Architecture arch);
 
+  // Use for testing.
+  SyscallTable(const char* const* table, size_t count)
+      : syscall_count_(count), syscall_table_(table) {}
+
   // Return the architecture enum for the given uname machine string.
   static Architecture ArchFromString(base::StringView machine);
 
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index a2661fd..25bdfbd 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -202,6 +202,9 @@
 
 PerfettoCmd::~PerfettoCmd() {
   PERFETTO_DCHECK(g_perfetto_cmd == this);
+  if (ctrl_c_handler_installed_) {
+    task_runner_.RemoveFileDescriptorWatch(ctrl_c_evt_.fd());
+  }
   g_perfetto_cmd = nullptr;
 }
 
@@ -216,6 +219,8 @@
                              data sources to be started before exiting. Exit
                              code is zero if a successful acknowledgement is
                              received, non-zero otherwise (error or timeout).
+  --clone TSID             : Creates a read-only clone of an existing tracing
+                             session, identified by its ID (see --query).
   --config         -c      : /path/to/trace/config/file or - for stdin
   --out            -o      : /path/to/out/trace/file or - for stdout
   --txt                    : Parse config as pbtxt. Not for production use.
@@ -268,6 +273,7 @@
   enum LongOption {
     OPT_ALERT_ID = 1000,
     OPT_BUGREPORT,
+    OPT_CLONE,
     OPT_CONFIG_ID,
     OPT_CONFIG_UID,
     OPT_SUBSCRIPTION_ID,
@@ -305,6 +311,7 @@
       {"reset-guardrails", no_argument, nullptr, OPT_RESET_GUARDRAILS},
       {"detach", required_argument, nullptr, OPT_DETACH},
       {"attach", required_argument, nullptr, OPT_ATTACH},
+      {"clone", required_argument, nullptr, OPT_CLONE},
       {"is_detached", required_argument, nullptr, OPT_IS_DETACHED},
       {"stop", no_argument, nullptr, OPT_STOP},
       {"query", no_argument, nullptr, OPT_QUERY},
@@ -374,6 +381,11 @@
       continue;
     }
 
+    if (option == OPT_CLONE) {
+      clone_tsid_ = static_cast<TracingSessionID>(atoll(optarg));
+      continue;
+    }
+
     if (option == 't') {
       has_config_options = true;
       config_options.time = std::string(optarg);
@@ -546,7 +558,7 @@
   bool parsed = false;
   const bool will_trace_or_trigger =
       !is_attach() && !query_service_ && !bugreport_;
-  if (!will_trace_or_trigger) {
+  if (!will_trace_or_trigger || clone_tsid_) {
     if ((!trace_config_raw.empty() || has_config_options)) {
       PERFETTO_ELOG("Cannot specify a trace config with this option");
       return 1;
@@ -576,7 +588,7 @@
   if (parsed) {
     *trace_config_->mutable_statsd_metadata() = std::move(statsd_metadata);
     trace_config_raw.clear();
-  } else if (will_trace_or_trigger) {
+  } else if (will_trace_or_trigger && !clone_tsid_) {
     PERFETTO_ELOG("The trace config is invalid, bailing out.");
     return 1;
   }
@@ -959,6 +971,7 @@
 }
 
 void PerfettoCmd::OnConnect() {
+  connected_ = true;
   LogUploadEvent(PerfettoStatsdAtom::kOnConnect);
 
   if (background_wait_) {
@@ -994,6 +1007,11 @@
     return;
   }
 
+  if (clone_tsid_.has_value()) {
+    consumer_endpoint_->CloneSession(*clone_tsid_);
+    return;
+  }
+
   if (expected_duration_ms_) {
     PERFETTO_LOG("Connected to the Perfetto traced service, TTL: %ds",
                  (expected_duration_ms_ + 999) / 1000);
@@ -1029,7 +1047,23 @@
 }
 
 void PerfettoCmd::OnDisconnect() {
-  PERFETTO_LOG("Disconnected from the Perfetto traced service");
+  if (connected_) {
+    PERFETTO_LOG("Disconnected from the traced service");
+  } else {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+    static const char kDocUrl[] =
+        "https://perfetto.dev/docs/quickstart/android-tracing";
+#else
+    static const char kDocUrl[] =
+        "https://perfetto.dev/docs/quickstart/linux-tracing";
+#endif
+    PERFETTO_LOG(
+        "Could not connect to the traced socket %s. Ensure traced is "
+        "running or use tracebox. See %s.",
+        GetConsumerSocket(), kDocUrl);
+  }
+
+  connected_ = false;
   task_runner_.Quit();
 }
 
@@ -1063,6 +1097,10 @@
 }
 
 void PerfettoCmd::OnTracingDisabled(const std::string& error) {
+  ReadbackTraceDataAndQuit(error);
+}
+
+void PerfettoCmd::ReadbackTraceDataAndQuit(const std::string& error) {
   if (!error.empty()) {
     // Some of these errors (e.g. unique session name already exists) are soft
     // errors and likely to happen in nominal condition. As such they shouldn't
@@ -1164,7 +1202,12 @@
 }
 
 void PerfettoCmd::SetupCtrlCSignalHandler() {
-  base::InstallCtrCHandler([] { g_perfetto_cmd->SignalCtrlC(); });
+  ctrl_c_handler_installed_ = true;
+  base::InstallCtrlCHandler([] {
+    if (!g_perfetto_cmd)
+      return;
+    g_perfetto_cmd->SignalCtrlC();
+  });
   task_runner_.AddFileDescriptorWatch(ctrl_c_evt_.fd(), [this] {
     PERFETTO_LOG("SIGINT/SIGTERM received: disabling tracing.");
     ctrl_c_evt_.Clear();
@@ -1219,6 +1262,20 @@
   // TODO(eseckler): Support GetTraceStats().
 }
 
+void PerfettoCmd::OnSessionCloned(bool success, const std::string& error) {
+  PERFETTO_DLOG("Cloned tracing session %" PRIu64 ", success=%d",
+                clone_tsid_.value_or(0), success);
+  std::string full_error;
+  if (!success) {
+    full_error = "Failed to clone tracing session " +
+                 std::to_string(clone_tsid_.value_or(0)) + ": " + error;
+  }
+
+  // Kick off the readback and file finalization (as if we started tracing and
+  // reached the duration_ms timeout).
+  ReadbackTraceDataAndQuit(full_error);
+}
+
 void PerfettoCmd::PrintServiceState(bool success,
                                     const TracingServiceState& svc_state) {
   if (!success) {
diff --git a/src/perfetto_cmd/perfetto_cmd.h b/src/perfetto_cmd/perfetto_cmd.h
index 28c7733..b64159e 100644
--- a/src/perfetto_cmd/perfetto_cmd.h
+++ b/src/perfetto_cmd/perfetto_cmd.h
@@ -67,6 +67,7 @@
   void OnAttach(bool, const TraceConfig&) override;
   void OnTraceStats(bool, const TraceStats&) override;
   void OnObservableEvents(const ObservableEvents&) override;
+  void OnSessionCloned(bool, const std::string&) override;
 
   void SignalCtrlC() { ctrl_c_evt_.Notify(); }
 
@@ -90,6 +91,8 @@
 
   int ConnectToServiceAndRun();
 
+  void ReadbackTraceDataAndQuit(const std::string& error);
+
   enum BgProcessStatus : char {
     kBackgroundOk = 0,
     kBackgroundOtherError = 1,
@@ -134,6 +137,7 @@
   std::vector<std::string> triggers_to_activate_;
   std::string trace_out_path_;
   base::EventFd ctrl_c_evt_;
+  bool ctrl_c_handler_installed_ = false;
   base::Pipe background_wait_pipe_;
   bool save_to_incidentd_ = false;
   bool report_to_android_framework_ = false;
@@ -151,7 +155,9 @@
   bool background_wait_ = false;
   bool ignore_guardrails_ = false;
   bool upload_flag_ = false;
+  bool connected_ = false;
   std::string uuid_;
+  base::Optional<TracingSessionID> clone_tsid_{};
 
   // How long we expect to trace for or 0 if the trace is indefinite.
   uint32_t expected_duration_ms_ = 0;
diff --git a/src/perfetto_cmd/perfetto_cmd_android.cc b/src/perfetto_cmd/perfetto_cmd_android.cc
index c1fa07b..b03493f 100644
--- a/src/perfetto_cmd/perfetto_cmd_android.cc
+++ b/src/perfetto_cmd/perfetto_cmd_android.cc
@@ -118,13 +118,13 @@
 // errors.
 void PerfettoCmd::SaveOutputToIncidentTraceOrCrash() {
   LogUploadEvent(PerfettoStatsdAtom::kUploadIncidentBegin);
-  char kIncidentTracePath[256];
-  sprintf(kIncidentTracePath, "%s/incident-trace", kStateDir);
+  base::StackString<256> kIncidentTracePath("%s/incident-trace", kStateDir);
 
-  char kTempIncidentTracePath[256];
-  sprintf(kTempIncidentTracePath, "%s.temp", kIncidentTracePath);
+  base::StackString<256> kTempIncidentTracePath("%s.temp",
+                                                kIncidentTracePath.c_str());
 
-  PERFETTO_CHECK(unlink(kTempIncidentTracePath) == 0 || errno == ENOENT);
+  PERFETTO_CHECK(unlink(kTempIncidentTracePath.c_str()) == 0 ||
+                 errno == ENOENT);
 
   // TODO(b/155024256) These should not be necessary (we flush when destroying
   // packet writer and sendfile should ignore file offset) however they should
@@ -133,8 +133,8 @@
   PERFETTO_CHECK(fseek(*trace_out_stream_, 0, SEEK_SET) == 0);
 
   // SELinux constrains the set of readers.
-  base::ScopedFile staging_fd =
-      base::OpenFile(kTempIncidentTracePath, O_CREAT | O_EXCL | O_RDWR, 0666);
+  base::ScopedFile staging_fd = base::OpenFile(kTempIncidentTracePath.c_str(),
+                                               O_CREAT | O_EXCL | O_RDWR, 0666);
   PERFETTO_CHECK(staging_fd);
 
   int fd = fileno(*trace_out_stream_);
@@ -169,7 +169,8 @@
   }
 
   staging_fd.reset();
-  PERFETTO_CHECK(rename(kTempIncidentTracePath, kIncidentTracePath) == 0);
+  PERFETTO_CHECK(
+      rename(kTempIncidentTracePath.c_str(), kIncidentTracePath.c_str()) == 0);
   // Note: not calling fsync(2), as we're not interested in the file being
   // consistent in case of a crash.
   LogUploadEvent(PerfettoStatsdAtom::kUploadIncidentSuccess);
diff --git a/src/profiling/common/unwind_support.cc b/src/profiling/common/unwind_support.cc
index 8c0f2e4..c0a504c 100644
--- a/src/profiling/common/unwind_support.cc
+++ b/src/profiling/common/unwind_support.cc
@@ -168,6 +168,8 @@
       return "MAPS_PARSE";
     case unwindstack::ERROR_INVALID_PARAMETER:
       return "INVALID_PARAMETER";
+    case unwindstack::ERROR_PTRACE_CALL:
+      return "PTRACE_CALL";
   }
 }
 
diff --git a/src/profiling/memory/heapprofd_client_api.map.txt b/src/profiling/memory/heapprofd_client_api.map.txt
index 11d8d58..471ccd6 100644
--- a/src/profiling/memory/heapprofd_client_api.map.txt
+++ b/src/profiling/memory/heapprofd_client_api.map.txt
@@ -1,13 +1,13 @@
 HEAPPROFD_API_S { # introduced=S
   global:
-    AHeapProfileEnableCallbackInfo_getSamplingInterval; # apex
-    AHeapProfile_reportSample; # apex
-    AHeapProfile_reportFree; # apex
-    AHeapProfile_reportAllocation; # apex
-    AHeapProfile_registerHeap; # apex
-    AHeapInfo_setEnabledCallback; # apex
-    AHeapInfo_setDisabledCallback; # apex
-    AHeapInfo_create; # apex
+    AHeapProfileEnableCallbackInfo_getSamplingInterval; # systemapi
+    AHeapProfile_reportSample; # systemapi
+    AHeapProfile_reportFree; # systemapi
+    AHeapProfile_reportAllocation; # systemapi
+    AHeapProfile_registerHeap; # systemapi
+    AHeapInfo_setEnabledCallback; # systemapi
+    AHeapInfo_setDisabledCallback; # systemapi
+    AHeapInfo_create; # systemapi
 };
 
 PRIVATE {
diff --git a/src/profiling/memory/heapprofd_end_to_end_test.cc b/src/profiling/memory/heapprofd_end_to_end_test.cc
index c342de0..de9f40e 100644
--- a/src/profiling/memory/heapprofd_end_to_end_test.cc
+++ b/src/profiling/memory/heapprofd_end_to_end_test.cc
@@ -1762,10 +1762,6 @@
   EXPECT_GT(total_allocated, 0u);
 }
 
-// Disable these tests when running with sanitizers. They (double) fork and that
-// seems to cause flaky crashes with sanitizers.
-#if !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER) && \
-    !defined(MEMORY_SANITIZER) && !defined(LEAK_SANITIZER)
 // On in-tree Android, we use the system heapprofd in fork or central mode.
 // For Linux and out-of-tree Android, we statically include a copy of
 // heapprofd and use that. This one does not support intercepting malloc.
@@ -1787,10 +1783,6 @@
            std::make_tuple(TestMode::kCentral, AllocatorMode::kCustom)),
     TestSuffix);
 #endif
-#else  // defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) ||
-       // defined(MEMORY_SANITIZER) || defined(LEAK_SANITIZER)
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HeapprofdEndToEnd);
-#endif
 
 }  // namespace
 }  // namespace profiling
diff --git a/src/profiling/memory/heapprofd_producer.h b/src/profiling/memory/heapprofd_producer.h
index 4c57a5d..92f830a 100644
--- a/src/profiling/memory/heapprofd_producer.h
+++ b/src/profiling/memory/heapprofd_producer.h
@@ -201,7 +201,7 @@
     HeapInfo& GetHeapInfo(uint32_t heap_id) {
       auto it = heap_infos.find(heap_id);
       if (it == heap_infos.end()) {
-        std::tie(it, std::ignore) = heap_infos.emplace(
+        it = heap_infos.emplace_hint(it,
             std::piecewise_construct, std::forward_as_tuple(heap_id),
             std::forward_as_tuple(callsites, dump_at_max_mode));
       }
diff --git a/src/profiling/memory/unwinding.cc b/src/profiling/memory/unwinding.cc
index 6d40a36..4a7a5d3 100644
--- a/src/profiling/memory/unwinding.cc
+++ b/src/profiling/memory/unwinding.cc
@@ -23,6 +23,7 @@
 #include <unwindstack/MachineArm64.h>
 #include <unwindstack/MachineMips.h>
 #include <unwindstack/MachineMips64.h>
+#include <unwindstack/MachineRiscv64.h>
 #include <unwindstack/MachineX86.h>
 #include <unwindstack/MachineX86_64.h>
 #include <unwindstack/Maps.h>
@@ -32,6 +33,7 @@
 #include <unwindstack/RegsArm64.h>
 #include <unwindstack/RegsMips.h>
 #include <unwindstack/RegsMips64.h>
+#include <unwindstack/RegsRiscv64.h>
 #include <unwindstack/RegsX86.h>
 #include <unwindstack/RegsX86_64.h>
 #include <unwindstack/Unwinder.h>
@@ -39,6 +41,7 @@
 #include <unwindstack/UserArm64.h>
 #include <unwindstack/UserMips.h>
 #include <unwindstack/UserMips64.h>
+#include <unwindstack/UserRiscv64.h>
 #include <unwindstack/UserX86.h>
 #include <unwindstack/UserX86_64.h>
 
@@ -113,6 +116,9 @@
     case unwindstack::ARCH_MIPS64:
       ret.reset(new unwindstack::RegsMips64());
       break;
+    case unwindstack::ARCH_RISCV64:
+      ret.reset(new unwindstack::RegsRiscv64());
+      break;
     case unwindstack::ARCH_UNKNOWN:
       break;
   }
diff --git a/src/profiling/memory/unwinding_unittest.cc b/src/profiling/memory/unwinding_unittest.cc
index 5a238e9..f4b3ef4 100644
--- a/src/profiling/memory/unwinding_unittest.cc
+++ b/src/profiling/memory/unwinding_unittest.cc
@@ -60,6 +60,10 @@
 }
 
 TEST(UnwindingTest, FDMapsParse) {
+#if defined(ADDRESS_SANITIZER)
+  PERFETTO_LOG("Skipping /proc/self/maps as ASAN distorts what is where");
+  GTEST_SKIP();
+#else
   base::ScopedFile proc_maps(base::OpenFile("/proc/self/maps", O_RDONLY));
   ASSERT_TRUE(proc_maps);
   FDMaps maps(std::move(proc_maps));
@@ -68,6 +72,7 @@
       maps.Find(reinterpret_cast<uint64_t>(&proc_maps)).get();
   ASSERT_NE(map_info, nullptr);
   ASSERT_EQ(map_info->name(), "[stack]");
+#endif
 }
 
 void __attribute__((noinline)) AssertFunctionOffset() {
diff --git a/src/profiling/memory/wire_protocol.h b/src/profiling/memory/wire_protocol.h
index 164358f..5cb4c38 100644
--- a/src/profiling/memory/wire_protocol.h
+++ b/src/profiling/memory/wire_protocol.h
@@ -27,6 +27,7 @@
 #include <unwindstack/MachineArm64.h>
 #include <unwindstack/MachineMips.h>
 #include <unwindstack/MachineMips64.h>
+#include <unwindstack/MachineRiscv64.h>
 #include <unwindstack/MachineX86.h>
 #include <unwindstack/MachineX86_64.h>
 
@@ -54,13 +55,15 @@
     constexpr_max(
       constexpr_max(
         constexpr_max(
-            constexpr_max(
-              sizeof(uint32_t) * unwindstack::ARM_REG_LAST,
-              sizeof(uint64_t) * unwindstack::ARM64_REG_LAST),
-            sizeof(uint32_t) * unwindstack::X86_REG_LAST),
-          sizeof(uint64_t) * unwindstack::X86_64_REG_LAST),
-        sizeof(uint32_t) * unwindstack::MIPS_REG_LAST),
-      sizeof(uint64_t) * unwindstack::MIPS64_REG_LAST
+          constexpr_max(
+              constexpr_max(
+                sizeof(uint32_t) * unwindstack::ARM_REG_LAST,
+                sizeof(uint64_t) * unwindstack::ARM64_REG_LAST),
+              sizeof(uint32_t) * unwindstack::X86_REG_LAST),
+            sizeof(uint64_t) * unwindstack::X86_64_REG_LAST),
+          sizeof(uint32_t) * unwindstack::MIPS_REG_LAST),
+        sizeof(uint64_t) * unwindstack::MIPS64_REG_LAST),
+      sizeof(uint64_t) * unwindstack::RISCV64_REG_MAX
   );
 // clang-format on
 
diff --git a/src/profiling/perf/perf_producer.cc b/src/profiling/perf/perf_producer.cc
index 092ac49..ee15555 100644
--- a/src/profiling/perf/perf_producer.cc
+++ b/src/profiling/perf/perf_producer.cc
@@ -236,6 +236,8 @@
       return Profiling::UNWIND_ERROR_MAPS_PARSE;
     case unwindstack::ERROR_INVALID_PARAMETER:
       return Profiling::UNWIND_ERROR_INVALID_PARAMETER;
+    case unwindstack::ERROR_PTRACE_CALL:
+      return Profiling::UNWIND_ERROR_PTRACE_CALL;
   }
   return Profiling::UNWIND_ERROR_UNKNOWN;
 }
diff --git a/src/profiling/perf/regs_parsing.cc b/src/profiling/perf/regs_parsing.cc
index 99bed42..37d072a 100644
--- a/src/profiling/perf/regs_parsing.cc
+++ b/src/profiling/perf/regs_parsing.cc
@@ -26,22 +26,30 @@
 #include <unwindstack/Elf.h>
 #include <unwindstack/MachineArm.h>
 #include <unwindstack/MachineArm64.h>
+#include <unwindstack/MachineRiscv64.h>
 #include <unwindstack/Regs.h>
 #include <unwindstack/RegsArm.h>
 #include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsRiscv64.h>
 #include <unwindstack/RegsX86.h>
 #include <unwindstack/RegsX86_64.h>
 #include <unwindstack/UserArm.h>
 #include <unwindstack/UserArm64.h>
+#include <unwindstack/UserRiscv64.h>
 #include <unwindstack/UserX86.h>
 #include <unwindstack/UserX86_64.h>
 
 // kernel uapi headers
 #include <uapi/asm-arm/asm/perf_regs.h>
+#undef PERF_REG_EXTENDED_MASK
 #include <uapi/asm-x86/asm/perf_regs.h>
+#undef PERF_REG_EXTENDED_MASK
 #define perf_event_arm_regs perf_event_arm64_regs
 #include <uapi/asm-arm64/asm/perf_regs.h>
+#undef PERF_REG_EXTENDED_MASK
 #undef perf_event_arm_regs
+#include <uapi/asm-riscv/asm/perf_regs.h>
+#undef PERF_REG_EXTENDED_MASK
 
 namespace perfetto {
 namespace profiling {
@@ -65,14 +73,14 @@
 // 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.
+// TODO(rsavitski): cleanly detect 32 bit traced_perf 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);
+      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:
@@ -85,6 +93,8 @@
       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);
+    case unwindstack::ARCH_RISCV64:
+      return (1ULL << PERF_REG_RISCV_MAX) - 1;
     default:
       PERFETTO_FATAL("Unsupported architecture");
   }
@@ -93,6 +103,7 @@
 // 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.
+// TODO(rsavitski): on riscv64, are 32 bit userspace processes posible?
 unwindstack::ArchEnum ArchForAbi(unwindstack::ArchEnum arch, uint64_t abi) {
   if (arch == unwindstack::ARCH_ARM64 && abi == PERF_SAMPLE_REGS_ABI_32) {
     return unwindstack::ARCH_ARM;
@@ -107,8 +118,8 @@
 // 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));
+      constexpr_max(constexpr_max(PERF_REG_ARM_MAX, PERF_REG_ARM64_MAX),
+                    constexpr_max(PERF_REG_X86_64_MAX, PERF_REG_RISCV_MAX));
   uint64_t regs[kMaxSize] = {};
 };
 
@@ -198,6 +209,22 @@
         unwindstack::RegsX86::Read(&x86_user_regs));
   }
 
+  if (arch == unwindstack::ARCH_RISCV64) {
+    static_assert(static_cast<int>(unwindstack::RISCV64_REG_PC) ==
+                          static_cast<int>(PERF_REG_RISCV_PC) &&
+                      static_cast<int>(unwindstack::RISCV64_REG_PC) == 0,
+                  "register layout mismatch");
+    static_assert(static_cast<int>(unwindstack::RISCV64_REG_MAX) ==
+                      static_cast<int>(PERF_REG_RISCV_MAX),
+                  "register layout mismatch");
+    // Register layout should match, memcpy the 32 registers directly.
+    unwindstack::riscv64_user_regs riscv64_user_regs = {};
+    memcpy(&riscv64_user_regs.regs[0], &raw_regs.regs[0],
+           sizeof(uint64_t) * PERF_REG_RISCV_MAX);
+    return std::unique_ptr<unwindstack::Regs>(
+        unwindstack::RegsRiscv64::Read(&riscv64_user_regs));
+  }
+
   PERFETTO_FATAL("Unsupported architecture");
 }
 
diff --git a/src/profiling/symbolizer/BUILD.gn b/src/profiling/symbolizer/BUILD.gn
index f5a1af7..94feb3e 100644
--- a/src/profiling/symbolizer/BUILD.gn
+++ b/src/profiling/symbolizer/BUILD.gn
@@ -37,23 +37,25 @@
   ]
 }
 
-source_set("symbolize_database") {
-  public_deps = [
-    ":symbolizer",
-    "../../../include/perfetto/ext/base",
-  ]
-  deps = [
-    "../../../gn:default_deps",
-    "../../../include/perfetto/protozero",
-    "../../../include/perfetto/trace_processor:trace_processor",
-    "../../../protos/perfetto/trace:zero",
-    "../../../protos/perfetto/trace/profiling:zero",
-    "../../trace_processor/util:stack_traces_util",
-  ]
-  sources = [
-    "symbolize_database.cc",
-    "symbolize_database.h",
-  ]
+if (enable_perfetto_trace_processor) {
+  source_set("symbolize_database") {
+    public_deps = [
+      ":symbolizer",
+      "../../../include/perfetto/ext/base",
+    ]
+    deps = [
+      "../../../gn:default_deps",
+      "../../../include/perfetto/protozero",
+      "../../../include/perfetto/trace_processor:trace_processor",
+      "../../../protos/perfetto/trace:zero",
+      "../../../protos/perfetto/trace/profiling:zero",
+      "../../trace_processor/util:stack_traces_util",
+    ]
+    sources = [
+      "symbolize_database.cc",
+      "symbolize_database.h",
+    ]
+  }
 }
 
 perfetto_unittest_source_set("unittests") {
diff --git a/src/profiling/symbolizer/local_symbolizer.cc b/src/profiling/symbolizer/local_symbolizer.cc
index 1fe4dbd..e37cee7 100644
--- a/src/profiling/symbolizer/local_symbolizer.cc
+++ b/src/profiling/symbolizer/local_symbolizer.cc
@@ -475,9 +475,9 @@
     const std::string& binary,
     uint64_t address) {
   std::vector<SymbolizedFrame> result;
-  char buffer[1024];
-  int size = sprintf(buffer, "\"%s\" 0x%" PRIx64 "\n", binary.c_str(), address);
-  if (subprocess_.Write(buffer, static_cast<size_t>(size)) < 0) {
+  base::StackString<1024> buffer("\"%s\" 0x%" PRIx64 "\n", binary.c_str(),
+                                 address);
+  if (subprocess_.Write(buffer.c_str(), buffer.len()) < 0) {
     PERFETTO_ELOG("Failed to write to llvm-symbolizer.");
     return result;
   }
diff --git a/src/protozero/filtering/filter_bytecode_generator.cc b/src/protozero/filtering/filter_bytecode_generator.cc
index 9362869..b8a059a 100644
--- a/src/protozero/filtering/filter_bytecode_generator.cc
+++ b/src/protozero/filtering/filter_bytecode_generator.cc
@@ -79,7 +79,7 @@
   PERFETTO_CHECK(endmessage_called_);
   PERFETTO_CHECK(max_msg_index_ < num_messages_);
   protozero::PackedVarInt words;
-  perfetto::base::Hash hasher;
+  perfetto::base::Hasher hasher;
   for (uint32_t word : bytecode_) {
     words.Append(word);
     hasher.Update(word);
diff --git a/src/protozero/filtering/filter_bytecode_parser.cc b/src/protozero/filtering/filter_bytecode_parser.cc
index 39c8bb6..371a43e 100644
--- a/src/protozero/filtering/filter_bytecode_parser.cc
+++ b/src/protozero/filtering/filter_bytecode_parser.cc
@@ -56,7 +56,7 @@
   if (packed_parse_err || words.empty())
     return false;
 
-  perfetto::base::Hash hasher;
+  perfetto::base::Hasher hasher;
   for (size_t i = 0; i < words.size() - 1; ++i)
     hasher.Update(words[i]);
 
diff --git a/src/protozero/filtering/filter_bytecode_parser_fuzzer.cc b/src/protozero/filtering/filter_bytecode_parser_fuzzer.cc
index 578f6c9..f14f101 100644
--- a/src/protozero/filtering/filter_bytecode_parser_fuzzer.cc
+++ b/src/protozero/filtering/filter_bytecode_parser_fuzzer.cc
@@ -35,7 +35,7 @@
                               const uint8_t* data,
                               size_t size) {
   protozero::PackedVarInt words;
-  perfetto::base::Hash hasher;
+  perfetto::base::Hasher hasher;
   for (size_t i = 0; i < size; i += sizeof(uint32_t)) {
     uint32_t word = 0;
     memcpy(&word, data, sizeof(uint32_t));
diff --git a/src/protozero/filtering/filter_bytecode_parser_unittest.cc b/src/protozero/filtering/filter_bytecode_parser_unittest.cc
index 4c5d8dd..e378d53 100644
--- a/src/protozero/filtering/filter_bytecode_parser_unittest.cc
+++ b/src/protozero/filtering/filter_bytecode_parser_unittest.cc
@@ -28,7 +28,7 @@
 
 bool LoadBytecode(FilterBytecodeParser* parser,
                   std::initializer_list<uint32_t> bytecode) {
-  perfetto::base::Hash hasher;
+  perfetto::base::Hasher hasher;
   protozero::PackedVarInt words;
   for (uint32_t w : bytecode) {
     words.Append(w);
diff --git a/src/protozero/filtering/filter_util.cc b/src/protozero/filtering/filter_util.cc
index 01a0bb4..209ea95 100644
--- a/src/protozero/filtering/filter_util.cc
+++ b/src/protozero/filtering/filter_util.cc
@@ -85,8 +85,7 @@
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
   // If the path is absolute, maps "C:/" -> "C:/" (without hardcoding 'C').
   if (proto_file.size() > 3 && proto_file[1] == ':') {
-    char win_drive[4];
-    sprintf(win_drive, "%c:/", proto_file[0]);
+    char win_drive[4]{proto_file[0], ':', '/', '\0'};
     dst.MapPath(win_drive, win_drive);
   }
 #endif
diff --git a/src/protozero/filtering/message_filter.cc b/src/protozero/filtering/message_filter.cc
index a971dfa..f3edf75 100644
--- a/src/protozero/filtering/message_filter.cc
+++ b/src/protozero/filtering/message_filter.cc
@@ -73,6 +73,11 @@
   stack_.emplace_back();
 }
 
+MessageFilter::MessageFilter(const MessageFilter& other)
+    : root_msg_index_(other.root_msg_index_), filter_(other.filter_) {
+  stack_.emplace_back();
+}
+
 MessageFilter::~MessageFilter() = default;
 
 bool MessageFilter::LoadFilterBytecode(const void* filter_data, size_t len) {
diff --git a/src/protozero/filtering/message_filter.h b/src/protozero/filtering/message_filter.h
index 80ddd0e..c752b2a 100644
--- a/src/protozero/filtering/message_filter.h
+++ b/src/protozero/filtering/message_filter.h
@@ -53,6 +53,7 @@
 class MessageFilter {
  public:
   MessageFilter();
+  explicit MessageFilter(const MessageFilter&);
   ~MessageFilter();
 
   struct InputSlice {
diff --git a/src/protozero/packed_repeated_fields.cc b/src/protozero/packed_repeated_fields.cc
index b0e23c5..16d4539 100644
--- a/src/protozero/packed_repeated_fields.cc
+++ b/src/protozero/packed_repeated_fields.cc
@@ -20,8 +20,10 @@
 
 namespace protozero {
 
+#if !PERFETTO_IS_AT_LEAST_CPP17()
 // static
 constexpr size_t PackedBufferBase::kOnStackStorageSize;
+#endif
 
 void PackedBufferBase::GrowSlowpath() {
   size_t write_off = static_cast<size_t>(write_ptr_ - storage_begin_);
diff --git a/src/protozero/proto_decoder.cc b/src/protozero/proto_decoder.cc
index a766a28..0595c1f 100644
--- a/src/protozero/proto_decoder.cc
+++ b/src/protozero/proto_decoder.cc
@@ -46,8 +46,8 @@
 
 // Parses one field and returns the field itself and a pointer to the next
 // field to parse. If parsing fails, the returned |next| == |buffer|.
-PERFETTO_ALWAYS_INLINE ParseFieldResult
-ParseOneField(const uint8_t* const buffer, const uint8_t* const end) {
+ParseFieldResult ParseOneField(const uint8_t* const buffer,
+                               const uint8_t* const end) {
   ParseFieldResult res{ParseFieldResult::kAbort, buffer, Field{}};
 
   // The first byte of a proto field is structured as follows:
@@ -172,7 +172,6 @@
   return res;
 }
 
-PERFETTO_ALWAYS_INLINE
 Field ProtoDecoder::ReadField() {
   ParseFieldResult res;
   do {
diff --git a/src/protozero/protoc_plugin/cppgen_plugin.cc b/src/protozero/protoc_plugin/cppgen_plugin.cc
index 3086ece..a963910 100644
--- a/src/protozero/protoc_plugin/cppgen_plugin.cc
+++ b/src/protozero/protoc_plugin/cppgen_plugin.cc
@@ -437,11 +437,11 @@
     case FieldDescriptor::TYPE_UINT64:
     case FieldDescriptor::TYPE_SINT64:
     case FieldDescriptor::TYPE_BOOL:
+    case FieldDescriptor::TYPE_ENUM:
       return "::protozero::PackedVarInt";
     case FieldDescriptor::TYPE_STRING:
     case FieldDescriptor::TYPE_BYTES:
     case FieldDescriptor::TYPE_MESSAGE:
-    case FieldDescriptor::TYPE_ENUM:
     case FieldDescriptor::TYPE_GROUP:
       break;  // Will abort()
   }
@@ -466,11 +466,11 @@
     case FieldDescriptor::TYPE_UINT64:
     case FieldDescriptor::TYPE_SINT64:
     case FieldDescriptor::TYPE_BOOL:
+    case FieldDescriptor::TYPE_ENUM:
       return "::protozero::proto_utils::ProtoWireType::kVarInt";
     case FieldDescriptor::TYPE_STRING:
     case FieldDescriptor::TYPE_BYTES:
     case FieldDescriptor::TYPE_MESSAGE:
-    case FieldDescriptor::TYPE_ENUM:
     case FieldDescriptor::TYPE_GROUP:
       break;  // Will abort()
   }
diff --git a/src/protozero/test/example_proto/test_messages.proto b/src/protozero/test/example_proto/test_messages.proto
index 9c93286..4753373 100644
--- a/src/protozero/test/example_proto/test_messages.proto
+++ b/src/protozero/test/example_proto/test_messages.proto
@@ -118,10 +118,9 @@
   // Repeated (even non-packed) bool fields are currently unsupported by our
   // protoc plugin.
   // repeated bool field_bool = 13 [packed = true];
-  // Repeated packed enum fields are currently unsupported by our protoc plugin.
-  // repeated SmallEnum small_enum = 51 [packed = true];
-  // repeated SignedEnum signed_enum = 52 [packed = true];
-  // repeated BigEnum big_enum = 53 [packed = true];
+  repeated SmallEnum small_enum = 51 [packed = true];
+  repeated SignedEnum signed_enum = 52 [packed = true];
+  repeated BigEnum big_enum = 53 [packed = true];
 }
 
 // The following two messages are for testing that unknown fields being
diff --git a/src/tools/ftrace_proto_gen/event_list b/src/tools/ftrace_proto_gen/event_list
index 0f271ff..fdb8d3e 100644
--- a/src/tools/ftrace_proto_gen/event_list
+++ b/src/tools/ftrace_proto_gen/event_list
@@ -422,3 +422,51 @@
 v4l2/vb2_v4l2_buf_done
 v4l2/vb2_v4l2_qbuf
 v4l2/vb2_v4l2_dqbuf
+panel/dsi_cmd_fifo_status
+panel/dsi_rx
+panel/dsi_tx
+android_fs/android_fs_dataread_end
+android_fs/android_fs_dataread_start
+android_fs/android_fs_datawrite_end
+android_fs/android_fs_datawrite_start
+android_fs/android_fs_fsync_end
+android_fs/android_fs_fsync_start
+ftrace/funcgraph_entry
+ftrace/funcgraph_exit
+virtio_video/virtio_video_cmd
+virtio_video/virtio_video_cmd_done
+virtio_video/virtio_video_resource_queue
+virtio_video/virtio_video_resource_queue_done
+vmscan/mm_shrink_slab_start
+vmscan/mm_shrink_slab_end
+trusty/trusty_smc
+trusty/trusty_smc_done
+trusty/trusty_std_call32
+trusty/trusty_std_call32_done
+trusty/trusty_share_memory
+trusty/trusty_share_memory_done
+trusty/trusty_reclaim_memory
+trusty/trusty_reclaim_memory_done
+trusty/trusty_irq
+trusty/trusty_ipc_handle_event
+trusty/trusty_ipc_connect
+trusty/trusty_ipc_connect_end
+trusty/trusty_ipc_write
+trusty/trusty_ipc_poll
+removed trusty/trusty_ipc_poll_end
+trusty/trusty_ipc_read
+trusty/trusty_ipc_read_end
+trusty/trusty_ipc_rx
+removed trusty/trusty_ipc_tx
+trusty/trusty_enqueue_nop
+cma/cma_alloc_start
+cma/cma_alloc_info
+lwis/tracing_mark_write
+virtio_gpu/virtio_gpu_cmd_queue
+virtio_gpu/virtio_gpu_cmd_response
+mali/mali_KCPU_CQS_SET
+mali/mali_KCPU_CQS_WAIT_START
+mali/mali_KCPU_CQS_WAIT_END
+mali/mali_KCPU_FENCE_SIGNAL
+mali/mali_KCPU_FENCE_WAIT_START
+mali/mali_KCPU_FENCE_WAIT_END
diff --git a/src/tools/ftrace_proto_gen/ftrace_proto_gen.cc b/src/tools/ftrace_proto_gen/ftrace_proto_gen.cc
index d37008c..edfc687 100644
--- a/src/tools/ftrace_proto_gen/ftrace_proto_gen.cc
+++ b/src/tools/ftrace_proto_gen/ftrace_proto_gen.cc
@@ -58,7 +58,8 @@
   std::string event_name = (name == "0") ? "zero" : name;
   // These groups have events where the name alone conflicts with an existing
   // proto:
-  if (group == "sde" || group == "g2d" || group == "dpu" || group == "mali") {
+  if (group == "sde" || group == "g2d" || group == "dpu" || group == "mali" ||
+      group == "lwis") {
     event_name = group + "_" + event_name;
   }
   return event_name;
@@ -84,35 +85,21 @@
   return lines;
 }
 
-bool GenerateProto(const std::string& group,
-                   const FtraceEvent& format,
-                   Proto* proto_out) {
-  proto_out->name = EventNameToProtoName(group, format.name);
-  proto_out->event_name = format.name;
-  std::set<std::string> seen;
-  // TODO(hjd): We should be cleverer about id assignment.
-  uint32_t i = 1;
-  for (const FtraceEvent::Field& field : format.fields) {
+std::vector<Proto::Field> ToProtoFields(const FtraceEvent& format) {
+  std::vector<Proto::Field> ret;
+  for (size_t i = 0; i < format.fields.size(); i++) {
+    const FtraceEvent::Field& field = format.fields[i];
     std::string name = GetNameFromTypeAndName(field.type_and_name);
-    // TODO(hjd): Handle dup names.
-    // sa_handler is problematic because glib headers redefine it at the
-    // preprocessor level. It's impossible to have a variable or a function
-    // called sa_handler. On the good side, we realistically don't care about
-    // this field, it's just easier to skip it.
-    if (name == "" || seen.count(name) || name == "sa_handler" ||
-        name == "errno")
+    // Skip tracepoint fields whose names cannot be used as an identifier in the
+    // generated C++ code due to stdlib header conflicts.
+    if (name == "" || name == "sa_handler" || name == "errno")
       continue;
-    seen.insert(name);
     ProtoType type = InferProtoType(field);
-    // Check we managed to infer a type.
     if (type.type == ProtoType::INVALID)
       continue;
-    Proto::Field protofield{std::move(type), name, i};
-    proto_out->AddField(std::move(protofield));
-    i++;
+    ret.push_back({type, std::move(name), static_cast<uint32_t>(i)});
   }
-
-  return true;
+  return ret;
 }
 
 void GenerateFtraceEventProto(const std::vector<FtraceEventName>& raw_eventlist,
diff --git a/src/tools/ftrace_proto_gen/ftrace_proto_gen.h b/src/tools/ftrace_proto_gen/ftrace_proto_gen.h
index 27d6197..1133392 100644
--- a/src/tools/ftrace_proto_gen/ftrace_proto_gen.h
+++ b/src/tools/ftrace_proto_gen/ftrace_proto_gen.h
@@ -29,9 +29,7 @@
 
 namespace perfetto {
 
-bool GenerateProto(const std::string& group,
-                   const FtraceEvent& format,
-                   Proto* proto_out);
+std::vector<Proto::Field> ToProtoFields(const FtraceEvent& format);
 
 std::string EventNameToProtoName(const std::string& group,
                                  const std::string& name);
diff --git a/src/tools/ftrace_proto_gen/ftrace_proto_gen_unittest.cc b/src/tools/ftrace_proto_gen/ftrace_proto_gen_unittest.cc
index c79eaf8..3f6223d 100644
--- a/src/tools/ftrace_proto_gen/ftrace_proto_gen_unittest.cc
+++ b/src/tools/ftrace_proto_gen/ftrace_proto_gen_unittest.cc
@@ -55,16 +55,15 @@
 
   EXPECT_EQ(InferProtoType(Field{"char foo", 0, 0, false}).ToString(),
             "string");
+
+  EXPECT_EQ(
+      InferProtoType(Field{"unsigned long args[6]", 0, 0, false}).ToString(),
+      "uint64");
 }
 
-TEST(FtraceEventParserTest, GenerateProtoName) {
-  FtraceEvent input;
-  Proto output;
-  input.name = "the_snake_case_name";
-
-  GenerateProto("group", input, &output);
-
-  EXPECT_EQ(output.name, "TheSnakeCaseNameFtraceEvent");
+TEST(FtraceEventParserTest, EventNameToProtoName) {
+  std::string s = EventNameToProtoName("group", "the_snake_case_name");
+  EXPECT_EQ(s, "TheSnakeCaseNameFtraceEvent");
 }
 
 }  // namespace
diff --git a/src/tools/ftrace_proto_gen/main.cc b/src/tools/ftrace_proto_gen/main.cc
index 41b61fd..d47b47c 100644
--- a/src/tools/ftrace_proto_gen/main.cc
+++ b/src/tools/ftrace_proto_gen/main.cc
@@ -189,13 +189,8 @@
           return 1;
         }
 
-        perfetto::Proto event_proto;
-        if (!perfetto::GenerateProto(group, format, &event_proto)) {
-          fprintf(stderr, "Could not generate proto for file %s\n",
-                  input_path.c_str());
-          return 1;
-        }
-        proto.MergeFrom(event_proto);
+        auto proto_fields = perfetto::ToProtoFields(format);
+        proto.UnionFields(proto_fields);
       }
 
       uint32_t i = 0;
diff --git a/src/tools/ftrace_proto_gen/proto_gen_utils.cc b/src/tools/ftrace_proto_gen/proto_gen_utils.cc
index 1021033..66dfae4 100644
--- a/src/tools/ftrace_proto_gen/proto_gen_utils.cc
+++ b/src/tools/ftrace_proto_gen/proto_gen_utils.cc
@@ -141,38 +141,39 @@
 }
 
 // static
-ProtoType ProtoType::String() {
-  return {STRING, 0, false};
+ProtoType ProtoType::String(bool is_repeated) {
+  return {STRING, 0, false, is_repeated};
 }
 
 // static
 ProtoType ProtoType::Invalid() {
-  return {INVALID, 0, false};
+  return {INVALID, 0, false, false};
 }
 
 // static
-ProtoType ProtoType::Numeric(uint16_t size, bool is_signed) {
+ProtoType ProtoType::Numeric(uint16_t size, bool is_signed, bool is_repeated) {
   PERFETTO_CHECK(size == 32 || size == 64);
-  return {NUMERIC, size, is_signed};
+  return {NUMERIC, size, is_signed, is_repeated};
 }
 
 // static
 ProtoType ProtoType::FromDescriptor(
-    google::protobuf::FieldDescriptor::Type type) {
+    google::protobuf::FieldDescriptor::Type type,
+    bool is_repeated) {
   if (type == google::protobuf::FieldDescriptor::Type::TYPE_UINT64)
-    return Numeric(64, false);
+    return Numeric(64, false, is_repeated);
 
   if (type == google::protobuf::FieldDescriptor::Type::TYPE_INT64)
-    return Numeric(64, true);
+    return Numeric(64, true, is_repeated);
 
   if (type == google::protobuf::FieldDescriptor::Type::TYPE_UINT32)
-    return Numeric(32, false);
+    return Numeric(32, false, is_repeated);
 
   if (type == google::protobuf::FieldDescriptor::Type::TYPE_INT32)
-    return Numeric(32, true);
+    return Numeric(32, true, is_repeated);
 
   if (type == google::protobuf::FieldDescriptor::Type::TYPE_STRING)
-    return String();
+    return String(is_repeated);
 
   return Invalid();
 }
@@ -181,14 +182,15 @@
   // Always need to prefer the LHS as it is the one already present
   // in the proto.
   if (one.type == ProtoType::STRING)
-    return ProtoType::String();
+    return ProtoType::String(one.is_repeated);
 
   if (one.is_signed || other.is_signed) {
     one = one.GetSigned();
     other = other.GetSigned();
   }
 
-  return ProtoType::Numeric(std::max(one.size, other.size), one.is_signed);
+  return ProtoType::Numeric(std::max(one.size, other.size), one.is_signed,
+                            one.is_repeated || other.is_repeated);
 }
 
 ProtoType InferProtoType(const FtraceEvent::Field& field) {
@@ -219,6 +221,13 @@
     return ProtoType::Numeric(64, /* is_signed= */ false);
   }
 
+  // Fixed size array for syscall args. Similar to ino_t choose the largest
+  // possible size to cover 32bit and 64bit.
+  if (StartsWith(field.type_and_name, "unsigned long args[6]")) {
+    return ProtoType::Numeric(64, /* is_signed= */ false,
+                              /* is_repeated= */ true);
+  }
+
   // Ints of various sizes:
   if (field.size <= 4)
     return ProtoType::Numeric(32, field.is_signed);
@@ -254,25 +263,28 @@
   std::string s;
   s += "message " + name + " {\n";
   for (const auto field : SortedFields()) {
-    s += "  optional " + field->type.ToString() + " " + field->name + " = " +
+    if (field->type.is_repeated)
+      s += "  repeated ";
+    else
+      s += "  optional ";
+    s += field->type.ToString() + " " + field->name + " = " +
          std::to_string(field->number) + ";\n";
   }
   s += "}\n";
   return s;
 }
 
-void Proto::MergeFrom(const Proto& other) {
-  // Always keep number from the left hand side.
-  PERFETTO_CHECK(name == other.name);
-  for (const auto& p : other.fields) {
-    auto it = fields.find(p.first);
-    if (it == fields.end()) {
-      Proto::Field field = p.second;
-      field.number = ++max_id;
-      AddField(std::move(field));
-    } else {
-      it->second.type = GetCommon(it->second.type, p.second.type);
+void Proto::UnionFields(const std::vector<Proto::Field>& candidate_fields) {
+  for (const auto& candidate : candidate_fields) {
+    auto it = fields.find(candidate.name);
+    if (it != fields.end()) {
+      // potentially expand proto type to cover both cases
+      it->second.type = GetCommon(it->second.type, candidate.type);
+      continue;
     }
+    Proto::Field new_field = candidate;
+    new_field.number = ++max_id;
+    AddField(std::move(new_field));
   }
 }
 
diff --git a/src/tools/ftrace_proto_gen/proto_gen_utils.h b/src/tools/ftrace_proto_gen/proto_gen_utils.h
index 4a75ddc..34e6a0b 100644
--- a/src/tools/ftrace_proto_gen/proto_gen_utils.h
+++ b/src/tools/ftrace_proto_gen/proto_gen_utils.h
@@ -58,14 +58,18 @@
   Type type;
   uint16_t size;
   bool is_signed;
+  bool is_repeated;
 
   ProtoType GetSigned() const;
   std::string ToString() const;
 
   static ProtoType Invalid();
-  static ProtoType String();
-  static ProtoType Numeric(uint16_t size, bool is_signed);
-  static ProtoType FromDescriptor(google::protobuf::FieldDescriptor::Type type);
+  static ProtoType String(bool is_repeated = false);
+  static ProtoType Numeric(uint16_t size,
+                           bool is_signed,
+                           bool is_repeated = false);
+  static ProtoType FromDescriptor(google::protobuf::FieldDescriptor::Type type,
+                                  bool is_repeated = false);
 };
 
 struct Proto {
@@ -81,7 +85,7 @@
   std::map<std::string, Field> fields;
 
   std::string ToString();
-  void MergeFrom(const Proto& other);
+  void UnionFields(const std::vector<Proto::Field>& candidate_fields);
   void AddField(Proto::Field field);
   std::vector<const Field*> SortedFields();
   uint32_t max_id = 0;
diff --git a/src/tools/proto_merger/main.cc b/src/tools/proto_merger/main.cc
index 35eaee1..8ff2c1a 100644
--- a/src/tools/proto_merger/main.cc
+++ b/src/tools/proto_merger/main.cc
@@ -222,12 +222,27 @@
     return 1;
   }
 
+  std::string input_contents;
+  if (!base::ReadFile(input_include + "/" + input, &input_contents)) {
+    PERFETTO_ELOG("Failed to read input");
+    return 1;
+  }
+
+  static constexpr char kPremable[] =
+      "// --- PREAMBLE ENDS HERE - EVERYTHING BELOW AUTOGENERATED ---\n";
+  size_t input_premable_idx = input_contents.find(kPremable);
+  std::string input_preamble =
+      input_premable_idx == std::string::npos
+          ? ""
+          : input_contents.substr(0, input_premable_idx + strlen(kPremable));
+
   ImportResult input_proto = ImportProto(input, input_include);
-  ProtoFile input_file = ProtoFileFromDescriptor(*input_proto.file_descriptor);
+  ProtoFile input_file = ProtoFileFromDescriptor(std::move(input_preamble),
+                                                 *input_proto.file_descriptor);
 
   ImportResult upstream_proto = ImportProto(upstream, upstream_include);
   ProtoFile upstream_file =
-      ProtoFileFromDescriptor(*upstream_proto.file_descriptor);
+      ProtoFileFromDescriptor("", *upstream_proto.file_descriptor);
 
   Allowlist allowed;
   if (!allowlist.empty()) {
diff --git a/src/tools/proto_merger/proto_file.cc b/src/tools/proto_merger/proto_file.cc
index 5209e90..8b28627 100644
--- a/src/tools/proto_merger/proto_file.cc
+++ b/src/tools/proto_merger/proto_file.cc
@@ -263,8 +263,10 @@
 }  // namespace
 
 ProtoFile ProtoFileFromDescriptor(
+    std::string premable,
     const google::protobuf::FileDescriptor& desc) {
   ProtoFile file;
+  file.preamble = std::move(premable);
   for (int i = 0; i < desc.enum_type_count(); ++i) {
     file.enums.push_back(EnumFromDescriptor(*desc.enum_type(i)));
   }
diff --git a/src/tools/proto_merger/proto_file.h b/src/tools/proto_merger/proto_file.h
index 1939a50..a7056da 100644
--- a/src/tools/proto_merger/proto_file.h
+++ b/src/tools/proto_merger/proto_file.h
@@ -75,6 +75,8 @@
     std::vector<Field> deleted_fields;
   };
 
+  std::string preamble;
+
   std::vector<Message> messages;
   std::vector<Enum> enums;
 
@@ -83,7 +85,8 @@
 };
 
 // Creates a ProtoFile struct from a libprotobuf-full descriptor clas.
-ProtoFile ProtoFileFromDescriptor(const google::protobuf::FileDescriptor&);
+ProtoFile ProtoFileFromDescriptor(std::string premable,
+                                  const google::protobuf::FileDescriptor&);
 
 }  // namespace proto_merger
 }  // namespace perfetto
diff --git a/src/tools/proto_merger/proto_file_serializer.cc b/src/tools/proto_merger/proto_file_serializer.cc
index dcaceb0..dca5a07 100644
--- a/src/tools/proto_merger/proto_file_serializer.cc
+++ b/src/tools/proto_merger/proto_file_serializer.cc
@@ -197,6 +197,8 @@
 
 std::string ProtoFileToDotProto(const ProtoFile& proto_file) {
   std::string output;
+  output += proto_file.preamble;
+
   for (const auto& en : proto_file.enums) {
     output += SerializeEnum(0, en);
   }
diff --git a/src/tools/proto_merger/proto_merger.cc b/src/tools/proto_merger/proto_merger.cc
index 9b7cf55..65e20d8 100644
--- a/src/tools/proto_merger/proto_merger.cc
+++ b/src/tools/proto_merger/proto_merger.cc
@@ -339,6 +339,10 @@
                              const ProtoFile& upstream,
                              const Allowlist& allowlist,
                              ProtoFile& out) {
+  // The preamble is taken directly from upstream. This allows private stuff
+  // to be in the preamble without being present in upstream.
+  out.preamble = input.preamble;
+
   // Compute all the enums and messages present in the input but deleted in the
   // source of truth.
   out.deleted_enums = ComputeDeletedByName(input.enums, upstream.enums);
diff --git a/src/tools/protoprofile/BUILD.gn b/src/tools/protoprofile/BUILD.gn
index faf43d7..2e00c05 100644
--- a/src/tools/protoprofile/BUILD.gn
+++ b/src/tools/protoprofile/BUILD.gn
@@ -32,5 +32,5 @@
     "../../trace_processor/util:proto_profiler",
   ]
   sources = [ "main.cc" ]
-  deps = [ "../../../gn:protobuf_full" ]
+  deps = [ "../../trace_processor/importers/proto:gen_cc_trace_descriptor" ]
 }
diff --git a/src/tools/protoprofile/main.cc b/src/tools/protoprofile/main.cc
index 882bb35..649cd22 100644
--- a/src/tools/protoprofile/main.cc
+++ b/src/tools/protoprofile/main.cc
@@ -17,10 +17,6 @@
 #include <algorithm>
 #include <vector>
 
-#include <google/protobuf/compiler/importer.h>
-#include <google/protobuf/dynamic_message.h>
-#include <google/protobuf/io/zero_copy_stream_impl.h>
-
 #include "perfetto/ext/base/file_utils.h"
 #include "perfetto/ext/base/flat_hash_map.h"
 #include "perfetto/ext/base/scoped_file.h"
@@ -29,6 +25,7 @@
 #include "perfetto/protozero/proto_decoder.h"
 #include "perfetto/protozero/proto_utils.h"
 #include "perfetto/protozero/scattered_heap_buffer.h"
+#include "src/trace_processor/importers/proto/trace.descriptor.h"
 #include "src/trace_processor/util/proto_profiler.h"
 
 #include "protos/third_party/pprof/profile.pbzero.h"
@@ -37,53 +34,12 @@
 namespace protoprofile {
 namespace {
 
-using ::google::protobuf::Descriptor;
-using ::google::protobuf::DynamicMessageFactory;
-using ::google::protobuf::FieldDescriptor;
-using ::google::protobuf::FileDescriptor;
-using ::google::protobuf::Message;
-using ::google::protobuf::compiler::DiskSourceTree;
-using ::google::protobuf::compiler::Importer;
-using ::google::protobuf::compiler::MultiFileErrorCollector;
-using protozero::proto_utils::ProtoWireType;
-
-class MultiFileErrorCollectorImpl : public MultiFileErrorCollector {
- public:
-  ~MultiFileErrorCollectorImpl() override;
-  void AddError(const std::string& filename,
-                int line,
-                int column,
-                const std::string& message) override;
-
-  void AddWarning(const std::string& filename,
-                  int line,
-                  int column,
-                  const std::string& message) override;
-};
-
-MultiFileErrorCollectorImpl::~MultiFileErrorCollectorImpl() = default;
-
-void MultiFileErrorCollectorImpl::AddError(const std::string& filename,
-                                           int line,
-                                           int column,
-                                           const std::string& message) {
-  PERFETTO_ELOG("Error %s %d:%d: %s", filename.c_str(), line, column,
-                message.c_str());
-}
-
-void MultiFileErrorCollectorImpl::AddWarning(const std::string& filename,
-                                             int line,
-                                             int column,
-                                             const std::string& message) {
-  PERFETTO_ELOG("Error %s %d:%d: %s", filename.c_str(), line, column,
-                message.c_str());
-}
-
 class PprofProfileComputer {
  public:
   std::string Compute(const uint8_t* ptr,
                       size_t size,
-                      const Descriptor* descriptor);
+                      const std::string& message_type,
+                      trace_processor::DescriptorPool* pool);
 
  private:
   int InternString(const std::string& str);
@@ -118,13 +74,24 @@
   return id;
 }
 
-std::string PprofProfileComputer::Compute(const uint8_t* ptr,
-                                          size_t size,
-                                          const Descriptor* descriptor) {
+std::string PprofProfileComputer::Compute(
+    const uint8_t* ptr,
+    size_t size,
+    const std::string& message_type,
+    trace_processor::DescriptorPool* pool) {
   PERFETTO_CHECK(InternString("") == 0);
 
-  trace_processor::util::SizeProfileComputer computer;
-  auto field_path_to_samples = computer.Compute(ptr, size, descriptor);
+  trace_processor::util::SizeProfileComputer computer(pool, message_type);
+  computer.Reset(ptr, size);
+
+  using PathToSamplesMap = std::unordered_map<
+      trace_processor::util::SizeProfileComputer::FieldPath,
+      std::vector<size_t>,
+      trace_processor::util::SizeProfileComputer::FieldPathHasher>;
+  PathToSamplesMap field_path_to_samples;
+  for (auto sample = computer.GetNext(); sample; sample = computer.GetNext()) {
+    field_path_to_samples[computer.GetPath()].push_back(*sample);
+  }
 
   protozero::HeapBuffered<third_party::perftools::profiles::pbzero::Profile>
       profile;
@@ -150,9 +117,14 @@
   sample_type->set_unit(InternString("bytes"));
 
   // For each unique field path we've seen write out the stats:
-  for (auto it = field_path_to_samples.GetIterator(); it; ++it) {
-    const auto& field_path = it.key();
-    auto& samples = it.value();
+  for (auto& entry : field_path_to_samples) {
+    std::vector<std::string> field_path;
+    for (const auto& field : entry.first) {
+      if (field.has_field_name())
+        field_path.push_back(field.field_name());
+      field_path.push_back(field.type_name());
+    }
+    std::vector<size_t>& samples = entry.second;
 
     protozero::PackedVarInt location_ids;
     auto* sample = profile->add_sample();
@@ -224,24 +196,18 @@
   std::string s;
   base::ReadFileDescriptor(proto_fd.get(), &s);
 
-  const Descriptor* descriptor;
-  DiskSourceTree dst;
-  dst.MapPath("", "");
-  MultiFileErrorCollectorImpl mfe;
-  Importer importer(&dst, &mfe);
-  const FileDescriptor* parsed_file =
-      importer.Import("protos/perfetto/trace/trace.proto");
-  DynamicMessageFactory dmf;
-  descriptor = parsed_file->message_type(0);
+  trace_processor::DescriptorPool pool;
+  base::Status status = pool.AddFromFileDescriptorSet(kTraceDescriptor.data(),
+                                                      kTraceDescriptor.size());
+  if (!status.ok()) {
+    PERFETTO_ELOG("Could not add Trace proto descriptor: %s",
+                  status.c_message());
+    return 1;
+  }
 
   const uint8_t* start = reinterpret_cast<const uint8_t*>(s.data());
   size_t size = s.size();
 
-  if (!descriptor) {
-    PERFETTO_ELOG("Could not parse trace.proto");
-    return 1;
-  }
-
   base::ScopedFile output_fd =
       base::OpenFile(output_path, O_WRONLY | O_TRUNC | O_CREAT, 0600);
   if (!output_fd) {
@@ -249,7 +215,8 @@
     return 1;
   }
   PprofProfileComputer computer;
-  std::string out = computer.Compute(start, size, descriptor);
+  std::string out =
+      computer.Compute(start, size, ".perfetto.protos.Trace", &pool);
   base::WriteAll(output_fd.get(), out.data(), out.size());
   base::FlushFile(output_fd.get());
 
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 600a8f5..02c11ed 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -51,6 +51,8 @@
   deps = [
     "../../gn:default_deps",
     "../../include/perfetto/ext/base",
+    "../../include/perfetto/trace_processor",
+    "../../protos/perfetto/trace_processor:zero",
   ]
 }
 
@@ -78,92 +80,15 @@
   }
 }
 
-source_set("ftrace_descriptors") {
-  sources = [
-    "importers/ftrace/ftrace_descriptors.cc",
-    "importers/ftrace/ftrace_descriptors.h",
-  ]
-  deps = [
-    "../../gn:default_deps",
-    "../../include/perfetto/ext/base:base",
-    "../protozero",
-  ]
-}
-
 source_set("storage_minimal") {
   sources = [
     "forwarding_trace_parser.cc",
     "forwarding_trace_parser.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/json/json_utils.cc",
-    "importers/json/json_utils.h",
-    "importers/ninja/ninja_log_parser.cc",
-    "importers/ninja/ninja_log_parser.h",
-    "importers/proto/android_camera_event_module.cc",
-    "importers/proto/android_camera_event_module.h",
-    "importers/proto/async_track_set_tracker.cc",
-    "importers/proto/async_track_set_tracker.h",
-    "importers/proto/chrome_string_lookup.cc",
-    "importers/proto/chrome_string_lookup.h",
-    "importers/proto/chrome_system_probes_module.cc",
-    "importers/proto/chrome_system_probes_module.h",
-    "importers/proto/chrome_system_probes_parser.cc",
-    "importers/proto/chrome_system_probes_parser.h",
-    "importers/proto/memory_tracker_snapshot_module.cc",
-    "importers/proto/memory_tracker_snapshot_module.h",
-    "importers/proto/memory_tracker_snapshot_parser.cc",
-    "importers/proto/memory_tracker_snapshot_parser.h",
-    "importers/proto/metadata_module.cc",
-    "importers/proto/metadata_module.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",
-    "importers/proto/proto_trace_parser.cc",
-    "importers/proto/proto_trace_parser.h",
-    "importers/proto/proto_trace_reader.cc",
-    "importers/proto/proto_trace_reader.h",
-    "importers/proto/proto_trace_tokenizer.cc",
-    "importers/proto/proto_trace_tokenizer.h",
-
-    # XXX: is this the correct place
-    "importers/proto/statsd_module.cc",
-    "importers/proto/statsd_module.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",
-    "importers/proto/track_event_tracker.cc",
-    "importers/proto/track_event_tracker.h",
-    "importers/proto/translation_table_module.cc",
-    "importers/proto/translation_table_module.h",
-    "importers/syscalls/syscall_tracker.h",
-    "importers/systrace/systrace_line.h",
-    "timestamped_trace_piece.h",
     "trace_blob.cc",
     "trace_processor_context.cc",
     "trace_processor_storage.cc",
     "trace_processor_storage_impl.cc",
     "trace_processor_storage_impl.h",
-    "trace_sorter.cc",
-    "trace_sorter.h",
-    "trace_sorter_queue.h",
     "virtual_destructors.cc",
   ]
   deps = [
@@ -171,143 +96,26 @@
     "../base",
     "../protozero",
     "containers",
-    "importers:gen_cc_chrome_track_event_descriptor",
-    "importers:gen_cc_statsd_atoms_descriptor",
-    "importers:gen_cc_track_event_descriptor",
     "importers/common",
+    "importers/common:parser_types",
+    "importers/ftrace:minimal",
+    "importers/fuchsia:fuchsia_record",
     "importers/memory_tracker:graph_processor",
-    "importers/proto:storage_minimal",
+    "importers/proto:gen_cc_chrome_track_event_descriptor",
+    "importers/proto:gen_cc_track_event_descriptor",
+    "importers/proto:minimal",
+    "importers/systrace:systrace_line",
+    "sorter",
     "storage",
     "tables",
     "types",
-    "util",
     "util:descriptors",
     "util:gzip",
-    "util:interned_message_view",
     "util:proto_to_args_parser",
     "util:stack_traces_util",
     "views",
   ]
-  public_deps = [
-    "../../include/perfetto/trace_processor:storage",
-    "../../protos/perfetto/common:zero",
-    "../../protos/perfetto/config:zero",
-    "../../protos/perfetto/trace:zero",
-    "../../protos/perfetto/trace/android:zero",
-    "../../protos/perfetto/trace/chrome:zero",
-    "../../protos/perfetto/trace/ftrace:zero",
-    "../../protos/perfetto/trace/interned_data:zero",
-    "../../protos/perfetto/trace/perfetto:zero",
-    "../../protos/perfetto/trace/power:zero",
-    "../../protos/perfetto/trace/profiling:zero",
-    "../../protos/perfetto/trace/ps:zero",
-    "../../protos/perfetto/trace/statsd:zero",
-    "../../protos/perfetto/trace/sys_stats:zero",
-    "../../protos/perfetto/trace/system_info:zero",
-    "../../protos/perfetto/trace/track_event:zero",
-    "../../protos/perfetto/trace/translation:zero",
-    "../../src/kernel_utils:syscall_table",
-  ]
-
-  # json_utils optionally depends on jsoncpp.
-  if (enable_perfetto_trace_processor_json) {
-    deps += [ "../../gn:jsoncpp" ]
-  }
-}
-
-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/drm_tracker.cc",
-    "importers/ftrace/drm_tracker.h",
-    "importers/ftrace/ftrace_module_impl.cc",
-    "importers/ftrace/ftrace_module_impl.h",
-    "importers/ftrace/ftrace_parser.cc",
-    "importers/ftrace/ftrace_parser.h",
-    "importers/ftrace/ftrace_tokenizer.cc",
-    "importers/ftrace/ftrace_tokenizer.h",
-    "importers/ftrace/iostat_tracker.cc",
-    "importers/ftrace/iostat_tracker.h",
-    "importers/ftrace/rss_stat_tracker.cc",
-    "importers/ftrace/rss_stat_tracker.h",
-    "importers/ftrace/sched_event_tracker.cc",
-    "importers/ftrace/sched_event_tracker.h",
-    "importers/ftrace/thread_state_tracker.cc",
-    "importers/ftrace/thread_state_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/i2c/i2c_tracker.cc",
-    "importers/i2c/i2c_tracker.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/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/frame_timeline_event_parser.cc",
-    "importers/proto/frame_timeline_event_parser.h",
-    "importers/proto/gpu_event_parser.cc",
-    "importers/proto/gpu_event_parser.h",
-    "importers/proto/graphics_event_module.cc",
-    "importers/proto/graphics_event_module.h",
-    "importers/proto/graphics_frame_event_parser.cc",
-    "importers/proto/graphics_frame_event_parser.h",
-    "importers/proto/heap_graph_module.cc",
-    "importers/proto/heap_graph_module.h",
-    "importers/proto/system_probes_module.cc",
-    "importers/proto/system_probes_module.h",
-    "importers/proto/system_probes_parser.cc",
-    "importers/proto/system_probes_parser.h",
-    "importers/proto/vulkan_memory_tracker.cc",
-    "importers/proto/vulkan_memory_tracker.h",
-    "importers/syscalls/syscall_tracker.cc",
-    "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",
-  ]
-  public_deps = [ ":storage_minimal" ]
-  deps = [
-    ":ftrace_descriptors",
-    "../../include/perfetto/ext/base:base",
-    "../../include/perfetto/ext/traced:sys_stats_counters",
-    "../../protos/perfetto/common:zero",
-    "../../protos/perfetto/trace:zero",
-    "../../protos/perfetto/trace/android:zero",
-    "../../protos/perfetto/trace/gpu:zero",
-    "../../protos/perfetto/trace/interned_data:zero",
-    "../protozero",
-    "importers/android_bugreport",
-    "importers/common",
-    "importers/proto:storage_full",
-    "importers/proto:storage_minimal",
-    "storage",
-    "tables",
-    "types",
-    "util",
-    "util:gzip",
-    "views",
-  ]
-  if (enable_perfetto_trace_processor_json) {
-    deps += [ "../../gn:jsoncpp" ]
-  }
+  public_deps = [ "../../include/perfetto/trace_processor:storage" ]
 }
 
 source_set("export_json") {
@@ -319,13 +127,11 @@
     ":storage_minimal",
     "../../gn:default_deps",
     "../base",
+    "importers/json:minimal",
     "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) {
@@ -342,36 +148,45 @@
     ]
 
     deps = [
-      ":demangle",
-      ":export_json",
       ":metatrace",
-      ":storage_full",
+      ":storage_minimal",
       "../../gn:default_deps",
-      "../../protos/perfetto/trace/ftrace:zero",
+      "../../protos/perfetto/common:zero",
+      "../../protos/perfetto/trace:zero",
+      "../../protos/perfetto/trace/perfetto:zero",
       "../base",
       "../protozero",
-      "analysis",
       "db",
       "dynamic",
       "importers/android_bugreport",
       "importers/common",
+      "importers/ftrace:full",
+      "importers/fuchsia:full",
+      "importers/gzip:full",
+      "importers/json:full",
+      "importers/json:minimal",
+      "importers/ninja",
+      "importers/proto:full",
+      "importers/proto:minimal",
+      "importers/systrace:full",
       "metrics",
+      "prelude/functions",
+      "prelude/operators",
       "sqlite",
+      "stdlib:gen_amalgamated_stdlib",
       "storage",
       "tables",
       "types",
       "util",
       "util:gzip",
       "util:protozero_to_text",
+      "util:stdlib",
       "views",
     ]
     public_deps = [
       "../../gn:sqlite",  # iterator_impl.h includes sqlite3.h.
       "../../include/perfetto/trace_processor",
     ]
-    if (enable_perfetto_trace_processor_json) {
-      deps += [ "../../gn:jsoncpp" ]
-    }
   }
 
   executable("trace_processor_shell") {
@@ -387,6 +202,7 @@
       "../base:version",
       "metrics",
       "util",
+      "util:stdlib",
     ]
     if (enable_perfetto_trace_processor_linenoise) {
       deps += [ "../../gn:linenoise" ]
@@ -410,94 +226,70 @@
   }
 }  # if (enable_perfetto_trace_processor_sqlite)
 
-perfetto_unittest_source_set("unittests") {
+perfetto_unittest_source_set("top_level_unittests") {
   testonly = true
+
   sources = [
     "forwarding_trace_parser_unittest.cc",
-    "importers/ftrace/binder_tracker_unittest.cc",
-    "importers/ftrace/sched_event_tracker_unittest.cc",
-    "importers/ftrace/thread_state_tracker_unittest.cc",
-    "importers/fuchsia/fuchsia_trace_utils_unittest.cc",
-    "importers/memory_tracker/graph_processor_unittest.cc",
-    "importers/memory_tracker/graph_unittest.cc",
-    "importers/memory_tracker/raw_process_memory_node_unittest.cc",
-    "importers/proto/async_track_set_tracker_unittest.cc",
-    "importers/proto/perf_sample_tracker_unittest.cc",
-    "importers/proto/proto_trace_parser_unittest.cc",
-    "importers/syscalls/syscall_tracker_unittest.cc",
-    "importers/systrace/systrace_parser_unittest.cc",
     "ref_counted_unittest.cc",
-    "trace_sorter_queue_unittest.cc",
-    "trace_sorter_unittest.cc",
   ]
   deps = [
-    ":gen_cc_test_messages_descriptor",
-    ":storage_full",
+    ":storage_minimal",
     "../../gn:default_deps",
     "../../gn:gtest_and_gmock",
-    "../../protos/perfetto/common:cpp",
-    "../../protos/perfetto/common:zero",
-    "../../protos/perfetto/trace:cpp",
-    "../../protos/perfetto/trace:minimal_zero",
-    "../../protos/perfetto/trace:zero",
-    "../../protos/perfetto/trace/android:zero",
-    "../../protos/perfetto/trace/chrome:zero",
-    "../../protos/perfetto/trace/ftrace:zero",
-    "../../protos/perfetto/trace/gpu:zero",
-    "../../protos/perfetto/trace/interned_data:zero",
-    "../../protos/perfetto/trace/profiling:cpp",
-    "../../protos/perfetto/trace/ps:zero",
-    "../../protos/perfetto/trace/sys_stats:zero",
-    "../../protos/perfetto/trace/track_event:zero",
-    "../base",
-    "../protozero",
-    "../protozero:testing_messages_zero",
-    "containers",
-    "containers:unittests",
-    "db:unittests",
-    "dynamic",
-    "importers/android_bugreport:unittests",
-    "importers/common",
-    "importers/common:unittests",
-    "importers/memory_tracker:graph_processor",
-    "importers/proto:storage_minimal",
-    "importers/proto:unittests",
-    "rpc:unittests",
-    "storage",
-    "tables:unittests",
-    "types",
-    "types:unittests",
-    "util:descriptors",
-    "util:unittests",
-    "views",
-    "views:unittests",
+    "../../include/perfetto/trace_processor",
   ]
 
-  if (enable_perfetto_trace_processor_sqlite) {
+  if (enable_perfetto_trace_processor_json && !is_win) {
+    # export_json_unittest.cc uses base::TempFile, which is not supported on
+    # windows.
+    sources += [ "export_json_unittest.cc" ]
     deps += [
-      ":lib",
-      "../../gn:sqlite",
-      "dynamic:unittests",
-      "sqlite:unittests",
+      ":export_json",
+      "../../gn:jsoncpp",
+      "containers",
+      "importers/common",
+      "importers/proto:minimal",
+      "storage",
+      "types",
     ]
   }
+}
 
+perfetto_unittest_source_set("unittests") {
+  testonly = true
+
+  # Do not add sources to this target: use top_level_unittests
+  # instead. This us really just a grouping
+  deps = [
+    ":top_level_unittests",
+    "containers:unittests",
+    "db:unittests",
+    "importers/android_bugreport:unittests",
+    "importers/common:unittests",
+    "importers/ftrace:unittests",
+    "importers/fuchsia:unittests",
+    "importers/memory_tracker:unittests",
+    "importers/proto:unittests",
+    "importers/syscalls:unittests",
+    "importers/systrace:unittests",
+    "rpc:unittests",
+    "sorter:unittests",
+    "tables:unittests",
+    "types:unittests",
+    "util:unittests",
+    "views:unittests",
+  ]
   if (enable_perfetto_trace_processor_json) {
-    sources += [
-      "importers/json/json_trace_tokenizer_unittest.cc",
-      "importers/json/json_utils_unittest.cc",
+    deps += [ "importers/json:unittests" ]
+  }
+  if (enable_perfetto_trace_processor_sqlite) {
+    deps += [
+      "dynamic:unittests",
+      "prelude/functions:unittests",
+      "prelude/operators:unittests",
+      "sqlite:unittests",
     ]
-    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",
-        "../../include/perfetto/ext/trace_processor:export_json",
-      ]
-    }
   }
 }
 
@@ -526,9 +318,6 @@
       "../base:test_support",
       "sqlite",
     ]
-    if (enable_perfetto_trace_processor_json) {
-      deps += [ "../../gn:jsoncpp" ]
-    }
   }
 }
 
@@ -552,7 +341,7 @@
   testonly = true
   sources = [ "trace_parsing_fuzzer.cc" ]
   deps = [
-    ":storage_full",
+    ":storage_minimal",
     "../../gn:default_deps",
     "../base",
   ]
diff --git a/src/trace_processor/OWNERS b/src/trace_processor/OWNERS
index c360c78..33d3273 100644
--- a/src/trace_processor/OWNERS
+++ b/src/trace_processor/OWNERS
@@ -1,5 +1,6 @@
 # For core Trace Processor changes.
 lalitm@google.com
+mayzner@google.com
 
 # For most Android-related metrics.
 ilkos@google.com
diff --git a/src/trace_processor/analysis/BUILD.gn b/src/trace_processor/analysis/BUILD.gn
deleted file mode 100644
index 0846610..0000000
--- a/src/trace_processor/analysis/BUILD.gn
+++ /dev/null
@@ -1,28 +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.
-
-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
deleted file mode 100644
index 7ad3533..0000000
--- a/src/trace_processor/analysis/describe_slice.cc
+++ /dev/null
@@ -1,58 +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/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
deleted file mode 100644
index 9dedeb8..0000000
--- a/src/trace_processor/analysis/describe_slice.h
+++ /dev/null
@@ -1,44 +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_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/containers/bit_vector.cc b/src/trace_processor/containers/bit_vector.cc
index ca523d5..0b00e28 100644
--- a/src/trace_processor/containers/bit_vector.cc
+++ b/src/trace_processor/containers/bit_vector.cc
@@ -42,8 +42,10 @@
   // one among those tested when writing this function.
   uint64_t result = 0;
   for (uint64_t bb = 1; mask; bb += bb) {
-    if (word & bb)
-      result |= mask & -mask;
+    if (word & bb) {
+      // MSVC doesn't like -mask so work around this by doing 0 - mask.
+      result |= mask & (0ull - mask);
+    }
     mask &= mask - 1;
   }
   return result;
diff --git a/src/trace_processor/containers/string_pool.cc b/src/trace_processor/containers/string_pool.cc
index 0189054..0ca5d0b 100644
--- a/src/trace_processor/containers/string_pool.cc
+++ b/src/trace_processor/containers/string_pool.cc
@@ -24,6 +24,7 @@
 namespace perfetto {
 namespace trace_processor {
 
+#if !PERFETTO_IS_AT_LEAST_CPP17()
 // static
 constexpr size_t StringPool::kNumBlockIndexBits;
 // static
@@ -38,6 +39,7 @@
 constexpr size_t StringPool::kBlockSizeBytes;
 // static
 constexpr size_t StringPool::kMinLargeStringSizeBytes;
+#endif
 
 StringPool::StringPool() {
   static_assert(
diff --git a/src/trace_processor/db/BUILD.gn b/src/trace_processor/db/BUILD.gn
index 996c358..bfe5e8a 100644
--- a/src/trace_processor/db/BUILD.gn
+++ b/src/trace_processor/db/BUILD.gn
@@ -36,6 +36,7 @@
     "../../../include/perfetto/ext/base",
     "../../../include/perfetto/trace_processor",
     "../containers",
+    "../util:glob",
   ]
 }
 
diff --git a/src/trace_processor/db/column.cc b/src/trace_processor/db/column.cc
index 8260da7..4fec8fc 100644
--- a/src/trace_processor/db/column.cc
+++ b/src/trace_processor/db/column.cc
@@ -18,6 +18,7 @@
 
 #include "src/trace_processor/db/compare.h"
 #include "src/trace_processor/db/table.h"
+#include "src/trace_processor/util/glob.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -277,6 +278,9 @@
         return cmp(storage<T>().Get(idx)) >= 0;
       });
       break;
+    case FilterOp::kGlob:
+      rm->Clear();
+      break;
     case FilterOp::kIsNull:
     case FilterOp::kIsNotNull:
       PERFETTO_FATAL("Should be handled above");
@@ -347,6 +351,14 @@
         return v.data() != nullptr && compare::String(v, str_value) >= 0;
       });
       break;
+    case FilterOp::kGlob: {
+      util::GlobMatcher matcher = util::GlobMatcher::FromPattern(str_value);
+      overlay().FilterInto(rm, [this, &matcher](uint32_t idx) {
+        auto v = GetStringPoolStringAtIdx(idx);
+        return v.data() != nullptr && matcher.Matches(v);
+      });
+      break;
+    }
     case FilterOp::kIsNull:
     case FilterOp::kIsNotNull:
       PERFETTO_FATAL("Should be handled above");
@@ -402,6 +414,9 @@
         return compare::Numeric(idx, id_value) >= 0;
       });
       break;
+    case FilterOp::kGlob:
+      rm->Clear();
+      break;
     case FilterOp::kIsNull:
     case FilterOp::kIsNotNull:
       PERFETTO_FATAL("Should be handled above");
diff --git a/src/trace_processor/db/column.h b/src/trace_processor/db/column.h
index 82aa7a3..1d1b347 100644
--- a/src/trace_processor/db/column.h
+++ b/src/trace_processor/db/column.h
@@ -42,6 +42,7 @@
   kLe,
   kIsNull,
   kIsNotNull,
+  kGlob,
 };
 
 // Represents a constraint on a column.
@@ -574,6 +575,7 @@
       case FilterOp::kNe:
       case FilterOp::kIsNull:
       case FilterOp::kIsNotNull:
+      case FilterOp::kGlob:
         break;
     }
     return false;
diff --git a/src/trace_processor/db/column_storage.h b/src/trace_processor/db/column_storage.h
index ac3f8a2..70c8385 100644
--- a/src/trace_processor/db/column_storage.h
+++ b/src/trace_processor/db/column_storage.h
@@ -78,7 +78,7 @@
 
   base::Optional<T> Get(uint32_t idx) const { return nv_.Get(idx); }
   void Append(T val) { nv_.Append(val); }
-  void Append(base::Optional<T> val) { nv_.Append(val); }
+  void Append(base::Optional<T> val) { nv_.Append(std::move(val)); }
   void Set(uint32_t idx, T val) { nv_.Set(idx, val); }
   uint32_t size() const { return nv_.size(); }
   bool IsDense() const { return nv_.IsDense(); }
diff --git a/src/trace_processor/db/table.cc b/src/trace_processor/db/table.cc
index c582789..4346377 100644
--- a/src/trace_processor/db/table.cc
+++ b/src/trace_processor/db/table.cc
@@ -37,14 +37,14 @@
 }
 
 Table Table::Copy() const {
-  Table table = CopyExceptRowMaps();
+  Table table = CopyExceptOverlays();
   for (const ColumnStorageOverlay& overlay : overlays_) {
     table.overlays_.emplace_back(overlay.Copy());
   }
   return table;
 }
 
-Table Table::CopyExceptRowMaps() const {
+Table Table::CopyExceptOverlays() const {
   Table table(string_pool_);
   table.row_count_ = row_count_;
   for (const Column& col : columns_) {
@@ -113,7 +113,7 @@
 
   // Return a copy of this table with the RowMaps using the computed ordered
   // RowMap.
-  Table table = CopyExceptRowMaps();
+  Table table = CopyExceptOverlays();
   RowMap rm(std::move(idx));
   for (const ColumnStorageOverlay& overlay : overlays_) {
     table.overlays_.emplace_back(overlay.SelectRows(rm));
diff --git a/src/trace_processor/db/table.h b/src/trace_processor/db/table.h
index af697a6..21962bf 100644
--- a/src/trace_processor/db/table.h
+++ b/src/trace_processor/db/table.h
@@ -127,7 +127,7 @@
   // Note: the RowMap should not reorder this table; this is guaranteed if the
   // passed RowMap is generated using |FilterToRowMap|.
   Table Apply(RowMap rm) const {
-    Table table = CopyExceptRowMaps();
+    Table table = CopyExceptOverlays();
     table.row_count_ = rm.size();
     table.overlays_.reserve(overlays_.size());
     for (const ColumnStorageOverlay& map : overlays_) {
@@ -227,7 +227,7 @@
   friend class Column;
   friend class View;
 
-  Table CopyExceptRowMaps() const;
+  Table CopyExceptOverlays() const;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/dynamic/BUILD.gn b/src/trace_processor/dynamic/BUILD.gn
index b721299..d20b554 100644
--- a/src/trace_processor/dynamic/BUILD.gn
+++ b/src/trace_processor/dynamic/BUILD.gn
@@ -12,6 +12,10 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("../../../gn/perfetto.gni")
+
+assert(enable_perfetto_trace_processor_sqlite)
+
 source_set("dynamic") {
   sources = [
     "ancestor_generator.cc",
@@ -20,8 +24,6 @@
     "connected_flow_generator.h",
     "descendant_generator.cc",
     "descendant_generator.h",
-    "describe_slice_generator.cc",
-    "describe_slice_generator.h",
     "dynamic_table_generator.cc",
     "dynamic_table_generator.h",
     "experimental_annotated_stack_generator.cc",
@@ -45,11 +47,10 @@
     "../../../gn:default_deps",
     "../../../gn:sqlite",
     "../../base",
-    "../analysis",
     "../containers",
     "../db",
-    "../importers/proto:storage_full",
-    "../importers/proto:storage_minimal",
+    "../importers/proto:full",
+    "../importers/proto:minimal",
     "../sqlite:sqlite_minimal",
     "../storage",
     "../tables",
@@ -61,6 +62,9 @@
 source_set("unittests") {
   testonly = true
   sources = [
+    "ancestor_generator_unittest.cc",
+    "connected_flow_generator_unittest.cc",
+    "descendant_generator_unittest.cc",
     "experimental_counter_dur_generator_unittest.cc",
     "experimental_flat_slice_generator_unittest.cc",
     "experimental_slice_layout_generator_unittest.cc",
diff --git a/src/trace_processor/dynamic/ancestor_generator.cc b/src/trace_processor/dynamic/ancestor_generator.cc
index 722724e..1f0fec4 100644
--- a/src/trace_processor/dynamic/ancestor_generator.cc
+++ b/src/trace_processor/dynamic/ancestor_generator.cc
@@ -27,28 +27,6 @@
 namespace trace_processor {
 namespace tables {
 
-#define PERFETTO_TP_ANCESTOR_SLICE_TABLE_DEF(NAME, PARENT, C) \
-  NAME(AncestorSliceTable, "ancestor_slice")                  \
-  PARENT(PERFETTO_TP_SLICE_TABLE_DEF, C)                      \
-  C(tables::SliceTable::Id, start_id, Column::Flag::kHidden)
-
-PERFETTO_TP_TABLE(PERFETTO_TP_ANCESTOR_SLICE_TABLE_DEF);
-
-#define PERFETTO_TP_ANCESTOR_STACK_PROFILE_CALLSITE_TABLE_DEF(NAME, PARENT, C) \
-  NAME(AncestorStackProfileCallsiteTable,                                      \
-       "experimental_ancestor_stack_profile_callsite")                         \
-  PARENT(PERFETTO_TP_STACK_PROFILE_CALLSITE_DEF, C)                            \
-  C(tables::StackProfileCallsiteTable::Id, start_id, Column::Flag::kHidden)
-
-PERFETTO_TP_TABLE(PERFETTO_TP_ANCESTOR_STACK_PROFILE_CALLSITE_TABLE_DEF);
-
-#define PERFETTO_TP_ANCESTOR_SLICE_BY_STACK_TABLE_DEF(NAME, PARENT, C) \
-  NAME(AncestorSliceByStackTable, "ancestor_slice_by_stack")           \
-  PARENT(PERFETTO_TP_SLICE_TABLE_DEF, C)                               \
-  C(int64_t, start_stack_id, Column::Flag::kHidden)
-
-PERFETTO_TP_TABLE(PERFETTO_TP_ANCESTOR_SLICE_BY_STACK_TABLE_DEF);
-
 AncestorSliceTable::~AncestorSliceTable() = default;
 AncestorStackProfileCallsiteTable::~AncestorStackProfileCallsiteTable() =
     default;
@@ -58,17 +36,16 @@
 
 namespace {
 
-uint32_t GetConstraintColumnIndex(AncestorGenerator::Ancestor type,
-                                  TraceProcessorContext* context) {
+uint32_t GetConstraintColumnIndex(AncestorGenerator::Ancestor type) {
   switch (type) {
     case AncestorGenerator::Ancestor::kSlice:
-      return context->storage->slice_table().GetColumnCount();
+      return tables::AncestorSliceTable::ColumnIndex::start_id;
     case AncestorGenerator::Ancestor::kStackProfileCallsite:
-      return context->storage->stack_profile_callsite_table().GetColumnCount();
+      return tables::AncestorStackProfileCallsiteTable::ColumnIndex::start_id;
     case AncestorGenerator::Ancestor::kSliceByStack:
-      return context->storage->slice_table().GetColumnCount();
+      return tables::AncestorSliceByStackTable::ColumnIndex::start_stack_id;
   }
-  return 0;
+  PERFETTO_FATAL("For GCC");
 }
 
 template <typename T>
@@ -121,15 +98,14 @@
 
 }  // namespace
 
-AncestorGenerator::AncestorGenerator(Ancestor type,
-                                     TraceProcessorContext* context)
-    : type_(type), context_(context) {}
+AncestorGenerator::AncestorGenerator(Ancestor type, const TraceStorage* storage)
+    : type_(type), storage_(storage) {}
 
 base::Status AncestorGenerator::ValidateConstraints(
     const QueryConstraints& qc) {
   const auto& cs = qc.constraints();
 
-  int column = static_cast<int>(GetConstraintColumnIndex(type_, context_));
+  int column = static_cast<int>(GetConstraintColumnIndex(type_));
   auto id_fn = [column](const QueryConstraints::Constraint& c) {
     return c.column == column && sqlite_utils::IsOpEq(c.op);
   };
@@ -143,15 +119,35 @@
     const std::vector<Order>&,
     const BitVector&,
     std::unique_ptr<Table>& table_return) {
-  uint32_t column = GetConstraintColumnIndex(type_, context_);
+  uint32_t column = GetConstraintColumnIndex(type_);
   auto constraint_it =
       std::find_if(cs.begin(), cs.end(), [column](const Constraint& c) {
         return c.col_idx == column && c.op == FilterOp::kEq;
       });
-  PERFETTO_DCHECK(constraint_it != cs.end());
-  if (constraint_it == cs.end() ||
-      constraint_it->value.type != SqlValue::Type::kLong) {
-    return base::ErrStatus("invalid start_id");
+  if (constraint_it == cs.end()) {
+    return base::ErrStatus("no start id specified.");
+  }
+  if (constraint_it->value.type == SqlValue::Type::kNull) {
+    // Nothing matches a null id so return an empty table.
+    switch (type_) {
+      case Ancestor::kSlice:
+        table_return = tables::AncestorSliceTable::SelectAndExtendParent(
+            storage_->slice_table(), {}, {});
+        break;
+      case Ancestor::kStackProfileCallsite:
+        table_return =
+            tables::AncestorStackProfileCallsiteTable::SelectAndExtendParent(
+                storage_->stack_profile_callsite_table(), {}, {});
+        break;
+      case Ancestor::kSliceByStack:
+        table_return = tables::AncestorSliceByStackTable::SelectAndExtendParent(
+            storage_->slice_table(), {}, {});
+        break;
+    }
+    return base::OkStatus();
+  }
+  if (constraint_it->value.type != SqlValue::Type::kLong) {
+    return base::ErrStatus("start id should be an integer.");
   }
 
   int64_t start_id = constraint_it->value.AsLong();
@@ -159,18 +155,17 @@
   switch (type_) {
     case Ancestor::kSlice:
       return BuildAncestorsTable<tables::AncestorSliceTable>(
-          SliceId(start_id_uint), context_->storage->slice_table(),
-          table_return);
+          SliceId(start_id_uint), storage_->slice_table(), table_return);
 
     case Ancestor::kStackProfileCallsite:
       return BuildAncestorsTable<tables::AncestorStackProfileCallsiteTable>(
-          CallsiteId(start_id_uint),
-          context_->storage->stack_profile_callsite_table(), table_return);
+          CallsiteId(start_id_uint), storage_->stack_profile_callsite_table(),
+          table_return);
 
     case Ancestor::kSliceByStack: {
       // Find the all slice ids that have the stack id and find all the
       // ancestors of the slice ids.
-      const auto& slice_table = context_->storage->slice_table();
+      const auto& slice_table = storage_->slice_table();
       auto it =
           slice_table.FilterToIterator({slice_table.stack_id().eq(start_id)});
       std::vector<tables::SliceTable::RowNumber> ancestors;
@@ -190,11 +185,11 @@
 Table::Schema AncestorGenerator::CreateSchema() {
   switch (type_) {
     case Ancestor::kSlice:
-      return tables::AncestorSliceTable::Schema();
+      return tables::AncestorSliceTable::ComputeStaticSchema();
     case Ancestor::kStackProfileCallsite:
-      return tables::AncestorStackProfileCallsiteTable::Schema();
+      return tables::AncestorStackProfileCallsiteTable::ComputeStaticSchema();
     case Ancestor::kSliceByStack:
-      return tables::AncestorSliceByStackTable::Schema();
+      return tables::AncestorSliceByStackTable::ComputeStaticSchema();
   }
   PERFETTO_FATAL("For GCC");
 }
diff --git a/src/trace_processor/dynamic/ancestor_generator.h b/src/trace_processor/dynamic/ancestor_generator.h
index 1354d54..ae569b5 100644
--- a/src/trace_processor/dynamic/ancestor_generator.h
+++ b/src/trace_processor/dynamic/ancestor_generator.h
@@ -23,6 +23,31 @@
 
 namespace perfetto {
 namespace trace_processor {
+namespace tables {
+
+#define PERFETTO_TP_ANCESTOR_SLICE_TABLE_DEF(NAME, PARENT, C) \
+  NAME(AncestorSliceTable, "ancestor_slice")                  \
+  PARENT(PERFETTO_TP_SLICE_TABLE_DEF, C)                      \
+  C(tables::SliceTable::Id, start_id, Column::Flag::kHidden)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_ANCESTOR_SLICE_TABLE_DEF);
+
+#define PERFETTO_TP_ANCESTOR_STACK_PROFILE_CALLSITE_TABLE_DEF(NAME, PARENT, C) \
+  NAME(AncestorStackProfileCallsiteTable,                                      \
+       "experimental_ancestor_stack_profile_callsite")                         \
+  PARENT(PERFETTO_TP_STACK_PROFILE_CALLSITE_DEF, C)                            \
+  C(tables::StackProfileCallsiteTable::Id, start_id, Column::Flag::kHidden)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_ANCESTOR_STACK_PROFILE_CALLSITE_TABLE_DEF);
+
+#define PERFETTO_TP_ANCESTOR_SLICE_BY_STACK_TABLE_DEF(NAME, PARENT, C) \
+  NAME(AncestorSliceByStackTable, "ancestor_slice_by_stack")           \
+  PARENT(PERFETTO_TP_SLICE_TABLE_DEF, C)                               \
+  C(int64_t, start_stack_id, Column::Flag::kHidden)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_ANCESTOR_SLICE_BY_STACK_TABLE_DEF);
+
+}  // namespace tables
 
 class TraceProcessorContext;
 
@@ -40,7 +65,7 @@
     kSliceByStack = 3
   };
 
-  AncestorGenerator(Ancestor type, TraceProcessorContext* context);
+  AncestorGenerator(Ancestor type, const TraceStorage* storage);
 
   Table::Schema CreateSchema() override;
   std::string TableName() override;
@@ -59,7 +84,7 @@
 
  private:
   Ancestor type_;
-  TraceProcessorContext* context_ = nullptr;
+  const TraceStorage* storage_ = nullptr;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/dynamic/ancestor_generator_unittest.cc b/src/trace_processor/dynamic/ancestor_generator_unittest.cc
new file mode 100644
index 0000000..d77ce2f
--- /dev/null
+++ b/src/trace_processor/dynamic/ancestor_generator_unittest.cc
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2022 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/ancestor_generator.h"
+
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+TEST(AncestorGenerator, SliceTableNullConstraint) {
+  // Insert a row to make sure that we are not returning an empty table just
+  // because the source is empty.
+  TraceStorage storage;
+  storage.mutable_slice_table()->Insert({});
+
+  AncestorGenerator generator{AncestorGenerator::Ancestor::kSlice, &storage};
+
+  // Check that if we pass start_id = NULL as a constraint, we correctly return
+  // an empty table.
+  std::unique_ptr<Table> res;
+  Constraint c{tables::AncestorSliceTable::ColumnIndex::start_id, FilterOp::kEq,
+               SqlValue()};
+  base::Status status = generator.ComputeTable({c}, {}, BitVector(), res);
+  ASSERT_TRUE(status.ok());
+  ASSERT_EQ(res->row_count(), 0u);
+}
+
+TEST(AncestorGenerator, CallsiteTableNullConstraint) {
+  // Insert a row to make sure that we are not returning an empty table just
+  // because the source is empty.
+  TraceStorage storage;
+  storage.mutable_stack_profile_callsite_table()->Insert({});
+
+  AncestorGenerator generator{
+      AncestorGenerator::Ancestor::kStackProfileCallsite, &storage};
+
+  // Check that if we pass start_id = NULL as a constraint, we correctly return
+  // an empty table.
+  std::unique_ptr<Table> res;
+  Constraint c{tables::AncestorStackProfileCallsiteTable::ColumnIndex::start_id,
+               FilterOp::kEq, SqlValue()};
+  base::Status status = generator.ComputeTable({c}, {}, BitVector(), res);
+  ASSERT_TRUE(status.ok());
+  ASSERT_EQ(res->row_count(), 0u);
+}
+
+TEST(AncestorGenerator, SliceByStackTableNullConstraint) {
+  // Insert a row to make sure that we are not returning an empty table just
+  // because the source is empty.
+  TraceStorage storage;
+  storage.mutable_slice_table()->Insert({});
+
+  AncestorGenerator generator{AncestorGenerator::Ancestor::kSliceByStack,
+                              &storage};
+
+  // Check that if we pass start_id = NULL as a constraint, we correctly return
+  // an empty table.
+  std::unique_ptr<Table> res;
+  Constraint c{tables::AncestorSliceTable::ColumnIndex::start_id, FilterOp::kEq,
+               SqlValue()};
+  base::Status status = generator.ComputeTable({c}, {}, BitVector(), res);
+  ASSERT_TRUE(status.ok());
+  ASSERT_EQ(res->row_count(), 0u);
+}
+
+}  // namespace
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/dynamic/connected_flow_generator.cc b/src/trace_processor/dynamic/connected_flow_generator.cc
index 1565a4c..90cae3f 100644
--- a/src/trace_processor/dynamic/connected_flow_generator.cc
+++ b/src/trace_processor/dynamic/connected_flow_generator.cc
@@ -29,20 +29,13 @@
 namespace trace_processor {
 namespace tables {
 
-#define PERFETTO_TP_CONNECTED_FLOW_TABLE_DEF(NAME, PARENT, C) \
-  NAME(ConnectedFlowTable, "not_exposed_to_sql")              \
-  PARENT(PERFETTO_TP_FLOW_TABLE_DEF, C)                       \
-  C(uint32_t, start_id, Column::Flag::kHidden)
-
-PERFETTO_TP_TABLE(PERFETTO_TP_CONNECTED_FLOW_TABLE_DEF);
-
 ConnectedFlowTable::~ConnectedFlowTable() = default;
 
 }  // namespace tables
 
 ConnectedFlowGenerator::ConnectedFlowGenerator(Mode mode,
-                                               TraceProcessorContext* context)
-    : mode_(mode), context_(context) {}
+                                               const TraceStorage* storage)
+    : mode_(mode), storage_(storage) {}
 
 ConnectedFlowGenerator::~ConnectedFlowGenerator() = default;
 
@@ -50,9 +43,9 @@
     const QueryConstraints& qc) {
   const auto& cs = qc.constraints();
 
-  auto flow_id_fn = [this](const QueryConstraints::Constraint& c) {
+  auto flow_id_fn = [](const QueryConstraints::Constraint& c) {
     return c.column == static_cast<int>(
-                           context_->storage->flow_table().GetColumnCount()) &&
+                           tables::ConnectedFlowTable::ColumnIndex::start_id) &&
            sqlite_utils::IsOpEq(c.op);
   };
   bool has_flow_id_cs =
@@ -92,7 +85,7 @@
 //  bfs.TakeResultingFlows();
 class BFS {
  public:
-  explicit BFS(TraceProcessorContext* context) : context_(context) {}
+  explicit BFS(const TraceStorage* storage) : storage_(storage) {}
 
   std::vector<tables::FlowTable::RowNumber> TakeResultingFlows() && {
     return std::move(flow_rows_);
@@ -133,7 +126,7 @@
 
   // Includes the relatives of |slice_id| to the list of slices to visit.
   BFS& GoToRelatives(SliceId slice_id, RelativesVisitMode visit_relatives) {
-    const auto& slice_table = context_->storage->slice_table();
+    const auto& slice_table = storage_->slice_table();
     if (visit_relatives & VISIT_ANCESTORS) {
       auto opt_ancestors =
           AncestorGenerator::GetAncestorSlices(slice_table, slice_id);
@@ -165,7 +158,7 @@
   void GoByFlow(SliceId slice_id, FlowDirection flow_direction) {
     PERFETTO_DCHECK(known_slices_.count(slice_id) != 0);
 
-    const auto& flow = context_->storage->flow_table();
+    const auto& flow = storage_->flow_table();
 
     const TypedColumn<SliceId>& start_col =
         flow_direction == FlowDirection::OUTGOING ? flow.slice_out()
@@ -190,7 +183,7 @@
 
   void GoToRelativesImpl(
       const std::vector<tables::SliceTable::RowNumber>& rows) {
-    const auto& slice = context_->storage->slice_table();
+    const auto& slice = storage_->slice_table();
     for (tables::SliceTable::RowNumber row : rows) {
       auto relative_slice_id = row.ToRowReference(slice).id();
       if (known_slices_.count(relative_slice_id))
@@ -204,7 +197,7 @@
   std::set<SliceId> known_slices_;
   std::vector<tables::FlowTable::RowNumber> flow_rows_;
 
-  TraceProcessorContext* context_;
+  const TraceStorage* storage_;
 };
 
 }  // namespace
@@ -214,15 +207,24 @@
     const std::vector<Order>&,
     const BitVector&,
     std::unique_ptr<Table>& table_return) {
-  const auto& flow = context_->storage->flow_table();
-  const auto& slice = context_->storage->slice_table();
+  const auto& flow = storage_->flow_table();
+  const auto& slice = storage_->slice_table();
 
-  auto it = std::find_if(cs.begin(), cs.end(), [&flow](const Constraint& c) {
-    return c.col_idx == flow.GetColumnCount() && c.op == FilterOp::kEq;
+  auto it = std::find_if(cs.begin(), cs.end(), [](const Constraint& c) {
+    return c.col_idx == tables::ConnectedFlowTable::ColumnIndex::start_id &&
+           c.op == FilterOp::kEq;
   });
-  PERFETTO_DCHECK(it != cs.end());
-  if (it == cs.end() || it->value.type != SqlValue::Type::kLong) {
-    return base::ErrStatus("invalid start_id");
+  if (it == cs.end()) {
+    return base::ErrStatus("no start id specified.");
+  }
+  if (it->value.type == SqlValue::Type::kNull) {
+    // Nothing matches a null id so return an empty table.
+    table_return =
+        tables::ConnectedFlowTable::SelectAndExtendParent(flow, {}, {});
+    return base::OkStatus();
+  }
+  if (it->value.type != SqlValue::Type::kLong) {
+    return base::ErrStatus("start id should be an integer.");
   }
 
   SliceId start_id{static_cast<uint32_t>(it->value.AsLong())};
@@ -232,7 +234,7 @@
                            static_cast<uint32_t>(start_id.value));
   }
 
-  BFS bfs(context_);
+  BFS bfs(storage_);
 
   switch (mode_) {
     case Mode::kDirectlyConnectedFlow:
@@ -261,7 +263,7 @@
 }
 
 Table::Schema ConnectedFlowGenerator::CreateSchema() {
-  return tables::ConnectedFlowTable::Schema();
+  return tables::ConnectedFlowTable::ComputeStaticSchema();
 }
 
 std::string ConnectedFlowGenerator::TableName() {
diff --git a/src/trace_processor/dynamic/connected_flow_generator.h b/src/trace_processor/dynamic/connected_flow_generator.h
index 66df74c..1324282 100644
--- a/src/trace_processor/dynamic/connected_flow_generator.h
+++ b/src/trace_processor/dynamic/connected_flow_generator.h
@@ -25,6 +25,16 @@
 
 namespace perfetto {
 namespace trace_processor {
+namespace tables {
+
+#define PERFETTO_TP_CONNECTED_FLOW_TABLE_DEF(NAME, PARENT, C) \
+  NAME(ConnectedFlowTable, "not_exposed_to_sql")              \
+  PARENT(PERFETTO_TP_FLOW_TABLE_DEF, C)                       \
+  C(uint32_t, start_id, Column::Flag::kHidden)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_CONNECTED_FLOW_TABLE_DEF);
+
+}  // namespace tables
 
 class TraceProcessorContext;
 
@@ -46,7 +56,7 @@
     kFollowingFlow,
   };
 
-  ConnectedFlowGenerator(Mode mode, TraceProcessorContext* context);
+  ConnectedFlowGenerator(Mode mode, const TraceStorage*);
   ~ConnectedFlowGenerator() override;
 
   Table::Schema CreateSchema() override;
@@ -60,7 +70,7 @@
 
  private:
   Mode mode_;
-  TraceProcessorContext* context_ = nullptr;
+  const TraceStorage* storage_ = nullptr;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/dynamic/connected_flow_generator_unittest.cc b/src/trace_processor/dynamic/connected_flow_generator_unittest.cc
new file mode 100644
index 0000000..827ec62
--- /dev/null
+++ b/src/trace_processor/dynamic/connected_flow_generator_unittest.cc
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 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/connected_flow_generator.h"
+
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+TEST(ConnectedFlowGenerator, SliceTableNullConstraint) {
+  // Insert a row to make sure that we are not returning an empty table just
+  // because the source is empty.
+  TraceStorage storage;
+  storage.mutable_slice_table()->Insert({});
+
+  ConnectedFlowGenerator generator{
+      ConnectedFlowGenerator::Mode::kDirectlyConnectedFlow, &storage};
+
+  // Check that if we pass start_id = NULL as a constraint, we correctly return
+  // an empty table.
+  std::unique_ptr<Table> res;
+  Constraint c{tables::ConnectedFlowTable::ColumnIndex::start_id, FilterOp::kEq,
+               SqlValue()};
+  base::Status status = generator.ComputeTable({c}, {}, BitVector(), res);
+  ASSERT_TRUE(status.ok());
+  ASSERT_EQ(res->row_count(), 0u);
+}
+
+}  // namespace
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/dynamic/descendant_generator.cc b/src/trace_processor/dynamic/descendant_generator.cc
index 72d260f..95e4bcc 100644
--- a/src/trace_processor/dynamic/descendant_generator.cc
+++ b/src/trace_processor/dynamic/descendant_generator.cc
@@ -27,20 +27,6 @@
 namespace trace_processor {
 namespace tables {
 
-#define PERFETTO_TP_DESCENDANT_SLICE_TABLE_DEF(NAME, PARENT, C) \
-  NAME(DescendantSliceTable, "descendant_slice")                \
-  PARENT(PERFETTO_TP_SLICE_TABLE_DEF, C)                        \
-  C(uint32_t, start_id, Column::Flag::kHidden)
-
-PERFETTO_TP_TABLE(PERFETTO_TP_DESCENDANT_SLICE_TABLE_DEF);
-
-#define PERFETTO_TP_DESCENDANT_SLICE_BY_STACK_TABLE_DEF(NAME, PARENT, C) \
-  NAME(DescendantSliceByStackTable, "descendant_slice_by_stack")         \
-  PARENT(PERFETTO_TP_SLICE_TABLE_DEF, C)                                 \
-  C(int64_t, start_id, Column::Flag::kHidden)
-
-PERFETTO_TP_TABLE(PERFETTO_TP_DESCENDANT_SLICE_BY_STACK_TABLE_DEF);
-
 DescendantSliceTable::~DescendantSliceTable() = default;
 DescendantSliceByStackTable::~DescendantSliceByStackTable() = default;
 
@@ -71,13 +57,21 @@
                            static_cast<uint32_t>(starting_id.value));
   }
 
-  // All nested descendents must be on the same track, with a ts between
-  // |start_id.ts| and |start_id.ts| + |start_id.dur|, and who's depth is larger
-  // then |start_row|'s. So we just use Filter to select all relevant slices.
-  auto cs = {slices.ts().ge(start_ref->ts()),
-             slices.ts().le(start_ref->ts() + start_ref->dur()),
-             slices.track_id().eq(start_ref->track_id().value),
-             slices.depth().gt(start_ref->depth())};
+  // As an optimization, for any finished slices, we only need to consider
+  // slices which started before the end of this slice (because slices on a
+  // track are always perfectly stacked).
+  // For unfinshed slices (i.e. -1 dur), we need to consider until the end of
+  // the trace so we cannot add any similar constraint.
+  std::vector<Constraint> cs;
+  if (start_ref->dur() >= 0) {
+    cs.emplace_back(slices.ts().le(start_ref->ts() + start_ref->dur()));
+  }
+
+  // All nested descendents must be on the same track, with a ts greater than
+  // |start_ref.ts| and whose depth is larger than |start_ref|'s.
+  cs.emplace_back(slices.ts().ge(start_ref->ts()));
+  cs.emplace_back(slices.track_id().eq(start_ref->track_id().value));
+  cs.emplace_back(slices.depth().gt(start_ref->depth()));
 
   // It's important we insert directly into |row_numbers_accumulator| and not
   // overwrite it because we expect the existing elements in
@@ -88,18 +82,27 @@
   return base::OkStatus();
 }
 
+uint32_t GetConstraintColumnIndex(DescendantGenerator::Descendant type) {
+  switch (type) {
+    case DescendantGenerator::Descendant::kSlice:
+      return tables::DescendantSliceTable::ColumnIndex::start_id;
+    case DescendantGenerator::Descendant::kSliceByStack:
+      return tables::DescendantSliceByStackTable::ColumnIndex::start_stack_id;
+  }
+  PERFETTO_FATAL("For GCC");
+}
+
 }  // namespace
 
 DescendantGenerator::DescendantGenerator(Descendant type,
-                                         TraceProcessorContext* context)
-    : type_(type), context_(context) {}
+                                         const TraceStorage* storage)
+    : type_(type), storage_(storage) {}
 
 base::Status DescendantGenerator::ValidateConstraints(
     const QueryConstraints& qc) {
   const auto& cs = qc.constraints();
 
-  int column =
-      static_cast<int>(tables::DescendantSliceTable::ColumnIndex::start_id);
+  int column = static_cast<int>(GetConstraintColumnIndex(type_));
   auto id_fn = [column](const QueryConstraints::Constraint& c) {
     return c.column == column && sqlite_utils::IsOpEq(c.op);
   };
@@ -113,17 +116,33 @@
     const std::vector<Order>&,
     const BitVector&,
     std::unique_ptr<Table>& table_return) {
-  const auto& slices = context_->storage->slice_table();
+  const auto& slices = storage_->slice_table();
 
-  uint32_t column = tables::DescendantSliceTable::ColumnIndex::start_id;
+  uint32_t column = GetConstraintColumnIndex(type_);
   auto constraint_it =
       std::find_if(cs.begin(), cs.end(), [column](const Constraint& c) {
         return c.col_idx == column && c.op == FilterOp::kEq;
       });
-  PERFETTO_DCHECK(constraint_it != cs.end());
-  if (constraint_it == cs.end() ||
-      constraint_it->value.type != SqlValue::Type::kLong) {
-    return base::ErrStatus("invalid start_id");
+  if (constraint_it == cs.end()) {
+    return base::ErrStatus("no start id specified.");
+  }
+  if (constraint_it->value.type == SqlValue::Type::kNull) {
+    // Nothing matches a null id so return an empty table.
+    switch (type_) {
+      case Descendant::kSlice:
+        table_return = tables::DescendantSliceTable::SelectAndExtendParent(
+            storage_->slice_table(), {}, {});
+        break;
+      case Descendant::kSliceByStack:
+        table_return =
+            tables::DescendantSliceByStackTable::SelectAndExtendParent(
+                storage_->slice_table(), {}, {});
+        break;
+    }
+    return base::OkStatus();
+  }
+  if (constraint_it->value.type != SqlValue::Type::kLong) {
+    return base::ErrStatus("start id should be an integer.");
   }
 
   int64_t start_id = constraint_it->value.AsLong();
@@ -155,9 +174,9 @@
 Table::Schema DescendantGenerator::CreateSchema() {
   switch (type_) {
     case Descendant::kSlice:
-      return tables::DescendantSliceTable::Schema();
+      return tables::DescendantSliceTable::ComputeStaticSchema();
     case Descendant::kSliceByStack:
-      return tables::DescendantSliceByStackTable::Schema();
+      return tables::DescendantSliceByStackTable::ComputeStaticSchema();
   }
   PERFETTO_FATAL("For GCC");
 }
diff --git a/src/trace_processor/dynamic/descendant_generator.h b/src/trace_processor/dynamic/descendant_generator.h
index 3df5223..ac5dd9d 100644
--- a/src/trace_processor/dynamic/descendant_generator.h
+++ b/src/trace_processor/dynamic/descendant_generator.h
@@ -23,6 +23,23 @@
 
 namespace perfetto {
 namespace trace_processor {
+namespace tables {
+
+#define PERFETTO_TP_DESCENDANT_SLICE_TABLE_DEF(NAME, PARENT, C) \
+  NAME(DescendantSliceTable, "descendant_slice")                \
+  PARENT(PERFETTO_TP_SLICE_TABLE_DEF, C)                        \
+  C(uint32_t, start_id, Column::Flag::kHidden)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_DESCENDANT_SLICE_TABLE_DEF);
+
+#define PERFETTO_TP_DESCENDANT_SLICE_BY_STACK_TABLE_DEF(NAME, PARENT, C) \
+  NAME(DescendantSliceByStackTable, "descendant_slice_by_stack")         \
+  PARENT(PERFETTO_TP_SLICE_TABLE_DEF, C)                                 \
+  C(int64_t, start_stack_id, Column::Flag::kHidden)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_DESCENDANT_SLICE_BY_STACK_TABLE_DEF);
+
+}  // namespace tables
 
 class TraceProcessorContext;
 
@@ -35,7 +52,7 @@
  public:
   enum class Descendant { kSlice = 1, kSliceByStack = 2 };
 
-  DescendantGenerator(Descendant type, TraceProcessorContext* context);
+  DescendantGenerator(Descendant type, const TraceStorage*);
 
   Table::Schema CreateSchema() override;
   std::string TableName() override;
@@ -54,7 +71,7 @@
 
  private:
   Descendant type_;
-  TraceProcessorContext* context_ = nullptr;
+  const TraceStorage* storage_ = nullptr;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/dynamic/descendant_generator_unittest.cc b/src/trace_processor/dynamic/descendant_generator_unittest.cc
new file mode 100644
index 0000000..9553d82
--- /dev/null
+++ b/src/trace_processor/dynamic/descendant_generator_unittest.cc
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 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/descendant_generator.h"
+
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+TEST(AncestorGenerator, SliceTableNullConstraint) {
+  // Insert a row to make sure that we are not returning an empty table just
+  // because the source is empty.
+  TraceStorage storage;
+  storage.mutable_slice_table()->Insert({});
+
+  DescendantGenerator generator{DescendantGenerator::Descendant::kSlice,
+                                &storage};
+
+  // Check that if we pass start_id = NULL as a constraint, we correctly return
+  // an empty table.
+  std::unique_ptr<Table> res;
+  Constraint c{tables::DescendantSliceTable::ColumnIndex::start_id,
+               FilterOp::kEq, SqlValue()};
+  base::Status status = generator.ComputeTable({c}, {}, BitVector(), res);
+  ASSERT_TRUE(status.ok());
+  ASSERT_EQ(res->row_count(), 0u);
+}
+
+TEST(AncestorGenerator, SliceByStackTableNullConstraint) {
+  // Insert a row to make sure that we are not returning an empty table just
+  // because the source is empty.
+  TraceStorage storage;
+  storage.mutable_slice_table()->Insert({});
+
+  DescendantGenerator generator{DescendantGenerator::Descendant::kSliceByStack,
+                                &storage};
+
+  // Check that if we pass start_id = NULL as a constraint, we correctly return
+  // an empty table.
+  std::unique_ptr<Table> res;
+  Constraint c{tables::DescendantSliceByStackTable::ColumnIndex::start_stack_id,
+               FilterOp::kEq, SqlValue()};
+  base::Status status = generator.ComputeTable({c}, {}, BitVector(), res);
+  ASSERT_TRUE(status.ok());
+  ASSERT_EQ(res->row_count(), 0u);
+}
+
+}  // namespace
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/dynamic/describe_slice_generator.cc b/src/trace_processor/dynamic/describe_slice_generator.cc
deleted file mode 100644
index cf0eab2..0000000
--- a/src/trace_processor/dynamic/describe_slice_generator.cc
+++ /dev/null
@@ -1,114 +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/dynamic/describe_slice_generator.h"
-
-#include "src/trace_processor/analysis/describe_slice.h"
-#include "src/trace_processor/sqlite/sqlite_utils.h"
-#include "src/trace_processor/types/trace_processor_context.h"
-#include "src/trace_processor/util/status_macros.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);
-
-  PERFETTO_CHECK(slice_id_it != cs.end());
-  // TODO(rsavitski): consider checking type of the SqlValue, as erroneous
-  // queries that pass a null here (or otherwise unexpected type) will crash.
-
-  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;
-
-base::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) &&
-           sqlite_utils::IsOpEq(c.op);
-  };
-  bool has_slice_id_cs =
-      std::find_if(cs.begin(), cs.end(), slice_id_fn) != cs.end();
-
-  return has_slice_id_cs
-             ? base::OkStatus()
-             : base::ErrStatus("Failed to find required constraints");
-}
-
-base::Status DescribeSliceGenerator::ComputeTable(
-    const std::vector<Constraint>& cs,
-    const std::vector<Order>&,
-    const BitVector&,
-    std::unique_ptr<Table>& table_return) {
-  auto input = GetDescribeSliceInputValues(cs);
-  const auto& slices = context_->storage->slice_table();
-
-  base::Optional<SliceDescription> opt_desc;
-  RETURN_IF_ERROR(
-      DescribeSlice(slices, SliceId{input.slice_id_value}, &opt_desc));
-
-  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);
-  }
-  table_return = std::move(table);
-  return base::OkStatus();
-}
-
-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
deleted file mode 100644
index d4795af..0000000
--- a/src/trace_processor/dynamic/describe_slice_generator.h
+++ /dev/null
@@ -1,56 +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_DYNAMIC_DESCRIBE_SLICE_GENERATOR_H_
-#define SRC_TRACE_PROCESSOR_DYNAMIC_DESCRIBE_SLICE_GENERATOR_H_
-
-#include "src/trace_processor/dynamic/dynamic_table_generator.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 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;
-  base::Status ValidateConstraints(const QueryConstraints&) override;
-  base::Status ComputeTable(const std::vector<Constraint>& cs,
-                            const std::vector<Order>& ob,
-                            const BitVector& cols_used,
-                            std::unique_ptr<Table>& table_return) 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_annotated_stack_generator.cc b/src/trace_processor/dynamic/experimental_annotated_stack_generator.cc
index 3cee91a..1527295 100644
--- a/src/trace_processor/dynamic/experimental_annotated_stack_generator.cc
+++ b/src/trace_processor/dynamic/experimental_annotated_stack_generator.cc
@@ -56,41 +56,56 @@
 //   /system/lib64/libc.so
 //   /system/framework/framework.jar
 //   /memfd:jit-cache (deleted)
+//   /data/dalvik-cache/arm64/<snip>.apk@classes.dex
+//   /data/app/<snip>/base.apk!libmonochrome_64.so
 //   [vdso]
 // TODO(rsavitski): consider moving this to a hidden column on
-// stack_profile_mapping, once this logic is sufficiently stable.
+// stack_profile_mapping.
 MapType ClassifyMap(NullTermStringView map) {
   if (map.empty())
     return MapType::kOther;
 
   // Primary mapping where modern ART puts jitted code.
-  // TODO(rsavitski): look into /memfd:jit-zygote-cache.
-  if (!strncmp(map.c_str(), "/memfd:jit-cache", 16))
+  // The Zygote's JIT region is inherited by all descendant apps, so it can
+  // still appear in their callstacks.
+  if (map.StartsWith("/memfd:jit-cache") ||
+      map.StartsWith("/memfd:jit-zygote-cache")) {
     return MapType::kArtJit;
+  }
 
   size_t last_slash_pos = map.rfind('/');
   if (last_slash_pos != NullTermStringView::npos) {
-    if (!strncmp(map.c_str() + last_slash_pos, "/libart.so", 10))
-      return MapType::kNativeLibart;
-    if (!strncmp(map.c_str() + last_slash_pos, "/libartd.so", 11))
+    base::StringView suffix = map.substr(last_slash_pos);
+    if (suffix.StartsWith("/libart.so") || suffix.StartsWith("/libartd.so"))
       return MapType::kNativeLibart;
   }
-
   size_t extension_pos = map.rfind('.');
   if (extension_pos != NullTermStringView::npos) {
-    if (!strncmp(map.c_str() + extension_pos, ".so", 3))
+    base::StringView suffix = map.substr(extension_pos);
+    if (suffix.StartsWith(".so"))
       return MapType::kNativeOther;
+    // unqualified dex
+    if (suffix.StartsWith(".dex"))
+      return MapType::kArtInterp;
     // dex with verification speedup info, produced by dex2oat
-    if (!strncmp(map.c_str() + extension_pos, ".vdex", 5))
+    if (suffix.StartsWith(".vdex"))
       return MapType::kArtInterp;
     // possibly uncompressed dex in a jar archive
-    if (!strncmp(map.c_str() + extension_pos, ".jar", 4))
+    if (suffix.StartsWith(".jar"))
+      return MapType::kArtInterp;
+    // android package (zip file), this can contain uncompressed dexes or
+    // native libraries that are mmap'd directly into the process. We rely on
+    // libunwindstack's MapInfo::GetFullName, which suffixes the mapping with
+    // "!lib.so" if it knows that the referenced piece of the archive is an
+    // uncompressed ELF file. So an unadorned ".apk" is assumed to be a dex
+    // file.
+    if (suffix.StartsWith(".apk"))
       return MapType::kArtInterp;
     // ahead of time compiled ELFs
-    if (!strncmp(map.c_str() + extension_pos, ".oat", 4))
+    if (suffix.StartsWith(".oat"))
       return MapType::kArtAot;
     // older/alternative name for .oat
-    if (!strncmp(map.c_str() + extension_pos, ".odex", 5))
+    if (suffix.StartsWith(".odex"))
       return MapType::kArtAot;
   }
   return MapType::kOther;
@@ -103,7 +118,7 @@
 }
 
 Table::Schema ExperimentalAnnotatedStackGenerator::CreateSchema() {
-  return tables::ExperimentalAnnotatedCallstackTable::Schema();
+  return tables::ExperimentalAnnotatedCallstackTable::ComputeStaticSchema();
 }
 
 base::Status ExperimentalAnnotatedStackGenerator::ValidateConstraints(
@@ -120,6 +135,8 @@
                    : base::ErrStatus("Failed to find required constraints");
 }
 
+// TODO(carlscab): Replace annotation logic with
+// src/trace_processor/util/annotated_callsites.h
 base::Status ExperimentalAnnotatedStackGenerator::ComputeTable(
     const std::vector<Constraint>& cs,
     const std::vector<Order>&,
@@ -165,7 +182,8 @@
 
   // Walk the callsites root-to-leaf, annotating:
   // * managed frames with their execution state (interpreted/jit/aot)
-  // * common ART frames, which are usually not relevant
+  // * common ART frames, which are usually not relevant to
+  //   visualisation/inspection
   //
   // This is not a per-frame decision, because we do not want to filter out ART
   // frames immediately after a JNI transition (such frames are often relevant).
@@ -173,13 +191,12 @@
   // As a consequence of the logic being based on a root-to-leaf walk, a given
   // callsite will always have the same annotation, as the parent path is always
   // the same, and children callsites do not affect their parents' annotations.
-  //
-  // This could also be implemented as a hidden column on the callsite table
-  // (populated at import time), but we want to be more flexible for now.
   StringId art_jni_trampoline =
       context_->storage->InternString("art_jni_trampoline");
 
   StringId common_frame = context_->storage->InternString("common-frame");
+  StringId common_frame_interp =
+      context_->storage->InternString("common-frame-interp");
   StringId art_interp = context_->storage->InternString("interp");
   StringId art_jit = context_->storage->InternString("jit");
   StringId art_aot = context_->storage->InternString("aot");
@@ -247,12 +264,41 @@
       continue;
     }
 
+    // Mixed callstack, tag libart frames as uninteresting (common-frame).
+    // Special case a subset of interpreter implementation frames as
+    // "common-frame-interp" using frame name prefixes. Those functions are
+    // actually executed, whereas the managed "interp" frames are synthesised as
+    // their caller by the unwinding library (based on the dex_pc virtual
+    // register restored using the libart's DWARF info). The heuristic covers
+    // the "nterp" and "switch" interpreter implementations.
+    //
+    // Example:
+    //  <towards root>
+    //  android.view.WindowLayout.computeFrames [interp]
+    //  nterp_op_iget_object_slow_path [common-frame-interp]
+    //
+    // This annotation is helpful when trying to answer "what mode was the
+    // process in?" based on the leaf frame of the callstack. As we want to
+    // classify such cases as interpreted, even though the leaf frame is
+    // libart.so.
+    //
+    // For "switch" interpreter, we match any frame starting with
+    // "art::interpreter::" according to itanium mangling.
     if (annotation_state == State::kEraseLibart &&
         map_type == MapType::kNativeLibart) {
+      NullTermStringView fname = context_->storage->GetString(fname_id);
+      if (fname.StartsWith("nterp_") || fname.StartsWith("Nterp") ||
+          fname.StartsWith("ExecuteNterp") ||
+          fname.StartsWith("ExecuteSwitchImpl") ||
+          fname.StartsWith("_ZN3art11interpreter")) {
+        annotations_reversed.push_back(common_frame_interp);
+        continue;
+      }
       annotations_reversed.push_back(common_frame);
       continue;
     }
 
+    // default - no special annotation
     annotations_reversed.push_back(kNullStringId);
   }
 
diff --git a/src/trace_processor/dynamic/experimental_counter_dur_generator.cc b/src/trace_processor/dynamic/experimental_counter_dur_generator.cc
index 2c9dd16..4939167 100644
--- a/src/trace_processor/dynamic/experimental_counter_dur_generator.cc
+++ b/src/trace_processor/dynamic/experimental_counter_dur_generator.cc
@@ -38,7 +38,7 @@
 ExperimentalCounterDurGenerator::~ExperimentalCounterDurGenerator() = default;
 
 Table::Schema ExperimentalCounterDurGenerator::CreateSchema() {
-  return tables::ExperimentalCounterDurTable::Schema();
+  return tables::ExperimentalCounterDurTable::ComputeStaticSchema();
 }
 
 std::string ExperimentalCounterDurGenerator::TableName() {
diff --git a/src/trace_processor/dynamic/experimental_flamegraph_generator.cc b/src/trace_processor/dynamic/experimental_flamegraph_generator.cc
index a1d7bc6..0112a75 100644
--- a/src/trace_processor/dynamic/experimental_flamegraph_generator.cc
+++ b/src/trace_processor/dynamic/experimental_flamegraph_generator.cc
@@ -359,7 +359,7 @@
 }
 
 Table::Schema ExperimentalFlamegraphGenerator::CreateSchema() {
-  return tables::ExperimentalFlamegraphNodesTable::Schema();
+  return tables::ExperimentalFlamegraphNodesTable::ComputeStaticSchema();
 }
 
 std::string ExperimentalFlamegraphGenerator::TableName() {
diff --git a/src/trace_processor/dynamic/experimental_flat_slice_generator.cc b/src/trace_processor/dynamic/experimental_flat_slice_generator.cc
index 928983b..4cf6da0 100644
--- a/src/trace_processor/dynamic/experimental_flat_slice_generator.cc
+++ b/src/trace_processor/dynamic/experimental_flat_slice_generator.cc
@@ -256,7 +256,7 @@
 }
 
 Table::Schema ExperimentalFlatSliceGenerator::CreateSchema() {
-  return tables::ExperimentalFlatSliceTable::Schema();
+  return tables::ExperimentalFlatSliceTable::ComputeStaticSchema();
 }
 
 std::string ExperimentalFlatSliceGenerator::TableName() {
diff --git a/src/trace_processor/dynamic/experimental_sched_upid_generator.cc b/src/trace_processor/dynamic/experimental_sched_upid_generator.cc
index 681566e..87d4a30 100644
--- a/src/trace_processor/dynamic/experimental_sched_upid_generator.cc
+++ b/src/trace_processor/dynamic/experimental_sched_upid_generator.cc
@@ -36,7 +36,7 @@
 ExperimentalSchedUpidGenerator::~ExperimentalSchedUpidGenerator() = default;
 
 Table::Schema ExperimentalSchedUpidGenerator::CreateSchema() {
-  return tables::ExperimentalSchedUpidTable::Schema();
+  return tables::ExperimentalSchedUpidTable::ComputeStaticSchema();
 }
 
 std::string ExperimentalSchedUpidGenerator::TableName() {
diff --git a/src/trace_processor/dynamic/experimental_slice_layout_generator.cc b/src/trace_processor/dynamic/experimental_slice_layout_generator.cc
index ded6fe4..0595a9f 100644
--- a/src/trace_processor/dynamic/experimental_slice_layout_generator.cc
+++ b/src/trace_processor/dynamic/experimental_slice_layout_generator.cc
@@ -30,12 +30,12 @@
 namespace {
 
 struct GroupInfo {
-  GroupInfo(int64_t _start, int64_t _end, uint32_t _max_height)
-      : start(_start), end(_end), max_height(_max_height) {}
+  GroupInfo(int64_t _start, int64_t _end, uint32_t _max_depth)
+      : start(_start), end(_end), layout_depth(0), max_depth(_max_depth) {}
   int64_t start;
   int64_t end;
-  uint32_t max_height;
   uint32_t layout_depth;
+  uint32_t max_depth;
 };
 
 static constexpr uint32_t kFilterTrackIdsColumnIndex =
@@ -52,7 +52,7 @@
 ExperimentalSliceLayoutGenerator::~ExperimentalSliceLayoutGenerator() = default;
 
 Table::Schema ExperimentalSliceLayoutGenerator::CreateSchema() {
-  return tables::ExperimentalSliceLayoutTable::Schema();
+  return tables::ExperimentalSliceLayoutTable::ComputeStaticSchema();
 }
 
 std::string ExperimentalSliceLayoutGenerator::TableName() {
@@ -193,9 +193,9 @@
     bool inserted;
     std::tie(it, inserted) = groups.emplace(
         std::piecewise_construct, std::forward_as_tuple(id_map[id]),
-        std::forward_as_tuple(start, end, depth + 1));
+        std::forward_as_tuple(start, end, depth));
     if (!inserted) {
-      it->second.max_height = std::max(it->second.max_height, depth + 1);
+      it->second.max_depth = std::max(it->second.max_depth, depth);
       it->second.end = std::max(it->second.end, end);
     }
   }
@@ -219,7 +219,7 @@
   std::vector<GroupInfo*> still_open;
   for (GroupInfo* group : sorted_groups) {
     int64_t start = group->start;
-    uint32_t max_height = group->max_height;
+    uint32_t max_depth = group->max_depth;
 
     // Discard all 'closed' groups where that groups end_ts is < our start_ts:
     {
@@ -241,13 +241,13 @@
     while (!done) {
       done = true;
       uint32_t start_depth = layout_depth;
-      uint32_t end_depth = layout_depth + max_height;
+      uint32_t end_depth = layout_depth + max_depth;
       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) {
+        uint32_t open_start_depth = open->layout_depth;
+        uint32_t open_end_depth = open->layout_depth + open->max_depth;
+        bool fully_above_open = end_depth < open_start_depth;
+        bool fully_below_open = open_end_depth < start_depth;
+        if (!fully_above_open && !fully_below_open) {
           // 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++;
diff --git a/src/trace_processor/dynamic/experimental_slice_layout_generator_unittest.cc b/src/trace_processor/dynamic/experimental_slice_layout_generator_unittest.cc
index 6e3a6ad..80e35d8 100644
--- a/src/trace_processor/dynamic/experimental_slice_layout_generator_unittest.cc
+++ b/src/trace_processor/dynamic/experimental_slice_layout_generator_unittest.cc
@@ -235,6 +235,54 @@
 )");
 }
 
+TEST(ExperimentalSliceLayoutGeneratorTest, PreviousGroupFullyNested) {
+  StringPool pool;
+  tables::SliceTable slice_table(&pool, nullptr);
+  StringId name = pool.InternString("Slice");
+
+  // This test ensures that our bounding box logic works when the bounding box
+  // of an earlier group is nested inside bounding box of a later group.
+  // In that case, we should still layout in a way which avoids overlaps.
+
+  // Group 1 exists just to create push group 2 down one row.
+  auto a = Insert(&slice_table, 0 /*ts*/, 1 /*dur*/, 1 /*track_id*/, name,
+                  base::nullopt);
+  base::ignore_result(a);
+
+  // Group 2 has a depth of 2 so it theoretically "nests" inside a group of
+  // depth 4.
+  auto c = Insert(&slice_table, 0 /*ts*/, 10 /*dur*/, 2 /*track_id*/, name,
+                  base::nullopt);
+  auto d = Insert(&slice_table, 0 /*ts*/, 9 /*dur*/, 2 /*track_id*/, name, c);
+  base::ignore_result(d);
+
+  // Group 3 has a depth of 4 so it could cause group 2 to "nest" if our
+  // layout algorithm did not work correctly.
+  auto p = Insert(&slice_table, 3 /*ts*/, 4 /*dur*/, 3 /*track_id*/, name,
+                  base::nullopt);
+  auto q = Insert(&slice_table, 3 /*ts*/, 3 /*dur*/, 3 /*track_id*/, name, p);
+  auto r = Insert(&slice_table, 3 /*ts*/, 2 /*dur*/, 3 /*track_id*/, name, q);
+  auto s = Insert(&slice_table, 3 /*ts*/, 1 /*dur*/, 3 /*track_id*/, name, r);
+  base::ignore_result(s);
+
+  ExperimentalSliceLayoutGenerator gen(&pool, &slice_table);
+
+  std::unique_ptr<Table> table;
+  auto status = gen.ComputeTable(
+      {Constraint{kColumn, FilterOp::kEq, SqlValue::String("1,2,3")}}, {},
+      BitVector(), table);
+  EXPECT_TRUE(status.ok());
+  ExpectOutput(*table, R"(
+#
+##########
+#########
+   ####
+   ###
+   ##
+   #
+)");
+}
+
 TEST(ExperimentalSliceLayoutGeneratorTest, FilterOutTracks) {
   StringPool pool;
   tables::SliceTable slice_table(&pool, nullptr);
diff --git a/src/trace_processor/export_json.cc b/src/trace_processor/export_json.cc
index 8d9d66a..7709681 100644
--- a/src/trace_processor/export_json.cc
+++ b/src/trace_processor/export_json.cc
@@ -777,27 +777,28 @@
 
   util::Status ExportSlices() {
     const auto& slices = storage_->slice_table();
-    for (uint32_t i = 0; i < slices.row_count(); ++i) {
+    for (auto it = slices.IterateRows(); it; ++it) {
       // Skip slices with empty category - these are ftrace/system slices that
       // were also imported into the raw table and will be exported from there
       // by trace_to_text.
       // TODO(b/153609716): Add a src column or do_not_export flag instead.
-      auto cat = slices.category().GetString(i);
+      if (!it.category())
+        continue;
+      auto cat = storage_->GetString(*it.category());
       if (cat.c_str() == nullptr || cat == "binder")
         continue;
 
       Json::Value event;
-      event["ts"] = Json::Int64(slices.ts()[i] / 1000);
-      event["cat"] = GetNonNullString(storage_, slices.category()[i]);
-      event["name"] = GetNonNullString(storage_, slices.name()[i]);
+      event["ts"] = Json::Int64(it.ts() / 1000);
+      event["cat"] = GetNonNullString(storage_, it.category());
+      event["name"] = GetNonNullString(storage_, it.name());
       event["pid"] = 0;
       event["tid"] = 0;
 
       base::Optional<UniqueTid> legacy_utid;
       std::string legacy_phase;
 
-      event["args"] =
-          args_builder_.GetArgs(slices.arg_set_id()[i]);  // Makes a copy.
+      event["args"] = args_builder_.GetArgs(it.arg_set_id());  // Makes a copy.
       if (event["args"].isMember(kLegacyEventArgsKey)) {
         const auto& legacy_args = event["args"][kLegacyEventArgsKey];
 
@@ -815,7 +816,7 @@
       // 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.
-      TrackId track_id = slices.track_id()[i];
+      TrackId track_id = it.track_id();
 
       const auto& track_table = storage_->track_table();
 
@@ -833,26 +834,21 @@
 
       const auto& thread_track = storage_->thread_track_table();
       const auto& process_track = storage_->process_track_table();
-      const auto& thread_slices = storage_->thread_slice_table();
       const auto& virtual_track_slices = storage_->virtual_track_slices();
 
-      int64_t duration_ns = slices.dur()[i];
+      int64_t duration_ns = it.dur();
       base::Optional<int64_t> thread_ts_ns;
       base::Optional<int64_t> thread_duration_ns;
       base::Optional<int64_t> thread_instruction_count;
       base::Optional<int64_t> thread_instruction_delta;
 
-      SliceId id = slices.id()[i];
-      base::Optional<uint32_t> thread_slice_row =
-          thread_slices.id().IndexOf(id);
-      if (thread_slice_row) {
-        thread_ts_ns = thread_slices.thread_ts()[*thread_slice_row];
-        thread_duration_ns = thread_slices.thread_dur()[*thread_slice_row];
-        thread_instruction_count =
-            thread_slices.thread_instruction_count()[*thread_slice_row];
-        thread_instruction_delta =
-            thread_slices.thread_instruction_delta()[*thread_slice_row];
+      if (it.thread_dur()) {
+        thread_ts_ns = it.thread_ts();
+        thread_duration_ns = it.thread_dur();
+        thread_instruction_count = it.thread_instruction_count();
+        thread_instruction_delta = it.thread_instruction_delta();
       } else {
+        SliceId id = it.id();
         base::Optional<uint32_t> vtrack_slice_row =
             virtual_track_slices.FindRowForSliceId(id);
         if (vtrack_slice_row) {
@@ -1008,7 +1004,7 @@
           // write the end event in this case.
           if (duration_ns > 0) {
             event["ph"] = legacy_phase.empty() ? "e" : "F";
-            event["ts"] = Json::Int64((slices.ts()[i] + duration_ns) / 1000);
+            event["ts"] = Json::Int64((it.ts() + duration_ns) / 1000);
             if (thread_ts_ns && thread_duration_ns && *thread_ts_ns > 0) {
               event["tts"] =
                   Json::Int64((*thread_ts_ns + *thread_duration_ns) / 1000);
diff --git a/src/trace_processor/export_json_unittest.cc b/src/trace_processor/export_json_unittest.cc
index 67c5927..07722a0 100644
--- a/src/trace_processor/export_json_unittest.cc
+++ b/src/trace_processor/export_json_unittest.cc
@@ -134,7 +134,7 @@
   StringId cat_id = context_.storage->InternString(base::StringView(kCategory));
   StringId name_id = context_.storage->InternString(base::StringView(kName));
   // The thread_slice table is a sub table of slice.
-  context_.storage->mutable_thread_slice_table()->Insert(
+  context_.storage->mutable_slice_table()->Insert(
       {kTimestamp, kDuration, track, cat_id, name_id, 0, 0, 0, SliceId(0u), 0,
        kThreadTimestamp, kThreadDuration, kThreadInstructionCount,
        kThreadInstructionDelta});
@@ -179,7 +179,7 @@
   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_thread_slice_table()->Insert(
+  context_.storage->mutable_slice_table()->Insert(
       {kTimestamp, kDuration, track, cat_id, name_id, 0, 0, 0, SliceId(0u), 0,
        kThreadTimestamp, kThreadDuration, kThreadInstructionCount,
        kThreadInstructionDelta});
diff --git a/src/trace_processor/forwarding_trace_parser.cc b/src/trace_processor/forwarding_trace_parser.cc
index 9f858cc..0cccce2 100644
--- a/src/trace_processor/forwarding_trace_parser.cc
+++ b/src/trace_processor/forwarding_trace_parser.cc
@@ -19,10 +19,9 @@
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/string_utils.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_reader.h"
-#include "src/trace_processor/trace_sorter.h"
+#include "src/trace_processor/sorter/trace_sorter.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -71,6 +70,7 @@
       auto scoped_trace = context_->storage->TraceExecutionTimeIntoStats(
           stats::guess_trace_type_duration_ns);
       trace_type = GuessTraceType(blob.data(), blob.size());
+      context_->trace_type = trace_type;
     }
     switch (trace_type) {
       case kJsonTraceType: {
@@ -100,8 +100,11 @@
       }
       case kNinjaLogTraceType: {
         PERFETTO_DLOG("Ninja log detected");
-        reader_.reset(new NinjaLogParser(context_));
-        break;
+        if (context_->ninja_log_parser) {
+          reader_ = std::move(context_->ninja_log_parser);
+          break;
+        }
+        return util::ErrStatus("Ninja support is disabled");
       }
       case kFuchsiaTraceType: {
         PERFETTO_DLOG("Fuchsia trace detected");
diff --git a/src/trace_processor/forwarding_trace_parser.h b/src/trace_processor/forwarding_trace_parser.h
index 44e1ec8..e63a272 100644
--- a/src/trace_processor/forwarding_trace_parser.h
+++ b/src/trace_processor/forwarding_trace_parser.h
@@ -26,18 +26,6 @@
 
 constexpr size_t kGuessTraceMaxLookahead = 64;
 
-enum TraceType {
-  kUnknownTraceType,
-  kProtoTraceType,
-  kJsonTraceType,
-  kFuchsiaTraceType,
-  kSystraceTraceType,
-  kGzipTraceType,
-  kCtraceTraceType,
-  kNinjaLogTraceType,
-  kAndroidBugreportTraceType,
-};
-
 TraceType GuessTraceType(const uint8_t* data, size_t size);
 
 class ForwardingTraceParser : public ChunkedTraceReader {
diff --git a/src/trace_processor/importers/BUILD.gn b/src/trace_processor/importers/BUILD.gn
deleted file mode 100644
index ad01aac..0000000
--- a/src/trace_processor/importers/BUILD.gn
+++ /dev/null
@@ -1,35 +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.
-
-import("../../../gn/perfetto_cc_proto_descriptor.gni")
-
-perfetto_cc_proto_descriptor("gen_cc_statsd_atoms_descriptor") {
-  descriptor_name = "atoms.descriptor"
-  descriptor_path = "./proto/atoms.descriptor"
-}
-
-perfetto_cc_proto_descriptor("gen_cc_config_descriptor") {
-  descriptor_name = "config.descriptor"
-  descriptor_target = "../../../protos/perfetto/config:descriptor"
-}
-
-perfetto_cc_proto_descriptor("gen_cc_chrome_track_event_descriptor") {
-  descriptor_name = "chrome_track_event.descriptor"
-  descriptor_target = "../../../protos/third_party/chromium:descriptor"
-}
-
-perfetto_cc_proto_descriptor("gen_cc_track_event_descriptor") {
-  descriptor_name = "track_event.descriptor"
-  descriptor_target = "../../../protos/perfetto/trace/track_event:descriptor"
-}
diff --git a/src/trace_processor/importers/additional_modules.cc b/src/trace_processor/importers/additional_modules.cc
deleted file mode 100644
index 7fbbeb5..0000000
--- a/src/trace_processor/importers/additional_modules.cc
+++ /dev/null
@@ -1,45 +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/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/statsd_module.h"
-#include "src/trace_processor/importers/proto/system_probes_module.h"
-#include "src/trace_processor/importers/proto/translation_table_module.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-void RegisterAdditionalModules(TraceProcessorContext* context) {
-  context->modules.emplace_back(new AndroidProbesModule(context));
-  context->modules.emplace_back(new GraphicsEventModule(context));
-  context->modules.emplace_back(new HeapGraphModule(context));
-  context->modules.emplace_back(new SystemProbesModule(context));
-  context->modules.emplace_back(new TranslationTableModule(context));
-  context->modules.emplace_back(new StatsdModule(context));
-
-  // 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->modules.emplace_back(new FtraceModuleImpl(context));
-  context->ftrace_module =
-      static_cast<FtraceModule*>(context->modules.back().get());
-}
-
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/importers/additional_modules.h b/src/trace_processor/importers/additional_modules.h
deleted file mode 100644
index 8040b64..0000000
--- a/src/trace_processor/importers/additional_modules.h
+++ /dev/null
@@ -1,30 +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_ADDITIONAL_MODULES_H_
-#define SRC_TRACE_PROCESSOR_IMPORTERS_ADDITIONAL_MODULES_H_
-
-#include "src/trace_processor/types/trace_processor_context.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-void RegisterAdditionalModules(TraceProcessorContext*);
-
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_ADDITIONAL_MODULES_H_
diff --git a/src/trace_processor/importers/android_bugreport/android_bugreport_parser.cc b/src/trace_processor/importers/android_bugreport/android_bugreport_parser.cc
index 75036a2..5e80c24 100644
--- a/src/trace_processor/importers/android_bugreport/android_bugreport_parser.cc
+++ b/src/trace_processor/importers/android_bugreport/android_bugreport_parser.cc
@@ -54,11 +54,121 @@
 }
 
 void AndroidBugreportParser::NotifyEndOfFile() {
-  if (!DetectYear()) {
+  if (!DetectYearAndBrFilename()) {
     context_->storage->IncrementStats(stats::android_br_parse_errors);
     return;
   }
+
   ParsePersistentLogcat();
+  ParseDumpstateTxt();
+  SortAndStoreLogcat();
+}
+
+void AndroidBugreportParser::ParseDumpstateTxt() {
+  // Dumpstate is organized in a two level hierarchy, beautifully flattened into
+  // one text file with load bearing ----- markers:
+  // 1. Various dumpstate sections, examples:
+  // ```
+  //   ------ DUMPSYS CRITICAL (/system/bin/dumpsys) ------
+  //   ...
+  //   ------ SYSTEM LOG (logcat -v threadtime -v printable -v uid) ------
+  //   ...
+  //   ------ IPTABLES (iptables -L -nvx) ------
+  //   ...
+  //   ------ DUMPSYS HIGH (/system/bin/dumpsys) ------
+  //   ...
+  //   ------ DUMPSYS (/system/bin/dumpsys) ------
+  // ```
+  //
+  // 2. Within the "------ DUMPSYS" section (note dumpsys != dumpstate), there
+  //    are multiple services. Note that there are at least 3 DUMPSYS sections
+  //    (CRITICAL, HIGH and default), with multiple services in each:
+  // ```
+  //    ------ DUMPSYS (/system/bin/dumpsys) ------
+  // DUMP OF SERVICE activity:
+  // ...
+  // ---------------------------------------------------------------------------
+  // DUMP OF SERVICE input_method:
+  // ...
+  // ---------------------------------------------------------------------------
+  // ```
+  // Here we put each line in a dedicated table, android_dumpstate, keeping
+  // track of the dumpstate `section` and dumpsys `service`.
+  AndroidLogParser log_parser(br_year_, context_->storage.get());
+  util::ZipFile* zf = zip_reader_->Find(dumpstate_fname_);
+  StringId section_id = StringId::Null();  // The current dumpstate section.
+  StringId service_id = StringId::Null();  // The current dumpsys service.
+  static constexpr size_t npos = base::StringView::npos;
+  enum { OTHER = 0, DUMPSYS, LOG } cur_sect = OTHER;
+  zf->DecompressLines([&](const std::vector<base::StringView>& lines) {
+    // Optimization for ParseLogLines() below. Avoids ctor/dtor-ing a new vector
+    // on every line.
+    std::vector<base::StringView> log_line(1);
+    for (const base::StringView& line : lines) {
+      if (line.StartsWith("------ ") && line.EndsWith(" ------")) {
+        // These lines mark the beginning and end of dumpstate sections:
+        // ------ DUMPSYS CRITICAL (/system/bin/dumpsys) ------
+        // ------ 0.356s was the duration of 'DUMPSYS CRITICAL' ------
+        base::StringView section = line.substr(7);
+        section = section.substr(0, section.size() - 7);
+        bool end_marker = section.find("was the duration of") != npos;
+        service_id = StringId::Null();
+        if (end_marker) {
+          section_id = StringId::Null();
+        } else {
+          section_id = context_->storage->InternString(section);
+          cur_sect = OTHER;
+          if (section.StartsWith("DUMPSYS")) {
+            cur_sect = DUMPSYS;
+          } else if (section.StartsWith("SYSTEM LOG") ||
+                     section.StartsWith("EVENT LOG") ||
+                     section.StartsWith("RADIO LOG")) {
+            // KERNEL LOG is deliberately omitted because SYSTEM LOG is a
+            // superset. KERNEL LOG contains all dupes.
+            cur_sect = LOG;
+          } else if (section.StartsWith("BLOCK STAT")) {
+            // Coalesce all the block stats into one section. Otherwise they
+            // pollute the table with one section per block device.
+            section_id = context_->storage->InternString("BLOCK STAT");
+          }
+        }
+        continue;
+      }
+      // Skip end marker lines for dumpsys sections.
+      if (cur_sect == DUMPSYS && line.StartsWith("--------- ") &&
+          line.find("was the duration of dumpsys") != npos) {
+        service_id = StringId::Null();
+        continue;
+      }
+      if (cur_sect == DUMPSYS && service_id.is_null() &&
+          line.StartsWith("----------------------------------------------")) {
+        continue;
+      }
+      if (cur_sect == DUMPSYS && line.StartsWith("DUMP OF SERVICE")) {
+        // DUMP OF SERVICE [CRITICAL|HIGH] ServiceName:
+        base::StringView svc = line.substr(line.rfind(' ') + 1);
+        svc = svc.substr(0, svc.size() - 1);
+        service_id = context_->storage->InternString(svc);
+      } else if (cur_sect == LOG) {
+        // Parse the non-persistent logcat and append to `log_events_`, together
+        // with the persistent one previously parsed by ParsePersistentLogcat().
+        // Skips entries that are already seen in the persistent logcat,
+        // handling us vs ms truncation.
+        PERFETTO_DCHECK(log_line.size() == 1);
+        log_line[0] = line;
+        log_parser.ParseLogLines(log_line, &log_events_,
+                                 log_events_last_sorted_idx_);
+      }
+
+      if (build_fpr_.empty() && line.StartsWith("Build fingerprint:")) {
+        build_fpr_ = line.substr(20, line.size() - 20).ToStdString();
+      }
+
+      // Append the line to the android_dumpstate table.
+      context_->storage->mutable_android_dumpstate_table()->Insert(
+          {section_id, service_id, context_->storage->InternString(line)});
+    }
+  });
 }
 
 void AndroidBugreportParser::ParsePersistentLogcat() {
@@ -83,19 +193,32 @@
 
   // Push all events into the AndroidLogParser. It will take care of string
   // interning into the pool. Appends entries into `log_events`.
-  std::vector<AndroidLogEvent> log_events;
   for (const auto& kv : log_paths) {
     util::ZipFile* zf = zip_reader_->Find(kv.second);
     zf->DecompressLines([&](const std::vector<base::StringView>& lines) {
-      log_parser.ParseLogLines(lines, &log_events);
+      log_parser.ParseLogLines(lines, &log_events_);
     });
   }
 
-  // Sort the union of all log events parsed from all files in /data/misc/logd.
-  std::sort(log_events.begin(), log_events.end());
+  // Do an initial sorting pass. This is not the final sorting because we
+  // haven't ingested the latest logs from dumpstate yet. But we need this sort
+  // to be able to de-dupe the same lines showing both in dumpstate and in the
+  // persistent log.
+  SortLogEvents();
+}
+
+void AndroidBugreportParser::SortAndStoreLogcat() {
+  // Sort the union of all log events parsed from both /data/misc/logd
+  // (persistent logcat on disk) and the dumpstate file (last in-memory logcat).
+  // Before the std::stable_sort, entries in `log_events_` are already "mostly"
+  // sorted, because we processed files in order (see notes above about kernel
+  // logs on why we need a final sort here).
+  // We need stable-sort to preserve FIFO-ness of events emitted at the same
+  // time, logcat is not granular enough (us for persistent, ms for dumpstate).
+  SortLogEvents();
 
   // Insert the globally sorted events into the android_logs table.
-  for (const auto& e : log_events) {
+  for (const auto& e : log_events_) {
     UniquePid utid = context_->process_tracker->UpdateThread(e.tid, e.pid);
     context_->storage->mutable_android_log_table()->Insert(
         {e.ts, utid, e.prio, e.tag, e.msg});
@@ -106,7 +229,7 @@
 // This is because logcat events have only the month and day.
 // This is obviously bugged for cases of bugreports collected across new year
 // but we'll live with that.
-bool AndroidBugreportParser::DetectYear() {
+bool AndroidBugreportParser::DetectYearAndBrFilename() {
   const util::ZipFile* br_file = nullptr;
   for (const auto& zf : zip_reader_->files()) {
     if (base::StartsWith(zf.name(), "bugreport-") &&
@@ -130,8 +253,14 @@
     return false;
   }
   br_year_ = *year;
+  dumpstate_fname_ = br_file->name();
   return true;
 }
 
+void AndroidBugreportParser::SortLogEvents() {
+  std::stable_sort(log_events_.begin(), log_events_.end());
+  log_events_last_sorted_idx_ = log_events_.size();
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/android_bugreport/android_bugreport_parser.h b/src/trace_processor/importers/android_bugreport/android_bugreport_parser.h
index 248121b..9969159 100644
--- a/src/trace_processor/importers/android_bugreport/android_bugreport_parser.h
+++ b/src/trace_processor/importers/android_bugreport/android_bugreport_parser.h
@@ -27,6 +27,7 @@
 class ZipReader;
 }
 
+struct AndroidLogEvent;
 class TraceProcessorContext;
 
 // Trace importer for Android bugreport.zip archives.
@@ -40,13 +41,20 @@
   void NotifyEndOfFile() override;
 
  private:
-  bool DetectYear();
+  bool DetectYearAndBrFilename();
   void ParsePersistentLogcat();
+  void ParseDumpstateTxt();
+  void SortAndStoreLogcat();
+  void SortLogEvents();
 
   TraceProcessorContext* const context_;
   int br_year_ = 0;  // The year when the bugreport has been taken.
+  std::string dumpstate_fname_;  // The name of bugreport-xxx-2022-08-04....txt
+  std::string build_fpr_;
   bool first_chunk_seen_ = false;
   std::unique_ptr<util::ZipReader> zip_reader_;
+  std::vector<AndroidLogEvent> log_events_;
+  size_t log_events_last_sorted_idx_ = 0;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/android_bugreport/android_log_parser.cc b/src/trace_processor/importers/android_bugreport/android_log_parser.cc
index afcb44e..cf42150 100644
--- a/src/trace_processor/importers/android_bugreport/android_log_parser.cc
+++ b/src/trace_processor/importers/android_bugreport/android_log_parser.cc
@@ -72,11 +72,15 @@
     // but flag the current token as invalid.
     invalid_chars_found = true;
   }
-  if (sep_found && !invalid_chars_found) {
-    *it = it->substr(next_it);
-    return num;
-  }
-  return base::nullopt;
+  if (!sep_found)
+    return base::nullopt;
+  // If we find non-digit characters, we want to still skip the token but return
+  // nullopt. The parser below relies on token skipping to deal with cases where
+  // the uid (which we don't care about) is literal ("root" rather than 0).
+  *it = it->substr(next_it);
+  if (invalid_chars_found)
+    return base::nullopt;
+  return num;
 }
 
 enum class LogcatFormat {
@@ -116,7 +120,8 @@
 // Parses a bunch of logcat lines and appends broken down events into
 // `log_events`.
 void AndroidLogParser::ParseLogLines(std::vector<base::StringView> lines,
-                                     std::vector<AndroidLogEvent>* log_events) {
+                                     std::vector<AndroidLogEvent>* log_events,
+                                     size_t dedupe_idx) {
   int parse_failures = 0;
   LogcatFormat fmt = LogcatFormat::kUnknown;
   for (auto line : lines) {
@@ -201,11 +206,43 @@
     int64_t secs = base::MkTime(year_, *month, *day, *hour, *minute, *sec);
     int64_t ts = secs * 1000000000ll + *ns;
 
-    log_events->emplace_back(AndroidLogEvent{
-        ts, static_cast<uint32_t>(*pid), static_cast<uint32_t>(*tid),
-        static_cast<uint32_t>(prio), storage_->InternString(cat),
-        storage_->InternString(msg)});
-  }
+    AndroidLogEvent evt{ts,
+                        static_cast<uint32_t>(*pid),
+                        static_cast<uint32_t>(*tid),
+                        static_cast<uint32_t>(prio),
+                        storage_->InternString(cat),
+                        storage_->InternString(msg)};
+
+    if (dedupe_idx > 0) {
+      // Search for dupes before inserting.
+      // Events in the [0, dedupe_idx] range are sorted by timestamp with ns
+      // resolution. Here we search for dupes within the same millisecond of
+      // the event we are trying to insert. The /1000000*1000000 is to deal with
+      // the fact that events coming from the persistent log have us resolution,
+      // while events from dumpstate (which are often dupes of persistent ones)
+      // have only ms resolution. Here we consider an event a dupe if it has
+      // the same ms-truncated solution, same pid, tid and message.
+      AndroidLogEvent etrunc = evt;
+      etrunc.ts = etrunc.ts / 1000000 * 1000000;
+      auto begin = log_events->begin();
+      auto end = log_events->begin() + static_cast<ssize_t>(dedupe_idx);
+      bool dupe_found = false;
+      for (auto eit = std::lower_bound(begin, end, etrunc); eit < end; ++eit) {
+        if (eit->ts / 1000000 * 1000000 != etrunc.ts)
+          break;
+        if (eit->msg == evt.msg && eit->tag == evt.tag && eit->tid == evt.tid &&
+            eit->pid == evt.pid) {
+          dupe_found = true;
+          break;
+        }
+      }
+      if (dupe_found) {
+        continue;  // Skip the current line.
+      }
+    }  // if (dedupe_idx)
+
+    log_events->emplace_back(std::move(evt));
+  }  //  for (line : lines)
   storage_->IncrementStats(stats::android_log_num_failed, parse_failures);
 }
 
diff --git a/src/trace_processor/importers/android_bugreport/android_log_parser.h b/src/trace_processor/importers/android_bugreport/android_log_parser.h
index 47a92c5..2609bda 100644
--- a/src/trace_processor/importers/android_bugreport/android_log_parser.h
+++ b/src/trace_processor/importers/android_bugreport/android_log_parser.h
@@ -60,8 +60,16 @@
       : storage_(storage), year_(year) {}
   ~AndroidLogParser() = default;
 
+  // Decodes logcat events for the input `lines` and appends them into
+  // `log_events`. If `dedupe_idx` is != 0, it checks for duplicate entries
+  // before inserting and skips the insertion if a dupe is found. Dupes are
+  // searched in the first `dedupe_idx` entries of `log_events`. In practice
+  // `dedupe_idx` is the log_events.size() for the last std::sort() call.
+  // The de-duping logic truncates timestamps to millisecond resolution, to
+  // handle the mismatching resolution of dumpstate (ms) vs persistent log (us).
   void ParseLogLines(std::vector<base::StringView> lines,
-                     std::vector<AndroidLogEvent>* log_events);
+                     std::vector<AndroidLogEvent>* log_events,
+                     size_t dedupe_idx = 0);
 
  private:
   TraceStorage* const storage_;
diff --git a/src/trace_processor/importers/android_bugreport/android_log_parser_unittest.cc b/src/trace_processor/importers/android_bugreport/android_log_parser_unittest.cc
index efa8d5a..227934e 100644
--- a/src/trace_processor/importers/android_bugreport/android_log_parser_unittest.cc
+++ b/src/trace_processor/importers/android_bugreport/android_log_parser_unittest.cc
@@ -26,11 +26,13 @@
 namespace perfetto {
 namespace trace_processor {
 
+const int64_t kStoNs = 1000000000LL;
+
 inline std::ostream& operator<<(std::ostream& stream,
                                 const AndroidLogEvent& e) {
   char tms[32];
-  time_t secs = static_cast<time_t>(e.ts / 1000000000);
-  int ns = static_cast<int>(e.ts - secs * 1000000000);
+  time_t secs = static_cast<time_t>(e.ts / kStoNs);
+  int ns = static_cast<int>(e.ts - secs * kStoNs);
   strftime(tms, sizeof(tms), "%Y-%m-%d %H:%M:%S", gmtime(&secs));
   base::StackString<64> tss("%s.%d", tms, ns);
 
@@ -67,23 +69,23 @@
       events,
       ElementsAreArray({
           AndroidLogEvent{
-              base::MkTime(2020, 1, 2, 3, 4, 5) * 1000000000 + 678901000, 1000,
+              base::MkTime(2020, 1, 2, 3, 4, 5) * kStoNs + 678901000, 1000,
               2000, P::PRIO_DEBUG, S("Tag"), S("message")},
           AndroidLogEvent{
-              base::MkTime(2020, 1, 2, 3, 4, 5) * 1000000000 + 678901000, 1000,
+              base::MkTime(2020, 1, 2, 3, 4, 5) * kStoNs + 678901000, 1000,
               2000, P::PRIO_VERBOSE, S("Tag"), S("message")},
           AndroidLogEvent{
-              base::MkTime(2020, 12, 31, 23, 59, 0) * 1000000000 + 123456000, 1,
-              2, P::PRIO_INFO, S("[tag:with:colon]"), S("moar long message")},
+              base::MkTime(2020, 12, 31, 23, 59, 0) * kStoNs + 123456000, 1, 2,
+              P::PRIO_INFO, S("[tag:with:colon]"), S("moar long message")},
           AndroidLogEvent{
-              base::MkTime(2020, 12, 31, 23, 59, 0) * 1000000000 + 123000000, 1,
-              2, P::PRIO_WARN, S("[tag:with:colon]"), S("moar long message")},
+              base::MkTime(2020, 12, 31, 23, 59, 0) * kStoNs + 123000000, 1, 2,
+              P::PRIO_WARN, S("[tag:with:colon]"), S("moar long message")},
           AndroidLogEvent{
-              base::MkTime(2020, 12, 31, 23, 59, 0) * 1000000000 + 100000000, 1,
-              2, P::PRIO_ERROR, S("[tag:with:colon]"), S("moar long message")},
+              base::MkTime(2020, 12, 31, 23, 59, 0) * kStoNs + 100000000, 1, 2,
+              P::PRIO_ERROR, S("[tag:with:colon]"), S("moar long message")},
           AndroidLogEvent{
-              base::MkTime(2020, 12, 31, 23, 59, 0) * 1000000000 + 10000000, 1,
-              2, P::PRIO_FATAL, S("[tag:with:colon]"), S("moar long message")},
+              base::MkTime(2020, 12, 31, 23, 59, 0) * kStoNs + 10000000, 1, 2,
+              P::PRIO_FATAL, S("[tag:with:colon]"), S("moar long message")},
       }));
 }
 
@@ -98,6 +100,8 @@
       {
           "07-28 14:25:20.355  0     1     2 I init   : Loaded kernel module",
           "07-28 14:25:54.876  1000   643   644 D PackageManager: No files",
+          "08-24 23:39:12.272  root     0     1 I        : c0  11835 binder: 1",
+          "08-24 23:39:12.421 radio  2532  2533 D TelephonyProvider: Using old",
       },
       &events);
 
@@ -106,14 +110,83 @@
       events,
       ElementsAreArray({
           AndroidLogEvent{
-              base::MkTime(2020, 7, 28, 14, 25, 20) * 1000000000 + 355000000, 1,
-              2, P::PRIO_INFO, S("init"), S("Loaded kernel module")},
+              base::MkTime(2020, 7, 28, 14, 25, 20) * kStoNs + 355000000, 1, 2,
+              P::PRIO_INFO, S("init"), S("Loaded kernel module")},
           AndroidLogEvent{
-              base::MkTime(2020, 7, 28, 14, 25, 54) * 1000000000 + 876000000,
-              643, 644, P::PRIO_DEBUG, S("PackageManager"), S("No files")},
+              base::MkTime(2020, 7, 28, 14, 25, 54) * kStoNs + 876000000, 643,
+              644, P::PRIO_DEBUG, S("PackageManager"), S("No files")},
+          AndroidLogEvent{
+              base::MkTime(2020, 8, 24, 23, 39, 12) * kStoNs + 272000000, 0, 1,
+              P::PRIO_INFO, S(""), S("c0  11835 binder: 1")},
+          AndroidLogEvent{
+              base::MkTime(2020, 8, 24, 23, 39, 12) * kStoNs + 421000000, 2532,
+              2533, P::PRIO_DEBUG, S("TelephonyProvider"), S("Using old")},
       }));
 }
 
+// Tests the deduping logic. This is used when parsing events first from the
+// persistent logcat (which has us resolution) and then from dumpstate (which
+// has ms resolution and sometimes contains dupes of the persistent entries).
+TEST(AndroidLogParserTest, Dedupe) {
+  TraceStorage storage;
+  AndroidLogParser alp(2020, &storage);
+  auto S = [&](const char* str) { return storage.InternString(str); };
+  using P = ::perfetto::protos::pbzero::AndroidLogPriority;
+  std::vector<AndroidLogEvent> events;
+
+  // Parse some initial events without any deduping.
+  alp.ParseLogLines(
+      {
+          "01-01 00:00:01.100000  0 1 1 I tag : M1",
+          "01-01 00:00:01.100111  0 1 1 I tag : M2",
+          "01-01 00:00:01.100111  0 1 1 I tag : M3",
+          "01-01 00:00:01.100222  0 1 1 I tag : M4",
+          "01-01 00:00:01.101000  0 1 1 I tag : M5",
+      },
+      &events);
+
+  ASSERT_EQ(events.size(), 5u);
+
+  // Add a batch of events with truncated timestamps, some of which are dupes.
+  alp.ParseLogLines(
+      {
+          "01-01 00:00:01.100  0 1 1 I tag : M1",  // Dupe
+          "01-01 00:00:01.100  0 1 1 I tag : M1.5",
+          "01-01 00:00:01.100  0 1 1 I tag : M3",  // Dupe
+          "01-01 00:00:01.100  0 1 1 I tag : M4",  // Dupe
+          "01-01 00:00:01.101  0 1 1 I tag : M5",  // Dupe
+          "01-01 00:00:01.101  0 1 1 I tag : M6",
+      },
+      &events, /*dedupe_idx=*/5);
+  EXPECT_EQ(storage.stats()[stats::android_log_num_failed].value, 0);
+
+  std::stable_sort(events.begin(), events.end());
+  ASSERT_THAT(events,
+              ElementsAreArray({
+                  AndroidLogEvent{
+                      base::MkTime(2020, 1, 1, 0, 0, 1) * kStoNs + 100000000, 1,
+                      1, P::PRIO_INFO, S("tag"), S("M1")},
+                  AndroidLogEvent{
+                      base::MkTime(2020, 1, 1, 0, 0, 1) * kStoNs + 100000000, 1,
+                      1, P::PRIO_INFO, S("tag"), S("M1.5")},
+                  AndroidLogEvent{
+                      base::MkTime(2020, 1, 1, 0, 0, 1) * kStoNs + 100111000, 1,
+                      1, P::PRIO_INFO, S("tag"), S("M2")},
+                  AndroidLogEvent{
+                      base::MkTime(2020, 1, 1, 0, 0, 1) * kStoNs + 100111000, 1,
+                      1, P::PRIO_INFO, S("tag"), S("M3")},
+                  AndroidLogEvent{
+                      base::MkTime(2020, 1, 1, 0, 0, 1) * kStoNs + 100222000, 1,
+                      1, P::PRIO_INFO, S("tag"), S("M4")},
+                  AndroidLogEvent{
+                      base::MkTime(2020, 1, 1, 0, 0, 1) * kStoNs + 101000000, 1,
+                      1, P::PRIO_INFO, S("tag"), S("M5")},
+                  AndroidLogEvent{
+                      base::MkTime(2020, 1, 1, 0, 0, 1) * kStoNs + 101000000, 1,
+                      1, P::PRIO_INFO, S("tag"), S("M6")},
+              }));
+}
+
 }  // namespace
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/common/BUILD.gn b/src/trace_processor/importers/common/BUILD.gn
index 5d19218..22a1154 100644
--- a/src/trace_processor/importers/common/BUILD.gn
+++ b/src/trace_processor/importers/common/BUILD.gn
@@ -20,9 +20,13 @@
     "args_tracker.h",
     "args_translation_table.cc",
     "args_translation_table.h",
+    "async_track_set_tracker.cc",
+    "async_track_set_tracker.h",
     "chunked_trace_reader.h",
     "clock_tracker.cc",
     "clock_tracker.h",
+    "deobfuscation_mapping_table.cc",
+    "deobfuscation_mapping_table.h",
     "event_tracker.cc",
     "event_tracker.h",
     "flow_tracker.cc",
@@ -37,16 +41,17 @@
     "slice_translation_table.h",
     "system_info_tracker.cc",
     "system_info_tracker.h",
-    "trace_parser.h",
+    "trace_parser.cc",
     "track_tracker.cc",
     "track_tracker.h",
   ]
   public_deps = [
-    "../:gen_cc_config_descriptor",
+    ":trace_parser_hdr",
     "../../util:proto_to_args_parser",
     "../../util:protozero_to_text",
   ]
   deps = [
+    ":parser_types",
     "../../../../gn:default_deps",
     "../../../../include/perfetto/trace_processor",
     "../../../../include/perfetto/trace_processor:basic_types",
@@ -56,13 +61,32 @@
     "../../../base",
     "../../storage",
     "../../types",
+    "../fuchsia:fuchsia_record",
+    "../systrace:systrace_line",
+  ]
+}
+
+source_set("trace_parser_hdr") {
+  sources = [ "trace_parser.h" ]
+  deps = [ "../../../../gn:default_deps" ]
+}
+
+source_set("parser_types") {
+  sources = [ "parser_types.h" ]
+  deps = [
+    "../../../../gn:default_deps",
+    "../../../../include/perfetto/trace_processor:storage",
+    "../../containers",
+    "../proto:packet_sequence_state_generation_hdr",
   ]
 }
 
 source_set("unittests") {
   sources = [
     "args_translation_table_unittest.cc",
+    "async_track_set_tracker_unittest.cc",
     "clock_tracker_unittest.cc",
+    "deobfuscation_mapping_table_unittest.cc",
     "event_tracker_unittest.cc",
     "flow_tracker_unittest.cc",
     "process_tracker_unittest.cc",
diff --git a/src/trace_processor/importers/common/args_tracker.cc b/src/trace_processor/importers/common/args_tracker.cc
index 5faa718..113c590 100644
--- a/src/trace_processor/importers/common/args_tracker.cc
+++ b/src/trace_processor/importers/common/args_tracker.cc
@@ -106,10 +106,10 @@
 }
 
 bool ArgsTracker::NeedsTranslation(const ArgsTranslationTable& table) const {
-  return std::any_of(args_.begin(), args_.end(),
-                     [&table](const GlobalArgsTracker::Arg& arg) {
-                       return table.NeedsTranslation(arg.key, arg.value.type);
-                     });
+  return std::any_of(
+      args_.begin(), args_.end(), [&table](const GlobalArgsTracker::Arg& arg) {
+        return table.NeedsTranslation(arg.flat_key, arg.key, arg.value.type);
+      });
 }
 
 ArgsTracker::BoundInserter::BoundInserter(ArgsTracker* args_tracker,
diff --git a/src/trace_processor/importers/common/args_tracker.h b/src/trace_processor/importers/common/args_tracker.h
index 43324a1..1c08247 100644
--- a/src/trace_processor/importers/common/args_tracker.h
+++ b/src/trace_processor/importers/common/args_tracker.h
@@ -147,6 +147,11 @@
         id);
   }
 
+  BoundInserter AddArgsTo(tables::ExperimentalProtoPathTable::Id id) {
+    return AddArgsTo(context_->storage->mutable_experimental_proto_path_table(),
+                     id);
+  }
+
   // Returns a CompactArgSet which contains the args inserted into this
   // ArgsTracker. Requires that every arg in this tracker was inserted for the
   // "arg_set_id" column given by |column| at the given |row_number|.
diff --git a/src/trace_processor/importers/common/args_translation_table.cc b/src/trace_processor/importers/common/args_translation_table.cc
index 1228fc8..d1559e0 100644
--- a/src/trace_processor/importers/common/args_translation_table.cc
+++ b/src/trace_processor/importers/common/args_translation_table.cc
@@ -15,7 +15,9 @@
  */
 
 #include "src/trace_processor/importers/common/args_translation_table.h"
+#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_utils.h"
+#include "perfetto/ext/base/string_view.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -23,21 +25,14 @@
 namespace {
 
 // The raw symbol name is namespace::Interface::Method_Sym::IPCStableHash.
-// We want to return Interface::Method.
+// We want to return namespace::Interface::Method.
 std::string ExtractMojoMethod(const std::string& method_symbol) {
   // The symbol ends with "()" for some platforms, but not for all of them.
   std::string without_sym_suffix = base::StripSuffix(method_symbol, "()");
   // This suffix is platform-independent, it's coming from Chromium code.
   // https://source.chromium.org/chromium/chromium/src/+/main:mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl;l=66;drc=9d9e6f5ce548ecf228aed711f55b11c7ea8bdb55
   constexpr char kSymSuffix[] = "_Sym::IPCStableHash";
-  without_sym_suffix = base::StripSuffix(without_sym_suffix, kSymSuffix);
-  auto parts = base::SplitString(without_sym_suffix, "::");
-  // Now we have namespace, Interface, and Method parts. We need only the last
-  // two. If we have too few parts, return all of them to simplify debugging.
-  if (parts.size() > 2) {
-    parts.erase(parts.begin(), parts.end() - 2);
-  }
-  return base::Join(parts, "::");
+  return base::StripSuffix(without_sym_suffix, kSymSuffix);
 }
 
 // The raw symbol name is namespace::Interface::Method_Sym::IPCStableHash.
@@ -56,6 +51,7 @@
 
 }  // namespace
 
+#if !PERFETTO_IS_AT_LEAST_CPP17()
 constexpr char ArgsTranslationTable::kChromeHistogramHashKey[];
 constexpr char ArgsTranslationTable::kChromeHistogramNameKey[];
 
@@ -72,6 +68,7 @@
 constexpr char ArgsTranslationTable::kMojoMethodRelPcKey[];
 constexpr char ArgsTranslationTable::kMojoMethodNameKey[];
 constexpr char ArgsTranslationTable::kMojoIntefaceTagKey[];
+#endif
 
 ArgsTranslationTable::ArgsTranslationTable(TraceStorage* storage)
     : storage_(storage),
@@ -95,12 +92,14 @@
           storage->InternString(kMojoMethodMappingIdKey)),
       interned_mojo_method_rel_pc_(storage->InternString(kMojoMethodRelPcKey)),
       interned_mojo_method_name_(storage->InternString(kMojoMethodNameKey)),
-      interned_mojo_interface_tag_(storage->InternString(kMojoIntefaceTagKey)) {
-}
+      interned_mojo_interface_tag_(storage->InternString(kMojoIntefaceTagKey)),
+      interned_obfuscated_view_dump_class_name_flat_key_(
+          storage->InternString(kObfuscatedViewDumpClassNameFlatKey)) {}
 
-bool ArgsTranslationTable::NeedsTranslation(StringId key_id,
+bool ArgsTranslationTable::NeedsTranslation(StringId flat_key_id,
+                                            StringId key_id,
                                             Variadic::Type type) const {
-  return KeyIdAndTypeToEnum(key_id, type).has_value();
+  return KeyIdAndTypeToEnum(flat_key_id, key_id, type).has_value();
 }
 
 void ArgsTranslationTable::TranslateArgs(
@@ -110,9 +109,10 @@
   base::Optional<uint64_t> rel_pc;
 
   for (const auto& arg : arg_set) {
-    const auto key_type = KeyIdAndTypeToEnum(arg.key, arg.value.type);
+    const auto key_type =
+        KeyIdAndTypeToEnum(arg.flat_key, arg.key, arg.value.type);
     if (!key_type.has_value()) {
-      inserter.AddArg(arg.key, arg.value, arg.update_policy);
+      inserter.AddArg(arg.flat_key, arg.key, arg.value, arg.update_policy);
       continue;
     }
 
@@ -163,6 +163,17 @@
         }
         break;
       }
+      case KeyType::kClassName: {
+        const base::Optional<StringId> translated_class_name =
+            TranslateClassName(arg.value.string_value);
+        if (translated_class_name) {
+          inserter.AddArg(arg.flat_key, arg.key,
+                          Variadic::String(*translated_class_name));
+        } else {
+          inserter.AddArg(arg.flat_key, arg.key, arg.value);
+        }
+        break;
+      }
       case KeyType::kMojoMethodMappingId: {
         mapping_id = arg.value.uint_value;
         break;
@@ -177,28 +188,32 @@
 }
 
 base::Optional<ArgsTranslationTable::KeyType>
-ArgsTranslationTable::KeyIdAndTypeToEnum(StringId key_id,
+ArgsTranslationTable::KeyIdAndTypeToEnum(StringId flat_key_id,
+                                         StringId key_id,
                                          Variadic::Type type) const {
-  if (type != Variadic::Type::kUint) {
-    return base::nullopt;
-  }
-  if (key_id == interned_chrome_histogram_hash_key_) {
-    return KeyType::kChromeHistogramHash;
-  }
-  if (key_id == interned_chrome_user_event_hash_key_) {
-    return KeyType::kChromeUserEventHash;
-  }
-  if (key_id == interned_chrome_performance_mark_mark_hash_key_) {
-    return KeyType::kChromePerformanceMarkMarkHash;
-  }
-  if (key_id == interned_chrome_performance_mark_site_hash_key_) {
-    return KeyType::kChromePerformanceMarkSiteHash;
-  }
-  if (key_id == interned_mojo_method_mapping_id_) {
-    return KeyType::kMojoMethodMappingId;
-  }
-  if (key_id == interned_mojo_method_rel_pc_) {
-    return KeyType::kMojoMethodRelPc;
+  if (type == Variadic::Type::kUint) {
+    if (key_id == interned_chrome_histogram_hash_key_) {
+      return KeyType::kChromeHistogramHash;
+    }
+    if (key_id == interned_chrome_user_event_hash_key_) {
+      return KeyType::kChromeUserEventHash;
+    }
+    if (key_id == interned_chrome_performance_mark_mark_hash_key_) {
+      return KeyType::kChromePerformanceMarkMarkHash;
+    }
+    if (key_id == interned_chrome_performance_mark_site_hash_key_) {
+      return KeyType::kChromePerformanceMarkSiteHash;
+    }
+    if (key_id == interned_mojo_method_mapping_id_) {
+      return KeyType::kMojoMethodMappingId;
+    }
+    if (key_id == interned_mojo_method_rel_pc_) {
+      return KeyType::kMojoMethodRelPc;
+    }
+  } else if (type == Variadic::Type::kString) {
+    if (flat_key_id == interned_obfuscated_view_dump_class_name_flat_key_) {
+      return KeyType::kClassName;
+    }
   }
   return base::nullopt;
 }
@@ -252,6 +267,11 @@
   return *loc;
 }
 
+base::Optional<StringId> ArgsTranslationTable::TranslateClassName(
+    StringId obfuscated_class_name_id) const {
+  return deobfuscation_mapping_table_.TranslateClass(obfuscated_class_name_id);
+}
+
 void ArgsTranslationTable::EmitMojoMethodLocation(
     base::Optional<uint64_t> mapping_id,
     base::Optional<uint64_t> rel_pc,
diff --git a/src/trace_processor/importers/common/args_translation_table.h b/src/trace_processor/importers/common/args_translation_table.h
index 2ef46af..e1b3178 100644
--- a/src/trace_processor/importers/common/args_translation_table.h
+++ b/src/trace_processor/importers/common/args_translation_table.h
@@ -23,6 +23,7 @@
 #include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_view.h"
 #include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/deobfuscation_mapping_table.h"
 #include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/util/proto_to_args_parser.h"
 
@@ -53,7 +54,9 @@
   explicit ArgsTranslationTable(TraceStorage* storage);
 
   // Returns true if an arg with the given key and type requires translation.
-  bool NeedsTranslation(StringId key_id, Variadic::Type type) const;
+  bool NeedsTranslation(StringId flat_key_id,
+                        StringId key_id,
+                        Variadic::Type type) const;
 
   void TranslateArgs(const ArgsTracker::CompactArgSet& arg_set,
                      ArgsTracker::BoundInserter& inserter) const;
@@ -78,6 +81,10 @@
                                       const SourceLocation& loc) {
     native_symbol_to_location_.Insert(std::make_pair(mapping_id, rel_pc), loc);
   }
+  void AddDeobfuscationMappingTable(
+      DeobfuscationMappingTable deobfuscation_mapping_table) {
+    deobfuscation_mapping_table_ = std::move(deobfuscation_mapping_table);
+  }
 
   base::Optional<base::StringView> TranslateChromeHistogramHashForTesting(
       uint64_t hash) const {
@@ -95,6 +102,10 @@
   TranslateChromePerformanceMarkMarkHashForTesting(uint64_t hash) const {
     return TranslateChromePerformanceMarkMarkHash(hash);
   }
+  base::Optional<StringId> TranslateClassNameForTesting(
+      StringId obfuscated_class_name_id) const {
+    return TranslateClassName(obfuscated_class_name_id);
+  }
 
  private:
   enum class KeyType {
@@ -104,6 +115,7 @@
     kChromePerformanceMarkSiteHash = 3,
     kMojoMethodMappingId = 4,
     kMojoMethodRelPc = 5,
+    kClassName = 6,
   };
 
   static constexpr char kChromeHistogramHashKey[] =
@@ -135,6 +147,9 @@
   static constexpr char kMojoIntefaceTagKey[] =
       "chrome_mojo_event_info.mojo_interface_tag";
 
+  static constexpr char kObfuscatedViewDumpClassNameFlatKey[] =
+      "android_view_dump.activity.view.class_name";
+
   TraceStorage* storage_;
   StringId interned_chrome_histogram_hash_key_;
   StringId interned_chrome_histogram_name_key_;
@@ -150,6 +165,10 @@
   StringId interned_mojo_method_name_;
   StringId interned_mojo_interface_tag_;
 
+  // A "flat_key" of an argument from the "args" table that has to be
+  // deobfuscated. A Java class name must be contained in this argument.
+  StringId interned_obfuscated_view_dump_class_name_flat_key_;
+
   base::FlatHashMap<uint64_t, std::string> chrome_histogram_hash_to_name_;
   base::FlatHashMap<uint64_t, std::string> chrome_user_event_hash_to_action_;
   base::FlatHashMap<uint64_t, std::string>
@@ -157,10 +176,13 @@
   base::FlatHashMap<uint64_t, std::string>
       chrome_performance_mark_mark_hash_to_name_;
   base::FlatHashMap<NativeSymbolKey, SourceLocation> native_symbol_to_location_;
+  // A translation mapping for obfuscated Java class names and its members.
+  DeobfuscationMappingTable deobfuscation_mapping_table_;
 
   // Returns the corresponding SupportedKey enum if the table knows how to
   // translate the argument with the given key and type, and nullopt otherwise.
-  base::Optional<KeyType> KeyIdAndTypeToEnum(StringId key_id,
+  base::Optional<KeyType> KeyIdAndTypeToEnum(StringId flat_key_id,
+                                             StringId key_id,
                                              Variadic::Type type) const;
 
   base::Optional<base::StringView> TranslateChromeHistogramHash(
@@ -174,6 +196,11 @@
   base::Optional<SourceLocation> TranslateNativeSymbol(MappingId mapping_id,
                                                        uint64_t rel_pc) const;
 
+  // Returns the deobfuscated name of a Java class or base::nullopt if
+  // translation is not found.
+  base::Optional<StringId> TranslateClassName(
+      StringId obfuscated_class_name_id) const;
+
   void EmitMojoMethodLocation(base::Optional<uint64_t> mapping_id,
                               base::Optional<uint64_t> rel_pc,
                               ArgsTracker::BoundInserter& inserter) const;
diff --git a/src/trace_processor/importers/common/args_translation_table_unittest.cc b/src/trace_processor/importers/common/args_translation_table_unittest.cc
index 55cb8f3..904e607 100644
--- a/src/trace_processor/importers/common/args_translation_table_unittest.cc
+++ b/src/trace_processor/importers/common/args_translation_table_unittest.cc
@@ -15,6 +15,8 @@
  */
 
 #include "src/trace_processor/importers/common/args_translation_table.h"
+#include "perfetto/ext/base/optional.h"
+#include "src/trace_processor/importers/common/deobfuscation_mapping_table.h"
 #include "test/gtest_and_gmock.h"
 
 namespace perfetto {
@@ -78,31 +80,83 @@
             base::nullopt);
 }
 
+TEST(ArgsTranslationTable, TranslateClassName) {
+  TraceStorage storage;
+  StringId xyz_id = storage.InternString("xyz");
+  StringId abc_id = storage.InternString("abc");
+  StringId class_x_id = storage.InternString("class_X");
+  DeobfuscationMappingTable deobfuscation_mapping;
+  deobfuscation_mapping.AddClassTranslation(
+      DeobfuscationMappingTable::PackageId{"app", 123}, xyz_id, class_x_id,
+      base::FlatHashMap<StringId, StringId>{});
+  ArgsTranslationTable table(&storage);
+  table.AddDeobfuscationMappingTable(std::move(deobfuscation_mapping));
+
+  EXPECT_EQ(table.TranslateClassNameForTesting(xyz_id),
+            base::Optional<StringId>(class_x_id));
+  EXPECT_EQ(table.TranslateClassNameForTesting(abc_id), base::nullopt);
+}
+
 TEST(ArgsTranslationTable, NeedsTranslation) {
   TraceStorage storage;
   ArgsTranslationTable table(&storage);
 
   EXPECT_TRUE(table.NeedsTranslation(
+      storage.InternString("unused_flat_key"),
+      storage.InternString("chrome_histogram_sample.name_hash"),
+      Variadic::Type::kUint));
+  EXPECT_TRUE(table.NeedsTranslation(
+      storage.InternString("unused_flat_key"),
+      storage.InternString("chrome_user_event.action_hash"),
+      Variadic::Type::kUint));
+  EXPECT_TRUE(table.NeedsTranslation(
+      storage.InternString("unused_flat_key"),
+      storage.InternString("chrome_hashed_performance_mark.site_hash"),
+      Variadic::Type::kUint));
+  EXPECT_TRUE(table.NeedsTranslation(
+      storage.InternString("unused_flat_key"),
+      storage.InternString("chrome_hashed_performance_mark.mark_hash"),
+      Variadic::Type::kUint));
+
+  // A real life case, where flat_key == key.
+  EXPECT_TRUE(table.NeedsTranslation(
+      storage.InternString("chrome_histogram_sample.name_hash"),
       storage.InternString("chrome_histogram_sample.name_hash"),
       Variadic::Type::kUint));
   EXPECT_TRUE(table.NeedsTranslation(
       storage.InternString("chrome_user_event.action_hash"),
+      storage.InternString("chrome_user_event.action_hash"),
       Variadic::Type::kUint));
   EXPECT_TRUE(table.NeedsTranslation(
       storage.InternString("chrome_hashed_performance_mark.site_hash"),
+      storage.InternString("chrome_hashed_performance_mark.site_hash"),
       Variadic::Type::kUint));
   EXPECT_TRUE(table.NeedsTranslation(
       storage.InternString("chrome_hashed_performance_mark.mark_hash"),
+      storage.InternString("chrome_hashed_performance_mark.mark_hash"),
       Variadic::Type::kUint));
 
   // The key needs translation, but the arg type is wrong (not uint).
   EXPECT_FALSE(table.NeedsTranslation(
+      storage.InternString("unused_flat_key"),
       storage.InternString("chrome_histogram_sample.name_hash"),
       Variadic::Type::kInt));
   // The key does not require translation.
   EXPECT_FALSE(table.NeedsTranslation(
+      storage.InternString("unused_flat_key"),
       storage.InternString("chrome_histogram_sample.name"),
       Variadic::Type::kUint));
+
+  // The key needs translation by flat_key.
+  EXPECT_TRUE(table.NeedsTranslation(
+      storage.InternString("android_view_dump.activity.view.class_name"),
+      storage.InternString("android_view_dump.activity[0].view[0].class_name"),
+      Variadic::Type::kString));
+  // The key does not require translation because flat_key and key are swapped.
+  EXPECT_FALSE(table.NeedsTranslation(
+      storage.InternString("android_view_dump.activity[0].view[0].class_name"),
+      storage.InternString("android_view_dump.activity.view.class_name"),
+      Variadic::Type::kString));
 }
 
 }  // namespace
diff --git a/src/trace_processor/importers/common/async_track_set_tracker.cc b/src/trace_processor/importers/common/async_track_set_tracker.cc
new file mode 100644
index 0000000..432bd39
--- /dev/null
+++ b/src/trace_processor/importers/common/async_track_set_tracker.cc
@@ -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.
+ */
+
+#include "src/trace_processor/importers/common/async_track_set_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 {
+
+AsyncTrackSetTracker::AsyncTrackSetTracker(TraceProcessorContext* context)
+    : android_source_(context->storage->InternString("android")),
+      context_(context) {}
+
+AsyncTrackSetTracker::TrackSetId AsyncTrackSetTracker::InternGlobalTrackSet(
+    StringId name) {
+  auto it = global_track_set_ids_.find(name);
+  if (it != global_track_set_ids_.end()) {
+    return it->second;
+  }
+
+  uint32_t id = static_cast<uint32_t>(track_sets_.size());
+  TrackSet set;
+  set.global_track_name = name;
+  set.scope = TrackSetScope::kGlobal;
+  set.nesting_behaviour = NestingBehaviour::kNestable;
+  track_sets_.emplace_back(set);
+
+  return global_track_set_ids_[name] = id;
+}
+
+AsyncTrackSetTracker::TrackSetId AsyncTrackSetTracker::InternProcessTrackSet(
+    UniquePid upid,
+    StringId name) {
+  ProcessTuple tuple{upid, name};
+
+  auto it = process_track_set_ids_.find(tuple);
+  if (it != process_track_set_ids_.end())
+    return it->second;
+
+  uint32_t id = static_cast<uint32_t>(track_sets_.size());
+
+  TrackSet set;
+  set.process_tuple = tuple;
+  set.scope = TrackSetScope::kProcess;
+  set.nesting_behaviour = NestingBehaviour::kNestable;
+  track_sets_.emplace_back(set);
+
+  process_track_set_ids_[tuple] = id;
+  return id;
+}
+
+AsyncTrackSetTracker::TrackSetId
+AsyncTrackSetTracker::InternAndroidLegacyUnnestableTrackSet(UniquePid upid,
+                                                            StringId name) {
+  ProcessTuple tuple{upid, name};
+
+  auto it = android_legacy_unnestable_track_set_ids_.find(tuple);
+  if (it != android_legacy_unnestable_track_set_ids_.end())
+    return it->second;
+
+  uint32_t id = static_cast<uint32_t>(track_sets_.size());
+
+  TrackSet set;
+  set.process_tuple = tuple;
+  set.scope = TrackSetScope::kProcess;
+  set.nesting_behaviour = NestingBehaviour::kLegacySaturatingUnnestable;
+  track_sets_.emplace_back(set);
+
+  android_legacy_unnestable_track_set_ids_[tuple] = id;
+  return id;
+}
+
+TrackId AsyncTrackSetTracker::Begin(TrackSetId id, int64_t cookie) {
+  PERFETTO_DCHECK(id < track_sets_.size());
+
+  TrackSet& set = track_sets_[id];
+  TrackState& state = GetOrCreateTrackForCookie(set, cookie);
+  switch (set.nesting_behaviour) {
+    case NestingBehaviour::kNestable:
+      state.nest_count++;
+      break;
+    case NestingBehaviour::kLegacySaturatingUnnestable:
+      PERFETTO_DCHECK(state.nest_count <= 1);
+      state.nest_count = 1;
+      break;
+  }
+  return state.id;
+}
+
+TrackId AsyncTrackSetTracker::End(TrackSetId id, int64_t cookie) {
+  PERFETTO_DCHECK(id < track_sets_.size());
+
+  TrackSet& set = track_sets_[id];
+  TrackState& state = GetOrCreateTrackForCookie(set, cookie);
+
+  // It's possible to have a nest count of 0 even when we know about the track.
+  // Suppose the following sequence of events for some |id| and |cookie|:
+  //   Begin
+  //   (trace starts)
+  //   Begin
+  //   End
+  //   End <- nest count == 0 here even though we have a record of this track.
+  if (state.nest_count > 0)
+    state.nest_count--;
+  return state.id;
+}
+
+TrackId AsyncTrackSetTracker::Scoped(TrackSetId id, int64_t ts, int64_t dur) {
+  PERFETTO_DCHECK(id < track_sets_.size());
+
+  TrackSet& set = track_sets_[id];
+  PERFETTO_DCHECK(set.nesting_behaviour !=
+                  NestingBehaviour::kLegacySaturatingUnnestable);
+
+  auto it = std::find_if(
+      set.tracks.begin(), set.tracks.end(), [ts](const TrackState& state) {
+        return state.slice_type == TrackState::SliceType::kTimestamp &&
+               state.ts_end <= ts;
+      });
+  if (it != set.tracks.end()) {
+    it->ts_end = ts + dur;
+    return it->id;
+  }
+
+  TrackState state;
+  state.slice_type = TrackState::SliceType::kTimestamp;
+  state.ts_end = ts + dur;
+  state.id = CreateTrackForSet(set);
+  set.tracks.emplace_back(state);
+
+  return state.id;
+}
+
+AsyncTrackSetTracker::TrackState&
+AsyncTrackSetTracker::GetOrCreateTrackForCookie(TrackSet& set, int64_t cookie) {
+  auto it = std::find_if(
+      set.tracks.begin(), set.tracks.end(), [cookie](const TrackState& state) {
+        return state.slice_type == TrackState::SliceType::kCookie &&
+               state.cookie == cookie;
+      });
+  if (it != set.tracks.end())
+    return *it;
+
+  it = std::find_if(
+      set.tracks.begin(), set.tracks.end(), [](const TrackState& state) {
+        return state.slice_type == TrackState::SliceType::kCookie &&
+               state.nest_count == 0;
+      });
+  if (it != set.tracks.end()) {
+    // Adopt this track for the cookie to make sure future slices with this
+    // cookie also get associated to this track.
+    it->cookie = cookie;
+    return *it;
+  }
+
+  TrackState state;
+  state.id = CreateTrackForSet(set);
+  state.slice_type = TrackState::SliceType::kCookie;
+  state.cookie = cookie;
+  state.nest_count = 0;
+  set.tracks.emplace_back(state);
+
+  return set.tracks.back();
+}
+
+TrackId AsyncTrackSetTracker::CreateTrackForSet(const TrackSet& set) {
+  switch (set.scope) {
+    case TrackSetScope::kGlobal:
+      // TODO(lalitm): propogate source from callers rather than just passing
+      // kNullStringId here.
+      return context_->track_tracker->CreateGlobalAsyncTrack(
+          set.global_track_name, kNullStringId);
+    case TrackSetScope::kProcess:
+      // TODO(lalitm): propogate source from callers rather than just passing
+      // kNullStringId here.
+      StringId source =
+          set.nesting_behaviour == NestingBehaviour::kLegacySaturatingUnnestable
+              ? android_source_
+              : kNullStringId;
+      return context_->track_tracker->CreateProcessAsyncTrack(
+          set.process_tuple.name, set.process_tuple.upid, source);
+  }
+  PERFETTO_FATAL("For GCC");
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/common/async_track_set_tracker.h b/src/trace_processor/importers/common/async_track_set_tracker.h
new file mode 100644
index 0000000..dc9093c
--- /dev/null
+++ b/src/trace_processor/importers/common/async_track_set_tracker.h
@@ -0,0 +1,175 @@
+/*
+ * 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_COMMON_ASYNC_TRACK_SET_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_ASYNC_TRACK_SET_TRACKER_H_
+
+#include "src/trace_processor/storage/trace_storage.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class TraceProcessorContext;
+class AsyncTrackSetTrackerUnittest;
+
+// Tracker used to reduce the number of trace processor tracks corresponding
+// to a single "UI track".
+//
+// UIs using trace processor want to display all slices in the same context
+// (e.g. same upid) and same name into a single track. However, because trace
+// processor does not allow parallel slices on a single track (because it breaks
+// things like span join, self time computation etc.), at the trace processor
+// level these parallel slices are put on different tracks.
+//
+// Creating a new track for every event, however, leads to an explosion of
+// tracks which is undesirable. This class exists to multiplex slices so that
+// n events correspond to a single track in a way which minimises the number of
+// tracks which needs to be merged by the UI.
+//
+// The intended usage of this class is for callers to first call one of the
+// Intern* methods to obtain a TrackSetId followed by Begin/End just before
+// calling into SliceTracker's Begin/End respectively. For example:
+//  TrackSetId set_id = track_set_tracker->InternProcessTrackSet(upid, name);
+//  if (event.begin) {
+//    TrackId id = track_set_tracker->Begin(set_id, cookie);
+//    slice_tracker->Begin(ts, id, ...)
+//  } else {
+//    ... (same thing with end)
+//  }
+// Alternatively, instead of Begin/End, Scoped can also be called if supported
+// by the track type.
+class AsyncTrackSetTracker {
+ public:
+  using TrackSetId = uint32_t;
+
+  explicit AsyncTrackSetTracker(TraceProcessorContext* context);
+  ~AsyncTrackSetTracker() = default;
+
+  // Interns a set of global async slice tracks associated with the given name.
+  TrackSetId InternGlobalTrackSet(StringId name);
+
+  // Interns a set of process async slice tracks associated with the given name
+  // and upid.
+  TrackSetId InternProcessTrackSet(UniquePid, StringId name);
+
+  // Interns a set of Android legacy unnesteable async slice tracks
+  // associated with the given upid and name.
+  // Scoped is *not* supported for this track set type.
+  TrackSetId InternAndroidLegacyUnnestableTrackSet(UniquePid, StringId name);
+
+  // Starts a new slice on the given async track set which has the given cookie.
+  TrackId Begin(TrackSetId id, int64_t cookie);
+
+  // Ends a new slice on the given async track set which has the given cookie.
+  TrackId End(TrackSetId id, int64_t cookie);
+
+  // Creates a scoped slice on the given async track set.
+  // This method makes sure that any other slice in this track set does
+  // not happen simultaneously on the returned track.
+  // Only supported on selected track set types; read the documentation for
+  // the Intern* method for your track type to check if supported.
+  TrackId Scoped(TrackSetId id, int64_t ts, int64_t dur);
+
+ private:
+  friend class AsyncTrackSetTrackerUnittest;
+
+  struct ProcessTuple {
+    UniquePid upid;
+    StringId name;
+
+    friend bool operator<(const ProcessTuple& l, const ProcessTuple& r) {
+      return std::tie(l.upid, l.name) < std::tie(r.upid, r.name);
+    }
+  };
+
+  // Indicates the nesting behaviour of slices associated to a single slice
+  // stack.
+  enum class NestingBehaviour {
+    // Indicates that slices are nestable; that is, a stack of slices with
+    // the same cookie should stack correctly (but are not allowed to overlap).
+    // This pattern should be the default behaviour that most async slices
+    // should use.
+    kNestable,
+
+    // Indicates that slices are unnestable but also saturating; that is
+    // calling Begin -> Begin only causes a single Begin to be recorded.
+    // This is only really useful for Android async slices which have this
+    // behaviour for legacy reasons. See the comment in
+    // SystraceParser::ParseSystracePoint for information on why
+    // this behaviour exists.
+    kLegacySaturatingUnnestable,
+  };
+
+  enum class TrackSetScope {
+    kGlobal,
+    kProcess,
+  };
+
+  struct TrackState {
+    TrackId id;
+
+    enum class SliceType { kCookie, kTimestamp };
+    SliceType slice_type;
+
+    union {
+      // Only valid for |slice_type| == |SliceType::kCookie|.
+      int64_t cookie;
+
+      // Only valid for |slice_type| == |SliceType::kTimestamp|.
+      int64_t ts_end;
+    };
+
+    // Only used for |slice_type| == |SliceType::kCookie|.
+    uint32_t nest_count;
+  };
+
+  struct TrackSet {
+    TrackSetScope scope;
+    union {
+      // Only set when |scope| == |TrackSetScope::kGlobal|.
+      StringId global_track_name;
+      // Only set when |scope| == |TrackSetScope::kFrameTimeline| or
+      // |TrackSetScope::kAndroidLegacyUnnestable|.
+      ProcessTuple process_tuple;
+    };
+    NestingBehaviour nesting_behaviour;
+    std::vector<TrackState> tracks;
+  };
+
+  // Returns the state for a track using the following algorithm:
+  // 1. If a track exists with the given cookie in the track set, returns
+  //    that track.
+  // 2. Otherwise, looks for any track in the set which is "open" (i.e.
+  //    does not have another slice currently scheduled).
+  // 3. Otherwise, creates a new track and associates it with the set.
+  TrackState& GetOrCreateTrackForCookie(TrackSet& set, int64_t cookie);
+
+  TrackId CreateTrackForSet(const TrackSet& set);
+
+  std::map<StringId, TrackSetId> global_track_set_ids_;
+  std::map<ProcessTuple, TrackSetId> process_track_set_ids_;
+  std::map<ProcessTuple, TrackSetId> android_legacy_unnestable_track_set_ids_;
+  std::vector<TrackSet> track_sets_;
+
+  const StringId android_source_ = kNullStringId;
+
+  TraceProcessorContext* const context_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_ASYNC_TRACK_SET_TRACKER_H_
diff --git a/src/trace_processor/importers/common/async_track_set_tracker_unittest.cc b/src/trace_processor/importers/common/async_track_set_tracker_unittest.cc
new file mode 100644
index 0000000..b10aec8
--- /dev/null
+++ b/src/trace_processor/importers/common/async_track_set_tracker_unittest.cc
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/importers/common/async_track_set_tracker.h"
+
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/global_args_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class AsyncTrackSetTrackerUnittest : public testing::Test {
+ public:
+  AsyncTrackSetTrackerUnittest() {
+    context_.storage.reset(new TraceStorage());
+    context_.global_args_tracker.reset(
+        new GlobalArgsTracker(context_.storage.get()));
+    context_.args_tracker.reset(new ArgsTracker(&context_));
+    context_.track_tracker.reset(new TrackTracker(&context_));
+    context_.async_track_set_tracker.reset(new AsyncTrackSetTracker(&context_));
+
+    storage_ = context_.storage.get();
+    tracker_ = context_.async_track_set_tracker.get();
+
+    nestable_id_ =
+        tracker_->InternProcessTrackSet(1, storage_->InternString("test"));
+    legacy_unnestable_id_ = tracker_->InternAndroidLegacyUnnestableTrackSet(
+        2, storage_->InternString("test"));
+  }
+
+ protected:
+  TraceStorage* storage_ = nullptr;
+  AsyncTrackSetTracker* tracker_ = nullptr;
+
+  AsyncTrackSetTracker::TrackSetId nestable_id_;
+  AsyncTrackSetTracker::TrackSetId legacy_unnestable_id_;
+
+ private:
+  TraceProcessorContext context_;
+};
+
+namespace {
+
+TEST_F(AsyncTrackSetTrackerUnittest, Smoke) {
+  auto set_id = tracker_->InternAndroidLegacyUnnestableTrackSet(
+      1, storage_->InternString("test"));
+
+  auto begin = tracker_->Begin(set_id, 1);
+  auto end = tracker_->End(set_id, 1);
+
+  ASSERT_EQ(begin, end);
+
+  uint32_t row = *storage_->process_track_table().id().IndexOf(begin);
+  ASSERT_EQ(storage_->process_track_table().upid()[row], 1u);
+  ASSERT_EQ(storage_->process_track_table().name()[row],
+            storage_->InternString("test"));
+}
+
+TEST_F(AsyncTrackSetTrackerUnittest, EndFirst) {
+  auto end = tracker_->End(nestable_id_, 1);
+
+  uint32_t row = *storage_->process_track_table().id().IndexOf(end);
+  ASSERT_EQ(storage_->process_track_table().upid()[row], 1u);
+  ASSERT_EQ(storage_->process_track_table().name()[row],
+            storage_->InternString("test"));
+}
+
+TEST_F(AsyncTrackSetTrackerUnittest, LegacySaturating) {
+  auto begin = tracker_->Begin(legacy_unnestable_id_, 1);
+  auto begin_2 = tracker_->Begin(legacy_unnestable_id_, 1);
+
+  ASSERT_EQ(begin, begin_2);
+}
+
+TEST_F(AsyncTrackSetTrackerUnittest, DoubleBegin) {
+  auto begin = tracker_->Begin(nestable_id_, 1);
+  auto end = tracker_->End(nestable_id_, 1);
+  auto begin_2 = tracker_->Begin(nestable_id_, 1);
+
+  ASSERT_EQ(begin, end);
+  ASSERT_EQ(begin, begin_2);
+}
+
+TEST_F(AsyncTrackSetTrackerUnittest, Nesting) {
+  auto begin = tracker_->Begin(nestable_id_, 1);
+  auto begin_nested = tracker_->Begin(nestable_id_, 1);
+  auto begin_other = tracker_->Begin(nestable_id_, 2);
+  auto end_nested = tracker_->End(nestable_id_, 1);
+  auto end = tracker_->End(nestable_id_, 1);
+  auto end_other = tracker_->Begin(nestable_id_, 2);
+
+  ASSERT_EQ(begin, begin_nested);
+  ASSERT_NE(begin, begin_other);
+  ASSERT_EQ(begin_nested, end_nested);
+  ASSERT_EQ(begin, end);
+  ASSERT_EQ(begin_other, end_other);
+}
+
+TEST_F(AsyncTrackSetTrackerUnittest, NestableMultipleEndAfterBegin) {
+  auto begin = tracker_->Begin(nestable_id_, 1);
+  auto end = tracker_->End(nestable_id_, 1);
+  auto end_2 = tracker_->End(nestable_id_, 1);
+
+  ASSERT_EQ(begin, end);
+  ASSERT_EQ(end, end_2);
+}
+
+TEST_F(AsyncTrackSetTrackerUnittest, OnlyScoped) {
+  TrackId a = tracker_->Scoped(nestable_id_, 100, 10);
+  TrackId b = tracker_->Scoped(nestable_id_, 105, 2);
+  TrackId c = tracker_->Scoped(nestable_id_, 107, 3);
+  TrackId d = tracker_->Scoped(nestable_id_, 110, 5);
+
+  ASSERT_NE(a, b);
+  ASSERT_EQ(b, c);
+  ASSERT_EQ(a, d);
+}
+
+TEST_F(AsyncTrackSetTrackerUnittest, MixScopedAndBeginEnd) {
+  TrackId a = tracker_->Scoped(nestable_id_, 100, 10);
+
+  TrackId begin = tracker_->Begin(nestable_id_, 777);
+  TrackId end = tracker_->End(nestable_id_, 777);
+
+  TrackId b = tracker_->Scoped(nestable_id_, 105, 2);
+
+  ASSERT_NE(a, begin);
+  ASSERT_NE(b, begin);
+  ASSERT_EQ(begin, end);
+}
+
+TEST_F(AsyncTrackSetTrackerUnittest, DifferentTracksInterleave) {
+  TrackId b1 = tracker_->Begin(nestable_id_, 666);
+  TrackId b2 = tracker_->Begin(legacy_unnestable_id_, 777);
+  TrackId e1 = tracker_->End(nestable_id_, 666);
+  TrackId e2 = tracker_->End(legacy_unnestable_id_, 777);
+
+  ASSERT_EQ(b1, e1);
+  ASSERT_EQ(b2, e2);
+  ASSERT_NE(b1, b2);
+}
+
+TEST_F(AsyncTrackSetTrackerUnittest, DifferentCookieInterleave) {
+  TrackId b1 = tracker_->Begin(legacy_unnestable_id_, 666);
+  TrackId b2 = tracker_->Begin(legacy_unnestable_id_, 777);
+  TrackId e1 = tracker_->End(legacy_unnestable_id_, 666);
+  TrackId e2 = tracker_->End(legacy_unnestable_id_, 777);
+
+  ASSERT_EQ(b1, e1);
+  ASSERT_EQ(b2, e2);
+  ASSERT_NE(b1, b2);
+}
+
+TEST_F(AsyncTrackSetTrackerUnittest, DifferentCookieSequential) {
+  TrackId b1 = tracker_->Begin(legacy_unnestable_id_, 666);
+  TrackId e1 = tracker_->End(legacy_unnestable_id_, 666);
+  TrackId b2 = tracker_->Begin(legacy_unnestable_id_, 777);
+  TrackId e2 = tracker_->End(legacy_unnestable_id_, 777);
+
+  ASSERT_EQ(b1, e1);
+  ASSERT_EQ(b1, b2);
+  ASSERT_EQ(b2, e2);
+}
+
+}  // namespace
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/common/clock_tracker.cc b/src/trace_processor/importers/common/clock_tracker.cc
index bb0158b..f68811a 100644
--- a/src/trace_processor/importers/common/clock_tracker.cc
+++ b/src/trace_processor/importers/common/clock_tracker.cc
@@ -50,7 +50,7 @@
 
   // Compute the fingerprint of the snapshot by hashing all clock ids. This is
   // used by the clock pathfinding logic.
-  base::Hash hasher;
+  base::Hasher hasher;
   for (const auto& clock : clocks)
     hasher.Update(clock.clock_id);
   const auto snapshot_hash = static_cast<SnapshotHash>(hasher.digest());
diff --git a/src/trace_processor/importers/common/deobfuscation_mapping_table.cc b/src/trace_processor/importers/common/deobfuscation_mapping_table.cc
new file mode 100644
index 0000000..af5bbf3
--- /dev/null
+++ b/src/trace_processor/importers/common/deobfuscation_mapping_table.cc
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2022 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/deobfuscation_mapping_table.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "perfetto/ext/base/string_view.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+bool DeobfuscationMappingTable::AddClassTranslation(
+    const PackageId& package,
+    StringId obfuscated_class_name,
+    StringId deobfuscated_class_name,
+    base::FlatHashMap<StringId, StringId> obfuscated_to_deobfuscated_members) {
+  if (PERFETTO_UNLIKELY(!default_package_id_)) {
+    default_package_id_ = package;
+  }
+
+  ObfuscatedClassesToMembers& classes_to_members =
+      class_per_package_translation_[package];
+  return classes_to_members
+      .Insert(obfuscated_class_name,
+              ClassTranslation{deobfuscated_class_name,
+                               std::move(obfuscated_to_deobfuscated_members)})
+      .second;
+}
+
+base::Optional<StringId> DeobfuscationMappingTable::TranslateClass(
+    StringId obfuscated_class_name) const {
+  if (PERFETTO_UNLIKELY(!default_package_id_.has_value())) {
+    return base::nullopt;
+  }
+  return TranslateClass(default_package_id_.value(), obfuscated_class_name);
+}
+
+base::Optional<StringId> DeobfuscationMappingTable::TranslateClass(
+    const PackageId& package,
+    StringId obfuscated_class_name) const {
+  const ObfuscatedClassesToMembers* classes_translation_ptr =
+      class_per_package_translation_.Find(package);
+  if (classes_translation_ptr == nullptr) {
+    return base::nullopt;
+  }
+  const ClassTranslation* class_translation_ptr =
+      classes_translation_ptr->Find(obfuscated_class_name);
+  if (class_translation_ptr == nullptr) {
+    return base::nullopt;
+  }
+  return class_translation_ptr->deobfuscated_class_name;
+}
+
+base::Optional<StringId> DeobfuscationMappingTable::TranslateMember(
+    const PackageId& package,
+    StringId obfuscated_class_name,
+    StringId obfuscated_member) const {
+  const ObfuscatedClassesToMembers* classes_translation_ptr =
+      class_per_package_translation_.Find(package);
+  if (classes_translation_ptr == nullptr) {
+    return base::nullopt;
+  }
+
+  const ClassTranslation* class_translation_ptr =
+      classes_translation_ptr->Find(obfuscated_class_name);
+  if (class_translation_ptr == nullptr) {
+    return base::nullopt;
+  }
+
+  const StringId* member_translation_ptr =
+      class_translation_ptr->members.Find(obfuscated_member);
+  if (member_translation_ptr == nullptr) {
+    return base::nullopt;
+  }
+  return *member_translation_ptr;
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/common/deobfuscation_mapping_table.h b/src/trace_processor/importers/common/deobfuscation_mapping_table.h
new file mode 100644
index 0000000..7022b19
--- /dev/null
+++ b/src/trace_processor/importers/common/deobfuscation_mapping_table.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2022 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_COMMON_DEOBFUSCATION_MAPPING_TABLE_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_DEOBFUSCATION_MAPPING_TABLE_H_
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "perfetto/ext/base/hash.h"
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/string_view.h"
+#include "src/trace_processor/storage/trace_storage.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Constains deobfuscation for Java class names and its members per |PackageId|.
+class DeobfuscationMappingTable {
+ public:
+  struct PackageId {
+    std::string package_name;
+    int64_t version_code;
+
+    bool operator==(const PackageId& other) const {
+      return package_name == other.package_name &&
+             version_code == other.version_code;
+    }
+  };
+
+  // For the given |package| and |obfuscated_class_name| adds translations of
+  // the class and its members.
+  //
+  // Returns `true` if the translation for the given class
+  // was inserted, `false` if there is already a translation for the given
+  // class.
+  bool AddClassTranslation(
+      const PackageId& package,
+      StringId obfuscated_class_name,
+      StringId deobfuscated_class_name,
+      base::FlatHashMap<StringId, StringId> obfuscated_to_deobfuscated_members);
+
+  // These functions return the deobfuscated class/member name from an
+  // obfuscated class/member name.
+  // If a package is not provided, the |default_package_id_| is used.
+  // If translation is not found, returns base::nullopt.
+
+  base::Optional<StringId> TranslateClass(StringId obfuscated_class_name) const;
+
+  base::Optional<StringId> TranslateClass(const PackageId& package,
+                                          StringId obfuscated_class_name) const;
+
+  base::Optional<StringId> TranslateMember(const PackageId& package,
+                                           StringId obfuscated_class_name,
+                                           StringId obfuscated_member) const;
+
+ private:
+  struct PackageIdHash {
+    std::size_t operator()(PackageId const& p) const noexcept {
+      return static_cast<std::size_t>(
+          base::Hasher::Combine(p.package_name, p.version_code));
+    }
+  };
+
+  using ObfuscatedToDeobfuscatedMembers = base::FlatHashMap<StringId, StringId>;
+  struct ClassTranslation {
+    StringId deobfuscated_class_name;
+    ObfuscatedToDeobfuscatedMembers members;
+  };
+
+  using ObfuscatedClassesToMembers =
+      base::FlatHashMap<StringId, ClassTranslation>;
+  base::FlatHashMap<PackageId, ObfuscatedClassesToMembers, PackageIdHash>
+      class_per_package_translation_;
+
+  // To translate entities which don't have a package id, we will use
+  // |default_package_id_|. |default_package_id_| is a package id of the first
+  // inserted entity with a package id;
+  // We need this because curently TraceProcessor doesn't use the package
+  // version of the arguments.
+  // TODO(b/244700870): start use the package version of arguments.
+  base::Optional<PackageId> default_package_id_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_DEOBFUSCATION_MAPPING_TABLE_H_
diff --git a/src/trace_processor/importers/common/deobfuscation_mapping_table_unittest.cc b/src/trace_processor/importers/common/deobfuscation_mapping_table_unittest.cc
new file mode 100644
index 0000000..5b24492
--- /dev/null
+++ b/src/trace_processor/importers/common/deobfuscation_mapping_table_unittest.cc
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2022 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/deobfuscation_mapping_table.h"
+#include <string>
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+using PackageId = DeobfuscationMappingTable::PackageId;
+
+TEST(DeobfuscationMappingTable, EmptyTableByDefault) {
+  TraceStorage storage;
+  StringId xyz_id = storage.InternString("xyz");
+
+  DeobfuscationMappingTable table;
+  EXPECT_EQ(table.TranslateClass(xyz_id), base::nullopt);
+  EXPECT_EQ(table.TranslateClass(PackageId{"app", 123}, xyz_id), base::nullopt);
+}
+
+TEST(DeobfuscationMappingTable, TranslateClassSingleInsert) {
+  TraceStorage storage;
+  StringId xyz_id = storage.InternString("xyz");
+  StringId abc_id = storage.InternString("abc");
+  StringId class_x_id = storage.InternString("class_X");
+
+  DeobfuscationMappingTable table;
+  table.AddClassTranslation(PackageId{"app", 123}, xyz_id, class_x_id,
+                            base::FlatHashMap<StringId, StringId>{});
+  EXPECT_EQ(table.TranslateClass(xyz_id), class_x_id);
+  EXPECT_EQ(table.TranslateClass(PackageId{"app", 123}, xyz_id), class_x_id);
+  EXPECT_EQ(table.TranslateClass(PackageId{"app", 124}, xyz_id), base::nullopt);
+  EXPECT_EQ(table.TranslateClass(PackageId{"app", 123}, abc_id), base::nullopt);
+}
+
+TEST(DeobfuscationMappingTable, TranslateClassMultipleInsert) {
+  TraceStorage storage;
+  StringId xyz_id = storage.InternString("xyz");
+  StringId abc_id = storage.InternString("abc");
+  StringId class_x_id = storage.InternString("class_X");
+  StringId class_y_id = storage.InternString("class_Y");
+  StringId class_a_id = storage.InternString("class_A");
+
+  DeobfuscationMappingTable table;
+  table.AddClassTranslation(PackageId{"app1", 123}, xyz_id, class_x_id,
+                            base::FlatHashMap<StringId, StringId>{});
+  table.AddClassTranslation(PackageId{"app2", 123}, xyz_id, class_y_id,
+                            base::FlatHashMap<StringId, StringId>{});
+  table.AddClassTranslation(PackageId{"app3", 123}, abc_id, class_a_id,
+                            base::FlatHashMap<StringId, StringId>{});
+  EXPECT_EQ(table.TranslateClass(xyz_id), class_x_id);
+  EXPECT_EQ(table.TranslateClass(abc_id), base::nullopt);
+  EXPECT_EQ(table.TranslateClass(PackageId{"app1", 123}, xyz_id), class_x_id);
+  EXPECT_EQ(table.TranslateClass(PackageId{"app2", 123}, xyz_id), class_y_id);
+  EXPECT_EQ(table.TranslateClass(PackageId{"app1", 123}, abc_id),
+            base::nullopt);
+}
+
+TEST(DeobfuscationMappingTable, TranslateMember) {
+  TraceStorage storage;
+  StringId xyz_id = storage.InternString("xyz");
+  StringId abc_id = storage.InternString("abc");
+  StringId class_x_id = storage.InternString("class_X");
+  StringId mmm_1_id = storage.InternString("mmm1");
+  StringId mmm_2_id = storage.InternString("mmm2");
+  StringId mmm_3_id = storage.InternString("mmm3");
+  StringId mmm_4_id = storage.InternString("mmm4");
+  StringId member_1_id = storage.InternString("member_1");
+  StringId member_2_id = storage.InternString("member_2");
+  StringId member_3_id = storage.InternString("member_3");
+
+  base::FlatHashMap<StringId, StringId> members;
+  members[mmm_1_id] = member_1_id;
+  members[mmm_2_id] = member_2_id;
+  members[mmm_3_id] = member_3_id;
+  DeobfuscationMappingTable table;
+  table.AddClassTranslation(PackageId{"app1", 123}, xyz_id, class_x_id,
+                            std::move(members));
+  EXPECT_EQ(table.TranslateMember(PackageId{"app1", 123}, xyz_id, mmm_2_id),
+            member_2_id);
+  EXPECT_EQ(table.TranslateMember(PackageId{"app1", 123}, xyz_id, mmm_4_id),
+            base::nullopt);
+  EXPECT_EQ(table.TranslateMember(PackageId{"app1", 123}, abc_id, mmm_2_id),
+            base::nullopt);
+  EXPECT_EQ(table.TranslateMember(PackageId{"app1", 124}, xyz_id, mmm_2_id),
+            base::nullopt);
+}
+
+}  // namespace
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/common/flow_tracker.h b/src/trace_processor/importers/common/flow_tracker.h
index 2d749d1..d52b031 100644
--- a/src/trace_processor/importers/common/flow_tracker.h
+++ b/src/trace_processor/importers/common/flow_tracker.h
@@ -81,7 +81,7 @@
   struct V1FlowIdHasher {
     size_t operator()(const V1FlowId& c) const {
       return std::hash<uint64_t>{}(
-          base::Hash::Combine(c.source_id, c.cat.raw_id(), c.name.raw_id()));
+          base::Hasher::Combine(c.source_id, c.cat.raw_id(), c.name.raw_id()));
     }
   };
 
diff --git a/src/trace_processor/importers/common/global_args_tracker.h b/src/trace_processor/importers/common/global_args_tracker.h
index 573a917..3f04105 100644
--- a/src/trace_processor/importers/common/global_args_tracker.h
+++ b/src/trace_processor/importers/common/global_args_tracker.h
@@ -59,7 +59,7 @@
 
   struct ArgHasher {
     uint64_t operator()(const Arg& arg) const noexcept {
-      base::Hash hash;
+      base::Hasher hash;
       hash.Update(arg.key.raw_id());
       // We don't hash arg.flat_key because it's a subsequence of arg.key.
       switch (arg.value.type) {
@@ -117,7 +117,7 @@
       valid_indexes.emplace_back(i);
     }
 
-    base::Hash hash;
+    base::Hasher hash;
     for (uint32_t i : valid_indexes) {
       hash.Update(ArgHasher()(args[i]));
     }
diff --git a/src/trace_processor/importers/common/parser_types.h b/src/trace_processor/importers/common/parser_types.h
new file mode 100644
index 0000000..9d5976c
--- /dev/null
+++ b/src/trace_processor/importers/common/parser_types.h
@@ -0,0 +1,68 @@
+/*
+ * 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_COMMON_PARSER_TYPES_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_PARSER_TYPES_H_
+
+#include <stdint.h>
+
+#include "perfetto/trace_processor/trace_blob_view.h"
+#include "src/trace_processor/containers/string_pool.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+struct InlineSchedSwitch {
+  int64_t prev_state;
+  int32_t next_pid;
+  int32_t next_prio;
+  StringPool::Id next_comm;
+};
+
+struct InlineSchedWaking {
+  int32_t pid;
+  int32_t target_cpu;
+  int32_t prio;
+  StringPool::Id comm;
+};
+
+struct TracePacketData {
+  TraceBlobView packet;
+  RefPtr<PacketSequenceStateGeneration> sequence_state;
+};
+
+struct TrackEventData {
+  TrackEventData(TraceBlobView pv,
+                 RefPtr<PacketSequenceStateGeneration> generation)
+      : trace_packet_data{std::move(pv), std::move(generation)} {}
+
+  explicit TrackEventData(TracePacketData tpd)
+      : trace_packet_data(std::move(tpd)) {}
+
+  static constexpr size_t kMaxNumExtraCounters = 8;
+
+  TracePacketData trace_packet_data;
+  base::Optional<int64_t> thread_timestamp;
+  base::Optional<int64_t> thread_instruction_count;
+  double counter_value = 0;
+  std::array<double, kMaxNumExtraCounters> extra_counter_values = {};
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_PARSER_TYPES_H_
diff --git a/src/trace_processor/importers/common/slice_tracker.cc b/src/trace_processor/importers/common/slice_tracker.cc
index a6aa237..9f93fac 100644
--- a/src/trace_processor/importers/common/slice_tracker.cc
+++ b/src/trace_processor/importers/common/slice_tracker.cc
@@ -381,10 +381,15 @@
       //          [     slice 2     ]
       // This is invalid in chrome and should be fixed. Duration events should
       // either be nested or disjoint, never partially intersecting.
+      // KI: if tracing both binder and system calls on android, "binder reply"
+      // slices will try to escape the enclosing sys_ioctl.
       PERFETTO_DLOG(
-          "Incorrect ordering of begin/end slice events around timestamp "
-          "%" PRId64,
-          ts);
+          "Incorrect ordering of begin/end slice events. "
+          "Truncating incomplete descendants to the end of slice "
+          "%s[%" PRId64 ", %" PRId64 "] due to an event at ts=%" PRId64 ".",
+          context_->storage->GetString(ref.name().value_or(kNullStringId))
+              .c_str(),
+          start_ts, end_ts, ts);
       context_->storage->IncrementStats(stats::misplaced_end_event);
 
       // Every slice below this one should have a pending duration. Update
@@ -416,7 +421,7 @@
 
   const auto& slices = context_->storage->slice_table();
 
-  base::Hash hash;
+  base::Hasher hash;
   for (size_t i = 0; i < stack.size(); i++) {
     auto ref = stack[i].row.ToRowReference(slices);
     hash.Update(ref.category().value_or(kNullStringId).raw_id());
diff --git a/src/trace_processor/importers/common/trace_parser.cc b/src/trace_processor/importers/common/trace_parser.cc
new file mode 100644
index 0000000..d075a57
--- /dev/null
+++ b/src/trace_processor/importers/common/trace_parser.cc
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 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/trace_parser.h"
+
+#include "src/trace_processor/importers/common/parser_types.h"
+#include "src/trace_processor/importers/fuchsia/fuchsia_record.h"
+#include "src/trace_processor/importers/systrace/systrace_line.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+void TraceParser::ParseTracePacket(int64_t, TracePacketData) {
+  PERFETTO_FATAL("Wrong parser type");
+}
+void TraceParser::ParseJsonPacket(int64_t, std::string) {
+  PERFETTO_FATAL("Wrong parser type");
+}
+void TraceParser::ParseFuchsiaRecord(int64_t, FuchsiaRecord) {
+  PERFETTO_FATAL("Wrong parser type");
+}
+void TraceParser::ParseTrackEvent(int64_t, TrackEventData) {
+  PERFETTO_FATAL("Wrong parser type");
+}
+void TraceParser::ParseSystraceLine(int64_t, SystraceLine) {
+  PERFETTO_FATAL("Wrong parser type");
+}
+void TraceParser::ParseFtraceEvent(uint32_t, int64_t, TracePacketData) {
+  PERFETTO_FATAL("Wrong parser type");
+}
+void TraceParser::ParseInlineSchedSwitch(uint32_t, int64_t, InlineSchedSwitch) {
+  PERFETTO_FATAL("Wrong parser type");
+}
+void TraceParser::ParseInlineSchedWaking(uint32_t, int64_t, InlineSchedWaking) {
+  PERFETTO_FATAL("Wrong parser type");
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/common/trace_parser.h b/src/trace_processor/importers/common/trace_parser.h
index 278a9d7..7766ef3 100644
--- a/src/trace_processor/importers/common/trace_parser.h
+++ b/src/trace_processor/importers/common/trace_parser.h
@@ -18,20 +18,32 @@
 #define SRC_TRACE_PROCESSOR_IMPORTERS_COMMON_TRACE_PARSER_H_
 
 #include <stdint.h>
+#include <string>
 
 namespace perfetto {
 namespace trace_processor {
 
-struct TimestampedTracePiece;
+class PacketSequenceStateGeneration;
+struct InlineSchedSwitch;
+class FuchsiaRecord;
+struct SystraceLine;
+struct InlineSchedWaking;
+struct TracePacketData;
+struct TrackEventData;
 
 class TraceParser {
  public:
   virtual ~TraceParser();
 
-  virtual void ParseTracePacket(int64_t timestamp, TimestampedTracePiece) = 0;
-  virtual void ParseFtracePacket(uint32_t cpu,
-                                 int64_t timestamp,
-                                 TimestampedTracePiece) = 0;
+  virtual void ParseTracePacket(int64_t, TracePacketData);
+  virtual void ParseJsonPacket(int64_t, std::string);
+  virtual void ParseFuchsiaRecord(int64_t, FuchsiaRecord);
+  virtual void ParseTrackEvent(int64_t, TrackEventData);
+  virtual void ParseSystraceLine(int64_t, SystraceLine);
+
+  virtual void ParseFtraceEvent(uint32_t, int64_t, TracePacketData);
+  virtual void ParseInlineSchedSwitch(uint32_t, int64_t, InlineSchedSwitch);
+  virtual void ParseInlineSchedWaking(uint32_t, int64_t, InlineSchedWaking);
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/common/track_tracker.cc b/src/trace_processor/importers/common/track_tracker.cc
index 8a3ebaa..58330e7 100644
--- a/src/trace_processor/importers/common/track_tracker.cc
+++ b/src/trace_processor/importers/common/track_tracker.cc
@@ -305,6 +305,57 @@
   return track;
 }
 
+TrackId TrackTracker::InternEnergyCounterTrack(StringId name,
+                                               int32_t consumer_id,
+                                               StringId consumer_type,
+                                               int32_t ordinal) {
+  auto it = energy_counter_tracks_.find(std::make_pair(name, consumer_id));
+  if (it != energy_counter_tracks_.end()) {
+    return it->second;
+  }
+  tables::EnergyCounterTrackTable::Row row(name);
+  row.consumer_id = consumer_id;
+  row.consumer_type = consumer_type;
+  row.ordinal = ordinal;
+  TrackId track =
+      context_->storage->mutable_energy_counter_track_table()->Insert(row).id;
+  energy_counter_tracks_[std::make_pair(name, consumer_id)] = track;
+  return track;
+}
+
+TrackId TrackTracker::InternUidCounterTrack(StringId name, int32_t uid) {
+  auto it = uid_counter_tracks_.find(std::make_pair(name, uid));
+  if (it != uid_counter_tracks_.end()) {
+    return it->second;
+  }
+
+  tables::UidCounterTrackTable::Row row(name);
+  row.uid = uid;
+  TrackId track =
+      context_->storage->mutable_uid_counter_track_table()->Insert(row).id;
+  uid_counter_tracks_[std::make_pair(name, uid)] = track;
+  return track;
+}
+
+TrackId TrackTracker::InternEnergyPerUidCounterTrack(StringId name,
+                                                     int32_t consumer_id,
+                                                     int32_t uid) {
+  auto it = energy_per_uid_counter_tracks_.find(std::make_pair(name, uid));
+  if (it != energy_per_uid_counter_tracks_.end()) {
+    return it->second;
+  }
+
+  tables::EnergyPerUidCounterTrackTable::Row row(name);
+  row.consumer_id = consumer_id;
+  row.uid = uid;
+  TrackId track =
+      context_->storage->mutable_energy_per_uid_counter_track_table()
+          ->Insert(row)
+          .id;
+  energy_per_uid_counter_tracks_[std::make_pair(name, uid)] = track;
+  return track;
+}
+
 TrackId TrackTracker::CreateGpuCounterTrack(StringId name,
                                             uint32_t gpu_id,
                                             StringId description,
diff --git a/src/trace_processor/importers/common/track_tracker.h b/src/trace_processor/importers/common/track_tracker.h
index a932901..f172148 100644
--- a/src/trace_processor/importers/common/track_tracker.h
+++ b/src/trace_processor/importers/common/track_tracker.h
@@ -93,6 +93,22 @@
   // Interns a counter track associated with a GPU into the storage.
   TrackId InternGpuCounterTrack(StringId name, uint32_t gpu_id);
 
+  // Interns energy counter track associated with a
+  // Energy breakdown into the storage.
+  TrackId InternEnergyCounterTrack(StringId name,
+                                   int32_t consumer_id,
+                                   StringId consumer_type,
+                                   int32_t ordinal);
+  // Interns a per process energy counter track associated with a
+  // Energy into the storage.
+  TrackId InternUidCounterTrack(StringId name, int32_t uid);
+
+  // Interns a per process energy consumer counter track associated with a
+  // Energy Uid into the storage.
+  TrackId InternEnergyPerUidCounterTrack(StringId name,
+                                         int32_t consumer_id,
+                                         int32_t uid);
+
   // Creates a counter track associated with a GPU into the storage.
   TrackId CreateGpuCounterTrack(StringId name,
                                 uint32_t gpu_id,
@@ -157,6 +173,10 @@
   std::map<std::pair<StringId, int32_t>, TrackId> irq_counter_tracks_;
   std::map<std::pair<StringId, int32_t>, TrackId> softirq_counter_tracks_;
   std::map<std::pair<StringId, uint32_t>, TrackId> gpu_counter_tracks_;
+  std::map<std::pair<StringId, int32_t>, TrackId> energy_counter_tracks_;
+  std::map<std::pair<StringId, int32_t>, TrackId> uid_counter_tracks_;
+  std::map<std::pair<StringId, int32_t>, TrackId>
+      energy_per_uid_counter_tracks_;
 
   base::Optional<TrackId> chrome_global_instant_track_id_;
   base::Optional<TrackId> trigger_track_id_;
diff --git a/src/trace_processor/importers/default_modules.cc b/src/trace_processor/importers/default_modules.cc
deleted file mode 100644
index b14f357..0000000
--- a/src/trace_processor/importers/default_modules.cc
+++ /dev/null
@@ -1,46 +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/importers/default_modules.h"
-#include "src/trace_processor/importers/ftrace/ftrace_module.h"
-#include "src/trace_processor/importers/proto/android_camera_event_module.h"
-#include "src/trace_processor/importers/proto/chrome_system_probes_module.h"
-#include "src/trace_processor/importers/proto/memory_tracker_snapshot_module.h"
-#include "src/trace_processor/importers/proto/metadata_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 MemoryTrackerSnapshotModule(context));
-  context->modules.emplace_back(new ChromeSystemProbesModule(context));
-  context->modules.emplace_back(new TrackEventModule(context));
-  context->modules.emplace_back(new ProfileModule(context));
-  context->modules.emplace_back(new MetadataModule(context));
-  context->modules.emplace_back(new AndroidCameraEventModule(context));
-}
-
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/importers/default_modules.h b/src/trace_processor/importers/default_modules.h
deleted file mode 100644
index f1dcbf2..0000000
--- a/src/trace_processor/importers/default_modules.h
+++ /dev/null
@@ -1,30 +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_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 {
-
-void RegisterDefaultModules(TraceProcessorContext*);
-
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_DEFAULT_MODULES_H_
diff --git a/src/trace_processor/importers/ftrace/BUILD.gn b/src/trace_processor/importers/ftrace/BUILD.gn
new file mode 100644
index 0000000..86c4ddc
--- /dev/null
+++ b/src/trace_processor/importers/ftrace/BUILD.gn
@@ -0,0 +1,107 @@
+# Copyright (C) 2022 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("minimal") {
+  sources = [
+    "ftrace_module.cc",
+    "ftrace_module.h",
+  ]
+  deps = [
+    "../../../../gn:default_deps",
+    "../common:parser_types",
+    "../common:trace_parser_hdr",
+    "../proto:proto_importer_module",
+  ]
+}
+
+source_set("full") {
+  sources = [
+    "binder_tracker.cc",
+    "binder_tracker.h",
+    "drm_tracker.cc",
+    "drm_tracker.h",
+    "ftrace_module_impl.cc",
+    "ftrace_module_impl.h",
+    "ftrace_parser.cc",
+    "ftrace_parser.h",
+    "ftrace_tokenizer.cc",
+    "ftrace_tokenizer.h",
+    "iostat_tracker.cc",
+    "iostat_tracker.h",
+    "mali_gpu_event_tracker.cc",
+    "mali_gpu_event_tracker.h",
+    "rss_stat_tracker.cc",
+    "rss_stat_tracker.h",
+    "sched_event_tracker.cc",
+    "sched_event_tracker.h",
+    "thread_state_tracker.cc",
+    "thread_state_tracker.h",
+    "v4l2_tracker.cc",
+    "v4l2_tracker.h",
+    "virtio_gpu_tracker.cc",
+    "virtio_gpu_tracker.h",
+    "virtio_video_tracker.cc",
+    "virtio_video_tracker.h",
+  ]
+  deps = [
+    ":ftrace_descriptors",
+    ":minimal",
+    "../../../../gn:default_deps",
+    "../../../../protos/perfetto/common:zero",
+    "../../../../protos/perfetto/trace:zero",
+    "../../../../protos/perfetto/trace/ftrace:zero",
+    "../../../../protos/perfetto/trace/interned_data:zero",
+    "../../../protozero",
+    "../../sorter",
+    "../../storage",
+    "../../types",
+    "../common",
+    "../common:parser_types",
+    "../i2c:full",
+    "../proto:minimal",
+    "../syscalls:full",
+    "../systrace:systrace_parser",
+  ]
+}
+
+source_set("ftrace_descriptors") {
+  sources = [
+    "ftrace_descriptors.cc",
+    "ftrace_descriptors.h",
+  ]
+  deps = [
+    "../../../../gn:default_deps",
+    "../../../../include/perfetto/ext/base:base",
+    "../../../protozero",
+  ]
+}
+
+perfetto_unittest_source_set("unittests") {
+  testonly = true
+  sources = [
+    "binder_tracker_unittest.cc",
+    "sched_event_tracker_unittest.cc",
+    "thread_state_tracker_unittest.cc",
+  ]
+  deps = [
+    "../../../../gn:default_deps",
+    "../../../../gn:gtest_and_gmock",
+    "../../storage",
+    "../../types",
+    "../common",
+    "../ftrace:full",
+  ]
+}
diff --git a/src/trace_processor/importers/ftrace/ftrace_descriptors.cc b/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
index 57de00e..b0ad138 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<FtraceMessageDescriptor, 428> descriptors{{
+std::array<FtraceMessageDescriptor, 476> descriptors{{
     {nullptr, 0, {}},
     {nullptr, 0, {}},
     {nullptr, 0, {}},
@@ -3560,10 +3560,11 @@
     },
     {
         "sys_enter",
-        1,
+        2,
         {
             {},
             {"id", ProtoSchemaType::kInt64},
+            {"args", ProtoSchemaType::kUint64},
         },
     },
     {
@@ -4730,6 +4731,529 @@
             {"timestamp", ProtoSchemaType::kInt64},
         },
     },
+    {
+        "dsi_cmd_fifo_status",
+        2,
+        {
+            {},
+            {"header", ProtoSchemaType::kUint32},
+            {"payload", ProtoSchemaType::kUint32},
+        },
+    },
+    {
+        "dsi_rx",
+        2,
+        {
+            {},
+            {"cmd", ProtoSchemaType::kUint32},
+            {"rx_buf", ProtoSchemaType::kUint32},
+        },
+    },
+    {
+        "dsi_tx",
+        3,
+        {
+            {},
+            {"last", ProtoSchemaType::kUint32},
+            {"tx_buf", ProtoSchemaType::kUint32},
+            {"type", ProtoSchemaType::kUint32},
+        },
+    },
+    {
+        "android_fs_dataread_end",
+        3,
+        {
+            {},
+            {"bytes", ProtoSchemaType::kInt32},
+            {"ino", ProtoSchemaType::kUint64},
+            {"offset", ProtoSchemaType::kInt64},
+        },
+    },
+    {
+        "android_fs_dataread_start",
+        7,
+        {
+            {},
+            {"bytes", ProtoSchemaType::kInt32},
+            {"cmdline", ProtoSchemaType::kString},
+            {"i_size", ProtoSchemaType::kInt64},
+            {"ino", ProtoSchemaType::kUint64},
+            {"offset", ProtoSchemaType::kInt64},
+            {"pathbuf", ProtoSchemaType::kString},
+            {"pid", ProtoSchemaType::kInt32},
+        },
+    },
+    {
+        "android_fs_datawrite_end",
+        3,
+        {
+            {},
+            {"bytes", ProtoSchemaType::kInt32},
+            {"ino", ProtoSchemaType::kUint64},
+            {"offset", ProtoSchemaType::kInt64},
+        },
+    },
+    {
+        "android_fs_datawrite_start",
+        7,
+        {
+            {},
+            {"bytes", ProtoSchemaType::kInt32},
+            {"cmdline", ProtoSchemaType::kString},
+            {"i_size", ProtoSchemaType::kInt64},
+            {"ino", ProtoSchemaType::kUint64},
+            {"offset", ProtoSchemaType::kInt64},
+            {"pathbuf", ProtoSchemaType::kString},
+            {"pid", ProtoSchemaType::kInt32},
+        },
+    },
+    {
+        "android_fs_fsync_end",
+        3,
+        {
+            {},
+            {"bytes", ProtoSchemaType::kInt32},
+            {"ino", ProtoSchemaType::kUint64},
+            {"offset", ProtoSchemaType::kInt64},
+        },
+    },
+    {
+        "android_fs_fsync_start",
+        5,
+        {
+            {},
+            {"cmdline", ProtoSchemaType::kString},
+            {"i_size", ProtoSchemaType::kInt64},
+            {"ino", ProtoSchemaType::kUint64},
+            {"pathbuf", ProtoSchemaType::kString},
+            {"pid", ProtoSchemaType::kInt32},
+        },
+    },
+    {
+        "funcgraph_entry",
+        2,
+        {
+            {},
+            {"depth", ProtoSchemaType::kInt32},
+            {"func", ProtoSchemaType::kUint64},
+        },
+    },
+    {
+        "funcgraph_exit",
+        5,
+        {
+            {},
+            {"calltime", ProtoSchemaType::kUint64},
+            {"depth", ProtoSchemaType::kInt32},
+            {"func", ProtoSchemaType::kUint64},
+            {"overrun", ProtoSchemaType::kUint64},
+            {"rettime", ProtoSchemaType::kUint64},
+        },
+    },
+    {
+        "virtio_video_cmd",
+        2,
+        {
+            {},
+            {"stream_id", ProtoSchemaType::kUint32},
+            {"type", ProtoSchemaType::kUint32},
+        },
+    },
+    {
+        "virtio_video_cmd_done",
+        2,
+        {
+            {},
+            {"stream_id", ProtoSchemaType::kUint32},
+            {"type", ProtoSchemaType::kUint32},
+        },
+    },
+    {
+        "virtio_video_resource_queue",
+        8,
+        {
+            {},
+            {"data_size0", ProtoSchemaType::kUint32},
+            {"data_size1", ProtoSchemaType::kUint32},
+            {"data_size2", ProtoSchemaType::kUint32},
+            {"data_size3", ProtoSchemaType::kUint32},
+            {"queue_type", ProtoSchemaType::kUint32},
+            {"resource_id", ProtoSchemaType::kInt32},
+            {"stream_id", ProtoSchemaType::kInt32},
+            {"timestamp", ProtoSchemaType::kUint64},
+        },
+    },
+    {
+        "virtio_video_resource_queue_done",
+        8,
+        {
+            {},
+            {"data_size0", ProtoSchemaType::kUint32},
+            {"data_size1", ProtoSchemaType::kUint32},
+            {"data_size2", ProtoSchemaType::kUint32},
+            {"data_size3", ProtoSchemaType::kUint32},
+            {"queue_type", ProtoSchemaType::kUint32},
+            {"resource_id", ProtoSchemaType::kInt32},
+            {"stream_id", ProtoSchemaType::kInt32},
+            {"timestamp", ProtoSchemaType::kUint64},
+        },
+    },
+    {
+        "mm_shrink_slab_start",
+        11,
+        {
+            {},
+            {"cache_items", ProtoSchemaType::kUint64},
+            {"delta", ProtoSchemaType::kUint64},
+            {"gfp_flags", ProtoSchemaType::kUint32},
+            {"lru_pgs", ProtoSchemaType::kUint64},
+            {"nr_objects_to_shrink", ProtoSchemaType::kInt64},
+            {"pgs_scanned", ProtoSchemaType::kUint64},
+            {"shr", ProtoSchemaType::kUint64},
+            {"shrink", ProtoSchemaType::kUint64},
+            {"total_scan", ProtoSchemaType::kUint64},
+            {"nid", ProtoSchemaType::kInt32},
+            {"priority", ProtoSchemaType::kInt32},
+        },
+    },
+    {
+        "mm_shrink_slab_end",
+        7,
+        {
+            {},
+            {"new_scan", ProtoSchemaType::kInt64},
+            {"retval", ProtoSchemaType::kInt32},
+            {"shr", ProtoSchemaType::kUint64},
+            {"shrink", ProtoSchemaType::kUint64},
+            {"total_scan", ProtoSchemaType::kInt64},
+            {"unused_scan", ProtoSchemaType::kInt64},
+            {"nid", ProtoSchemaType::kInt32},
+        },
+    },
+    {
+        "trusty_smc",
+        4,
+        {
+            {},
+            {"r0", ProtoSchemaType::kUint64},
+            {"r1", ProtoSchemaType::kUint64},
+            {"r2", ProtoSchemaType::kUint64},
+            {"r3", ProtoSchemaType::kUint64},
+        },
+    },
+    {
+        "trusty_smc_done",
+        1,
+        {
+            {},
+            {"ret", ProtoSchemaType::kUint64},
+        },
+    },
+    {
+        "trusty_std_call32",
+        4,
+        {
+            {},
+            {"r0", ProtoSchemaType::kUint64},
+            {"r1", ProtoSchemaType::kUint64},
+            {"r2", ProtoSchemaType::kUint64},
+            {"r3", ProtoSchemaType::kUint64},
+        },
+    },
+    {
+        "trusty_std_call32_done",
+        1,
+        {
+            {},
+            {"ret", ProtoSchemaType::kInt64},
+        },
+    },
+    {
+        "trusty_share_memory",
+        3,
+        {
+            {},
+            {"len", ProtoSchemaType::kUint64},
+            {"lend", ProtoSchemaType::kUint32},
+            {"nents", ProtoSchemaType::kUint32},
+        },
+    },
+    {
+        "trusty_share_memory_done",
+        5,
+        {
+            {},
+            {"handle", ProtoSchemaType::kUint64},
+            {"len", ProtoSchemaType::kUint64},
+            {"lend", ProtoSchemaType::kUint32},
+            {"nents", ProtoSchemaType::kUint32},
+            {"ret", ProtoSchemaType::kInt32},
+        },
+    },
+    {
+        "trusty_reclaim_memory",
+        1,
+        {
+            {},
+            {"id", ProtoSchemaType::kUint64},
+        },
+    },
+    {
+        "trusty_reclaim_memory_done",
+        2,
+        {
+            {},
+            {"id", ProtoSchemaType::kUint64},
+            {"ret", ProtoSchemaType::kInt32},
+        },
+    },
+    {
+        "trusty_irq",
+        1,
+        {
+            {},
+            {"irq", ProtoSchemaType::kInt32},
+        },
+    },
+    {
+        "trusty_ipc_handle_event",
+        3,
+        {
+            {},
+            {"chan", ProtoSchemaType::kUint32},
+            {"event_id", ProtoSchemaType::kUint32},
+            {"srv_name", ProtoSchemaType::kString},
+        },
+    },
+    {
+        "trusty_ipc_connect",
+        3,
+        {
+            {},
+            {"chan", ProtoSchemaType::kUint32},
+            {"port", ProtoSchemaType::kString},
+            {"state", ProtoSchemaType::kInt32},
+        },
+    },
+    {
+        "trusty_ipc_connect_end",
+        3,
+        {
+            {},
+            {"chan", ProtoSchemaType::kUint32},
+            {"err", ProtoSchemaType::kInt32},
+            {"state", ProtoSchemaType::kInt32},
+        },
+    },
+    {
+        "trusty_ipc_write",
+        6,
+        {
+            {},
+            {"buf_id", ProtoSchemaType::kUint64},
+            {"chan", ProtoSchemaType::kUint32},
+            {"kind_shm", ProtoSchemaType::kInt32},
+            {"len_or_err", ProtoSchemaType::kInt32},
+            {"shm_cnt", ProtoSchemaType::kUint64},
+            {"srv_name", ProtoSchemaType::kString},
+        },
+    },
+    {
+        "trusty_ipc_poll",
+        3,
+        {
+            {},
+            {"chan", ProtoSchemaType::kUint32},
+            {"poll_mask", ProtoSchemaType::kUint32},
+            {"srv_name", ProtoSchemaType::kString},
+        },
+    },
+    {nullptr, 0, {}},
+    {
+        "trusty_ipc_read",
+        2,
+        {
+            {},
+            {"chan", ProtoSchemaType::kUint32},
+            {"srv_name", ProtoSchemaType::kString},
+        },
+    },
+    {
+        "trusty_ipc_read_end",
+        5,
+        {
+            {},
+            {"buf_id", ProtoSchemaType::kUint64},
+            {"chan", ProtoSchemaType::kUint32},
+            {"len_or_err", ProtoSchemaType::kInt32},
+            {"shm_cnt", ProtoSchemaType::kUint64},
+            {"srv_name", ProtoSchemaType::kString},
+        },
+    },
+    {
+        "trusty_ipc_rx",
+        3,
+        {
+            {},
+            {"buf_id", ProtoSchemaType::kUint64},
+            {"chan", ProtoSchemaType::kUint32},
+            {"srv_name", ProtoSchemaType::kString},
+        },
+    },
+    {nullptr, 0, {}},
+    {
+        "trusty_enqueue_nop",
+        3,
+        {
+            {},
+            {"arg1", ProtoSchemaType::kUint32},
+            {"arg2", ProtoSchemaType::kUint32},
+            {"arg3", ProtoSchemaType::kUint32},
+        },
+    },
+    {
+        "cma_alloc_start",
+        3,
+        {
+            {},
+            {"align", ProtoSchemaType::kUint32},
+            {"count", ProtoSchemaType::kUint32},
+            {"name", ProtoSchemaType::kString},
+        },
+    },
+    {
+        "cma_alloc_info",
+        10,
+        {
+            {},
+            {"align", ProtoSchemaType::kUint32},
+            {"count", ProtoSchemaType::kUint32},
+            {"err_iso", ProtoSchemaType::kUint32},
+            {"err_mig", ProtoSchemaType::kUint32},
+            {"err_test", ProtoSchemaType::kUint32},
+            {"name", ProtoSchemaType::kString},
+            {"nr_mapped", ProtoSchemaType::kUint64},
+            {"nr_migrated", ProtoSchemaType::kUint64},
+            {"nr_reclaimed", ProtoSchemaType::kUint64},
+            {"pfn", ProtoSchemaType::kUint64},
+        },
+    },
+    {
+        "lwis_tracing_mark_write",
+        5,
+        {
+            {},
+            {"lwis_name", ProtoSchemaType::kString},
+            {"type", ProtoSchemaType::kUint32},
+            {"pid", ProtoSchemaType::kInt32},
+            {"func_name", ProtoSchemaType::kString},
+            {"value", ProtoSchemaType::kInt64},
+        },
+    },
+    {
+        "virtio_gpu_cmd_queue",
+        9,
+        {
+            {},
+            {"ctx_id", ProtoSchemaType::kUint32},
+            {"dev", ProtoSchemaType::kInt32},
+            {"fence_id", ProtoSchemaType::kUint64},
+            {"flags", ProtoSchemaType::kUint32},
+            {"name", ProtoSchemaType::kString},
+            {"num_free", ProtoSchemaType::kUint32},
+            {"seqno", ProtoSchemaType::kUint32},
+            {"type", ProtoSchemaType::kUint32},
+            {"vq", ProtoSchemaType::kUint32},
+        },
+    },
+    {
+        "virtio_gpu_cmd_response",
+        9,
+        {
+            {},
+            {"ctx_id", ProtoSchemaType::kUint32},
+            {"dev", ProtoSchemaType::kInt32},
+            {"fence_id", ProtoSchemaType::kUint64},
+            {"flags", ProtoSchemaType::kUint32},
+            {"name", ProtoSchemaType::kString},
+            {"num_free", ProtoSchemaType::kUint32},
+            {"seqno", ProtoSchemaType::kUint32},
+            {"type", ProtoSchemaType::kUint32},
+            {"vq", ProtoSchemaType::kUint32},
+        },
+    },
+    {
+        "mali_mali_KCPU_CQS_SET",
+        5,
+        {
+            {},
+            {"id", ProtoSchemaType::kUint32},
+            {"info_val1", ProtoSchemaType::kUint64},
+            {"info_val2", ProtoSchemaType::kUint64},
+            {"kctx_id", ProtoSchemaType::kUint32},
+            {"kctx_tgid", ProtoSchemaType::kInt32},
+        },
+    },
+    {
+        "mali_mali_KCPU_CQS_WAIT_START",
+        5,
+        {
+            {},
+            {"id", ProtoSchemaType::kUint32},
+            {"info_val1", ProtoSchemaType::kUint64},
+            {"info_val2", ProtoSchemaType::kUint64},
+            {"kctx_id", ProtoSchemaType::kUint32},
+            {"kctx_tgid", ProtoSchemaType::kInt32},
+        },
+    },
+    {
+        "mali_mali_KCPU_CQS_WAIT_END",
+        5,
+        {
+            {},
+            {"id", ProtoSchemaType::kUint32},
+            {"info_val1", ProtoSchemaType::kUint64},
+            {"info_val2", ProtoSchemaType::kUint64},
+            {"kctx_id", ProtoSchemaType::kUint32},
+            {"kctx_tgid", ProtoSchemaType::kInt32},
+        },
+    },
+    {
+        "mali_mali_KCPU_FENCE_SIGNAL",
+        5,
+        {
+            {},
+            {"info_val1", ProtoSchemaType::kUint64},
+            {"info_val2", ProtoSchemaType::kUint64},
+            {"kctx_tgid", ProtoSchemaType::kInt32},
+            {"kctx_id", ProtoSchemaType::kUint32},
+            {"id", ProtoSchemaType::kUint32},
+        },
+    },
+    {
+        "mali_mali_KCPU_FENCE_WAIT_START",
+        5,
+        {
+            {},
+            {"info_val1", ProtoSchemaType::kUint64},
+            {"info_val2", ProtoSchemaType::kUint64},
+            {"kctx_tgid", ProtoSchemaType::kInt32},
+            {"kctx_id", ProtoSchemaType::kUint32},
+            {"id", ProtoSchemaType::kUint32},
+        },
+    },
+    {
+        "mali_mali_KCPU_FENCE_WAIT_END",
+        5,
+        {
+            {},
+            {"info_val1", ProtoSchemaType::kUint64},
+            {"info_val2", ProtoSchemaType::kUint64},
+            {"kctx_tgid", ProtoSchemaType::kInt32},
+            {"kctx_id", ProtoSchemaType::kUint32},
+            {"id", ProtoSchemaType::kUint32},
+        },
+    },
 }};
 
 }  // namespace
diff --git a/src/trace_processor/importers/ftrace/ftrace_module.cc b/src/trace_processor/importers/ftrace/ftrace_module.cc
index 840f330..f883614 100644
--- a/src/trace_processor/importers/ftrace/ftrace_module.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_module.cc
@@ -15,12 +15,23 @@
  */
 
 #include "src/trace_processor/importers/ftrace/ftrace_module.h"
+#include <cstdint>
+#include "src/trace_processor/importers/common/parser_types.h"
 
 namespace perfetto {
 namespace trace_processor {
 
-void FtraceModule::ParseFtracePacket(uint32_t /*cpu*/,
-                                     const TimestampedTracePiece&) {}
+void FtraceModule::ParseFtraceEventData(uint32_t /*cpu*/,
+                                        int64_t /*ts*/,
+                                        const TracePacketData&) {}
+
+void FtraceModule::ParseInlineSchedSwitch(uint32_t /*cpu*/,
+                                          int64_t /*ts*/,
+                                          const InlineSchedSwitch&) {}
+
+void FtraceModule::ParseInlineSchedWaking(uint32_t /*cpu*/,
+                                          int64_t /*ts*/,
+                                          const InlineSchedWaking&) {}
 
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/ftrace/ftrace_module.h b/src/trace_processor/importers/ftrace/ftrace_module.h
index 8d2382c..f9b289d 100644
--- a/src/trace_processor/importers/ftrace/ftrace_module.h
+++ b/src/trace_processor/importers/ftrace/ftrace_module.h
@@ -17,16 +17,26 @@
 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_FTRACE_MODULE_H_
 #define SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_FTRACE_MODULE_H_
 
+#include "src/trace_processor/importers/common/parser_types.h"
+#include "src/trace_processor/importers/common/trace_parser.h"
 #include "src/trace_processor/importers/proto/proto_importer_module.h"
-#include "src/trace_processor/timestamped_trace_piece.h"
 
 namespace perfetto {
 namespace trace_processor {
 
 class FtraceModule : public ProtoImporterModule {
  public:
-  virtual void ParseFtracePacket(uint32_t cpu,
-                                 const TimestampedTracePiece& ttp);
+  virtual void ParseFtraceEventData(uint32_t cpu,
+                                    int64_t ts,
+                                    const TracePacketData& data);
+
+  virtual void ParseInlineSchedSwitch(uint32_t cpu,
+                                      int64_t ts,
+                                      const InlineSchedSwitch& data);
+
+  virtual void ParseInlineSchedWaking(uint32_t cpu,
+                                      int64_t ts,
+                                      const InlineSchedWaking& data);
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/ftrace/ftrace_module_impl.cc b/src/trace_processor/importers/ftrace/ftrace_module_impl.cc
index d7579ec..7b1a3f3 100644
--- a/src/trace_processor/importers/ftrace/ftrace_module_impl.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_module_impl.cc
@@ -19,7 +19,6 @@
 #include "perfetto/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/importers/ftrace/ftrace_parser.h"
 #include "src/trace_processor/importers/ftrace/ftrace_tokenizer.h"
-#include "src/trace_processor/timestamped_trace_piece.h"
 
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 
@@ -40,31 +39,21 @@
     int64_t /*packet_timestamp*/,
     PacketSequenceState* seq_state,
     uint32_t field_id) {
-  if (field_id == TracePacket::kFtraceEventsFieldNumber) {
-    auto ftrace_field = decoder.ftrace_events();
-    return tokenizer_.TokenizeFtraceBundle(
-        packet->slice(ftrace_field.data, ftrace_field.size), seq_state,
-        decoder.trusted_packet_sequence_id());
+  switch (field_id) {
+    case TracePacket::kFtraceEventsFieldNumber: {
+      auto ftrace_field = decoder.ftrace_events();
+      return tokenizer_.TokenizeFtraceBundle(
+          packet->slice(ftrace_field.data, ftrace_field.size), seq_state,
+          decoder.trusted_packet_sequence_id());
+    }
+    case TracePacket::kFtraceStatsFieldNumber: {
+      parser_.ParseFtraceStats(decoder.ftrace_stats(),
+                               decoder.trusted_packet_sequence_id());
+      return ModuleResult::Handled();
+    }
   }
   return ModuleResult::Ignored();
 }
 
-void FtraceModuleImpl::ParsePacket(
-    const protos::pbzero::TracePacket::Decoder& decoder,
-    const TimestampedTracePiece&,
-    uint32_t field_id) {
-  if (field_id == TracePacket::kFtraceStatsFieldNumber) {
-    parser_.ParseFtraceStats(decoder.ftrace_stats());
-  }
-}
-
-void FtraceModuleImpl::ParseFtracePacket(uint32_t cpu,
-                                         const TimestampedTracePiece& ttp) {
-  util::Status res = parser_.ParseFtraceEvent(cpu, ttp);
-  if (!res.ok()) {
-    PERFETTO_ELOG("%s", res.message().c_str());
-  }
-}
-
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/ftrace/ftrace_module_impl.h b/src/trace_processor/importers/ftrace/ftrace_module_impl.h
index da6ffd9..a341869 100644
--- a/src/trace_processor/importers/ftrace/ftrace_module_impl.h
+++ b/src/trace_processor/importers/ftrace/ftrace_module_impl.h
@@ -18,13 +18,12 @@
 #define SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_FTRACE_MODULE_IMPL_H_
 
 #include "perfetto/base/build_config.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "src/trace_processor/importers/common/parser_types.h"
 #include "src/trace_processor/importers/ftrace/ftrace_module.h"
 #include "src/trace_processor/importers/ftrace/ftrace_parser.h"
 #include "src/trace_processor/importers/ftrace/ftrace_tokenizer.h"
 #include "src/trace_processor/importers/proto/proto_importer_module.h"
-#include "src/trace_processor/timestamped_trace_piece.h"
-
-#include "protos/perfetto/trace/trace_packet.pbzero.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -33,7 +32,7 @@
 
 class FtraceModuleImpl : public FtraceModule {
  public:
-  FtraceModuleImpl(TraceProcessorContext* context);
+  explicit FtraceModuleImpl(TraceProcessorContext* context);
 
   ModuleResult TokenizePacket(
       const protos::pbzero::TracePacket::Decoder& decoder,
@@ -42,12 +41,32 @@
       PacketSequenceState* state,
       uint32_t field_id) override;
 
-  void ParsePacket(const protos::pbzero::TracePacket::Decoder& decoder,
-                   const TimestampedTracePiece&,
-                   uint32_t field_id) override;
+  void ParseFtraceEventData(uint32_t cpu,
+                            int64_t ts,
+                            const TracePacketData& data) override {
+    util::Status res = parser_.ParseFtraceEvent(cpu, ts, data);
+    if (!res.ok()) {
+      PERFETTO_ELOG("%s", res.message().c_str());
+    }
+  }
 
-  void ParseFtracePacket(uint32_t cpu,
-                         const TimestampedTracePiece& ttp) override;
+  void ParseInlineSchedSwitch(uint32_t cpu,
+                              int64_t ts,
+                              const InlineSchedSwitch& data) override {
+    util::Status res = parser_.ParseInlineSchedSwitch(cpu, ts, data);
+    if (!res.ok()) {
+      PERFETTO_ELOG("%s", res.message().c_str());
+    }
+  }
+
+  void ParseInlineSchedWaking(uint32_t cpu,
+                              int64_t ts,
+                              const InlineSchedWaking& data) override {
+    util::Status res = parser_.ParseInlineSchedWaking(cpu, ts, data);
+    if (!res.ok()) {
+      PERFETTO_ELOG("%s", res.message().c_str());
+    }
+  }
 
  private:
   FtraceTokenizer tokenizer_;
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.cc b/src/trace_processor/importers/ftrace/ftrace_parser.cc
index 9364d71..29582c9 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.cc
@@ -17,14 +17,20 @@
 #include "src/trace_processor/importers/ftrace/ftrace_parser.h"
 
 #include "perfetto/base/logging.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "perfetto/ext/base/string_writer.h"
 #include "perfetto/protozero/proto_decoder.h"
 #include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/async_track_set_tracker.h"
+#include "src/trace_processor/importers/common/parser_types.h"
 #include "src/trace_processor/importers/common/process_tracker.h"
 #include "src/trace_processor/importers/ftrace/binder_tracker.h"
 #include "src/trace_processor/importers/ftrace/thread_state_tracker.h"
+#include "src/trace_processor/importers/ftrace/v4l2_tracker.h"
+#include "src/trace_processor/importers/ftrace/virtio_video_tracker.h"
 #include "src/trace_processor/importers/i2c/i2c_tracker.h"
-#include "src/trace_processor/importers/proto/async_track_set_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/importers/syscalls/syscall_tracker.h"
 #include "src/trace_processor/importers/systrace/systrace_parser.h"
 #include "src/trace_processor/storage/stats.h"
@@ -34,6 +40,7 @@
 
 #include "protos/perfetto/common/gpu_counter_descriptor.pbzero.h"
 #include "protos/perfetto/trace/ftrace/binder.pbzero.h"
+#include "protos/perfetto/trace/ftrace/cma.pbzero.h"
 #include "protos/perfetto/trace/ftrace/cpuhp.pbzero.h"
 #include "protos/perfetto/trace/ftrace/cros_ec.pbzero.h"
 #include "protos/perfetto/trace/ftrace/dmabuf_heap.pbzero.h"
@@ -50,6 +57,7 @@
 #include "protos/perfetto/trace/ftrace/irq.pbzero.h"
 #include "protos/perfetto/trace/ftrace/kmem.pbzero.h"
 #include "protos/perfetto/trace/ftrace/lowmemorykiller.pbzero.h"
+#include "protos/perfetto/trace/ftrace/lwis.pbzero.h"
 #include "protos/perfetto/trace/ftrace/mali.pbzero.h"
 #include "protos/perfetto/trace/ftrace/mm_event.pbzero.h"
 #include "protos/perfetto/trace/ftrace/net.pbzero.h"
@@ -66,6 +74,7 @@
 #include "protos/perfetto/trace/ftrace/task.pbzero.h"
 #include "protos/perfetto/trace/ftrace/tcp.pbzero.h"
 #include "protos/perfetto/trace/ftrace/thermal.pbzero.h"
+#include "protos/perfetto/trace/ftrace/trusty.pbzero.h"
 #include "protos/perfetto/trace/ftrace/ufs.pbzero.h"
 #include "protos/perfetto/trace/ftrace/vmscan.pbzero.h"
 #include "protos/perfetto/trace/ftrace/workqueue.pbzero.h"
@@ -90,17 +99,25 @@
 // TODO(lalitm): going through this array is O(n) on a hot-path (see
 // ParseTypedFtraceToRaw). Consider changing this if we end up adding a lot of
 // events here.
-constexpr auto kKernelFunctionFields = std::array<FtraceEventAndFieldId, 3>{
-    {FtraceEventAndFieldId{
-         protos::pbzero::FtraceEvent::kSchedBlockedReasonFieldNumber,
-         protos::pbzero::SchedBlockedReasonFtraceEvent::kCallerFieldNumber},
-     FtraceEventAndFieldId{
-         protos::pbzero::FtraceEvent::kWorkqueueExecuteStartFieldNumber,
-         protos::pbzero::WorkqueueExecuteStartFtraceEvent::
-             kFunctionFieldNumber},
-     FtraceEventAndFieldId{
-         protos::pbzero::FtraceEvent::kWorkqueueQueueWorkFieldNumber,
-         protos::pbzero::WorkqueueQueueWorkFtraceEvent::kFunctionFieldNumber}}};
+constexpr auto kKernelFunctionFields = std::array<FtraceEventAndFieldId, 6>{
+    FtraceEventAndFieldId{
+        protos::pbzero::FtraceEvent::kSchedBlockedReasonFieldNumber,
+        protos::pbzero::SchedBlockedReasonFtraceEvent::kCallerFieldNumber},
+    FtraceEventAndFieldId{
+        protos::pbzero::FtraceEvent::kWorkqueueExecuteStartFieldNumber,
+        protos::pbzero::WorkqueueExecuteStartFtraceEvent::kFunctionFieldNumber},
+    FtraceEventAndFieldId{
+        protos::pbzero::FtraceEvent::kWorkqueueQueueWorkFieldNumber,
+        protos::pbzero::WorkqueueQueueWorkFtraceEvent::kFunctionFieldNumber},
+    FtraceEventAndFieldId{
+        protos::pbzero::FtraceEvent::kFuncgraphEntryFieldNumber,
+        protos::pbzero::FuncgraphEntryFtraceEvent::kFuncFieldNumber},
+    FtraceEventAndFieldId{
+        protos::pbzero::FtraceEvent::kFuncgraphExitFieldNumber,
+        protos::pbzero::FuncgraphExitFtraceEvent::kFuncFieldNumber},
+    FtraceEventAndFieldId{
+        protos::pbzero::FtraceEvent::kMmShrinkSlabStartFieldNumber,
+        protos::pbzero::MmShrinkSlabStartFtraceEvent::kShrinkFieldNumber}};
 
 std::string GetUfsCmdString(uint32_t ufsopcode, uint32_t gid) {
   std::string buffer;
@@ -203,6 +220,8 @@
       rss_stat_tracker_(context),
       drm_tracker_(context),
       iostat_tracker_(context),
+      virtio_gpu_tracker_(context),
+      mali_gpu_event_tracker_(context),
       sched_wakeup_name_id_(context->storage->InternString("sched_wakeup")),
       sched_waking_name_id_(context->storage->InternString("sched_waking")),
       cpu_id_(context->storage->InternString("cpu")),
@@ -264,7 +283,29 @@
       ufs_clkgating_id_(context->storage->InternString(
           "io.ufs.clkgating (OFF:0/REQ_OFF/REQ_ON/ON:3)")),
       ufs_command_count_id_(
-          context->storage->InternString("io.ufs.command.count")) {
+          context->storage->InternString("io.ufs.command.count")),
+      shrink_slab_id_(context_->storage->InternString("mm_vmscan_shrink_slab")),
+      shrink_name_id_(context->storage->InternString("shrink_name")),
+      shrink_total_scan_id_(context->storage->InternString("total_scan")),
+      shrink_freed_id_(context->storage->InternString("freed")),
+      shrink_priority_id_(context->storage->InternString("priority")),
+      trusty_category_id_(context_->storage->InternString("tipc")),
+      trusty_name_trusty_std_id_(context_->storage->InternString("trusty_std")),
+      trusty_name_tipc_rx_id_(context_->storage->InternString("tipc_rx")),
+      cma_alloc_id_(context_->storage->InternString("mm_cma_alloc")),
+      cma_name_id_(context_->storage->InternString("cma_name")),
+      cma_pfn_id_(context_->storage->InternString("cma_pfn")),
+      cma_req_pages_id_(context_->storage->InternString("cma_req_pages")),
+      cma_nr_migrated_id_(context_->storage->InternString("cma_nr_migrated")),
+      cma_nr_reclaimed_id_(context_->storage->InternString("cma_nr_reclaimed")),
+      cma_nr_mapped_id_(context_->storage->InternString("cma_nr_mapped")),
+      cma_nr_isolate_fail_id_(
+          context_->storage->InternString("cma_nr_isolate_fail")),
+      cma_nr_migrate_fail_id_(
+          context_->storage->InternString("cma_nr_migrate_fail")),
+      cma_nr_test_fail_id_(context_->storage->InternString("cma_nr_test_fail")),
+      syscall_ret_id_(context->storage->InternString("ret")),
+      syscall_args_id_(context->storage->InternString("args")) {
   // 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++) {
@@ -338,7 +379,8 @@
            context->storage->InternString("mem.mm.kern_alloc.avg_lat"))}};
 }
 
-void FtraceParser::ParseFtraceStats(ConstBytes blob) {
+void FtraceParser::ParseFtraceStats(ConstBytes blob,
+                                    uint32_t packet_sequence_id) {
   protos::pbzero::FtraceStats::Decoder evt(blob.data, blob.size);
   bool is_start =
       evt.phase() == protos::pbzero::FtraceStats::Phase::START_OF_TRACE;
@@ -464,89 +506,45 @@
   //    medatata table.
   // Both will be reported in the 'Info & stats' page in the UI.
   if (is_start) {
-    std::string error_str;
-    for (auto it = evt.failed_ftrace_events(); it; ++it) {
-      storage->IncrementStats(stats::ftrace_setup_errors, 1);
-      error_str += "Ftrace event failed: " + it->as_std_string() + "\n";
+    if (seen_errors_for_sequence_id_.count(packet_sequence_id) == 0) {
+      std::string error_str;
+      for (auto it = evt.failed_ftrace_events(); it; ++it) {
+        storage->IncrementStats(stats::ftrace_setup_errors, 1);
+        error_str += "Ftrace event failed: " + it->as_std_string() + "\n";
+      }
+      for (auto it = evt.unknown_ftrace_events(); it; ++it) {
+        storage->IncrementStats(stats::ftrace_setup_errors, 1);
+        error_str += "Ftrace event unknown: " + it->as_std_string() + "\n";
+      }
+      if (evt.atrace_errors().size > 0) {
+        storage->IncrementStats(stats::ftrace_setup_errors, 1);
+        error_str += "Atrace failures: " + evt.atrace_errors().ToStdString();
+      }
+      if (!error_str.empty()) {
+        auto error_str_id = storage->InternString(base::StringView(error_str));
+        context_->metadata_tracker->AppendMetadata(
+            metadata::ftrace_setup_errors, Variadic::String(error_str_id));
+        seen_errors_for_sequence_id_.insert(packet_sequence_id);
+      }
     }
-    for (auto it = evt.unknown_ftrace_events(); it; ++it) {
-      storage->IncrementStats(stats::ftrace_setup_errors, 1);
-      error_str += "Ftrace event unknown: " + it->as_std_string() + "\n";
+    if (evt.preserve_ftrace_buffer()) {
+      preserve_ftrace_buffer_ = true;
     }
-    if (evt.atrace_errors().size > 0) {
-      storage->IncrementStats(stats::ftrace_setup_errors, 1);
-      error_str += "Atrace failures: " + evt.atrace_errors().ToStdString();
-    }
-    auto error_str_id = storage->InternString(base::StringView(error_str));
-    context_->metadata_tracker->SetMetadata(metadata::ftrace_setup_errors,
-                                            Variadic::String(error_str_id));
   }
 }
 
-PERFETTO_ALWAYS_INLINE
 util::Status FtraceParser::ParseFtraceEvent(uint32_t cpu,
-                                            const TimestampedTracePiece& ttp) {
-  int64_t ts = ttp.timestamp;
-
-  // On the first ftrace packet, check the metadata table for the
-  // ts of the event which is specified in the config. If it exists we can use
-  // it to filter out ftrace packets which happen earlier than it.
-  if (PERFETTO_UNLIKELY(!has_seen_first_ftrace_packet_)) {
-    DropFtraceDataBefore drop_before = context_->config.drop_ftrace_data_before;
-    switch (drop_before) {
-      case DropFtraceDataBefore::kNoDrop: {
-        drop_ftrace_data_before_ts_ = 0;
-        break;
-      }
-      case DropFtraceDataBefore::kAllDataSourcesStarted:
-      case DropFtraceDataBefore::kTracingStarted: {
-        metadata::KeyId event_key =
-            drop_before == DropFtraceDataBefore::kAllDataSourcesStarted
-                ? metadata::all_data_source_started_ns
-                : metadata::tracing_started_ns;
-        const auto& metadata = context_->storage->metadata_table();
-        base::Optional<uint32_t> opt_row =
-            metadata.name().IndexOf(metadata::kNames[event_key]);
-        if (opt_row) {
-          drop_ftrace_data_before_ts_ = *metadata.int_value()[*opt_row];
-        }
-        break;
-      }
-    }
-    has_seen_first_ftrace_packet_ = true;
-  }
-
+                                            int64_t ts,
+                                            const TracePacketData& data) {
+  MaybeOnFirstFtraceEvent();
   if (PERFETTO_UNLIKELY(ts < drop_ftrace_data_before_ts_)) {
     context_->storage->IncrementStats(
         stats::ftrace_packet_before_tracing_start);
     return util::OkStatus();
   }
-
   using protos::pbzero::FtraceEvent;
-  SchedEventTracker* sched_tracker = SchedEventTracker::GetOrCreate(context_);
-
-  // Handle the (optional) alternative encoding format for sched_switch.
-  if (ttp.type == TimestampedTracePiece::Type::kInlineSchedSwitch) {
-    const auto& event = ttp.sched_switch;
-    sched_tracker->PushSchedSwitchCompact(cpu, ts, event.prev_state,
-                                          static_cast<uint32_t>(event.next_pid),
-                                          event.next_prio, event.next_comm);
-    return util::OkStatus();
-  }
-
-  // Handle the (optional) alternative encoding format for sched_waking.
-  if (ttp.type == TimestampedTracePiece::Type::kInlineSchedWaking) {
-    const auto& event = ttp.sched_waking;
-    sched_tracker->PushSchedWakingCompact(
-        cpu, ts, static_cast<uint32_t>(event.pid), event.target_cpu, event.prio,
-        event.comm);
-    return util::OkStatus();
-  }
-
-  PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kFtraceEvent);
-  const TraceBlobView& event = ttp.ftrace_event.event;
-  PacketSequenceStateGeneration* seq_state =
-      ttp.ftrace_event.sequence_state.get();
+  const TraceBlobView& event = data.packet;
+  PacketSequenceStateGeneration* seq_state = data.sequence_state.get();
   ProtoDecoder decoder(event.data(), event.length());
   uint64_t raw_pid = 0;
   if (auto pid_field = decoder.FindField(FtraceEvent::kPidFieldNumber)) {
@@ -562,274 +560,299 @@
     if (is_metadata_field)
       continue;
 
-    ConstBytes data = fld.as_bytes();
+    ConstBytes fld_bytes = fld.as_bytes();
     if (fld.id() == FtraceEvent::kGenericFieldNumber) {
-      ParseGenericFtrace(ts, cpu, pid, data);
+      ParseGenericFtrace(ts, cpu, pid, fld_bytes);
     } else if (fld.id() != FtraceEvent::kSchedSwitchFieldNumber) {
       // sched_switch parsing populates the raw table by itself
-      ParseTypedFtraceToRaw(fld.id(), ts, cpu, pid, data, seq_state);
+      ParseTypedFtraceToRaw(fld.id(), ts, cpu, pid, fld_bytes, seq_state);
     }
 
     switch (fld.id()) {
       case FtraceEvent::kSchedSwitchFieldNumber: {
-        ParseSchedSwitch(cpu, ts, data);
+        ParseSchedSwitch(cpu, ts, fld_bytes);
         break;
       }
       case FtraceEvent::kSchedWakingFieldNumber: {
-        ParseSchedWaking(ts, pid, data);
+        ParseSchedWaking(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kSchedProcessFreeFieldNumber: {
-        ParseSchedProcessFree(ts, data);
+        ParseSchedProcessFree(ts, fld_bytes);
         break;
       }
       case FtraceEvent::kCpuFrequencyFieldNumber: {
-        ParseCpuFreq(ts, data);
+        ParseCpuFreq(ts, fld_bytes);
         break;
       }
       case FtraceEvent::kGpuFrequencyFieldNumber: {
-        ParseGpuFreq(ts, data);
+        ParseGpuFreq(ts, fld_bytes);
         break;
       }
       case FtraceEvent::kCpuIdleFieldNumber: {
-        ParseCpuIdle(ts, data);
+        ParseCpuIdle(ts, fld_bytes);
         break;
       }
       case FtraceEvent::kPrintFieldNumber: {
-        ParsePrint(ts, pid, data);
+        ParsePrint(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kZeroFieldNumber: {
-        ParseZero(ts, pid, data);
+        ParseZero(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kRssStatThrottledFieldNumber:
       case FtraceEvent::kRssStatFieldNumber: {
-        rss_stat_tracker_.ParseRssStat(ts, fld.id(), pid, data);
+        rss_stat_tracker_.ParseRssStat(ts, fld.id(), pid, fld_bytes);
         break;
       }
       case FtraceEvent::kIonHeapGrowFieldNumber: {
-        ParseIonHeapGrowOrShrink(ts, pid, data, true);
+        ParseIonHeapGrowOrShrink(ts, pid, fld_bytes, true);
         break;
       }
       case FtraceEvent::kIonHeapShrinkFieldNumber: {
-        ParseIonHeapGrowOrShrink(ts, pid, data, false);
+        ParseIonHeapGrowOrShrink(ts, pid, fld_bytes, false);
         break;
       }
       case FtraceEvent::kIonStatFieldNumber: {
-        ParseIonStat(ts, pid, data);
+        ParseIonStat(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kDmaHeapStatFieldNumber: {
-        ParseDmaHeapStat(ts, pid, data);
+        ParseDmaHeapStat(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kSignalGenerateFieldNumber: {
-        ParseSignalGenerate(ts, data);
+        ParseSignalGenerate(ts, fld_bytes);
         break;
       }
       case FtraceEvent::kSignalDeliverFieldNumber: {
-        ParseSignalDeliver(ts, pid, data);
+        ParseSignalDeliver(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kOomScoreAdjUpdateFieldNumber: {
-        ParseOOMScoreAdjUpdate(ts, data);
+        ParseOOMScoreAdjUpdate(ts, fld_bytes);
         break;
       }
       case FtraceEvent::kMarkVictimFieldNumber: {
-        ParseOOMKill(ts, data);
+        ParseOOMKill(ts, fld_bytes);
         break;
       }
       case FtraceEvent::kMmEventRecordFieldNumber: {
-        ParseMmEventRecord(ts, pid, data);
+        ParseMmEventRecord(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kSysEnterFieldNumber: {
-        ParseSysEvent(ts, pid, true, data);
+        ParseSysEnterEvent(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kSysExitFieldNumber: {
-        ParseSysEvent(ts, pid, false, data);
+        ParseSysExitEvent(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kTaskNewtaskFieldNumber: {
-        ParseTaskNewTask(ts, pid, data);
+        ParseTaskNewTask(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kTaskRenameFieldNumber: {
-        ParseTaskRename(data);
+        ParseTaskRename(fld_bytes);
         break;
       }
       case FtraceEvent::kBinderTransactionFieldNumber: {
-        ParseBinderTransaction(ts, pid, data);
+        ParseBinderTransaction(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kBinderTransactionReceivedFieldNumber: {
-        ParseBinderTransactionReceived(ts, pid, data);
+        ParseBinderTransactionReceived(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kBinderTransactionAllocBufFieldNumber: {
-        ParseBinderTransactionAllocBuf(ts, pid, data);
+        ParseBinderTransactionAllocBuf(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kBinderLockFieldNumber: {
-        ParseBinderLock(ts, pid, data);
+        ParseBinderLock(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kBinderUnlockFieldNumber: {
-        ParseBinderUnlock(ts, pid, data);
+        ParseBinderUnlock(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kBinderLockedFieldNumber: {
-        ParseBinderLocked(ts, pid, data);
+        ParseBinderLocked(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kSdeTracingMarkWriteFieldNumber: {
-        ParseSdeTracingMarkWrite(ts, pid, data);
+        ParseSdeTracingMarkWrite(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kClockSetRateFieldNumber: {
-        ParseClockSetRate(ts, data);
+        ParseClockSetRate(ts, fld_bytes);
         break;
       }
       case FtraceEvent::kClockEnableFieldNumber: {
-        ParseClockEnable(ts, data);
+        ParseClockEnable(ts, fld_bytes);
         break;
       }
       case FtraceEvent::kClockDisableFieldNumber: {
-        ParseClockDisable(ts, data);
+        ParseClockDisable(ts, fld_bytes);
         break;
       }
       case FtraceEvent::kScmCallStartFieldNumber: {
-        ParseScmCallStart(ts, pid, data);
+        ParseScmCallStart(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kScmCallEndFieldNumber: {
-        ParseScmCallEnd(ts, pid, data);
+        ParseScmCallEnd(ts, pid, fld_bytes);
+        break;
+      }
+      case FtraceEvent::kCmaAllocStartFieldNumber: {
+        ParseCmaAllocStart(ts, pid);
+        break;
+      }
+      case FtraceEvent::kCmaAllocInfoFieldNumber: {
+        ParseCmaAllocInfo(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kMmVmscanDirectReclaimBeginFieldNumber: {
-        ParseDirectReclaimBegin(ts, pid, data);
+        ParseDirectReclaimBegin(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kMmVmscanDirectReclaimEndFieldNumber: {
-        ParseDirectReclaimEnd(ts, pid, data);
+        ParseDirectReclaimEnd(ts, pid, fld_bytes);
+        break;
+      }
+      case FtraceEvent::kMmShrinkSlabStartFieldNumber: {
+        ParseShrinkSlabStart(ts, pid, fld_bytes, seq_state);
+        break;
+      }
+      case FtraceEvent::kMmShrinkSlabEndFieldNumber: {
+        ParseShrinkSlabEnd(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kWorkqueueExecuteStartFieldNumber: {
-        ParseWorkqueueExecuteStart(cpu, ts, pid, data, seq_state);
+        ParseWorkqueueExecuteStart(cpu, ts, pid, fld_bytes, seq_state);
         break;
       }
       case FtraceEvent::kWorkqueueExecuteEndFieldNumber: {
-        ParseWorkqueueExecuteEnd(ts, pid, data);
+        ParseWorkqueueExecuteEnd(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kIrqHandlerEntryFieldNumber: {
-        ParseIrqHandlerEntry(cpu, ts, data);
+        ParseIrqHandlerEntry(cpu, ts, fld_bytes);
         break;
       }
       case FtraceEvent::kIrqHandlerExitFieldNumber: {
-        ParseIrqHandlerExit(cpu, ts, data);
+        ParseIrqHandlerExit(cpu, ts, fld_bytes);
         break;
       }
       case FtraceEvent::kSoftirqEntryFieldNumber: {
-        ParseSoftIrqEntry(cpu, ts, data);
+        ParseSoftIrqEntry(cpu, ts, fld_bytes);
         break;
       }
       case FtraceEvent::kSoftirqExitFieldNumber: {
-        ParseSoftIrqExit(cpu, ts, data);
+        ParseSoftIrqExit(cpu, ts, fld_bytes);
         break;
       }
       case FtraceEvent::kGpuMemTotalFieldNumber: {
-        ParseGpuMemTotal(ts, data);
+        ParseGpuMemTotal(ts, fld_bytes);
         break;
       }
       case FtraceEvent::kThermalTemperatureFieldNumber: {
-        ParseThermalTemperature(ts, data);
+        ParseThermalTemperature(ts, fld_bytes);
         break;
       }
       case FtraceEvent::kCdevUpdateFieldNumber: {
-        ParseCdevUpdate(ts, data);
+        ParseCdevUpdate(ts, fld_bytes);
         break;
       }
       case FtraceEvent::kSchedBlockedReasonFieldNumber: {
-        ParseSchedBlockedReason(data, seq_state);
+        ParseSchedBlockedReason(fld_bytes, seq_state);
         break;
       }
       case FtraceEvent::kFastrpcDmaStatFieldNumber: {
-        ParseFastRpcDmaStat(ts, pid, data);
+        ParseFastRpcDmaStat(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kG2dTracingMarkWriteFieldNumber: {
-        ParseG2dTracingMarkWrite(ts, pid, data);
+        ParseG2dTracingMarkWrite(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kDpuTracingMarkWriteFieldNumber: {
-        ParseDpuTracingMarkWrite(ts, pid, data);
+        ParseDpuTracingMarkWrite(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kMaliTracingMarkWriteFieldNumber: {
-        ParseMaliTracingMarkWrite(ts, pid, data);
+        ParseMaliTracingMarkWrite(ts, pid, fld_bytes);
+        break;
+      }
+      case FtraceEvent::kLwisTracingMarkWriteFieldNumber: {
+        ParseLwisTracingMarkWrite(ts, pid, fld_bytes);
+        break;
+      }
+      case FtraceEvent::kVirtioGpuCmdQueueFieldNumber:
+      case FtraceEvent::kVirtioGpuCmdResponseFieldNumber: {
+        virtio_gpu_tracker_.ParseVirtioGpu(ts, fld.id(), pid, fld_bytes);
         break;
       }
       case FtraceEvent::kCpuhpPauseFieldNumber: {
-        ParseCpuhpPause(ts, pid, data);
+        ParseCpuhpPause(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kNetifReceiveSkbFieldNumber: {
-        ParseNetifReceiveSkb(cpu, ts, data);
+        ParseNetifReceiveSkb(cpu, ts, fld_bytes);
         break;
       }
       case FtraceEvent::kNetDevXmitFieldNumber: {
-        ParseNetDevXmit(cpu, ts, data);
+        ParseNetDevXmit(cpu, ts, fld_bytes);
         break;
       }
       case FtraceEvent::kInetSockSetStateFieldNumber: {
-        ParseInetSockSetState(ts, pid, data);
+        ParseInetSockSetState(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kTcpRetransmitSkbFieldNumber: {
-        ParseTcpRetransmitSkb(ts, data);
+        ParseTcpRetransmitSkb(ts, fld_bytes);
         break;
       }
       case FtraceEvent::kNapiGroReceiveEntryFieldNumber: {
-        ParseNapiGroReceiveEntry(cpu, ts, data);
+        ParseNapiGroReceiveEntry(cpu, ts, fld_bytes);
         break;
       }
       case FtraceEvent::kNapiGroReceiveExitFieldNumber: {
-        ParseNapiGroReceiveExit(cpu, ts, data);
+        ParseNapiGroReceiveExit(cpu, ts, fld_bytes);
         break;
       }
       case FtraceEvent::kCpuFrequencyLimitsFieldNumber: {
-        ParseCpuFrequencyLimits(ts, data);
+        ParseCpuFrequencyLimits(ts, fld_bytes);
         break;
       }
       case FtraceEvent::kKfreeSkbFieldNumber: {
-        ParseKfreeSkb(ts, data);
+        ParseKfreeSkb(ts, fld_bytes);
         break;
       }
       case FtraceEvent::kCrosEcSensorhubDataFieldNumber: {
-        ParseCrosEcSensorhubData(ts, data);
+        ParseCrosEcSensorhubData(ts, fld_bytes);
         break;
       }
       case FtraceEvent::kUfshcdCommandFieldNumber: {
-        ParseUfshcdCommand(ts, data);
+        ParseUfshcdCommand(ts, fld_bytes);
         break;
       }
       case FtraceEvent::kWakeupSourceActivateFieldNumber: {
-        ParseWakeSourceActivate(ts, data);
+        ParseWakeSourceActivate(ts, fld_bytes);
         break;
       }
       case FtraceEvent::kWakeupSourceDeactivateFieldNumber: {
-        ParseWakeSourceDeactivate(ts, data);
+        ParseWakeSourceDeactivate(ts, fld_bytes);
         break;
       }
       case FtraceEvent::kUfshcdClkGatingFieldNumber: {
-        ParseUfshcdClkGating(ts, data);
+        ParseUfshcdClkGating(ts, fld_bytes);
         break;
       }
       case FtraceEvent::kSuspendResumeFieldNumber: {
-        ParseSuspendResume(ts, data);
+        ParseSuspendResume(ts, fld_bytes);
         break;
       }
       case FtraceEvent::kDrmVblankEventFieldNumber:
@@ -842,33 +865,141 @@
       case FtraceEvent::kDmaFenceSignaledFieldNumber:
       case FtraceEvent::kDmaFenceWaitStartFieldNumber:
       case FtraceEvent::kDmaFenceWaitEndFieldNumber: {
-        drm_tracker_.ParseDrm(ts, fld.id(), pid, data);
+        drm_tracker_.ParseDrm(ts, fld.id(), pid, fld_bytes);
         break;
       }
       case FtraceEvent::kF2fsIostatFieldNumber: {
-        iostat_tracker_.ParseF2fsIostat(ts, data);
+        iostat_tracker_.ParseF2fsIostat(ts, fld_bytes);
         break;
       }
       case FtraceEvent::kF2fsIostatLatencyFieldNumber: {
-        iostat_tracker_.ParseF2fsIostatLatency(ts, data);
+        iostat_tracker_.ParseF2fsIostatLatency(ts, fld_bytes);
         break;
       }
       case FtraceEvent::kSchedCpuUtilCfsFieldNumber: {
-        ParseSchedCpuUtilCfs(ts, data);
+        ParseSchedCpuUtilCfs(ts, fld_bytes);
         break;
       }
       case FtraceEvent::kI2cReadFieldNumber: {
-        ParseI2cReadEvent(ts, pid, data);
+        ParseI2cReadEvent(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kI2cWriteFieldNumber: {
-        ParseI2cWriteEvent(ts, pid, data);
+        ParseI2cWriteEvent(ts, pid, fld_bytes);
         break;
       }
       case FtraceEvent::kI2cResultFieldNumber: {
-        ParseI2cResultEvent(ts, pid, data);
+        ParseI2cResultEvent(ts, pid, fld_bytes);
         break;
       }
+      case FtraceEvent::kFuncgraphEntryFieldNumber: {
+        ParseFuncgraphEntry(ts, pid, fld_bytes, seq_state);
+        break;
+      }
+      case FtraceEvent::kFuncgraphExitFieldNumber: {
+        ParseFuncgraphExit(ts, pid, fld_bytes, seq_state);
+        break;
+      }
+      case FtraceEvent::kV4l2QbufFieldNumber:
+      case FtraceEvent::kV4l2DqbufFieldNumber:
+      case FtraceEvent::kVb2V4l2BufQueueFieldNumber:
+      case FtraceEvent::kVb2V4l2BufDoneFieldNumber:
+      case FtraceEvent::kVb2V4l2QbufFieldNumber:
+      case FtraceEvent::kVb2V4l2DqbufFieldNumber: {
+        V4l2Tracker::GetOrCreate(context_)->ParseV4l2Event(fld.id(), ts, pid,
+                                                           fld_bytes);
+        break;
+      }
+      case FtraceEvent::kVirtioVideoCmdFieldNumber:
+      case FtraceEvent::kVirtioVideoCmdDoneFieldNumber:
+      case FtraceEvent::kVirtioVideoResourceQueueFieldNumber:
+      case FtraceEvent::kVirtioVideoResourceQueueDoneFieldNumber: {
+        VirtioVideoTracker::GetOrCreate(context_)->ParseVirtioVideoEvent(
+            fld.id(), ts, fld_bytes);
+        break;
+      }
+      case FtraceEvent::kTrustySmcFieldNumber: {
+        ParseTrustySmc(pid, ts, fld_bytes);
+        break;
+      }
+      case FtraceEvent::kTrustySmcDoneFieldNumber: {
+        ParseTrustySmcDone(pid, ts, fld_bytes);
+        break;
+      }
+      case FtraceEvent::kTrustyStdCall32FieldNumber: {
+        ParseTrustyStdCall32(pid, ts, fld_bytes);
+        break;
+      }
+      case FtraceEvent::kTrustyStdCall32DoneFieldNumber: {
+        ParseTrustyStdCall32Done(pid, ts, fld_bytes);
+        break;
+      }
+      case FtraceEvent::kTrustyShareMemoryFieldNumber: {
+        ParseTrustyShareMemory(pid, ts, fld_bytes);
+        break;
+      }
+      case FtraceEvent::kTrustyShareMemoryDoneFieldNumber: {
+        ParseTrustyShareMemoryDone(pid, ts, fld_bytes);
+        break;
+      }
+      case FtraceEvent::kTrustyReclaimMemoryFieldNumber: {
+        ParseTrustyReclaimMemory(pid, ts, fld_bytes);
+        break;
+      }
+      case FtraceEvent::kTrustyReclaimMemoryDoneFieldNumber: {
+        ParseTrustyReclaimMemoryDone(pid, ts, fld_bytes);
+        break;
+      }
+      case FtraceEvent::kTrustyIrqFieldNumber: {
+        ParseTrustyIrq(pid, ts, fld_bytes);
+        break;
+      }
+      case FtraceEvent::kTrustyIpcHandleEventFieldNumber: {
+        ParseTrustyIpcHandleEvent(pid, ts, fld_bytes);
+        break;
+      }
+      case FtraceEvent::kTrustyIpcConnectFieldNumber: {
+        ParseTrustyIpcConnect(pid, ts, fld_bytes);
+        break;
+      }
+      case FtraceEvent::kTrustyIpcConnectEndFieldNumber: {
+        ParseTrustyIpcConnectEnd(pid, ts, fld_bytes);
+        break;
+      }
+      case FtraceEvent::kTrustyIpcWriteFieldNumber: {
+        ParseTrustyIpcWrite(pid, ts, fld_bytes);
+        break;
+      }
+      case FtraceEvent::kTrustyIpcReadFieldNumber: {
+        ParseTrustyIpcRead(pid, ts, fld_bytes);
+        break;
+      }
+      case FtraceEvent::kTrustyIpcReadEndFieldNumber: {
+        ParseTrustyIpcReadEnd(pid, ts, fld_bytes);
+        break;
+      }
+      case FtraceEvent::kTrustyIpcPollFieldNumber: {
+        ParseTrustyIpcPoll(pid, ts, fld_bytes);
+        break;
+      }
+      case FtraceEvent::kTrustyIpcRxFieldNumber: {
+        ParseTrustyIpcRx(pid, ts, fld_bytes);
+        break;
+      }
+      case FtraceEvent::kTrustyEnqueueNopFieldNumber: {
+        ParseTrustyEnqueueNop(pid, ts, fld_bytes);
+        break;
+      }
+      case FtraceEvent::kMaliMaliKCPUCQSSETFieldNumber:
+      case FtraceEvent::kMaliMaliKCPUCQSWAITSTARTFieldNumber:
+      case FtraceEvent::kMaliMaliKCPUCQSWAITENDFieldNumber:
+      case FtraceEvent::kMaliMaliKCPUFENCESIGNALFieldNumber:
+      case FtraceEvent::kMaliMaliKCPUFENCEWAITSTARTFieldNumber:
+      case FtraceEvent::kMaliMaliKCPUFENCEWAITENDFieldNumber: {
+        mali_gpu_event_tracker_.ParseMaliGpuEvent(ts, fld.id(), pid);
+        break;
+      }
+
       default:
         break;
     }
@@ -877,6 +1008,73 @@
   PERFETTO_DCHECK(!decoder.bytes_left());
   return util::OkStatus();
 }
+util::Status FtraceParser::ParseInlineSchedSwitch(
+    uint32_t cpu,
+    int64_t ts,
+    const InlineSchedSwitch& data) {
+  MaybeOnFirstFtraceEvent();
+  if (PERFETTO_UNLIKELY(ts < drop_ftrace_data_before_ts_)) {
+    context_->storage->IncrementStats(
+        stats::ftrace_packet_before_tracing_start);
+    return util::OkStatus();
+  }
+
+  using protos::pbzero::FtraceEvent;
+  SchedEventTracker* sched_tracker = SchedEventTracker::GetOrCreate(context_);
+  sched_tracker->PushSchedSwitchCompact(cpu, ts, data.prev_state,
+                                        static_cast<uint32_t>(data.next_pid),
+                                        data.next_prio, data.next_comm);
+  return util::OkStatus();
+}
+
+util::Status FtraceParser::ParseInlineSchedWaking(
+    uint32_t cpu,
+    int64_t ts,
+    const InlineSchedWaking& data) {
+  MaybeOnFirstFtraceEvent();
+  if (PERFETTO_UNLIKELY(ts < drop_ftrace_data_before_ts_)) {
+    context_->storage->IncrementStats(
+        stats::ftrace_packet_before_tracing_start);
+    return util::OkStatus();
+  }
+  using protos::pbzero::FtraceEvent;
+  SchedEventTracker* sched_tracker = SchedEventTracker::GetOrCreate(context_);
+  sched_tracker->PushSchedWakingCompact(cpu, ts,
+                                        static_cast<uint32_t>(data.pid),
+                                        data.target_cpu, data.prio, data.comm);
+  return util::OkStatus();
+}
+
+void FtraceParser::MaybeOnFirstFtraceEvent() {
+  if (PERFETTO_LIKELY(has_seen_first_ftrace_packet_)) {
+    return;
+  }
+
+  DropFtraceDataBefore drop_before =
+      preserve_ftrace_buffer_ ? DropFtraceDataBefore::kNoDrop
+                              : context_->config.drop_ftrace_data_before;
+  switch (drop_before) {
+    case DropFtraceDataBefore::kNoDrop: {
+      drop_ftrace_data_before_ts_ = 0;
+      break;
+    }
+    case DropFtraceDataBefore::kAllDataSourcesStarted:
+    case DropFtraceDataBefore::kTracingStarted: {
+      metadata::KeyId event_key =
+          drop_before == DropFtraceDataBefore::kAllDataSourcesStarted
+              ? metadata::all_data_source_started_ns
+              : metadata::tracing_started_ns;
+      const auto& metadata = context_->storage->metadata_table();
+      base::Optional<uint32_t> opt_row =
+          metadata.name().IndexOf(metadata::kNames[event_key]);
+      if (opt_row) {
+        drop_ftrace_data_before_ts_ = *metadata.int_value()[*opt_row];
+      }
+      break;
+    }
+  }
+  has_seen_first_ftrace_packet_ = true;
+}
 
 void FtraceParser::ParseGenericFtrace(int64_t ts,
                                       uint32_t cpu,
@@ -1077,6 +1275,28 @@
 void FtraceParser::ParsePrint(int64_t timestamp,
                               uint32_t pid,
                               ConstBytes blob) {
+  // Atrace slices are emitted as begin/end events written into the tracefs
+  // trace_marker. If we're tracing syscalls, the reconstructed atrace slice
+  // would start and end in the middle of different sys_write slices (on the
+  // same track). Since trace_processor enforces strict slice nesting, we need
+  // to resolve this conflict. The chosen approach is to distort the data, and
+  // pretend that the write syscall ended at the atrace slice's boundary.
+  //
+  // In other words, this true structure:
+  // [write...].....[write...]
+  // ....[atrace_slice..].....
+  //
+  // Is turned into:
+  // [wr][atrace_slice..]
+  // ...............[wri]
+  //
+  base::Optional<UniqueTid> opt_utid =
+      context_->process_tracker->GetThreadOrNull(pid);
+  if (opt_utid) {
+    SyscallTracker::GetOrCreate(context_)->MaybeTruncateOngoingWriteSlice(
+        timestamp, *opt_utid);
+  }
+
   protos::pbzero::PrintFtraceEvent::Decoder evt(blob.data, blob.size);
   SystraceParser::GetOrCreate(context_)->ParsePrintEvent(timestamp, pid,
                                                          evt.buf());
@@ -1153,6 +1373,23 @@
       evt.name(), tgid, evt.value());
 }
 
+void FtraceParser::ParseLwisTracingMarkWrite(int64_t timestamp,
+                                             uint32_t pid,
+                                             ConstBytes blob) {
+  protos::pbzero::LwisTracingMarkWriteFtraceEvent::Decoder evt(blob.data,
+                                                               blob.size);
+  if (!evt.type()) {
+    context_->storage->IncrementStats(stats::systrace_parse_failure);
+    return;
+  }
+
+  uint32_t tgid = static_cast<uint32_t>(evt.pid());
+  SystraceParser::GetOrCreate(context_)->ParseKernelTracingMarkWrite(
+      timestamp, pid, static_cast<char>(evt.type()), false /*trace_begin*/,
+      evt.func_name(), tgid, evt.value());
+}
+
+
 /** Parses ion heap events present in Pixel kernels. */
 void FtraceParser::ParseIonHeapGrowOrShrink(int64_t timestamp,
                                             uint32_t pid,
@@ -1354,28 +1591,51 @@
       timestamp, evt.avg_lat(), counter_names.avg_lat, utid);
 }
 
-void FtraceParser::ParseSysEvent(int64_t timestamp,
-                                 uint32_t pid,
-                                 bool is_enter,
-                                 ConstBytes blob) {
+void FtraceParser::ParseSysEnterEvent(int64_t timestamp,
+                                      uint32_t pid,
+                                      ConstBytes blob) {
   protos::pbzero::SysEnterFtraceEvent::Decoder evt(blob.data, blob.size);
   uint32_t syscall_num = static_cast<uint32_t>(evt.id());
   UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
 
   SyscallTracker* syscall_tracker = SyscallTracker::GetOrCreate(context_);
-  if (is_enter) {
-    syscall_tracker->Enter(timestamp, utid, syscall_num);
-  } else {
-    syscall_tracker->Exit(timestamp, utid, syscall_num);
-  }
+  auto args_callback = [this, &evt](ArgsTracker::BoundInserter* inserter) {
+    // process all syscall arguments
+    uint32_t count = 0;
+    for (auto it = evt.args(); it; ++it) {
+      if (syscall_arg_name_ids_.size() == count) {
+        base::StackString<32> string_arg("args[%" PRId32 "]", count);
+        auto string_id =
+            context_->storage->InternString(string_arg.string_view());
+        syscall_arg_name_ids_.emplace_back(string_id);
+      }
+      inserter->AddArg(syscall_args_id_, syscall_arg_name_ids_[count],
+                       Variadic::UnsignedInteger(*it));
+      ++count;
+    }
+  };
+  syscall_tracker->Enter(timestamp, utid, syscall_num, args_callback);
+}
 
-  // We are reusing the same function for sys_enter and sys_exit.
-  // It is fine as the arguments are the same, but we need to be sure that the
-  // protobuf field id for both are the same.
-  static_assert(
-      static_cast<int>(protos::pbzero::SysEnterFtraceEvent::kIdFieldNumber) ==
-          static_cast<int>(protos::pbzero::SysExitFtraceEvent::kIdFieldNumber),
-      "field mismatch");
+void FtraceParser::ParseSysExitEvent(int64_t timestamp,
+                                     uint32_t pid,
+                                     ConstBytes blob) {
+  // Note: Although this seems duplicated to ParseSysEnterEvent, it is
+  //       not. We decode SysExitFtraceEvent here to handle the return
+  //       value of a syscall whereas SysEnterFtraceEvent is decoded
+  //       above to handle the syscall arguments.
+  protos::pbzero::SysExitFtraceEvent::Decoder evt(blob.data, blob.size);
+  uint32_t syscall_num = static_cast<uint32_t>(evt.id());
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+
+  SyscallTracker* syscall_tracker = SyscallTracker::GetOrCreate(context_);
+  auto args_callback = [this, &evt](ArgsTracker::BoundInserter* inserter) {
+    if (evt.has_ret()) {
+      const auto ret = evt.ret();
+      inserter->AddArg(syscall_ret_id_, Variadic::Integer(ret));
+    }
+  };
+  syscall_tracker->Exit(timestamp, utid, syscall_num, args_callback);
 }
 
 void FtraceParser::ParseI2cReadEvent(int64_t timestamp,
@@ -1551,9 +1811,8 @@
   TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
   protos::pbzero::ScmCallStartFtraceEvent::Decoder evt(blob.data, blob.size);
 
-  char str[64];
-  sprintf(str, "scm id=%#" PRIx64, evt.x0());
-  StringId name_id = context_->storage->InternString(str);
+  base::StackString<64> str("scm id=%#" PRIx64, evt.x0());
+  StringId name_id = context_->storage->InternString(str.string_view());
   context_->slice_tracker->Begin(timestamp, track_id, kNullStringId, name_id);
 }
 
@@ -1566,6 +1825,59 @@
   context_->slice_tracker->End(timestamp, track_id);
 }
 
+void FtraceParser::ParseCmaAllocStart(int64_t timestamp, uint32_t pid) {
+  base::Optional<VersionNumber> kernel_version =
+      SystemInfoTracker::GetOrCreate(context_)->GetKernelVersion();
+  // CmaAllocInfo event only exists after 5.10
+  if (kernel_version < VersionNumber{5, 10})
+    return;
+
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
+
+  context_->slice_tracker->Begin(timestamp, track_id, kNullStringId,
+                                 cma_alloc_id_);
+}
+
+void FtraceParser::ParseCmaAllocInfo(int64_t timestamp,
+                                     uint32_t pid,
+                                     ConstBytes blob) {
+  base::Optional<VersionNumber> kernel_version =
+      SystemInfoTracker::GetOrCreate(context_)->GetKernelVersion();
+  // CmaAllocInfo event only exists after 5.10
+  if (kernel_version < VersionNumber{5, 10})
+    return;
+
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
+  protos::pbzero::CmaAllocInfoFtraceEvent::Decoder cma_alloc_info(blob.data,
+                                                                  blob.size);
+  auto args_inserter = [this,
+                        &cma_alloc_info](ArgsTracker::BoundInserter* inserter) {
+    inserter->AddArg(cma_name_id_,
+                     Variadic::String(context_->storage->InternString(
+                         cma_alloc_info.name())));
+    inserter->AddArg(cma_pfn_id_,
+                     Variadic::UnsignedInteger(cma_alloc_info.pfn()));
+    inserter->AddArg(cma_req_pages_id_,
+                     Variadic::UnsignedInteger(cma_alloc_info.count()));
+    inserter->AddArg(cma_nr_migrated_id_,
+                     Variadic::UnsignedInteger(cma_alloc_info.nr_migrated()));
+    inserter->AddArg(cma_nr_reclaimed_id_,
+                     Variadic::UnsignedInteger(cma_alloc_info.nr_reclaimed()));
+    inserter->AddArg(cma_nr_mapped_id_,
+                     Variadic::UnsignedInteger(cma_alloc_info.nr_mapped()));
+    inserter->AddArg(cma_nr_isolate_fail_id_,
+                     Variadic::UnsignedInteger(cma_alloc_info.err_iso()));
+    inserter->AddArg(cma_nr_migrate_fail_id_,
+                     Variadic::UnsignedInteger(cma_alloc_info.err_mig()));
+    inserter->AddArg(cma_nr_test_fail_id_,
+                     Variadic::UnsignedInteger(cma_alloc_info.err_test()));
+  };
+  context_->slice_tracker->End(timestamp, track_id, kNullStringId,
+                               kNullStringId, args_inserter);
+}
+
 void FtraceParser::ParseDirectReclaimBegin(int64_t timestamp,
                                            uint32_t pid,
                                            ConstBytes blob) {
@@ -1610,6 +1922,50 @@
                                kNullStringId, args_inserter);
 }
 
+void FtraceParser::ParseShrinkSlabStart(
+    int64_t timestamp,
+    uint32_t pid,
+    ConstBytes blob,
+    PacketSequenceStateGeneration* seq_state) {
+  protos::pbzero::MmShrinkSlabStartFtraceEvent::Decoder shrink_slab_start(
+      blob.data, blob.size);
+
+  StringId shrink_name =
+      InternedKernelSymbolOrFallback(shrink_slab_start.shrink(), seq_state);
+
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  TrackId track = context_->track_tracker->InternThreadTrack(utid);
+
+  auto args_inserter = [this, &shrink_slab_start,
+                        shrink_name](ArgsTracker::BoundInserter* inserter) {
+    inserter->AddArg(shrink_name_id_, Variadic::String(shrink_name));
+    inserter->AddArg(shrink_total_scan_id_,
+                     Variadic::UnsignedInteger(shrink_slab_start.total_scan()));
+    inserter->AddArg(shrink_priority_id_,
+                     Variadic::Integer(shrink_slab_start.priority()));
+  };
+
+  context_->slice_tracker->Begin(timestamp, track, kNullStringId,
+                                 shrink_slab_id_, args_inserter);
+}
+
+void FtraceParser::ParseShrinkSlabEnd(int64_t timestamp,
+                                      uint32_t pid,
+                                      ConstBytes blob) {
+  protos::pbzero::MmShrinkSlabEndFtraceEvent::Decoder shrink_slab_end(
+      blob.data, blob.size);
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  TrackId track = context_->track_tracker->InternThreadTrack(utid);
+
+  auto args_inserter =
+      [this, &shrink_slab_end](ArgsTracker::BoundInserter* inserter) {
+        inserter->AddArg(shrink_freed_id_,
+                         Variadic::Integer(shrink_slab_end.retval()));
+      };
+  context_->slice_tracker->End(timestamp, track, kNullStringId, kNullStringId,
+                               args_inserter);
+}
+
 void FtraceParser::ParseWorkqueueExecuteStart(
     uint32_t cpu,
     int64_t timestamp,
@@ -1618,19 +1974,7 @@
     PacketSequenceStateGeneration* seq_state) {
   protos::pbzero::WorkqueueExecuteStartFtraceEvent::Decoder evt(blob.data,
                                                                 blob.size);
-
-  auto* interned_string = seq_state->LookupInternedMessage<
-      protos::pbzero::InternedData::kKernelSymbolsFieldNumber,
-      protos::pbzero::InternedString>(static_cast<uint32_t>(evt.function()));
-  StringId name_id;
-  if (interned_string) {
-    protozero::ConstBytes str = interned_string->str();
-    name_id = context_->storage->InternString(
-        base::StringView(reinterpret_cast<const char*>(str.data), str.size));
-  } else {
-    base::StackString<255> slice_name("%#" PRIx64, evt.function());
-    name_id = context_->storage->InternString(slice_name.string_view());
-  }
+  StringId name_id = InternedKernelSymbolOrFallback(evt.function(), seq_state);
 
   UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
   TrackId track = context_->track_tracker->InternThreadTrack(utid);
@@ -1937,9 +2281,9 @@
     skaddr_to_stream_[evt.skaddr()] = ++num_of_tcp_stream_;
   }
   uint32_t stream = skaddr_to_stream_[evt.skaddr()];
-  char stream_str[64];
-  sprintf(stream_str, "TCP stream#%" PRIu32 "", stream);
-  StringId stream_id = context_->storage->InternString(stream_str);
+  base::StackString<64> stream_str("TCP stream#%" PRIu32 "", stream);
+  StringId stream_id =
+      context_->storage->InternString(stream_str.string_view());
 
   StringId slice_name_id;
   if (evt.newstate() == TCP_SYN_SENT) {
@@ -2120,6 +2464,314 @@
                                        static_cast<double>(clk_state), track);
 }
 
+void FtraceParser::ParseTrustySmc(uint32_t pid,
+                                  int64_t timestamp,
+                                  protozero::ConstBytes blob) {
+  protos::pbzero::TrustySmcFtraceEvent::Decoder evt(blob.data, blob.size);
+
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  TrackId track = context_->track_tracker->InternThreadTrack(utid);
+
+  base::StackString<48> name("trusty_smc:r0= %" PRIu64, evt.r0());
+  StringId name_generic = context_->storage->InternString(name.string_view());
+
+  context_->slice_tracker->Begin(timestamp, track, trusty_category_id_,
+                                 name_generic);
+}
+
+void FtraceParser::ParseTrustySmcDone(uint32_t pid,
+                                      int64_t timestamp,
+                                      protozero::ConstBytes blob) {
+  protos::pbzero::TrustySmcDoneFtraceEvent::Decoder evt(blob.data, blob.size);
+
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  TrackId track = context_->track_tracker->InternThreadTrack(utid);
+
+  context_->slice_tracker->End(timestamp, track, trusty_category_id_);
+  base::StackString<256> name("trusty_smc_done:r0= %" PRIu64, evt.ret());
+  StringId name_generic = context_->storage->InternString(name.string_view());
+  context_->slice_tracker->Scoped(timestamp, track, trusty_category_id_,
+                                  name_generic, 0);
+}
+
+void FtraceParser::ParseTrustyStdCall32(uint32_t pid,
+                                        int64_t timestamp,
+                                        protozero::ConstBytes blob) {
+  protos::pbzero::TrustyStdCall32FtraceEvent::Decoder evt(blob.data, blob.size);
+
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  TrackId track = context_->track_tracker->InternThreadTrack(utid);
+
+  context_->slice_tracker->Begin(timestamp, track, trusty_category_id_,
+                                 trusty_name_trusty_std_id_);
+}
+
+void FtraceParser::ParseTrustyStdCall32Done(uint32_t pid,
+                                            int64_t timestamp,
+                                            protozero::ConstBytes blob) {
+  protos::pbzero::TrustyStdCall32DoneFtraceEvent::Decoder evt(blob.data,
+                                                              blob.size);
+
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  TrackId track = context_->track_tracker->InternThreadTrack(utid);
+
+  context_->slice_tracker->End(timestamp, track, trusty_category_id_);
+  if (evt.ret() < 0) {
+    base::StackString<256> name("trusty_err_std: err= %" PRIi64, evt.ret());
+    StringId name_generic = context_->storage->InternString(name.string_view());
+    context_->slice_tracker->Scoped(timestamp, track, trusty_category_id_,
+                                    name_generic, 0);
+  }
+}
+
+void FtraceParser::ParseTrustyShareMemory(uint32_t pid,
+                                          int64_t timestamp,
+                                          protozero::ConstBytes blob) {
+  protos::pbzero::TrustyShareMemoryFtraceEvent::Decoder evt(blob.data,
+                                                            blob.size);
+
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  TrackId track = context_->track_tracker->InternThreadTrack(utid);
+
+  base::StackString<256> name(
+      "trusty_share_mem: len= %" PRIu64 " nents= %" PRIu32 " lend= %" PRIu32,
+      static_cast<uint64_t>(evt.len()), evt.nents(), evt.lend());
+  StringId name_generic = context_->storage->InternString(name.string_view());
+
+  context_->slice_tracker->Begin(timestamp, track, trusty_category_id_,
+                                 name_generic);
+}
+
+void FtraceParser::ParseTrustyShareMemoryDone(uint32_t pid,
+                                              int64_t timestamp,
+                                              protozero::ConstBytes blob) {
+  protos::pbzero::TrustyShareMemoryDoneFtraceEvent::Decoder evt(blob.data,
+                                                                blob.size);
+
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  TrackId track = context_->track_tracker->InternThreadTrack(utid);
+  context_->slice_tracker->End(timestamp, track, trusty_category_id_);
+
+  base::StackString<256> name("trusty_share_mem: handle= %" PRIu64
+                              " ret= %" PRIi32,
+                              evt.handle(), evt.ret());
+  StringId name_generic = context_->storage->InternString(name.string_view());
+  context_->slice_tracker->Scoped(timestamp, track, trusty_category_id_,
+                                  name_generic, 0);
+}
+
+void FtraceParser::ParseTrustyReclaimMemory(uint32_t pid,
+                                            int64_t timestamp,
+                                            protozero::ConstBytes blob) {
+  protos::pbzero::TrustyReclaimMemoryFtraceEvent::Decoder evt(blob.data,
+                                                              blob.size);
+
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  TrackId track = context_->track_tracker->InternThreadTrack(utid);
+
+  base::StackString<256> name("trusty_reclaim_mem: id=%" PRIu64, evt.id());
+  StringId name_generic = context_->storage->InternString(name.string_view());
+
+  context_->slice_tracker->Begin(timestamp, track, trusty_category_id_,
+                                 name_generic);
+}
+
+void FtraceParser::ParseTrustyReclaimMemoryDone(uint32_t pid,
+                                                int64_t timestamp,
+                                                protozero::ConstBytes blob) {
+  protos::pbzero::TrustyReclaimMemoryDoneFtraceEvent::Decoder evt(blob.data,
+                                                                  blob.size);
+
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  TrackId track = context_->track_tracker->InternThreadTrack(utid);
+  context_->slice_tracker->End(timestamp, track, trusty_category_id_);
+
+  if (evt.ret() < 0) {
+    base::StackString<256> name("trusty_reclaim_mem_err: err= %" PRIi32,
+                                evt.ret());
+    StringId name_generic = context_->storage->InternString(name.string_view());
+    context_->slice_tracker->Scoped(timestamp, track, trusty_category_id_,
+                                    name_generic, 0);
+  }
+}
+
+void FtraceParser::ParseTrustyIrq(uint32_t pid,
+                                  int64_t timestamp,
+                                  protozero::ConstBytes blob) {
+  protos::pbzero::TrustyIrqFtraceEvent::Decoder evt(blob.data, blob.size);
+
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  TrackId track = context_->track_tracker->InternThreadTrack(utid);
+
+  base::StackString<256> name("trusty_irq: irq= %" PRIi32, evt.irq());
+  StringId name_generic = context_->storage->InternString(name.string_view());
+
+  context_->slice_tracker->Scoped(timestamp, track, trusty_category_id_,
+                                  name_generic, 0);
+}
+
+void FtraceParser::ParseTrustyIpcHandleEvent(uint32_t pid,
+                                             int64_t timestamp,
+                                             protozero::ConstBytes blob) {
+  protos::pbzero::TrustyIpcHandleEventFtraceEvent::Decoder evt(blob.data,
+                                                               blob.size);
+
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  TrackId track = context_->track_tracker->InternThreadTrack(utid);
+
+  base::StackString<256> name(
+      "trusty_ipc_handle_event: chan=%" PRIu32 " srv_name=%s event=%" PRIu32,
+      evt.chan(), evt.srv_name().ToStdString().c_str(), evt.event_id());
+  StringId name_generic = context_->storage->InternString(name.string_view());
+
+  context_->slice_tracker->Scoped(timestamp, track, trusty_category_id_,
+                                  name_generic, 0);
+}
+
+void FtraceParser::ParseTrustyEnqueueNop(uint32_t pid,
+                                         int64_t timestamp,
+                                         protozero::ConstBytes blob) {
+  protos::pbzero::TrustyEnqueueNopFtraceEvent::Decoder evt(blob.data,
+                                                           blob.size);
+
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  TrackId track = context_->track_tracker->InternThreadTrack(utid);
+
+  base::StackString<256> name("trusty_enqueue_nop: arg1= %" PRIu32
+                              " arg2= %" PRIu32 " arg3=%" PRIu32,
+                              evt.arg1(), evt.arg2(), evt.arg3());
+  StringId name_generic = context_->storage->InternString(name.string_view());
+  context_->slice_tracker->Scoped(timestamp, track, trusty_category_id_,
+                                  name_generic, 0);
+}
+
+void FtraceParser::ParseTrustyIpcConnect(uint32_t pid,
+                                         int64_t timestamp,
+                                         protozero::ConstBytes blob) {
+  protos::pbzero::TrustyIpcConnectFtraceEvent::Decoder evt(blob.data,
+                                                           blob.size);
+
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  TrackId track = context_->track_tracker->InternThreadTrack(utid);
+
+  base::StackString<256> name("tipc_connect: %s",
+                              evt.port().ToStdString().c_str());
+  StringId name_generic = context_->storage->InternString(name.string_view());
+
+  context_->slice_tracker->Begin(timestamp, track, trusty_category_id_,
+                                 name_generic);
+}
+
+void FtraceParser::ParseTrustyIpcConnectEnd(uint32_t pid,
+                                            int64_t timestamp,
+                                            protozero::ConstBytes blob) {
+  protos::pbzero::TrustyIpcConnectEndFtraceEvent::Decoder evt(blob.data,
+                                                              blob.size);
+
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  TrackId track = context_->track_tracker->InternThreadTrack(utid);
+
+  context_->slice_tracker->End(timestamp, track, trusty_category_id_);
+  if (evt.err()) {
+    base::StackString<256> name("tipc_err_connect:err= %" PRIi32, evt.err());
+    StringId name_generic = context_->storage->InternString(name.string_view());
+    context_->slice_tracker->Scoped(timestamp, track, trusty_category_id_,
+                                    name_generic, 0);
+  }
+}
+
+void FtraceParser::ParseTrustyIpcWrite(uint32_t pid,
+                                       int64_t timestamp,
+                                       protozero::ConstBytes blob) {
+  protos::pbzero::TrustyIpcWriteFtraceEvent::Decoder evt(blob.data, blob.size);
+
+  StringId name_generic = kNullStringId;
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  TrackId track = context_->track_tracker->InternThreadTrack(utid);
+
+  if (evt.shm_cnt() > 0) {
+    base::StackString<256> name("tipc_write: %s shm_cnt:[%" PRIu64 "]",
+                                evt.srv_name().ToStdString().c_str(),
+                                evt.shm_cnt());
+    name_generic = context_->storage->InternString(name.string_view());
+  } else {
+    base::StackString<256> name("tipc_write: %s",
+                                evt.srv_name().ToStdString().c_str());
+    name_generic = context_->storage->InternString(name.string_view());
+  }
+  context_->slice_tracker->Scoped(timestamp, track, trusty_category_id_,
+                                  name_generic, 0);
+
+  if (evt.len_or_err() < 0) {
+    base::StackString<256> name("tipc_err_write:len_or_err= %" PRIi32,
+                                evt.len_or_err());
+    name_generic = context_->storage->InternString(name.string_view());
+    context_->slice_tracker->Scoped(timestamp, track, trusty_category_id_,
+                                    name_generic, 0);
+  }
+}
+
+void FtraceParser::ParseTrustyIpcRead(uint32_t pid,
+                                      int64_t timestamp,
+                                      protozero::ConstBytes blob) {
+  protos::pbzero::TrustyIpcReadFtraceEvent::Decoder evt(blob.data, blob.size);
+
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  TrackId track = context_->track_tracker->InternThreadTrack(utid);
+
+  base::StackString<256> name("tipc_read: %s",
+                              evt.srv_name().ToStdString().c_str());
+  StringId name_generic = context_->storage->InternString(name.string_view());
+  context_->slice_tracker->Begin(timestamp, track, trusty_category_id_,
+                                 name_generic);
+}
+
+void FtraceParser::ParseTrustyIpcReadEnd(uint32_t pid,
+                                         int64_t timestamp,
+                                         protozero::ConstBytes blob) {
+  protos::pbzero::TrustyIpcReadEndFtraceEvent::Decoder evt(blob.data,
+                                                           blob.size);
+
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  TrackId track = context_->track_tracker->InternThreadTrack(utid);
+  context_->slice_tracker->End(timestamp, track, trusty_category_id_);
+
+  if (evt.len_or_err() <= 0) {
+    base::StackString<256> name("tipc_err_read:len_or_err= %" PRIi32,
+                                evt.len_or_err());
+    StringId name_generic = context_->storage->InternString(name.string_view());
+    context_->slice_tracker->Scoped(timestamp, track, trusty_category_id_,
+                                    name_generic, 0);
+  }
+}
+
+void FtraceParser::ParseTrustyIpcPoll(uint32_t pid,
+                                      int64_t timestamp,
+                                      protozero::ConstBytes blob) {
+  protos::pbzero::TrustyIpcPollFtraceEvent::Decoder evt(blob.data, blob.size);
+
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  TrackId track = context_->track_tracker->InternThreadTrack(utid);
+
+  base::StackString<256> name("tipc_poll: %s",
+                              evt.srv_name().ToStdString().c_str());
+  StringId name_generic = context_->storage->InternString(name.string_view());
+  context_->slice_tracker->Scoped(timestamp, track, trusty_category_id_,
+                                  name_generic, 0);
+}
+
+void FtraceParser::ParseTrustyIpcRx(uint32_t pid,
+                                    int64_t ts,
+                                    protozero::ConstBytes blob) {
+  protos::pbzero::TrustyIpcRxFtraceEvent::Decoder evt(blob.data, blob.size);
+
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  TrackId track = context_->track_tracker->InternThreadTrack(utid);
+
+  context_->slice_tracker->Scoped(ts, track, trusty_category_id_,
+                                  trusty_name_tipc_rx_id_, 0);
+}
+
 void FtraceParser::ParseUfshcdCommand(int64_t timestamp,
                                       protozero::ConstBytes blob) {
   protos::pbzero::UfshcdCommandFtraceEvent::Decoder evt(blob.data, blob.size);
@@ -2135,20 +2787,17 @@
 
   // Parse ufs command tag
   base::StackString<32> cmd_track_name("io.ufs.command.tag[%03d]", evt.tag());
-  auto async_track =
-    context_->async_track_set_tracker->InternGlobalTrackSet(
-    context_->storage->InternString(cmd_track_name.string_view()));
+  auto async_track = context_->async_track_set_tracker->InternGlobalTrackSet(
+      context_->storage->InternString(cmd_track_name.string_view()));
   if (evt.str_t() == 0) {
     std::string ufs_op_str = GetUfsCmdString(evt.opcode(), evt.group_id());
-    StringId ufs_slice_name = context_->storage->InternString(
-      base::StringView(ufs_op_str));
-    TrackId start_id =
-      context_->async_track_set_tracker->Begin(async_track, 0);
+    StringId ufs_slice_name =
+        context_->storage->InternString(base::StringView(ufs_op_str));
+    TrackId start_id = context_->async_track_set_tracker->Begin(async_track, 0);
     context_->slice_tracker->Begin(timestamp, start_id, kNullStringId,
                                    ufs_slice_name);
   } else {
-    TrackId end_id =
-      context_->async_track_set_tracker->End(async_track, 0);
+    TrackId end_id = context_->async_track_set_tracker->End(async_track, 0);
     context_->slice_tracker->End(timestamp, end_id);
   }
 }
@@ -2207,18 +2856,24 @@
   auto async_track = context_->async_track_set_tracker->InternGlobalTrackSet(
       suspend_resume_name_id_);
 
+  // Hard code fix the timekeeping_freeze action's value to zero, the value is
+  // processor_id and device could enter suspend/resume from different
+  // processor.
+  auto val =
+      (evt.action().ToStdString() == "timekeeping_freeze") ? 0 : evt.val();
+
   base::StackString<64> str("%s(%" PRIu32 ")",
-                            evt.action().ToStdString().c_str(), evt.val());
+                            evt.action().ToStdString().c_str(), val);
   StringId slice_name_id = context_->storage->InternString(str.string_view());
 
   if (evt.start()) {
     TrackId start_id = context_->async_track_set_tracker->Begin(
-        async_track, static_cast<int64_t>(evt.val()));
+        async_track, static_cast<int64_t>(val));
     context_->slice_tracker->Begin(timestamp, start_id, suspend_resume_name_id_,
                                    slice_name_id);
   } else {
     TrackId end_id = context_->async_track_set_tracker->End(
-        async_track, static_cast<int64_t>(evt.val()));
+        async_track, static_cast<int64_t>(val));
     context_->slice_tracker->End(timestamp, end_id);
   }
 }
@@ -2255,5 +2910,59 @@
       timestamp, static_cast<double>(evt.nr_running()), nrr_track);
 }
 
+void FtraceParser::ParseFuncgraphEntry(
+    int64_t timestamp,
+    uint32_t pid,
+    protozero::ConstBytes blob,
+    PacketSequenceStateGeneration* seq_state) {
+  // TODO(rsavitski): remove if/when we stop collapsing all idle (swapper)
+  // threads to a single track, otherwise this breaks slice nesting.
+  if (pid == 0)
+    return;
+
+  protos::pbzero::FuncgraphEntryFtraceEvent::Decoder evt(blob.data, blob.size);
+  StringId name_id = InternedKernelSymbolOrFallback(evt.func(), seq_state);
+
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  TrackId track = context_->track_tracker->InternThreadTrack(utid);
+  context_->slice_tracker->Begin(timestamp, track, kNullStringId, name_id);
+}
+
+void FtraceParser::ParseFuncgraphExit(
+    int64_t timestamp,
+    uint32_t pid,
+    protozero::ConstBytes blob,
+    PacketSequenceStateGeneration* seq_state) {
+  // TODO(rsavitski): remove if/when we stop collapsing all idle (swapper)
+  // threads to a single track, otherwise this breaks slice nesting.
+  if (pid == 0)
+    return;
+
+  protos::pbzero::FuncgraphExitFtraceEvent::Decoder evt(blob.data, blob.size);
+  StringId name_id = InternedKernelSymbolOrFallback(evt.func(), seq_state);
+
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  TrackId track = context_->track_tracker->InternThreadTrack(utid);
+  context_->slice_tracker->End(timestamp, track, kNullStringId, name_id);
+}
+
+StringId FtraceParser::InternedKernelSymbolOrFallback(
+    uint64_t key,
+    PacketSequenceStateGeneration* seq_state) {
+  auto* interned_string = seq_state->LookupInternedMessage<
+      protos::pbzero::InternedData::kKernelSymbolsFieldNumber,
+      protos::pbzero::InternedString>(key);
+  StringId name_id;
+  if (interned_string) {
+    protozero::ConstBytes str = interned_string->str();
+    name_id = context_->storage->InternString(
+        base::StringView(reinterpret_cast<const char*>(str.data), str.size));
+  } else {
+    base::StackString<255> slice_name("%#" PRIx64, key);
+    name_id = context_->storage->InternString(slice_name.string_view());
+  }
+  return name_id;
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.h b/src/trace_processor/importers/ftrace/ftrace_parser.h
index 913cb1b..c2a40a2 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.h
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.h
@@ -19,14 +19,20 @@
 
 #include "perfetto/trace_processor/status.h"
 #include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/common/parser_types.h"
+#include "src/trace_processor/importers/common/system_info_tracker.h"
+#include "src/trace_processor/importers/common/trace_parser.h"
 #include "src/trace_processor/importers/ftrace/drm_tracker.h"
 #include "src/trace_processor/importers/ftrace/ftrace_descriptors.h"
 #include "src/trace_processor/importers/ftrace/iostat_tracker.h"
+#include "src/trace_processor/importers/ftrace/mali_gpu_event_tracker.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/importers/ftrace/virtio_gpu_tracker.h"
 #include "src/trace_processor/types/trace_processor_context.h"
 
+#include <unordered_set>
+
 namespace perfetto {
 namespace trace_processor {
 
@@ -34,9 +40,17 @@
  public:
   explicit FtraceParser(TraceProcessorContext* context);
 
-  void ParseFtraceStats(protozero::ConstBytes);
+  void ParseFtraceStats(protozero::ConstBytes, uint32_t packet_sequence_id);
 
-  util::Status ParseFtraceEvent(uint32_t cpu, const TimestampedTracePiece& ttp);
+  util::Status ParseFtraceEvent(uint32_t cpu,
+                                int64_t ts,
+                                const TracePacketData& data);
+  util::Status ParseInlineSchedSwitch(uint32_t cpu,
+                                      int64_t ts,
+                                      const InlineSchedSwitch& data);
+  util::Status ParseInlineSchedWaking(uint32_t cpu,
+                                      int64_t ts,
+                                      const InlineSchedWaking& data);
 
  private:
   void ParseGenericFtrace(int64_t timestamp,
@@ -69,6 +83,9 @@
   void ParseMaliTracingMarkWrite(int64_t timestamp,
                                  uint32_t pid,
                                  protozero::ConstBytes);
+  void ParseLwisTracingMarkWrite(int64_t timestamp,
+                                 uint32_t pid,
+                                 protozero::ConstBytes);
   void ParseIonHeapGrowOrShrink(int64_t timestamp,
                                 uint32_t pid,
                                 protozero::ConstBytes,
@@ -84,10 +101,12 @@
   void ParseMmEventRecord(int64_t timestamp,
                           uint32_t pid,
                           protozero::ConstBytes);
-  void ParseSysEvent(int64_t timestamp,
-                     uint32_t pid,
-                     bool is_enter,
-                     protozero::ConstBytes);
+  void ParseSysEnterEvent(int64_t timestamp,
+                          uint32_t pid,
+                          protozero::ConstBytes);
+  void ParseSysExitEvent(int64_t timestamp,
+                         uint32_t pid,
+                         protozero::ConstBytes);
   void ParseI2cReadEvent(int64_t timestamp,
                          uint32_t pid,
                          protozero::ConstBytes);
@@ -128,12 +147,23 @@
                          uint32_t pid,
                          protozero::ConstBytes);
   void ParseScmCallEnd(int64_t timestamp, uint32_t pid, protozero::ConstBytes);
+  void ParseCmaAllocStart(int64_t timestamp, uint32_t pid);
+  void ParseCmaAllocInfo(int64_t timestamp,
+                         uint32_t pid,
+                         protozero::ConstBytes);
   void ParseDirectReclaimBegin(int64_t timestamp,
                                uint32_t pid,
                                protozero::ConstBytes);
   void ParseDirectReclaimEnd(int64_t timestamp,
                              uint32_t pid,
                              protozero::ConstBytes);
+  void ParseShrinkSlabStart(int64_t timestamp,
+                            uint32_t pid,
+                            protozero::ConstBytes,
+                            PacketSequenceStateGeneration* seq_state);
+  void ParseShrinkSlabEnd(int64_t timestamp,
+                          uint32_t pid,
+                          protozero::ConstBytes);
   void ParseWorkqueueExecuteStart(uint32_t cpu,
                                   int64_t timestamp,
                                   uint32_t pid,
@@ -181,15 +211,73 @@
   void ParseUfshcdClkGating(int64_t timestamp, protozero::ConstBytes);
 
   void ParseCrosEcSensorhubData(int64_t timestamp, protozero::ConstBytes);
+
   void ParseWakeSourceActivate(int64_t timestamp, protozero::ConstBytes);
   void ParseWakeSourceDeactivate(int64_t timestamp, protozero::ConstBytes);
   void ParseSuspendResume(int64_t timestamp, protozero::ConstBytes);
   void ParseSchedCpuUtilCfs(int64_t timestap, protozero::ConstBytes);
 
+  void ParseFuncgraphEntry(int64_t timestamp,
+                           uint32_t pid,
+                           protozero::ConstBytes blob,
+                           PacketSequenceStateGeneration* seq_state);
+  void ParseFuncgraphExit(int64_t timestamp,
+                          uint32_t pid,
+                          protozero::ConstBytes blob,
+                          PacketSequenceStateGeneration* seq_state);
+
+  void MaybeOnFirstFtraceEvent();
+  StringId InternedKernelSymbolOrFallback(
+      uint64_t key,
+      PacketSequenceStateGeneration* seq_state);
+  void ParseTrustySmc(uint32_t pid, int64_t timestamp, protozero::ConstBytes);
+  void ParseTrustySmcDone(uint32_t pid,
+                          int64_t timestamp,
+                          protozero::ConstBytes);
+  void ParseTrustyStdCall32(uint32_t pid,
+                            int64_t ts,
+                            protozero::ConstBytes data);
+  void ParseTrustyStdCall32Done(uint32_t pid,
+                                int64_t ts,
+                                protozero::ConstBytes data);
+  void ParseTrustyShareMemory(uint32_t pid, int64_t ts, protozero::ConstBytes);
+  void ParseTrustyShareMemoryDone(uint32_t pid,
+                                  int64_t ts,
+                                  protozero::ConstBytes);
+  void ParseTrustyReclaimMemory(uint32_t pid,
+                                int64_t ts,
+                                protozero::ConstBytes);
+  void ParseTrustyReclaimMemoryDone(uint32_t pid,
+                                    int64_t ts,
+                                    protozero::ConstBytes);
+  void ParseTrustyIrq(uint32_t pid, int64_t ts, protozero::ConstBytes);
+  void ParseTrustyIpcHandleEvent(uint32_t pid,
+                                 int64_t ts,
+                                 protozero::ConstBytes);
+  void ParseTrustyIpcConnect(uint32_t pid, int64_t ts, protozero::ConstBytes);
+  void ParseTrustyIpcConnectEnd(uint32_t pid,
+                                int64_t ts,
+                                protozero::ConstBytes);
+  void ParseTrustyIpcWrite(uint32_t pid, int64_t ts, protozero::ConstBytes);
+  void ParseTrustyIpcWriteEnd(uint32_t pid, int64_t ts, protozero::ConstBytes);
+  void ParseTrustyIpcRead(uint32_t pid, int64_t ts, protozero::ConstBytes);
+  void ParseTrustyIpcReadEnd(uint32_t pid, int64_t ts, protozero::ConstBytes);
+  void ParseTrustyIpcPoll(uint32_t pid, int64_t ts, protozero::ConstBytes);
+  void ParseTrustyIpcRx(uint32_t pid, int64_t ts, protozero::ConstBytes);
+  void ParseTrustyEnqueueNop(uint32_t pid, int64_t ts, protozero::ConstBytes);
+  void ParseMaliKcpuCqsSet(uint32_t pid, int64_t ts);
+  void ParseMaliKcpuCqsWaitStart(uint32_t pid, int64_t ts);
+  void ParseMaliKcpuCqsWaitEnd(uint32_t pid, int64_t ts);
+  void ParseMaliKcpuFenceSignal(uint32_t pid, int64_t ts);
+  void ParseMaliKcpuFenceWaitStart(uint32_t pid, int64_t ts);
+  void ParseMaliKcpuFenceWaitEnd(uint32_t pid, int64_t ts);
+
   TraceProcessorContext* context_;
   RssStatTracker rss_stat_tracker_;
   DrmTracker drm_tracker_;
   IostatTracker iostat_tracker_;
+  VirtioGpuTracker virtio_gpu_tracker_;
+  MaliGpuEventTracker mali_gpu_event_tracker_;
 
   const StringId sched_wakeup_name_id_;
   const StringId sched_waking_name_id_;
@@ -240,6 +328,27 @@
   const StringId cros_ec_arg_sample_ts_id_;
   const StringId ufs_clkgating_id_;
   const StringId ufs_command_count_id_;
+  const StringId shrink_slab_id_;
+  const StringId shrink_name_id_;
+  const StringId shrink_total_scan_id_;
+  const StringId shrink_freed_id_;
+  const StringId shrink_priority_id_;
+  const StringId trusty_category_id_;
+  const StringId trusty_name_trusty_std_id_;
+  const StringId trusty_name_tipc_rx_id_;
+  const StringId cma_alloc_id_;
+  const StringId cma_name_id_;
+  const StringId cma_pfn_id_;
+  const StringId cma_req_pages_id_;
+  const StringId cma_nr_migrated_id_;
+  const StringId cma_nr_reclaimed_id_;
+  const StringId cma_nr_mapped_id_;
+  const StringId cma_nr_isolate_fail_id_;
+  const StringId cma_nr_migrate_fail_id_;
+  const StringId cma_nr_test_fail_id_;
+  const StringId syscall_ret_id_;
+  const StringId syscall_args_id_;
+  std::vector<StringId> syscall_arg_name_ids_;
 
   struct FtraceMessageStrings {
     // The string id of name of the event field (e.g. sched_switch's id).
@@ -290,6 +399,14 @@
   // Stores information about the timestamp from the metadata table which is
   // used to filter ftrace packets which happen before this point.
   int64_t drop_ftrace_data_before_ts_ = 0;
+
+  // Does not skip any ftrace events.
+  bool preserve_ftrace_buffer_ = false;
+
+  // Sequence ids for which ftrace_errors have been seen. Used to avoid
+  // putting them in the metadata multiple times (the ftrace data sources
+  // re-emits begin stats on every flush).
+  std::unordered_set<uint32_t> seen_errors_for_sequence_id_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc b/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
index 3ca59f7..06478a6 100644
--- a/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_tokenizer.cc
@@ -19,9 +19,10 @@
 #include "perfetto/base/logging.h"
 #include "perfetto/protozero/proto_decoder.h"
 #include "perfetto/protozero/proto_utils.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state.h"
+#include "src/trace_processor/sorter/trace_sorter.h"
 #include "src/trace_processor/storage/stats.h"
 #include "src/trace_processor/storage/trace_storage.h"
-#include "src/trace_processor/trace_sorter.h"
 
 #include "protos/perfetto/common/builtin_clock.pbzero.h"
 #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
@@ -69,6 +70,14 @@
   }
 
   uint32_t cpu = decoder.cpu();
+  static constexpr uint32_t kMaxCpuCount = 1024;
+  if (PERFETTO_UNLIKELY(cpu >= kMaxCpuCount)) {
+    return base::ErrStatus(
+        "CPU %u is greater than maximum allowed of %u. This is likely because "
+        "of trace corruption",
+        cpu, kMaxCpuCount);
+  }
+
   ClockTracker::ClockId clock_id;
   switch (decoder.ftrace_clock()) {
     case FtraceClock::FTRACE_CLOCK_UNSPECIFIED:
@@ -78,6 +87,9 @@
       clock_id = ClockTracker::SeqScopedClockIdToGlobal(
           packet_sequence_id, kFtraceGlobalClockIdForOldKernels);
       break;
+    case FtraceClock::FTRACE_CLOCK_MONO_RAW:
+      clock_id = BuiltinClock::BUILTIN_CLOCK_MONOTONIC_RAW;
+      break;
     case FtraceClock::FTRACE_CLOCK_LOCAL:
       return base::ErrStatus("Unable to parse ftrace packets with local clock");
     default:
@@ -145,7 +157,8 @@
       ResolveTraceTime(context_, clock_id, int64_timestamp);
   if (!timestamp)
     return;
-  context_->sorter->PushFtraceEvent(cpu, *timestamp, std::move(event), state);
+  context_->sorter->PushFtraceEvent(cpu, *timestamp, std::move(event),
+                                    state->current_generation());
 }
 
 PERFETTO_ALWAYS_INLINE
diff --git a/src/trace_processor/importers/ftrace/mali_gpu_event_tracker.cc b/src/trace_processor/importers/ftrace/mali_gpu_event_tracker.cc
new file mode 100644
index 0000000..d3f6595
--- /dev/null
+++ b/src/trace_processor/importers/ftrace/mali_gpu_event_tracker.cc
@@ -0,0 +1,99 @@
+#include "src/trace_processor/importers/ftrace/mali_gpu_event_tracker.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
+#include "protos/perfetto/trace/ftrace/mali.pbzero.h"
+#include "src/trace_processor/importers/common/async_track_set_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 {
+
+MaliGpuEventTracker::MaliGpuEventTracker(TraceProcessorContext* context)
+    : context_(context),
+      mali_KCPU_CQS_SET_id_(
+          context->storage->InternString("mali_KCPU_CQS_SET")),
+      mali_KCPU_CQS_WAIT_id_(
+          context->storage->InternString("mali_KCPU_CQS_WAIT")),
+      mali_KCPU_FENCE_SIGNAL_id_(
+          context->storage->InternString("mali_KCPU_FENCE_SIGNAL")),
+      mali_KCPU_FENCE_WAIT_id_(
+          context->storage->InternString("mali_KCPU_FENCE_WAIT")) {}
+
+void MaliGpuEventTracker::ParseMaliGpuEvent(int64_t ts,
+                                            int32_t field_id,
+                                            uint32_t pid) {
+  using protos::pbzero::FtraceEvent;
+
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
+
+  switch (field_id) {
+    case FtraceEvent::kMaliMaliKCPUCQSSETFieldNumber: {
+      ParseMaliKcpuCqsSet(ts, track_id);
+      break;
+    }
+    case FtraceEvent::kMaliMaliKCPUCQSWAITSTARTFieldNumber: {
+      ParseMaliKcpuCqsWaitStart(ts, track_id);
+      break;
+    }
+    case FtraceEvent::kMaliMaliKCPUCQSWAITENDFieldNumber: {
+      ParseMaliKcpuCqsWaitEnd(ts, track_id);
+      break;
+    }
+    case FtraceEvent::kMaliMaliKCPUFENCESIGNALFieldNumber: {
+      ParseMaliKcpuFenceSignal(ts, track_id);
+      break;
+    }
+    case FtraceEvent::kMaliMaliKCPUFENCEWAITSTARTFieldNumber: {
+      ParseMaliKcpuFenceWaitStart(ts, track_id);
+      break;
+    }
+    case FtraceEvent::kMaliMaliKCPUFENCEWAITENDFieldNumber: {
+      ParseMaliKcpuFenceWaitEnd(ts, track_id);
+      break;
+    }
+    default:
+      PERFETTO_DFATAL("Unexpected field id");
+      break;
+  }
+}
+
+void MaliGpuEventTracker::ParseMaliKcpuCqsSet(int64_t timestamp,
+                                              TrackId track_id) {
+  context_->slice_tracker->Scoped(timestamp, track_id, kNullStringId,
+                                  mali_KCPU_CQS_SET_id_, 0);
+}
+
+void MaliGpuEventTracker::ParseMaliKcpuCqsWaitStart(int64_t timestamp,
+                                                    TrackId track_id) {
+  context_->slice_tracker->Begin(timestamp, track_id, kNullStringId,
+                                 mali_KCPU_CQS_WAIT_id_);
+}
+
+void MaliGpuEventTracker::ParseMaliKcpuCqsWaitEnd(int64_t timestamp,
+                                                  TrackId track_id) {
+  context_->slice_tracker->End(timestamp, track_id, kNullStringId,
+                               mali_KCPU_CQS_WAIT_id_);
+}
+
+void MaliGpuEventTracker::ParseMaliKcpuFenceSignal(int64_t timestamp,
+                                                   TrackId track_id) {
+  context_->slice_tracker->Scoped(timestamp, track_id, kNullStringId,
+                                  mali_KCPU_FENCE_SIGNAL_id_, 0);
+}
+
+void MaliGpuEventTracker::ParseMaliKcpuFenceWaitStart(int64_t timestamp,
+                                                      TrackId track_id) {
+  context_->slice_tracker->Begin(timestamp, track_id, kNullStringId,
+                                 mali_KCPU_FENCE_WAIT_id_);
+}
+
+void MaliGpuEventTracker::ParseMaliKcpuFenceWaitEnd(int64_t timestamp,
+                                                    TrackId track_id) {
+  context_->slice_tracker->End(timestamp, track_id, kNullStringId,
+                               mali_KCPU_FENCE_WAIT_id_);
+}
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/ftrace/mali_gpu_event_tracker.h b/src/trace_processor/importers/ftrace/mali_gpu_event_tracker.h
new file mode 100644
index 0000000..9c2dceb
--- /dev/null
+++ b/src/trace_processor/importers/ftrace/mali_gpu_event_tracker.h
@@ -0,0 +1,36 @@
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_MALI_GPU_EVENT_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_MALI_GPU_EVENT_TRACKER_H_
+
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "perfetto/ext/base/string_view.h"
+#include "perfetto/protozero/field.h"
+#include "src/trace_processor/storage/trace_storage.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class TraceProcessorContext;
+
+class MaliGpuEventTracker {
+ public:
+  explicit MaliGpuEventTracker(TraceProcessorContext*);
+  void ParseMaliGpuEvent(int64_t timestamp, int32_t field_id, uint32_t pid);
+
+ private:
+  TraceProcessorContext* context_;
+  StringId mali_KCPU_CQS_SET_id_;
+  StringId mali_KCPU_CQS_WAIT_id_;
+  StringId mali_KCPU_FENCE_SIGNAL_id_;
+  StringId mali_KCPU_FENCE_WAIT_id_;
+  void ParseMaliKcpuFenceSignal(int64_t timestamp, TrackId track_id);
+  void ParseMaliKcpuFenceWaitStart(int64_t timestamp, TrackId track_id);
+  void ParseMaliKcpuFenceWaitEnd(int64_t timestamp, TrackId track_id);
+  void ParseMaliKcpuCqsSet(int64_t timestamp, TrackId track_id);
+  void ParseMaliKcpuCqsWaitStart(int64_t timestamp, TrackId track_id);
+  void ParseMaliKcpuCqsWaitEnd(int64_t timestamp, TrackId track_id);
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_MALI_GPU_EVENT_TRACKER_H_
diff --git a/src/trace_processor/importers/ftrace/sched_event_tracker.cc b/src/trace_processor/importers/ftrace/sched_event_tracker.cc
index 8124572..3ee1fa1 100644
--- a/src/trace_processor/importers/ftrace/sched_event_tracker.cc
+++ b/src/trace_processor/importers/ftrace/sched_event_tracker.cc
@@ -207,6 +207,61 @@
       ts, cpu, prev_utid, prev_state_string_id, next_utid);
 }
 
+// Processes a sched_waking that was decoded from a compact representation,
+// adding to the raw and instants tables.
+void SchedEventTracker::PushSchedWakingCompact(uint32_t cpu,
+                                               int64_t ts,
+                                               uint32_t wakee_pid,
+                                               int32_t target_cpu,
+                                               int32_t prio,
+                                               StringId comm_id) {
+  // At this stage all events should be globally timestamp ordered.
+  if (ts < context_->event_tracker->max_timestamp()) {
+    PERFETTO_ELOG(
+        "sched_waking event out of order by %.4f ms, skipping",
+        static_cast<double>(context_->event_tracker->max_timestamp() - ts) /
+            1e6);
+    context_->storage->IncrementStats(stats::sched_waking_out_of_order);
+    return;
+  }
+  context_->event_tracker->UpdateMaxTimestamp(ts);
+
+  // We infer the task that emitted the event (i.e. common_pid) from the
+  // scheduling slices. Drop the event if we haven't seen any sched_switch
+  // events for this cpu yet.
+  // Note that if sched_switch wasn't enabled, we will have to skip all
+  // compact waking events.
+  auto* pending_sched = PendingSchedByCPU(cpu);
+  if (pending_sched->last_utid == std::numeric_limits<UniqueTid>::max()) {
+    context_->storage->IncrementStats(stats::compact_sched_waking_skipped);
+    return;
+  }
+  auto curr_utid = pending_sched->last_utid;
+
+  if (PERFETTO_LIKELY(context_->config.ingest_ftrace_in_raw_table)) {
+    // Add an entry to the raw table.
+    RawId id = context_->storage->mutable_raw_table()
+                   ->Insert({ts, sched_waking_id_, cpu, curr_utid})
+                   .id;
+
+    using SW = protos::pbzero::SchedWakingFtraceEvent;
+    auto inserter = context_->args_tracker->AddArgsTo(id);
+    auto add_raw_arg = [this, &inserter](int field_num, Variadic var) {
+      StringId key = sched_waking_field_ids_[static_cast<size_t>(field_num)];
+      inserter.AddArg(key, var);
+    };
+    add_raw_arg(SW::kCommFieldNumber, Variadic::String(comm_id));
+    add_raw_arg(SW::kPidFieldNumber, Variadic::Integer(wakee_pid));
+    add_raw_arg(SW::kPrioFieldNumber, Variadic::Integer(prio));
+    add_raw_arg(SW::kTargetCpuFieldNumber, Variadic::Integer(target_cpu));
+  }
+
+  // Add a waking entry to the ThreadState table.
+  auto wakee_utid = context_->process_tracker->GetOrCreateThread(wakee_pid);
+  ThreadStateTracker::GetOrCreate(context_)->PushWakingEvent(ts, wakee_utid,
+                                                             curr_utid);
+}
+
 PERFETTO_ALWAYS_INLINE
 uint32_t SchedEventTracker::AddRawEventAndStartSlice(uint32_t cpu,
                                                      int64_t ts,
@@ -256,9 +311,11 @@
 }
 
 StringId SchedEventTracker::TaskStateToStringId(int64_t task_state_int) {
-  auto kernel_version =
+  using ftrace_utils::TaskState;
+
+  base::Optional<VersionNumber> kernel_version =
       SystemInfoTracker::GetOrCreate(context_)->GetKernelVersion();
-  auto task_state = ftrace_utils::TaskState(
+  TaskState task_state = TaskState::FromRawPrevState(
       static_cast<uint16_t>(task_state_int), kernel_version);
   return task_state.is_valid()
              ? context_->storage->InternString(task_state.ToString().data())
@@ -280,65 +337,5 @@
   slices->mutable_end_state()->Set(pending_slice_idx, prev_state);
 }
 
-// Processes a sched_waking that was decoded from a compact representation,
-// adding to the raw and instants tables.
-void SchedEventTracker::PushSchedWakingCompact(uint32_t cpu,
-                                               int64_t ts,
-                                               uint32_t wakee_pid,
-                                               int32_t target_cpu,
-                                               int32_t prio,
-                                               StringId comm_id) {
-  // At this stage all events should be globally timestamp ordered.
-  if (ts < context_->event_tracker->max_timestamp()) {
-    PERFETTO_ELOG(
-        "sched_waking event out of order by %.4f ms, skipping",
-        static_cast<double>(context_->event_tracker->max_timestamp() - ts) /
-            1e6);
-    context_->storage->IncrementStats(stats::sched_waking_out_of_order);
-    return;
-  }
-  context_->event_tracker->UpdateMaxTimestamp(ts);
-
-  // We infer the task that emitted the event (i.e. common_pid) from the
-  // scheduling slices. Drop the event if we haven't seen any sched_switch
-  // events for this cpu yet.
-  // Note that if sched_switch wasn't enabled, we will have to skip all
-  // compact waking events.
-  auto* pending_sched = PendingSchedByCPU(cpu);
-  if (pending_sched->last_utid == std::numeric_limits<UniqueTid>::max()) {
-    context_->storage->IncrementStats(stats::compact_sched_waking_skipped);
-    return;
-  }
-  auto curr_utid = pending_sched->last_utid;
-
-  if (PERFETTO_LIKELY(context_->config.ingest_ftrace_in_raw_table)) {
-    // Add an entry to the raw table.
-    RawId id = context_->storage->mutable_raw_table()
-                   ->Insert({ts, sched_waking_id_, cpu, curr_utid})
-                   .id;
-
-    // "success" is hardcoded as always 1 by the kernel, with a TODO to remove
-    // it.
-    static constexpr int32_t kHardcodedSuccess = 1;
-
-    using SW = protos::pbzero::SchedWakingFtraceEvent;
-    auto inserter = context_->args_tracker->AddArgsTo(id);
-    auto add_raw_arg = [this, &inserter](int field_num, Variadic var) {
-      StringId key = sched_waking_field_ids_[static_cast<size_t>(field_num)];
-      inserter.AddArg(key, var);
-    };
-    add_raw_arg(SW::kCommFieldNumber, Variadic::String(comm_id));
-    add_raw_arg(SW::kPidFieldNumber, Variadic::Integer(wakee_pid));
-    add_raw_arg(SW::kPrioFieldNumber, Variadic::Integer(prio));
-    add_raw_arg(SW::kSuccessFieldNumber, Variadic::Integer(kHardcodedSuccess));
-    add_raw_arg(SW::kTargetCpuFieldNumber, Variadic::Integer(target_cpu));
-  }
-
-  // Add a waking entry to the ThreadState table.
-  auto wakee_utid = context_->process_tracker->GetOrCreateThread(wakee_pid);
-  ThreadStateTracker::GetOrCreate(context_)->PushWakingEvent(ts, wakee_utid,
-                                                             curr_utid);
-}
-
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/ftrace/v4l2_tracker.cc b/src/trace_processor/importers/ftrace/v4l2_tracker.cc
new file mode 100644
index 0000000..66c1709
--- /dev/null
+++ b/src/trace_processor/importers/ftrace/v4l2_tracker.cc
@@ -0,0 +1,557 @@
+/*
+ * Copyright (C) 2022 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 <memory>
+
+#include "perfetto/ext/base/hash.h"
+#include "perfetto/ext/base/string_utils.h"
+
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/flow_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/v4l2_tracker.h"
+
+#include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
+#include "protos/perfetto/trace/ftrace/v4l2.pbzero.h"
+
+#include "v4l2_tracker.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+namespace {
+using protos::pbzero::FtraceEvent;
+using protos::pbzero::V4l2DqbufFtraceEvent;
+using protos::pbzero::V4l2QbufFtraceEvent;
+using protos::pbzero::Vb2V4l2BufDoneFtraceEvent;
+using protos::pbzero::Vb2V4l2BufQueueFtraceEvent;
+using protos::pbzero::Vb2V4l2DqbufFtraceEvent;
+using protos::pbzero::Vb2V4l2QbufFtraceEvent;
+using protozero::ConstBytes;
+}  // namespace
+
+V4l2Tracker::V4l2Tracker(TraceProcessorContext* context)
+    : context_(context),
+      buf_event_ids_(*context->storage),
+      buf_type_ids_(*context_->storage),
+      buf_field_ids_(*context->storage),
+      tc_type_ids_(*context->storage) {}
+
+V4l2Tracker::~V4l2Tracker() = default;
+
+void V4l2Tracker::ParseV4l2Event(uint64_t fld_id,
+                                 int64_t timestamp,
+                                 uint32_t pid,
+                                 const ConstBytes& bytes) {
+  switch (fld_id) {
+    case FtraceEvent::kV4l2QbufFieldNumber: {
+      V4l2QbufFtraceEvent::Decoder pb_evt(bytes.data, bytes.size);
+      BufferEvent evt;
+      evt.device_minor = pb_evt.minor();
+      evt.index = pb_evt.index();
+      evt.type = pb_evt.type();
+      evt.bytesused = pb_evt.bytesused();
+      evt.flags = pb_evt.flags();
+      evt.field = pb_evt.field();
+      evt.timestamp = pb_evt.timestamp();
+      evt.sequence = pb_evt.sequence();
+      evt.timecode_flags = pb_evt.timecode_flags();
+      evt.timecode_frames = pb_evt.timecode_frames();
+      evt.timecode_hours = pb_evt.timecode_hours();
+      evt.timecode_minutes = pb_evt.timecode_minutes();
+      evt.timecode_seconds = pb_evt.timecode_seconds();
+      evt.timecode_type = pb_evt.timecode_type();
+      evt.timecode_userbits0 = pb_evt.timecode_userbits0();
+      evt.timecode_userbits1 = pb_evt.timecode_userbits1();
+      evt.timecode_userbits2 = pb_evt.timecode_userbits2();
+      evt.timecode_userbits3 = pb_evt.timecode_userbits3();
+
+      base::StackString<64> buf_name(
+          "VIDIOC_QBUF minor=%" PRIu32 " seq=%" PRIu32 " type=%" PRIu32
+          " index=%" PRIu32,
+          evt.device_minor, evt.sequence, *evt.type, *evt.index);
+
+      StringId buf_name_id =
+          context_->storage->InternString(buf_name.string_view());
+      base::Optional<SliceId> slice_id =
+          AddSlice(buf_name_id, timestamp, pid, evt);
+
+      uint64_t hash = base::Hasher::Combine(evt.device_minor, evt.sequence,
+                                            *evt.type, *evt.index);
+
+      QueuedBuffer queued_buffer;
+      queued_buffer.queue_slice_id = slice_id;
+
+      queued_buffers_.Insert(hash, std::move(queued_buffer));
+      break;
+    }
+    case FtraceEvent::kV4l2DqbufFieldNumber: {
+      V4l2DqbufFtraceEvent::Decoder pb_evt(bytes.data, bytes.size);
+      BufferEvent evt;
+      evt.device_minor = pb_evt.minor();
+      evt.index = pb_evt.index();
+      evt.type = pb_evt.type();
+      evt.bytesused = pb_evt.bytesused();
+      evt.flags = pb_evt.flags();
+      evt.field = pb_evt.field();
+      evt.timestamp = pb_evt.timestamp();
+      evt.sequence = pb_evt.sequence();
+      evt.timecode_flags = pb_evt.timecode_flags();
+      evt.timecode_frames = pb_evt.timecode_frames();
+      evt.timecode_hours = pb_evt.timecode_hours();
+      evt.timecode_minutes = pb_evt.timecode_minutes();
+      evt.timecode_seconds = pb_evt.timecode_seconds();
+      evt.timecode_type = pb_evt.timecode_type();
+      evt.timecode_userbits0 = pb_evt.timecode_userbits0();
+      evt.timecode_userbits1 = pb_evt.timecode_userbits1();
+      evt.timecode_userbits2 = pb_evt.timecode_userbits2();
+      evt.timecode_userbits3 = pb_evt.timecode_userbits3();
+
+      base::StackString<64> buf_name(
+          "VIDIOC_DQBUF minor=%" PRIu32 " seq=%" PRIu32 " type=%" PRIu32
+          " index=%" PRIu32,
+          evt.device_minor, evt.sequence, *evt.type, *evt.index);
+
+      StringId buf_name_id =
+          context_->storage->InternString(buf_name.string_view());
+      base::Optional<SliceId> slice_id =
+          AddSlice(buf_name_id, timestamp, pid, evt);
+
+      uint64_t hash = base::Hasher::Combine(evt.device_minor, evt.sequence,
+                                            *evt.type, *evt.index);
+
+      const QueuedBuffer* queued_buffer = queued_buffers_.Find(hash);
+      if (queued_buffer) {
+        if (queued_buffer->queue_slice_id && slice_id) {
+          context_->flow_tracker->InsertFlow(*queued_buffer->queue_slice_id,
+                                             *slice_id);
+        }
+
+        queued_buffers_.Erase(hash);
+      }
+      break;
+    }
+    case FtraceEvent::kVb2V4l2BufQueueFieldNumber: {
+      Vb2V4l2BufQueueFtraceEvent::Decoder pb_evt(bytes.data, bytes.size);
+      BufferEvent evt;
+      evt.device_minor = pb_evt.minor();
+      evt.index = base::nullopt;
+      evt.type = base::nullopt;
+      evt.bytesused = base::nullopt;
+      evt.flags = pb_evt.flags();
+      evt.field = pb_evt.field();
+      evt.timestamp = pb_evt.timestamp();
+      evt.sequence = pb_evt.sequence();
+      evt.timecode_flags = pb_evt.timecode_flags();
+      evt.timecode_frames = pb_evt.timecode_frames();
+      evt.timecode_hours = pb_evt.timecode_hours();
+      evt.timecode_minutes = pb_evt.timecode_minutes();
+      evt.timecode_seconds = pb_evt.timecode_seconds();
+      evt.timecode_type = pb_evt.timecode_type();
+      evt.timecode_userbits0 = pb_evt.timecode_userbits0();
+      evt.timecode_userbits1 = pb_evt.timecode_userbits1();
+      evt.timecode_userbits2 = pb_evt.timecode_userbits2();
+      evt.timecode_userbits3 = pb_evt.timecode_userbits3();
+
+      base::StackString<64> buf_name(
+          "vb2_v4l2_buf_queue minor=%" PRIu32 " seq=%" PRIu32 " type=%" PRIu32
+          " index=%" PRIu32,
+          evt.device_minor, evt.sequence, *evt.type, *evt.index);
+
+      StringId buf_name_id =
+          context_->storage->InternString(buf_name.string_view());
+      AddSlice(buf_name_id, timestamp, pid, evt);
+      break;
+    }
+    case FtraceEvent::kVb2V4l2BufDoneFieldNumber: {
+      Vb2V4l2BufDoneFtraceEvent::Decoder pb_evt(bytes.data, bytes.size);
+      BufferEvent evt;
+      evt.device_minor = pb_evt.minor();
+      evt.index = base::nullopt;
+      evt.type = base::nullopt;
+      evt.bytesused = base::nullopt;
+      evt.flags = pb_evt.flags();
+      evt.field = pb_evt.field();
+      evt.timestamp = pb_evt.timestamp();
+      evt.sequence = pb_evt.sequence();
+      evt.timecode_flags = pb_evt.timecode_flags();
+      evt.timecode_frames = pb_evt.timecode_frames();
+      evt.timecode_hours = pb_evt.timecode_hours();
+      evt.timecode_minutes = pb_evt.timecode_minutes();
+      evt.timecode_seconds = pb_evt.timecode_seconds();
+      evt.timecode_type = pb_evt.timecode_type();
+      evt.timecode_userbits0 = pb_evt.timecode_userbits0();
+      evt.timecode_userbits1 = pb_evt.timecode_userbits1();
+      evt.timecode_userbits2 = pb_evt.timecode_userbits2();
+      evt.timecode_userbits3 = pb_evt.timecode_userbits3();
+
+      base::StackString<64> buf_name(
+          "vb2_v4l2_buf_done minor=%" PRIu32 " seq=%" PRIu32 " type=%" PRIu32
+          " index=%" PRIu32,
+          evt.device_minor, evt.sequence, *evt.type, *evt.index);
+
+      StringId buf_name_id =
+          context_->storage->InternString(buf_name.string_view());
+      AddSlice(buf_name_id, timestamp, pid, evt);
+      break;
+    }
+    case FtraceEvent::kVb2V4l2QbufFieldNumber: {
+      Vb2V4l2QbufFtraceEvent::Decoder pb_evt(bytes.data, bytes.size);
+      BufferEvent evt;
+      evt.device_minor = pb_evt.minor();
+      evt.index = base::nullopt;
+      evt.type = base::nullopt;
+      evt.bytesused = base::nullopt;
+      evt.flags = pb_evt.flags();
+      evt.field = pb_evt.field();
+      evt.timestamp = pb_evt.timestamp();
+      evt.sequence = pb_evt.sequence();
+      evt.timecode_flags = pb_evt.timecode_flags();
+      evt.timecode_frames = pb_evt.timecode_frames();
+      evt.timecode_hours = pb_evt.timecode_hours();
+      evt.timecode_minutes = pb_evt.timecode_minutes();
+      evt.timecode_seconds = pb_evt.timecode_seconds();
+      evt.timecode_type = pb_evt.timecode_type();
+      evt.timecode_userbits0 = pb_evt.timecode_userbits0();
+      evt.timecode_userbits1 = pb_evt.timecode_userbits1();
+      evt.timecode_userbits2 = pb_evt.timecode_userbits2();
+      evt.timecode_userbits3 = pb_evt.timecode_userbits3();
+
+      base::StackString<64> buf_name(
+          "vb2_v4l2_qbuf minor=%" PRIu32 " seq=%" PRIu32 " type=%" PRIu32
+          " index=%" PRIu32,
+          evt.device_minor, evt.sequence, *evt.type, *evt.index);
+
+      StringId buf_name_id =
+          context_->storage->InternString(buf_name.string_view());
+      AddSlice(buf_name_id, timestamp, pid, evt);
+      break;
+    }
+    case FtraceEvent::kVb2V4l2DqbufFieldNumber: {
+      Vb2V4l2DqbufFtraceEvent::Decoder pb_evt(bytes.data, bytes.size);
+      BufferEvent evt;
+      evt.device_minor = pb_evt.minor();
+      evt.index = base::nullopt;
+      evt.type = base::nullopt;
+      evt.bytesused = base::nullopt;
+      evt.flags = pb_evt.flags();
+      evt.field = pb_evt.field();
+      evt.timestamp = pb_evt.timestamp();
+      evt.sequence = pb_evt.sequence();
+      evt.timecode_flags = pb_evt.timecode_flags();
+      evt.timecode_frames = pb_evt.timecode_frames();
+      evt.timecode_hours = pb_evt.timecode_hours();
+      evt.timecode_minutes = pb_evt.timecode_minutes();
+      evt.timecode_seconds = pb_evt.timecode_seconds();
+      evt.timecode_type = pb_evt.timecode_type();
+      evt.timecode_userbits0 = pb_evt.timecode_userbits0();
+      evt.timecode_userbits1 = pb_evt.timecode_userbits1();
+      evt.timecode_userbits2 = pb_evt.timecode_userbits2();
+      evt.timecode_userbits3 = pb_evt.timecode_userbits3();
+
+      base::StackString<64> buf_name(
+          "vb2_v4l2_qbuf minor=%" PRIu32 " seq=%" PRIu32 " type=%" PRIu32
+          " index=%" PRIu32,
+          evt.device_minor, evt.sequence, *evt.type, *evt.index);
+
+      StringId buf_name_id =
+          context_->storage->InternString(buf_name.string_view());
+      AddSlice(buf_name_id, timestamp, pid, evt);
+      break;
+    }
+    default:
+      break;
+  }
+}
+
+base::Optional<SliceId> V4l2Tracker::AddSlice(StringId buf_name_id,
+                                              int64_t timestamp,
+                                              uint32_t pid,
+                                              const BufferEvent& evt) {
+  UniqueTid utid = context_->process_tracker->GetOrCreateThread(pid);
+  TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
+
+  base::Optional<SliceId> slice_id = context_->slice_tracker->Scoped(
+      timestamp, track_id, buf_event_ids_.v4l2, buf_name_id, 0,
+      [this, &evt](ArgsTracker::BoundInserter* inserter) {
+        this->AddArgs(evt, inserter);
+      });
+
+  return slice_id;
+}
+
+void V4l2Tracker::AddArgs(const BufferEvent& evt,
+                          ArgsTracker::BoundInserter* inserter) {
+  inserter->AddArg(buf_event_ids_.device_minor,
+                   Variadic::Integer(evt.device_minor));
+
+  if (evt.index)
+    inserter->AddArg(buf_event_ids_.index, Variadic::Integer(*evt.index));
+  if (evt.type)
+    inserter->AddArg(buf_event_ids_.type,
+                     Variadic::String(buf_type_ids_.Map(*evt.type)));
+  if (evt.bytesused)
+    inserter->AddArg(buf_event_ids_.bytesused,
+                     Variadic::Integer(*evt.bytesused));
+
+  inserter->AddArg(buf_event_ids_.flags,
+                   Variadic::String(InternBufFlags(evt.flags)));
+  inserter->AddArg(buf_event_ids_.field,
+                   Variadic::String(buf_field_ids_.Map(evt.field)));
+  inserter->AddArg(buf_event_ids_.timestamp, Variadic::Integer(evt.timestamp));
+  inserter->AddArg(buf_event_ids_.timecode_type,
+                   Variadic::String(tc_type_ids_.Map(evt.timecode_type)));
+  inserter->AddArg(buf_event_ids_.timecode_flags,
+                   Variadic::String(InternTcFlags(evt.timecode_flags)));
+  inserter->AddArg(buf_event_ids_.timecode_frames,
+                   Variadic::Integer(evt.timecode_frames));
+  inserter->AddArg(buf_event_ids_.timecode_seconds,
+                   Variadic::Integer(evt.timecode_seconds));
+  inserter->AddArg(buf_event_ids_.timecode_minutes,
+                   Variadic::Integer(evt.timecode_minutes));
+  inserter->AddArg(buf_event_ids_.timecode_hours,
+                   Variadic::Integer(evt.timecode_hours));
+  inserter->AddArg(buf_event_ids_.timecode_userbits0,
+                   Variadic::Integer(evt.timecode_userbits0));
+  inserter->AddArg(buf_event_ids_.timecode_userbits1,
+                   Variadic::Integer(evt.timecode_userbits1));
+  inserter->AddArg(buf_event_ids_.timecode_userbits2,
+                   Variadic::Integer(evt.timecode_userbits2));
+  inserter->AddArg(buf_event_ids_.timecode_userbits3,
+                   Variadic::Integer(evt.timecode_userbits3));
+  inserter->AddArg(buf_event_ids_.sequence, Variadic::Integer(evt.sequence));
+}
+
+V4l2Tracker::BufferEventStringIds::BufferEventStringIds(TraceStorage& storage)
+    : v4l2(storage.InternString("Video 4 Linux 2")),
+      v4l2_qbuf(storage.InternString("v4l2_qbuf")),
+      v4l2_dqbuf(storage.InternString("v4l2_dqbuf")),
+      device_minor(storage.InternString("minor")),
+      index(storage.InternString("index")),
+      type(storage.InternString("type")),
+      bytesused(storage.InternString("bytesused")),
+      flags(storage.InternString("flags")),
+      field(storage.InternString("field")),
+      timestamp(storage.InternString("timestamp")),
+      timecode_type(storage.InternString("timecode_type")),
+      timecode_flags(storage.InternString("timecode_flags")),
+      timecode_frames(storage.InternString("timecode_frames")),
+      timecode_seconds(storage.InternString("timecode_seconds")),
+      timecode_minutes(storage.InternString("timecode_minutes")),
+      timecode_hours(storage.InternString("timecode_hours")),
+      timecode_userbits0(storage.InternString("timecode_userbits0")),
+      timecode_userbits1(storage.InternString("timecode_userbits1")),
+      timecode_userbits2(storage.InternString("timecode_userbits2")),
+      timecode_userbits3(storage.InternString("timecode_userbits3")),
+      sequence(storage.InternString("sequence")) {}
+
+V4l2Tracker::BufferTypeStringIds::BufferTypeStringIds(TraceStorage& storage)
+    : video_capture(storage.InternString("VIDEO_CAPTURE")),
+      video_output(storage.InternString("VIDEO_OUTPUT")),
+      video_overlay(storage.InternString("VIDEO_OVERLAY")),
+      vbi_capture(storage.InternString("VBI_CAPTURE")),
+      vbi_output(storage.InternString("VBI_OUTPUT")),
+      sliced_vbi_capture(storage.InternString("SLICED_VBI_CAPTURE")),
+      sliced_vbi_output(storage.InternString("SLICED_VBI_OUTPUT")),
+      video_output_overlay(storage.InternString("VIDEO_OUTPUT_OVERLAY")),
+      video_capture_mplane(storage.InternString("VIDEO_CAPTURE_MPLANE")),
+      video_output_mplane(storage.InternString("VIDEO_OUTPUT_MPLANE")),
+      sdr_capture(storage.InternString("SDR_CAPTURE")),
+      sdr_output(storage.InternString("SDR_OUTPUT")),
+      meta_capture(storage.InternString("META_CAPTURE")),
+      meta_output(storage.InternString("META_OUTPUT")),
+      priv(storage.InternString("PRIVATE")) {}
+
+StringId V4l2Tracker::BufferTypeStringIds::Map(uint32_t buf_type) {
+  // Values taken from linux/videodev2.h
+  switch (buf_type) {
+    case 1:
+      return video_capture;
+    case 2:
+      return video_output;
+    case 3:
+      return video_overlay;
+    case 4:
+      return vbi_capture;
+    case 5:
+      return vbi_output;
+    case 6:
+      return sliced_vbi_capture;
+    case 7:
+      return sliced_vbi_output;
+    case 8:
+      return video_output_overlay;
+    case 9:
+      return video_capture_mplane;
+    case 10:
+      return video_output_mplane;
+    case 11:
+      return sdr_capture;
+    case 12:
+      return sdr_output;
+    case 13:
+      return meta_capture;
+    case 14:
+      return meta_output;
+    case 0x80:
+      return priv;
+    default:
+      return kNullStringId;
+  }
+}
+
+V4l2Tracker::BufferFieldStringIds::BufferFieldStringIds(TraceStorage& storage)
+    : any(storage.InternString("ANY")),
+      none(storage.InternString("NONE")),
+      top(storage.InternString("TOP")),
+      bottom(storage.InternString("BOTTOM")),
+      interlaced(storage.InternString("INTERLACED")),
+      seq_tb(storage.InternString("SEQ_TB")),
+      seq_bt(storage.InternString("SEQ_BT")),
+      alternate(storage.InternString("ALTERNATE")),
+      interlaced_tb(storage.InternString("INTERLACED_TB")),
+      interlaced_bt(storage.InternString("INTERLACED_BT")) {}
+
+StringId V4l2Tracker::BufferFieldStringIds::Map(uint32_t field) {
+  // Values taken from linux/videodev2.h
+  switch (field) {
+    case 0:
+      return any;
+    case 1:
+      return none;
+    case 2:
+      return top;
+    case 3:
+      return bottom;
+    case 4:
+      return interlaced;
+    case 5:
+      return seq_tb;
+    case 6:
+      return seq_bt;
+    case 7:
+      return alternate;
+    case 8:
+      return interlaced_tb;
+    case 9:
+      return interlaced_bt;
+    default:
+      return kNullStringId;
+  }
+}
+
+V4l2Tracker::TimecodeTypeStringIds::TimecodeTypeStringIds(TraceStorage& storage)
+    : type_24fps(storage.InternString("24FPS")),
+      type_25fps(storage.InternString("25FPS")),
+      type_30fps(storage.InternString("30FPS")),
+      type_50fps(storage.InternString("50FPS")),
+      type_60fps(storage.InternString("60FPS")) {}
+
+StringId V4l2Tracker::TimecodeTypeStringIds::Map(uint32_t type) {
+  switch (type) {
+    case 1:
+      return type_24fps;
+    case 2:
+      return type_25fps;
+    case 3:
+      return type_30fps;
+    case 4:
+      return type_50fps;
+    case 5:
+      return type_60fps;
+    default:
+      return kNullStringId;
+  }
+}
+
+StringId V4l2Tracker::InternBufFlags(uint32_t flags) {
+  std::vector<std::string> present_flags;
+
+  if (flags & 0x00000001)
+    present_flags.push_back("MAPPED");
+  if (flags & 0x00000002)
+    present_flags.push_back("QUEUED");
+  if (flags & 0x00000004)
+    present_flags.push_back("DONE");
+  if (flags & 0x00000008)
+    present_flags.push_back("KEYFRAME");
+  if (flags & 0x00000010)
+    present_flags.push_back("PFRAME");
+  if (flags & 0x00000020)
+    present_flags.push_back("BFRAME");
+  if (flags & 0x00000040)
+    present_flags.push_back("ERROR");
+  if (flags & 0x00000080)
+    present_flags.push_back("IN_REQUEST");
+  if (flags & 0x00000100)
+    present_flags.push_back("TIMECODE");
+  if (flags & 0x00000200)
+    present_flags.push_back("M2M_HOLD_CAPTURE_BUF");
+  if (flags & 0x00000400)
+    present_flags.push_back("PREPARED");
+  if (flags & 0x00000800)
+    present_flags.push_back("NO_CACHE_INVALIDATE");
+  if (flags & 0x00001000)
+    present_flags.push_back("NO_CACHE_CLEAN");
+  if (flags & 0x0000e000)
+    present_flags.push_back("TIMESTAMP_MASK");
+  if (flags == 0x00000000)
+    present_flags.push_back("TIMESTAMP_UNKNOWN");
+  if (flags & 0x00002000)
+    present_flags.push_back("TIMESTAMP_MONOTONIC");
+  if (flags & 0x00004000)
+    present_flags.push_back("TIMESTAMP_COPY");
+  if (flags & 0x00070000)
+    present_flags.push_back("TSTAMP_SRC_MASK");
+  if (flags == 0x00000000)
+    present_flags.push_back("TSTAMP_SRC_EOF");
+  if (flags & 0x00010000)
+    present_flags.push_back("TSTAMP_SRC_SOE");
+  if (flags & 0x00100000)
+    present_flags.push_back("LAST");
+  if (flags & 0x00800000)
+    present_flags.push_back("REQUEST_FD");
+
+  return context_->storage->InternString(
+      base::Join(present_flags, "|").c_str());
+}
+
+StringId V4l2Tracker::InternTcFlags(uint32_t flags) {
+  std::vector<std::string> present_flags;
+
+  if (flags == 0x0000)
+    present_flags.push_back("USERBITS_USERDEFINED");
+  if (flags & 0x0001)
+    present_flags.push_back("FLAG_DROPFRAME");
+  if (flags & 0x0002)
+    present_flags.push_back("FLAG_COLORFRAME");
+  if ((flags & 0x000C) == 0x0004)
+    present_flags.push_back("USERBITS_field(01)");
+  if ((flags & 0x000C) == 0x0008)
+    present_flags.push_back("USERBITS_field(10)");
+  if ((flags & 0x000C) == 0x000C)
+    present_flags.push_back("USERBITS_field(11)");
+  if (flags & 0x0008)
+    present_flags.push_back("USERBITS_8BITCHARS");
+
+  return context_->storage->InternString(
+      base::Join(present_flags, "|").c_str());
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/ftrace/v4l2_tracker.h b/src/trace_processor/importers/ftrace/v4l2_tracker.h
new file mode 100644
index 0000000..f204e1d
--- /dev/null
+++ b/src/trace_processor/importers/ftrace/v4l2_tracker.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2022 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_FTRACE_V4L2_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_V4L2_TRACKER_H_
+
+#include <stdint.h>
+#include <cstdint>
+
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "perfetto/ext/base/optional.h"
+
+#include "perfetto/protozero/field.h"
+#include "src/trace_processor/importers/common/args_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 {
+
+class TraceProcessorContext;
+
+class V4l2Tracker : public Destructible {
+ public:
+  // Declared public for testing only.
+  explicit V4l2Tracker(TraceProcessorContext*);
+  V4l2Tracker(const V4l2Tracker&) = delete;
+  V4l2Tracker& operator=(const V4l2Tracker&) = delete;
+  ~V4l2Tracker() override;
+
+  static V4l2Tracker* GetOrCreate(TraceProcessorContext* context) {
+    if (!context->v4l2_tracker) {
+      context->v4l2_tracker.reset(new V4l2Tracker(context));
+    }
+    return static_cast<V4l2Tracker*>(context->v4l2_tracker.get());
+  }
+
+  void ParseV4l2Event(uint64_t fld_id,
+                      int64_t timestamp,
+                      uint32_t pid,
+                      const protozero::ConstBytes&);
+
+ private:
+  struct BufferEvent {
+   public:
+    int32_t device_minor;
+    base::Optional<uint32_t> index;
+    base::Optional<uint32_t> type;
+    base::Optional<uint32_t> bytesused;
+    uint32_t flags;
+    uint32_t field;
+    int64_t timestamp;
+    uint32_t sequence;
+    uint32_t timecode_flags;
+    uint32_t timecode_frames;
+    uint32_t timecode_hours;
+    uint32_t timecode_minutes;
+    uint32_t timecode_seconds;
+    uint32_t timecode_type;
+    uint32_t timecode_userbits0;
+    uint32_t timecode_userbits1;
+    uint32_t timecode_userbits2;
+    uint32_t timecode_userbits3;
+  };
+
+  struct BufferEventStringIds {
+    explicit BufferEventStringIds(TraceStorage& storage);
+
+    const StringId v4l2;
+    const StringId v4l2_qbuf;
+    const StringId v4l2_dqbuf;
+    const StringId device_minor;
+    const StringId index;
+    const StringId type;
+    const StringId bytesused;
+    const StringId flags;
+    const StringId field;
+    const StringId timestamp;
+    const StringId timecode_type;
+    const StringId timecode_flags;
+    const StringId timecode_frames;
+    const StringId timecode_seconds;
+    const StringId timecode_minutes;
+    const StringId timecode_hours;
+    const StringId timecode_userbits0;
+    const StringId timecode_userbits1;
+    const StringId timecode_userbits2;
+    const StringId timecode_userbits3;
+    const StringId sequence;
+  };
+
+  struct BufferTypeStringIds {
+    explicit BufferTypeStringIds(TraceStorage& storage);
+
+    StringId Map(uint32_t type);
+
+    const StringId video_capture;
+    const StringId video_output;
+    const StringId video_overlay;
+    const StringId vbi_capture;
+    const StringId vbi_output;
+    const StringId sliced_vbi_capture;
+    const StringId sliced_vbi_output;
+    const StringId video_output_overlay;
+    const StringId video_capture_mplane;
+    const StringId video_output_mplane;
+    const StringId sdr_capture;
+    const StringId sdr_output;
+    const StringId meta_capture;
+    const StringId meta_output;
+    const StringId priv;
+  };
+
+  struct BufferFieldStringIds {
+    explicit BufferFieldStringIds(TraceStorage& storage);
+
+    StringId Map(uint32_t field);
+
+    const StringId any;
+    const StringId none;
+    const StringId top;
+    const StringId bottom;
+    const StringId interlaced;
+    const StringId seq_tb;
+    const StringId seq_bt;
+    const StringId alternate;
+    const StringId interlaced_tb;
+    const StringId interlaced_bt;
+  };
+
+  struct TimecodeTypeStringIds {
+    explicit TimecodeTypeStringIds(TraceStorage& storage);
+
+    StringId Map(uint32_t field);
+
+    const StringId type_24fps;
+    const StringId type_25fps;
+    const StringId type_30fps;
+    const StringId type_50fps;
+    const StringId type_60fps;
+  };
+
+  struct QueuedBuffer {
+    base::Optional<SliceId> queue_slice_id;
+  };
+
+  base::Optional<SliceId> AddSlice(StringId buf_name_id,
+                                   int64_t timestamp,
+                                   uint32_t pid,
+                                   const BufferEvent& evt);
+
+  void AddArgs(const BufferEvent& evt, ArgsTracker::BoundInserter* inserter);
+
+  StringId InternBufFlags(uint32_t flags);
+  StringId InternTcFlags(uint32_t flags);
+
+  TraceProcessorContext* const context_;
+  base::FlatHashMap<uint64_t, QueuedBuffer> queued_buffers_;
+
+  BufferEventStringIds buf_event_ids_;
+  BufferTypeStringIds buf_type_ids_;
+  BufferFieldStringIds buf_field_ids_;
+  TimecodeTypeStringIds tc_type_ids_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_V4L2_TRACKER_H_
diff --git a/src/trace_processor/importers/ftrace/virtio_gpu_tracker.cc b/src/trace_processor/importers/ftrace/virtio_gpu_tracker.cc
new file mode 100644
index 0000000..d4d09a2
--- /dev/null
+++ b/src/trace_processor/importers/ftrace/virtio_gpu_tracker.cc
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2022 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/virtio_gpu_tracker.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
+#include "protos/perfetto/trace/ftrace/virtio_gpu.pbzero.h"
+#include "src/trace_processor/importers/common/async_track_set_tracker.h"
+#include "src/trace_processor/importers/common/event_tracker.h"
+#include "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
+
+enum virtio_gpu_ctrl_type {
+  VIRTIO_GPU_UNDEFINED = 0,
+
+  /* 2d commands */
+  VIRTIO_GPU_CMD_GET_DISPLAY_INFO = 0x0100,
+  VIRTIO_GPU_CMD_RESOURCE_CREATE_2D,
+  VIRTIO_GPU_CMD_RESOURCE_UNREF,
+  VIRTIO_GPU_CMD_SET_SCANOUT,
+  VIRTIO_GPU_CMD_RESOURCE_FLUSH,
+  VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D,
+  VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING,
+  VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING,
+  VIRTIO_GPU_CMD_GET_CAPSET_INFO,
+  VIRTIO_GPU_CMD_GET_CAPSET,
+  VIRTIO_GPU_CMD_GET_EDID,
+  VIRTIO_GPU_CMD_RESOURCE_ASSIGN_UUID,
+  VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB,
+  VIRTIO_GPU_CMD_SET_SCANOUT_BLOB,
+
+  /* 3d commands */
+  VIRTIO_GPU_CMD_CTX_CREATE = 0x0200,
+  VIRTIO_GPU_CMD_CTX_DESTROY,
+  VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE,
+  VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE,
+  VIRTIO_GPU_CMD_RESOURCE_CREATE_3D,
+  VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D,
+  VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D,
+  VIRTIO_GPU_CMD_SUBMIT_3D,
+  VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB,
+  VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB,
+
+  /* cursor commands */
+  VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300,
+  VIRTIO_GPU_CMD_MOVE_CURSOR,
+
+  /* success responses */
+  VIRTIO_GPU_RESP_OK_NODATA = 0x1100,
+  VIRTIO_GPU_RESP_OK_DISPLAY_INFO,
+  VIRTIO_GPU_RESP_OK_CAPSET_INFO,
+  VIRTIO_GPU_RESP_OK_CAPSET,
+  VIRTIO_GPU_RESP_OK_EDID,
+  VIRTIO_GPU_RESP_OK_RESOURCE_UUID,
+  VIRTIO_GPU_RESP_OK_MAP_INFO,
+
+  /* error responses */
+  VIRTIO_GPU_RESP_ERR_UNSPEC = 0x1200,
+  VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY,
+  VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID,
+  VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID,
+  VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID,
+  VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER,
+};
+
+static const char* virtio_gpu_ctrl_name(uint32_t type) {
+  switch (type) {
+#define ENUM(n)            \
+  case VIRTIO_GPU_CMD_##n: \
+    return #n
+    /* 2d commands */
+    ENUM(GET_DISPLAY_INFO);
+    ENUM(RESOURCE_CREATE_2D);
+    ENUM(RESOURCE_UNREF);
+    ENUM(SET_SCANOUT);
+    ENUM(RESOURCE_FLUSH);
+    ENUM(TRANSFER_TO_HOST_2D);
+    ENUM(RESOURCE_ATTACH_BACKING);
+    ENUM(RESOURCE_DETACH_BACKING);
+    ENUM(GET_CAPSET_INFO);
+    ENUM(GET_CAPSET);
+    ENUM(GET_EDID);
+    ENUM(RESOURCE_ASSIGN_UUID);
+    ENUM(RESOURCE_CREATE_BLOB);
+    ENUM(SET_SCANOUT_BLOB);
+
+    /* 3d commands */
+    ENUM(CTX_CREATE);
+    ENUM(CTX_DESTROY);
+    ENUM(CTX_ATTACH_RESOURCE);
+    ENUM(CTX_DETACH_RESOURCE);
+    ENUM(RESOURCE_CREATE_3D);
+    ENUM(TRANSFER_TO_HOST_3D);
+    ENUM(TRANSFER_FROM_HOST_3D);
+    ENUM(SUBMIT_3D);
+    ENUM(RESOURCE_MAP_BLOB);
+    ENUM(RESOURCE_UNMAP_BLOB);
+
+    /* cursor commands */
+    ENUM(UPDATE_CURSOR);
+    ENUM(MOVE_CURSOR);
+#undef ENUM
+    default:
+      return "";
+  }
+}
+
+namespace perfetto {
+namespace trace_processor {
+
+VirtioGpuTracker::VirtioGpuTracker(TraceProcessorContext* context)
+    : virtgpu_control_queue_(context, "Control"),
+      virtgpu_cursor_queue_(context, "Cursor") {}
+
+void VirtioGpuTracker::ParseVirtioGpu(int64_t timestamp,
+                                      int32_t field_id,
+                                      uint32_t pid,
+                                      protozero::ConstBytes blob) {
+  using protos::pbzero::FtraceEvent;
+
+  switch (field_id) {
+    case FtraceEvent::kVirtioGpuCmdQueueFieldNumber: {
+      ParseVirtioGpuCmdQueue(timestamp, pid, blob);
+      break;
+    }
+    case FtraceEvent::kVirtioGpuCmdResponseFieldNumber: {
+      ParseVirtioGpuCmdResponse(timestamp, pid, blob);
+      break;
+    }
+    default:
+      PERFETTO_DFATAL("Unexpected field id");
+      break;
+  }
+}
+VirtioGpuTracker::VirtioGpuQueue::VirtioGpuQueue(TraceProcessorContext* context,
+                                                 const char* name)
+    : context_(context) {
+  base::StackString<255> num_free_name("Virtgpu %s Free", name);
+  base::StackString<255> latency_name("Virtgpu %s Latency", name);
+  base::StackString<255> queue_track_name("Virtgpu %s Queue", name);
+
+  num_free_id_ = context->storage->InternString(num_free_name.string_view());
+  latency_id_ = context->storage->InternString(latency_name.string_view());
+  queue_track_id_ =
+      context->storage->InternString(queue_track_name.string_view());
+}
+
+void VirtioGpuTracker::VirtioGpuQueue::HandleNumFree(int64_t timestamp,
+                                                     uint32_t num_free) {
+  TrackId track =
+      context_->track_tracker->InternGlobalCounterTrack(num_free_id_);
+  context_->event_tracker->PushCounter(timestamp, static_cast<double>(num_free),
+                                       track);
+}
+
+void VirtioGpuTracker::VirtioGpuQueue::HandleCmdQueue(int64_t timestamp,
+                                                      uint32_t seqno,
+                                                      uint32_t type,
+                                                      uint64_t fence_id) {
+  auto async_track =
+      context_->async_track_set_tracker->InternGlobalTrackSet(queue_track_id_);
+  TrackId start_id =
+      context_->async_track_set_tracker->Begin(async_track, seqno);
+
+  context_->slice_tracker->Begin(
+      timestamp, start_id, kNullStringId,
+      context_->storage->InternString(
+          base::StringView(virtio_gpu_ctrl_name(type))));
+
+  /* cmds with a fence do not necessarily get an immediate response from
+   * the host, so we should not use them for calculating latency:
+   */
+  if (!fence_id) {
+    start_timestamps_[seqno] = timestamp;
+  }
+}
+
+void VirtioGpuTracker::VirtioGpuQueue::HandleCmdResponse(int64_t timestamp,
+                                                         uint32_t seqno) {
+  auto async_track =
+      context_->async_track_set_tracker->InternGlobalTrackSet(queue_track_id_);
+  TrackId end_id = context_->async_track_set_tracker->End(async_track, seqno);
+  context_->slice_tracker->End(timestamp, end_id);
+
+  int64_t* start_timestamp = start_timestamps_.Find(seqno);
+  if (!start_timestamp)
+    return;
+
+  int64_t duration = timestamp - *start_timestamp;
+
+  TrackId track =
+      context_->track_tracker->InternGlobalCounterTrack(latency_id_);
+  context_->event_tracker->PushCounter(timestamp,
+                                       static_cast<double>(duration), track);
+
+  start_timestamps_.Erase(seqno);
+}
+
+void VirtioGpuTracker::ParseVirtioGpuCmdQueue(int64_t timestamp,
+                                              uint32_t /*pid*/,
+                                              protozero::ConstBytes blob) {
+  protos::pbzero::VirtioGpuCmdQueueFtraceEvent::Decoder evt(blob.data,
+                                                            blob.size);
+
+  auto name = base::StringView(evt.name());
+  if (name == "control") {
+    virtgpu_control_queue_.HandleNumFree(timestamp, evt.num_free());
+    virtgpu_control_queue_.HandleCmdQueue(timestamp, evt.seqno(), evt.type(),
+                                          evt.fence_id());
+  } else if (name == "cursor") {
+    virtgpu_cursor_queue_.HandleNumFree(timestamp, evt.num_free());
+    virtgpu_cursor_queue_.HandleCmdQueue(timestamp, evt.seqno(), evt.type(),
+                                         evt.fence_id());
+  }
+}
+
+void VirtioGpuTracker::ParseVirtioGpuCmdResponse(int64_t timestamp,
+                                                 uint32_t /*pid*/,
+                                                 protozero::ConstBytes blob) {
+  protos::pbzero::VirtioGpuCmdResponseFtraceEvent::Decoder evt(blob.data,
+                                                               blob.size);
+  auto name = base::StringView(evt.name());
+  if (name == "control") {
+    virtgpu_control_queue_.HandleNumFree(timestamp, evt.num_free());
+    virtgpu_control_queue_.HandleCmdResponse(timestamp, evt.seqno());
+  } else if (name == "cursor") {
+    virtgpu_cursor_queue_.HandleNumFree(timestamp, evt.num_free());
+    virtgpu_cursor_queue_.HandleCmdResponse(timestamp, evt.seqno());
+  }
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/ftrace/virtio_gpu_tracker.h b/src/trace_processor/importers/ftrace/virtio_gpu_tracker.h
new file mode 100644
index 0000000..8dc50d6
--- /dev/null
+++ b/src/trace_processor/importers/ftrace/virtio_gpu_tracker.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2022 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_FTRACE_VIRTIO_GPU_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_VIRTIO_GPU_TRACKER_H_
+
+#include <deque>
+#include <memory>
+
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "perfetto/ext/base/string_view.h"
+#include "perfetto/protozero/field.h"
+#include "src/trace_processor/storage/trace_storage.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class TraceProcessorContext;
+
+class VirtioGpuTracker {
+ public:
+  explicit VirtioGpuTracker(TraceProcessorContext*);
+
+  void ParseVirtioGpu(int64_t timestamp,
+                      int32_t field_id,
+                      uint32_t pid,
+                      protozero::ConstBytes blob);
+
+ private:
+  class VirtioGpuQueue {
+    TraceProcessorContext* context_;
+    StringId num_free_id_;
+    StringId latency_id_;
+    StringId queue_track_id_;
+
+    // Maps a seqno to the timestamp of a VirtioGpuCmdQueue.  The events
+    // come in pairs of VirtioGpuCmdQueue plus VirtioGpuCmdResponse and
+    // can be matched up via their seqno field.  To calculate the slice
+    // duration we need to lookup the timestamp of the matching CmdQueue
+    // event when we get the CmdResponse event.
+    base::FlatHashMap<uint32_t, int64_t> start_timestamps_;
+
+   public:
+    VirtioGpuQueue(TraceProcessorContext* context, const char* name);
+
+    void HandleNumFree(int64_t timestamp, uint32_t num_free);
+    void HandleCmdQueue(int64_t timestamp,
+                        uint32_t seqno,
+                        uint32_t type,
+                        uint64_t fence_id);
+    void HandleCmdResponse(int64_t timestamp, uint32_t seqno);
+  };
+
+  VirtioGpuQueue virtgpu_control_queue_;
+  VirtioGpuQueue virtgpu_cursor_queue_;
+
+  void ParseVirtioGpuCmdQueue(int64_t timestamp,
+                              uint32_t pid,
+                              protozero::ConstBytes);
+  void ParseVirtioGpuCmdResponse(int64_t timestamp,
+                                 uint32_t pid,
+                                 protozero::ConstBytes blob);
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_VIRTIO_GPU_TRACKER_H_
diff --git a/src/trace_processor/importers/ftrace/virtio_video_tracker.cc b/src/trace_processor/importers/ftrace/virtio_video_tracker.cc
new file mode 100644
index 0000000..8cbdfae
--- /dev/null
+++ b/src/trace_processor/importers/ftrace/virtio_video_tracker.cc
@@ -0,0 +1,234 @@
+
+/*
+ * Copyright (C) 2022 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 <cstdint>
+#include <functional>
+#include <memory>
+
+#include "perfetto/ext/base/hash.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "perfetto/protozero/field.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/async_track_set_tracker.h"
+#include "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/importers/ftrace/virtio_video_tracker.h"
+
+#include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
+#include "protos/perfetto/trace/ftrace/virtio_video.pbzero.h"
+#include "src/trace_processor/storage/trace_storage.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+namespace {
+using protos::pbzero::FtraceEvent;
+using protos::pbzero::VirtioVideoCmdDoneFtraceEvent;
+using protos::pbzero::VirtioVideoCmdFtraceEvent;
+using protos::pbzero::VirtioVideoResourceQueueDoneFtraceEvent;
+using protos::pbzero::VirtioVideoResourceQueueFtraceEvent;
+using protozero::ConstBytes;
+using TrackSetId = AsyncTrackSetTracker::TrackSetId;
+
+/* VIRTIO_VIDEO_QUEUE_TYPE_INPUT */
+constexpr uint64_t kVirtioVideoQueueTypeInput = 0x100;
+
+/* VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT */
+constexpr uint64_t kVirtioVideoQueueTypeOutput = 0x101;
+
+constexpr int64_t kVirtioVideoCmdDuration = 100000;
+}  // namespace
+
+VirtioVideoTracker::VirtioVideoTracker(TraceProcessorContext* context)
+    : context_(context),
+      unknown_id_(context->storage->InternString("Unknown")),
+      input_queue_id_(context->storage->InternString("INPUT")),
+      output_queue_id_(context->storage->InternString("OUTPUT")),
+      fields_string_ids_(*context->storage) {
+  TraceStorage& storage = *context_->storage;
+
+  command_names_.Insert(0x100, storage.InternString("QUERY_CAPABILITY"));
+  command_names_.Insert(0x101, storage.InternString("STREAM_CREATE"));
+  command_names_.Insert(0x102, storage.InternString("STREAM_DESTROY"));
+  command_names_.Insert(0x103, storage.InternString("STREAM_DRAIN"));
+  command_names_.Insert(0x104, storage.InternString("RESOURCE_CREATE"));
+  command_names_.Insert(0x105, storage.InternString("RESOURCE_QUEUE"));
+  command_names_.Insert(0x106, storage.InternString("RESOURCE_DESTROY_ALL"));
+  command_names_.Insert(0x107, storage.InternString("QUEUE_CLEAR"));
+  command_names_.Insert(0x108, storage.InternString("GET_PARAMS"));
+  command_names_.Insert(0x109, storage.InternString("SET_PARAMS"));
+  command_names_.Insert(0x10a, storage.InternString("QUERY_CONTROL"));
+  command_names_.Insert(0x10b, storage.InternString("GET_CONTROL"));
+  command_names_.Insert(0x10c, storage.InternString("SET_CONTROL"));
+  command_names_.Insert(0x10d, storage.InternString("GET_PARAMS_EXT"));
+  command_names_.Insert(0x10e, storage.InternString("SET_PARAMS_EXT"));
+}
+
+VirtioVideoTracker::~VirtioVideoTracker() = default;
+
+void VirtioVideoTracker::ParseVirtioVideoEvent(uint64_t fld_id,
+                                               int64_t timestamp,
+                                               const ConstBytes& blob) {
+  switch (fld_id) {
+    case FtraceEvent::kVirtioVideoResourceQueueFieldNumber: {
+      VirtioVideoResourceQueueFtraceEvent::Decoder pb_evt(blob.data, blob.size);
+
+      uint64_t hash = base::Hasher::Combine(
+          pb_evt.stream_id(), pb_evt.resource_id(), pb_evt.queue_type());
+
+      base::StackString<64> name("Resource #%" PRIu32, pb_evt.resource_id());
+      StringId name_id = context_->storage->InternString(name.string_view());
+
+      TrackSetId track_set_id =
+          InternOrCreateBufferTrack(pb_evt.stream_id(), pb_evt.queue_type());
+      TrackId begin_id = context_->async_track_set_tracker->Begin(
+          track_set_id, static_cast<int64_t>(hash));
+      context_->slice_tracker->Begin(timestamp, begin_id, kNullStringId,
+                                     name_id);
+      break;
+    }
+    case FtraceEvent::kVirtioVideoResourceQueueDoneFieldNumber: {
+      VirtioVideoResourceQueueDoneFtraceEvent::Decoder pb_evt(blob.data,
+                                                              blob.size);
+
+      uint64_t hash = base::Hasher::Combine(
+          pb_evt.stream_id(), pb_evt.resource_id(), pb_evt.queue_type());
+
+      TrackSetId track_set_id =
+          InternOrCreateBufferTrack(pb_evt.stream_id(), pb_evt.queue_type());
+
+      TrackId end_id = context_->async_track_set_tracker->End(
+          track_set_id, static_cast<int64_t>(hash));
+      context_->slice_tracker->End(
+          timestamp, end_id, {}, {},
+          [this, &pb_evt](ArgsTracker::BoundInserter* args) {
+            this->AddCommandSliceArgs(&pb_evt, args);
+          });
+      break;
+    }
+    case FtraceEvent::kVirtioVideoCmdFieldNumber: {
+      VirtioVideoCmdFtraceEvent::Decoder pb_evt(blob.data, blob.size);
+      AddCommandSlice(timestamp, pb_evt.stream_id(), pb_evt.type(), false);
+      break;
+    }
+    case FtraceEvent::kVirtioVideoCmdDoneFieldNumber: {
+      VirtioVideoCmdDoneFtraceEvent::Decoder pb_evt(blob.data, blob.size);
+      AddCommandSlice(timestamp, pb_evt.stream_id(), pb_evt.type(), true);
+      break;
+    }
+  }
+}
+
+VirtioVideoTracker::FieldsStringIds::FieldsStringIds(TraceStorage& storage)
+    : stream_id(storage.InternString("stream_id")),
+      resource_id(storage.InternString("resource_id")),
+      queue_type(storage.InternString("queue_type")),
+      data_size0(storage.InternString("data_size0")),
+      data_size1(storage.InternString("data_size1")),
+      data_size2(storage.InternString("data_size2")),
+      data_size3(storage.InternString("data_size3")),
+      timestamp(storage.InternString("timestamp")) {}
+
+TrackSetId VirtioVideoTracker::InternOrCreateBufferTrack(int32_t stream_id,
+                                                         uint32_t queue_type) {
+  const char* queue_name;
+
+  switch (queue_type) {
+    case kVirtioVideoQueueTypeInput: {
+      queue_name = "INPUT";
+      break;
+    }
+    case kVirtioVideoQueueTypeOutput: {
+      queue_name = "OUTPUT";
+      break;
+    }
+    default: {
+      queue_name = "Unknown";
+      break;
+    }
+  }
+
+  base::StackString<64> track_name("virtio_video stream #%" PRId32 " %s",
+                                   stream_id, queue_name);
+  StringId track_name_id =
+      context_->storage->InternString(track_name.string_view());
+  return context_->async_track_set_tracker->InternGlobalTrackSet(track_name_id);
+}
+
+void VirtioVideoTracker::AddCommandSlice(int64_t timestamp,
+                                         uint32_t stream_id,
+                                         uint64_t type,
+                                         bool response) {
+  const StringId* cmd_name_id = command_names_.Find(type);
+  if (!cmd_name_id) {
+    cmd_name_id = &unknown_id_;
+  }
+
+  const char* suffix = response ? "Responses" : "Requests";
+
+  base::StackString<64> track_name("virtio_video stream #%" PRId32 " %s",
+                                   stream_id, suffix);
+  StringId track_name_id =
+      context_->storage->InternString(track_name.string_view());
+
+  TrackSetId track_set_id =
+      context_->async_track_set_tracker->InternGlobalTrackSet(track_name_id);
+
+  TrackId track_id = context_->async_track_set_tracker->Scoped(
+      track_set_id, timestamp, kVirtioVideoCmdDuration);
+
+  context_->slice_tracker->Scoped(timestamp, track_id, kNullStringId,
+                                  *cmd_name_id, kVirtioVideoCmdDuration);
+}
+
+void VirtioVideoTracker::AddCommandSliceArgs(
+    protos::pbzero::VirtioVideoResourceQueueDoneFtraceEvent::Decoder* pb_evt,
+    ArgsTracker::BoundInserter* args) {
+  StringId queue_type_id;
+  switch (pb_evt->queue_type()) {
+    case kVirtioVideoQueueTypeInput: {
+      queue_type_id = input_queue_id_;
+      break;
+    }
+    case kVirtioVideoQueueTypeOutput: {
+      queue_type_id = output_queue_id_;
+      break;
+    }
+    default: {
+      queue_type_id = unknown_id_;
+      break;
+    }
+  }
+
+  args->AddArg(fields_string_ids_.stream_id,
+               Variadic::Integer(pb_evt->stream_id()));
+  args->AddArg(fields_string_ids_.resource_id,
+               Variadic::Integer(pb_evt->resource_id()));
+  args->AddArg(fields_string_ids_.queue_type, Variadic::String(queue_type_id));
+  args->AddArg(fields_string_ids_.data_size0,
+               Variadic::Integer(pb_evt->data_size0()));
+  args->AddArg(fields_string_ids_.data_size1,
+               Variadic::Integer(pb_evt->data_size1()));
+  args->AddArg(fields_string_ids_.data_size2,
+               Variadic::Integer(pb_evt->data_size2()));
+  args->AddArg(fields_string_ids_.data_size3,
+               Variadic::Integer(pb_evt->data_size3()));
+  args->AddArg(fields_string_ids_.timestamp,
+               Variadic::UnsignedInteger(pb_evt->timestamp()));
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/ftrace/virtio_video_tracker.h b/src/trace_processor/importers/ftrace/virtio_video_tracker.h
new file mode 100644
index 0000000..0ec0069
--- /dev/null
+++ b/src/trace_processor/importers/ftrace/virtio_video_tracker.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2022 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_FTRACE_VIRTIO_VIDEO_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_VIRTIO_VIDEO_TRACKER_H_
+
+#include <cstdint>
+
+#include "perfetto/ext/base/flat_hash_map.h"
+
+#include "perfetto/protozero/field.h"
+#include "protos/perfetto/trace/ftrace/virtio_video.pbzero.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/async_track_set_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 {
+
+class TraceProcessorContext;
+
+class VirtioVideoTracker : public Destructible {
+ public:
+  // Declared public for testing only.
+  explicit VirtioVideoTracker(TraceProcessorContext*);
+  VirtioVideoTracker(const VirtioVideoTracker&) = delete;
+  VirtioVideoTracker& operator=(const VirtioVideoTracker&) = delete;
+  ~VirtioVideoTracker() override;
+
+  static VirtioVideoTracker* GetOrCreate(TraceProcessorContext* context) {
+    if (!context->virtio_video_tracker) {
+      context->virtio_video_tracker.reset(new VirtioVideoTracker(context));
+    }
+    return static_cast<VirtioVideoTracker*>(
+        context->virtio_video_tracker.get());
+  }
+
+  void ParseVirtioVideoEvent(uint64_t fld_id,
+                             int64_t timestamp,
+                             const protozero::ConstBytes&);
+
+ private:
+  struct FieldsStringIds {
+    FieldsStringIds(TraceStorage& storage);
+
+    StringId stream_id;
+    StringId resource_id;
+    StringId queue_type;
+    StringId data_size0;
+    StringId data_size1;
+    StringId data_size2;
+    StringId data_size3;
+    StringId timestamp;
+  };
+
+  AsyncTrackSetTracker::TrackSetId InternOrCreateBufferTrack(
+      int32_t stream_id,
+      uint32_t queue_type);
+
+  void AddCommandSlice(int64_t timestamp,
+                       uint32_t stream_id,
+                       uint64_t type,
+                       bool response);
+
+  void AddCommandSliceArgs(
+      protos::pbzero::VirtioVideoResourceQueueDoneFtraceEvent::Decoder*,
+      ArgsTracker::BoundInserter*);
+
+  TraceProcessorContext* const context_;
+
+  StringId unknown_id_;
+  StringId input_queue_id_;
+  StringId output_queue_id_;
+
+  FieldsStringIds fields_string_ids_;
+  base::FlatHashMap<uint64_t, StringId> command_names_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_FTRACE_VIRTIO_VIDEO_TRACKER_H_
diff --git a/src/trace_processor/importers/fuchsia/BUILD.gn b/src/trace_processor/importers/fuchsia/BUILD.gn
new file mode 100644
index 0000000..47d430b
--- /dev/null
+++ b/src/trace_processor/importers/fuchsia/BUILD.gn
@@ -0,0 +1,91 @@
+# Copyright (C) 2022 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("minimal") {
+  sources = [ "fuchsia_trace_utils.h" ]
+  deps = [
+    ":fuchsia_record",
+    "../../../../gn:default_deps",
+    "../../../../include/perfetto/trace_processor:storage",
+    "../../../base",
+    "../../storage",
+  ]
+}
+
+source_set("full") {
+  sources = [
+    "fuchsia_trace_parser.cc",
+    "fuchsia_trace_parser.h",
+    "fuchsia_trace_tokenizer.cc",
+    "fuchsia_trace_tokenizer.h",
+    "fuchsia_trace_utils.cc",
+  ]
+  deps = [
+    ":fuchsia_record",
+    ":minimal",
+    "../../../../gn:default_deps",
+    "../../sorter",
+    "../../storage",
+    "../../types",
+    "../common",
+    "../proto:minimal",
+  ]
+}
+
+source_set("fuchsia_record") {
+  sources = [
+    "fuchsia_record.cc",
+    "fuchsia_record.h",
+  ]
+  deps = [
+    "../../../../gn:default_deps",
+    "../../../../include/perfetto/trace_processor:storage",
+    "../../containers",
+  ]
+}
+
+perfetto_unittest_source_set("unittests") {
+  testonly = true
+  sources = [
+    "fuchsia_parser_unittest.cc",
+    "fuchsia_trace_utils_unittest.cc",
+  ]
+  deps = [
+    ":full",
+    ":minimal",
+    "../../../../gn:default_deps",
+    "../../../../gn:gtest_and_gmock",
+    "../../../../protos/perfetto/common:zero",
+    "../../../../protos/perfetto/config:zero",
+    "../../../../protos/perfetto/trace:zero",
+    "../../../../protos/perfetto/trace/android:zero",
+    "../../../../protos/perfetto/trace/chrome:zero",
+    "../../../../protos/perfetto/trace/ftrace: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",
+    "../../../protozero",
+    "../../sorter",
+    "../../storage",
+    "../../util:descriptors",
+    "../common",
+    "../ftrace:full",
+    "../proto:full",
+    "../proto:minimal",
+  ]
+}
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_parser_unittest.cc b/src/trace_processor/importers/fuchsia/fuchsia_parser_unittest.cc
new file mode 100644
index 0000000..9fa99a5
--- /dev/null
+++ b/src/trace_processor/importers/fuchsia/fuchsia_parser_unittest.cc
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) 2022 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/fuchsia/fuchsia_trace_parser.h"
+#include "src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.h"
+
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/string_view.h"
+#include "perfetto/protozero/scattered_heap_buffer.h"
+#include "perfetto/trace_processor/trace_blob.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/args_translation_table.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/flow_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/proto/additional_modules.h"
+#include "src/trace_processor/importers/proto/default_modules.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/stack_profile_tracker.h"
+#include "src/trace_processor/sorter/trace_sorter.h"
+#include "src/trace_processor/storage/metadata.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/util/descriptors.h"
+#include "test/gtest_and_gmock.h"
+
+#include "protos/perfetto/common/builtin_clock.pbzero.h"
+#include "protos/perfetto/common/sys_stats_counters.pbzero.h"
+#include "protos/perfetto/config/trace_config.pbzero.h"
+#include "protos/perfetto/trace/android/packages_list.pbzero.h"
+#include "protos/perfetto/trace/chrome/chrome_benchmark_metadata.pbzero.h"
+#include "protos/perfetto/trace/chrome/chrome_trace_event.pbzero.h"
+#include "protos/perfetto/trace/clock_snapshot.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_event_bundle.pbzero.h"
+#include "protos/perfetto/trace/ftrace/generic.pbzero.h"
+#include "protos/perfetto/trace/ftrace/power.pbzero.h"
+#include "protos/perfetto/trace/ftrace/sched.pbzero.h"
+#include "protos/perfetto/trace/ftrace/task.pbzero.h"
+#include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
+#include "protos/perfetto/trace/profiling/profile_packet.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/trace.pbzero.h"
+#include "protos/perfetto/trace/trace_packet.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/debug_annotation.pbzero.h"
+#include "protos/perfetto/trace/track_event/log_message.pbzero.h"
+#include "protos/perfetto/trace/track_event/process_descriptor.pbzero.h"
+#include "protos/perfetto/trace/track_event/source_location.pbzero.h"
+#include "protos/perfetto/trace/track_event/task_execution.pbzero.h"
+#include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"
+#include "protos/perfetto/trace/track_event/track_descriptor.pbzero.h"
+#include "protos/perfetto/trace/track_event/track_event.pbzero.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+using ::testing::_;
+using ::testing::Args;
+using ::testing::AtLeast;
+using ::testing::DoAll;
+using ::testing::ElementsAreArray;
+using ::testing::Eq;
+using ::testing::HasSubstr;
+using ::testing::IgnoreResult;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::InvokeArgument;
+using ::testing::NiceMock;
+using ::testing::Pointwise;
+using ::testing::Return;
+using ::testing::ReturnRef;
+using ::testing::UnorderedElementsAreArray;
+class MockSchedEventTracker : public SchedEventTracker {
+ public:
+  explicit MockSchedEventTracker(TraceProcessorContext* context)
+      : SchedEventTracker(context) {}
+
+  MOCK_METHOD9(PushSchedSwitch,
+               void(uint32_t cpu,
+                    int64_t timestamp,
+                    uint32_t prev_pid,
+                    base::StringView prev_comm,
+                    int32_t prev_prio,
+                    int64_t prev_state,
+                    uint32_t next_pid,
+                    base::StringView next_comm,
+                    int32_t next_prio));
+};
+
+class MockProcessTracker : public ProcessTracker {
+ public:
+  explicit MockProcessTracker(TraceProcessorContext* context)
+      : ProcessTracker(context) {}
+
+  MOCK_METHOD4(SetProcessMetadata,
+               UniquePid(uint32_t pid,
+                         base::Optional<uint32_t> ppid,
+                         base::StringView process_name,
+                         base::StringView cmdline));
+
+  MOCK_METHOD3(UpdateThreadName,
+               UniqueTid(uint32_t tid,
+                         StringId thread_name_id,
+                         ThreadNamePriority priority));
+  MOCK_METHOD3(UpdateThreadNameByUtid,
+               void(UniqueTid utid,
+                    StringId thread_name_id,
+                    ThreadNamePriority priority));
+  MOCK_METHOD2(UpdateThread, UniqueTid(uint32_t tid, uint32_t tgid));
+
+  MOCK_METHOD1(GetOrCreateProcess, UniquePid(uint32_t pid));
+  MOCK_METHOD2(SetProcessNameIfUnset,
+               void(UniquePid upid, StringId process_name_id));
+};
+class MockBoundInserter : public ArgsTracker::BoundInserter {
+ public:
+  MockBoundInserter()
+      : ArgsTracker::BoundInserter(&tracker_, nullptr, 0u), tracker_(nullptr) {
+    ON_CALL(*this, AddArg(_, _, _, _)).WillByDefault(ReturnRef(*this));
+  }
+
+  MOCK_METHOD4(
+      AddArg,
+      ArgsTracker::BoundInserter&(StringId flat_key,
+                                  StringId key,
+                                  Variadic v,
+                                  ArgsTracker::UpdatePolicy update_policy));
+
+ private:
+  ArgsTracker tracker_;
+};
+
+class MockEventTracker : public EventTracker {
+ public:
+  explicit MockEventTracker(TraceProcessorContext* context)
+      : EventTracker(context) {}
+  virtual ~MockEventTracker() = default;
+
+  MOCK_METHOD9(PushSchedSwitch,
+               void(uint32_t cpu,
+                    int64_t timestamp,
+                    uint32_t prev_pid,
+                    base::StringView prev_comm,
+                    int32_t prev_prio,
+                    int64_t prev_state,
+                    uint32_t next_pid,
+                    base::StringView next_comm,
+                    int32_t next_prio));
+
+  MOCK_METHOD3(PushCounter,
+               base::Optional<CounterId>(int64_t timestamp,
+                                         double value,
+                                         TrackId track_id));
+};
+
+class MockSliceTracker : public SliceTracker {
+ public:
+  explicit MockSliceTracker(TraceProcessorContext* context)
+      : SliceTracker(context) {}
+
+  MOCK_METHOD5(Begin,
+               base::Optional<SliceId>(int64_t timestamp,
+                                       TrackId track_id,
+                                       StringId cat,
+                                       StringId name,
+                                       SetArgsCallback args_callback));
+  MOCK_METHOD5(End,
+               base::Optional<SliceId>(int64_t timestamp,
+                                       TrackId track_id,
+                                       StringId cat,
+                                       StringId name,
+                                       SetArgsCallback args_callback));
+  MOCK_METHOD6(Scoped,
+               base::Optional<SliceId>(int64_t timestamp,
+                                       TrackId track_id,
+                                       StringId cat,
+                                       StringId name,
+                                       int64_t duration,
+                                       SetArgsCallback args_callback));
+  MOCK_METHOD4(StartSlice,
+               base::Optional<SliceId>(int64_t timestamp,
+                                       TrackId track_id,
+                                       SetArgsCallback args_callback,
+                                       std::function<SliceId()> inserter));
+};
+
+class FuchsiaTraceParserTest : public ::testing::Test {
+ public:
+  FuchsiaTraceParserTest() {
+    context_.storage.reset(new TraceStorage());
+    storage_ = context_.storage.get();
+    context_.track_tracker.reset(new TrackTracker(&context_));
+    context_.global_args_tracker.reset(
+        new GlobalArgsTracker(context_.storage.get()));
+    context_.global_stack_profile_tracker.reset(
+        new GlobalStackProfileTracker());
+    context_.args_tracker.reset(new ArgsTracker(&context_));
+    context_.args_translation_table.reset(new ArgsTranslationTable(storage_));
+    context_.metadata_tracker.reset(
+        new MetadataTracker(context_.storage.get()));
+    event_ = new MockEventTracker(&context_);
+    context_.event_tracker.reset(event_);
+    sched_ = new MockSchedEventTracker(&context_);
+    context_.sched_tracker.reset(sched_);
+    process_ = new NiceMock<MockProcessTracker>(&context_);
+    context_.process_tracker.reset(process_);
+    slice_ = new NiceMock<MockSliceTracker>(&context_);
+    context_.slice_tracker.reset(slice_);
+    context_.slice_translation_table.reset(new SliceTranslationTable(storage_));
+    context_.clock_tracker.reset(new ClockTracker(context_.storage.get()));
+    clock_ = context_.clock_tracker.get();
+    context_.flow_tracker.reset(new FlowTracker(&context_));
+    context_.sorter.reset(new TraceSorter(&context_, CreateParser(),
+                                          TraceSorter::SortingMode::kFullSort));
+    context_.descriptor_pool_.reset(new DescriptorPool());
+
+    RegisterDefaultModules(&context_);
+    RegisterAdditionalModules(&context_);
+  }
+
+  void push_word(uint64_t word) { trace_bytes_.push_back(word); }
+
+  void ResetTraceBuffers() {
+    trace_bytes_.clear();
+    // Write the FXT Magic Bytes
+    push_word(0x0016547846040010);
+  }
+
+  void SetUp() override { ResetTraceBuffers(); }
+
+  util::Status Tokenize() {
+    const size_t num_bytes = trace_bytes_.size() * sizeof(uint64_t);
+    std::unique_ptr<uint8_t[]> raw_trace(new uint8_t[num_bytes]);
+    memcpy(raw_trace.get(), trace_bytes_.data(), num_bytes);
+    context_.chunk_reader.reset(new FuchsiaTraceTokenizer(&context_));
+    auto status = context_.chunk_reader->Parse(TraceBlobView(
+        TraceBlob::TakeOwnership(std::move(raw_trace), num_bytes)));
+
+    ResetTraceBuffers();
+    return status;
+  }
+
+ protected:
+  std::vector<uint64_t> trace_bytes_;
+  std::unique_ptr<TraceParser> CreateParser() {
+    return std::unique_ptr<TraceParser>(new FuchsiaTraceParser(&context_));
+  }
+
+  TraceProcessorContext context_;
+  MockEventTracker* event_;
+  MockSchedEventTracker* sched_;
+  MockProcessTracker* process_;
+  MockSliceTracker* slice_;
+  ClockTracker* clock_;
+  TraceStorage* storage_;
+};
+
+TEST_F(FuchsiaTraceParserTest, CorruptedFxt) {
+  // Invalid record of size 0
+  push_word(0x0016547846040000);
+  EXPECT_FALSE(Tokenize().ok());
+}
+
+TEST_F(FuchsiaTraceParserTest, InlineInstantEvent) {
+  // Inline name of 8 bytes
+  uint64_t name_ref = uint64_t{0x8008} << 48;
+  // Inline category of 8 bytes
+  uint64_t category_ref = uint64_t{0x8008} << 32;
+  // Inline threadref
+  uint64_t threadref = uint64_t{0};
+  // Instant Event
+  uint64_t event_type = 0 << 16;
+  uint64_t size = 6 << 4;
+  uint64_t record_type = 4;
+
+  auto header =
+      name_ref | category_ref | threadref | event_type | size | record_type;
+  push_word(header);
+  // Timestamp
+  push_word(0xAAAAAAAAAAAAAAAA);
+  // Pid + tid
+  push_word(0xBBBBBBBBBBBBBBBB);
+  push_word(0xCCCCCCCCCCCCCCCC);
+  // Inline Category
+  push_word(0xDDDDDDDDDDDDDDDD);
+  // Inline Name
+  push_word(0xEEEEEEEEEEEEEEEE);
+  EXPECT_TRUE(Tokenize().ok());
+  EXPECT_EQ(context_.storage->stats()[stats::fuchsia_invalid_event].value, 0);
+}
+
+TEST_F(FuchsiaTraceParserTest, FxtWithProtos) {
+  // Serialize some protos to bytes
+  protozero::HeapBuffered<protos::pbzero::Trace> protos;
+  {
+    auto* packet = protos->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* packet = protos->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);
+    legacy_event->set_phase('B');
+  }
+  {
+    auto* packet = protos->add_packet();
+    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);
+    legacy_event->set_phase('E');
+  }
+  {
+    auto* packet = protos->add_packet();
+    packet->set_trusted_packet_sequence_id(1);
+    auto* event = packet->set_track_event();
+    event->set_timestamp_absolute_us(1005);
+    event->set_thread_time_absolute_us(2003);
+    event->add_category_iids(2);
+    event->add_category_iids(3);
+    auto* legacy_event = event->set_legacy_event();
+    legacy_event->set_name_iid(2);
+    legacy_event->set_phase('X');
+    legacy_event->set_duration_us(23);         // absolute end: 1028.
+    legacy_event->set_thread_duration_us(12);  // absolute end: 2015.
+  }
+
+  protos->Finalize();
+  std::vector<uint8_t> perfetto_bytes = protos.SerializeAsArray();
+
+  // Set up an FXT Perfetto Blob Header
+  uint64_t blob_type_perfetto = uint64_t{3} << 48;
+  uint64_t unpadded_blob_size_bytes = uint64_t{perfetto_bytes.size()} << 32;
+  uint64_t blob_name_ref = uint64_t{0x8008} << 16;
+  uint64_t size_words = ((perfetto_bytes.size() + 7) / 8 + 2) << 4;
+  uint64_t record_type = 5;
+
+  uint64_t header = blob_type_perfetto | unpadded_blob_size_bytes |
+                    blob_name_ref | size_words | record_type;
+
+  // Pad the blob to a multiple of 8 bytes.
+  while (perfetto_bytes.size() % 8) {
+    perfetto_bytes.push_back(0);
+  }
+
+  push_word(header);
+  // Inline Name Ref
+  push_word(0xBBBBBBBBBBBBBBBB);
+  trace_bytes_.insert(trace_bytes_.end(),
+                      reinterpret_cast<uint64_t*>(perfetto_bytes.data()),
+                      reinterpret_cast<uint64_t*>(perfetto_bytes.data() +
+                                                  perfetto_bytes.size()));
+  EXPECT_CALL(*process_, UpdateThread(16, 15)).WillRepeatedly(Return(1u));
+
+  tables::ThreadTable::Row row(16);
+  row.upid = 1u;
+  storage_->mutable_thread_table()->Insert(row);
+
+  MockBoundInserter inserter;
+
+  StringId unknown_cat = storage_->InternString("unknown(1)");
+  ASSERT_NE(storage_, nullptr);
+
+  constexpr TrackId track{0u};
+  constexpr TrackId thread_time_track{1u};
+
+  InSequence in_sequence;  // Below slices should be sorted by timestamp.
+  // Only the begin thread time can be imported into the counter table.
+  EXPECT_CALL(*event_, PushCounter(1005000, testing::DoubleEq(2003000),
+                                   thread_time_track));
+  EXPECT_CALL(*slice_, StartSlice(1005000, track, _, _))
+      .WillOnce(DoAll(IgnoreResult(InvokeArgument<3>()),
+                      InvokeArgument<2>(&inserter), Return(SliceId(0u))));
+  EXPECT_CALL(*event_, PushCounter(1010000, testing::DoubleEq(2005000),
+                                   thread_time_track));
+  EXPECT_CALL(*slice_, StartSlice(1010000, track, _, _))
+      .WillOnce(DoAll(IgnoreResult(InvokeArgument<3>()),
+                      InvokeArgument<2>(&inserter), Return(SliceId(1u))));
+  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(SliceId(1u))));
+
+  auto status = Tokenize();
+  EXPECT_TRUE(status.ok());
+  context_.sorter->ExtractEventsForced();
+}
+
+}  // namespace
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_record.cc b/src/trace_processor/importers/fuchsia/fuchsia_record.cc
index dfe70a6..2819e20 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_record.cc
+++ b/src/trace_processor/importers/fuchsia/fuchsia_record.cc
@@ -19,7 +19,7 @@
 namespace perfetto {
 namespace trace_processor {
 
-void FuchsiaRecord::InsertString(uint32_t index, StringId string_id) {
+void FuchsiaRecord::InsertString(uint32_t index, StringPool::Id string_id) {
   StringTableEntry entry;
   entry.index = index;
   entry.string_id = string_id;
@@ -27,16 +27,15 @@
   string_entries_.push_back(entry);
 }
 
-StringId FuchsiaRecord::GetString(uint32_t index) {
+StringPool::Id FuchsiaRecord::GetString(uint32_t index) {
   for (const auto& entry : string_entries_) {
     if (entry.index == index)
       return entry.string_id;
   }
-  return StringId();
+  return StringPool::Id();
 }
 
-void FuchsiaRecord::InsertThread(uint32_t index,
-                                 fuchsia_trace_utils::ThreadInfo info) {
+void FuchsiaRecord::InsertThread(uint32_t index, FuchsiaThreadInfo info) {
   ThreadTableEntry entry;
   entry.index = index;
   entry.info = info;
@@ -44,12 +43,12 @@
   thread_entries_.push_back(entry);
 }
 
-fuchsia_trace_utils::ThreadInfo FuchsiaRecord::GetThread(uint32_t index) {
+FuchsiaThreadInfo FuchsiaRecord::GetThread(uint32_t index) {
   for (const auto& entry : thread_entries_) {
     if (entry.index == index)
       return entry.info;
   }
-  return fuchsia_trace_utils::ThreadInfo();
+  return FuchsiaThreadInfo();
 }
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_record.h b/src/trace_processor/importers/fuchsia/fuchsia_record.h
index f90d039..a2b4dd4 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_record.h
+++ b/src/trace_processor/importers/fuchsia/fuchsia_record.h
@@ -18,14 +18,18 @@
 #define SRC_TRACE_PROCESSOR_IMPORTERS_FUCHSIA_FUCHSIA_RECORD_H_
 
 #include "perfetto/trace_processor/trace_blob_view.h"
-#include "src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h"
-#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/containers/string_pool.h"
 
 #include <vector>
 
 namespace perfetto {
 namespace trace_processor {
 
+struct FuchsiaThreadInfo {
+  uint64_t pid;
+  uint64_t tid;
+};
+
 // Data from a trace provider that is necessary for interpreting a binary
 // record. Namely, the record itself and the entries of the string table and the
 // thread table that are referenced by the record. This enables understanding
@@ -37,19 +41,19 @@
 
   struct StringTableEntry {
     uint32_t index;
-    StringId string_id;
+    StringPool::Id string_id;
   };
 
   struct ThreadTableEntry {
     uint32_t index;
-    fuchsia_trace_utils::ThreadInfo info;
+    FuchsiaThreadInfo info;
   };
 
-  void InsertString(uint32_t, StringId);
-  StringId GetString(uint32_t);
+  void InsertString(uint32_t, StringPool::Id);
+  StringPool::Id GetString(uint32_t);
 
-  void InsertThread(uint32_t, fuchsia_trace_utils::ThreadInfo);
-  fuchsia_trace_utils::ThreadInfo GetThread(uint32_t);
+  void InsertThread(uint32_t, FuchsiaThreadInfo);
+  FuchsiaThreadInfo GetThread(uint32_t);
 
   void set_ticks_per_second(uint64_t ticks_per_second) {
     ticks_per_second_ = ticks_per_second;
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc b/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc
index 3723b86..e36da64 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc
+++ b/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.cc
@@ -22,6 +22,8 @@
 #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/fuchsia/fuchsia_trace_utils.h"
+#include "src/trace_processor/importers/proto/proto_trace_parser.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -61,25 +63,23 @@
 }  // namespace
 
 FuchsiaTraceParser::FuchsiaTraceParser(TraceProcessorContext* context)
-    : context_(context) {}
+    : context_(context), proto_parser_(new ProtoTraceParser(context_)) {}
 
 FuchsiaTraceParser::~FuchsiaTraceParser() = default;
 
-void FuchsiaTraceParser::ParseFtracePacket(uint32_t,
-                                           int64_t,
-                                           TimestampedTracePiece) {
-  PERFETTO_FATAL("Fuchsia Trace Parser cannot handle ftrace packets.");
+void FuchsiaTraceParser::ParseTrackEvent(int64_t ts, TrackEventData data) {
+  proto_parser_->ParseTrackEvent(ts, std::move(data));
 }
 
-void FuchsiaTraceParser::ParseTracePacket(int64_t, TimestampedTracePiece ttp) {
-  PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kFuchsiaRecord);
+void FuchsiaTraceParser::ParseTracePacket(int64_t ts, TracePacketData data) {
+  proto_parser_->ParseTracePacket(ts, std::move(data));
+}
 
+void FuchsiaTraceParser::ParseFuchsiaRecord(int64_t, FuchsiaRecord fr) {
   // The timestamp is also present in the record, so we'll ignore the one passed
   // as an argument.
-  fuchsia_trace_utils::RecordCursor cursor(
-      ttp.fuchsia_record.record_view()->data(),
-      ttp.fuchsia_record.record_view()->length());
-  FuchsiaRecord& record = ttp.fuchsia_record;
+  fuchsia_trace_utils::RecordCursor cursor(fr.record_view()->data(),
+                                           fr.record_view()->length());
   ProcessTracker* procs = context_->process_tracker.get();
   SliceTracker* slices = context_->slice_tracker.get();
 
@@ -103,18 +103,18 @@
           fuchsia_trace_utils::ReadField<uint32_t>(header, 48, 63);
 
       int64_t ts;
-      if (!cursor.ReadTimestamp(record.get_ticks_per_second(), &ts)) {
+      if (!cursor.ReadTimestamp(fr.get_ticks_per_second(), &ts)) {
         context_->storage->IncrementStats(stats::fuchsia_invalid_event);
         return;
       }
-      fuchsia_trace_utils::ThreadInfo tinfo;
+      FuchsiaThreadInfo tinfo;
       if (fuchsia_trace_utils::IsInlineThread(thread_ref)) {
         if (!cursor.ReadInlineThread(&tinfo)) {
           context_->storage->IncrementStats(stats::fuchsia_invalid_event);
           return;
         }
       } else {
-        tinfo = record.GetThread(thread_ref);
+        tinfo = fr.GetThread(thread_ref);
       }
       StringId cat;
       if (fuchsia_trace_utils::IsInlineString(cat_ref)) {
@@ -125,7 +125,7 @@
         }
         cat = context_->storage->InternString(cat_string_view);
       } else {
-        cat = record.GetString(cat_ref);
+        cat = fr.GetString(cat_ref);
       }
       StringId name;
       if (fuchsia_trace_utils::IsInlineString(name_ref)) {
@@ -136,7 +136,7 @@
         }
         name = context_->storage->InternString(name_string_view);
       } else {
-        name = record.GetString(name_ref);
+        name = fr.GetString(name_ref);
       }
 
       // Read arguments
@@ -163,7 +163,7 @@
           }
           arg.name = context_->storage->InternString(arg_name_view);
         } else {
-          arg.name = record.GetString(arg_name_ref);
+          arg.name = fr.GetString(arg_name_ref);
         }
 
         switch (arg_type) {
@@ -217,7 +217,7 @@
               }
               value = context_->storage->InternString(arg_value_view);
             } else {
-              value = record.GetString(arg_value_ref);
+              value = fr.GetString(arg_value_ref);
             }
             arg.value = fuchsia_trace_utils::ArgValue::String(value);
             break;
@@ -342,7 +342,7 @@
         }
         case kDurationComplete: {
           int64_t end_ts;
-          if (!cursor.ReadTimestamp(record.get_ticks_per_second(), &end_ts)) {
+          if (!cursor.ReadTimestamp(fr.get_ticks_per_second(), &end_ts)) {
             context_->storage->IncrementStats(stats::fuchsia_invalid_event);
             return;
           }
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.h b/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.h
index a0e39c2..01cf3e3 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.h
+++ b/src/trace_processor/importers/fuchsia/fuchsia_trace_parser.h
@@ -19,7 +19,7 @@
 
 #include "src/trace_processor/importers/common/trace_parser.h"
 #include "src/trace_processor/importers/fuchsia/fuchsia_record.h"
-#include "src/trace_processor/timestamped_trace_piece.h"
+#include "src/trace_processor/importers/proto/proto_trace_parser.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -32,11 +32,13 @@
   ~FuchsiaTraceParser() override;
 
   // TraceParser implementation
-  void ParseTracePacket(int64_t timestamp, TimestampedTracePiece) override;
-  void ParseFtracePacket(uint32_t, int64_t, TimestampedTracePiece) override;
+  void ParseFuchsiaRecord(int64_t timestamp, FuchsiaRecord fr) override;
+  void ParseTrackEvent(int64_t, TrackEventData) override;
+  void ParseTracePacket(int64_t ts, TracePacketData data) override;
 
  private:
   TraceProcessorContext* const context_;
+  std::unique_ptr<ProtoTraceParser> proto_parser_;
 };
 
 }  // 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 44b76cb..8b7f14f 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.cc
+++ b/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.cc
@@ -24,7 +24,9 @@
 #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/trace_sorter.h"
+#include "src/trace_processor/importers/proto/proto_trace_parser.h"
+#include "src/trace_processor/importers/proto/proto_trace_reader.h"
+#include "src/trace_processor/sorter/trace_sorter.h"
 #include "src/trace_processor/types/task_state.h"
 #include "src/trace_processor/types/trace_processor_context.h"
 
@@ -38,6 +40,7 @@
 constexpr uint32_t kString = 2;
 constexpr uint32_t kThread = 3;
 constexpr uint32_t kEvent = 4;
+constexpr uint32_t kBlob = 5;
 constexpr uint32_t kKernelObject = 7;
 constexpr uint32_t kContextSwitch = 8;
 
@@ -64,7 +67,15 @@
 }  // namespace
 
 FuchsiaTraceTokenizer::FuchsiaTraceTokenizer(TraceProcessorContext* context)
-    : context_(context) {
+    : context_(context),
+      proto_reader_(context),
+      running_string_id_(context->storage->InternString("Running")),
+      runnable_string_id_(context->storage->InternString("R")),
+      preempted_string_id_(context->storage->InternString("R+")),
+      blocked_string_id_(context->storage->InternString("S")),
+      suspended_string_id_(context->storage->InternString("T")),
+      exit_dying_string_id_(context->storage->InternString("Z")),
+      exit_dead_string_id_(context->storage->InternString("X")) {
   RegisterProvider(0, "");
 }
 
@@ -173,7 +184,30 @@
   leftover_bytes_.insert(leftover_bytes_.end(),
                          full_view.data() + record_offset,
                          full_view.data() + size);
-  return util::OkStatus();
+
+  TraceBlob perfetto_blob =
+      TraceBlob::CopyFrom(proto_trace_data_.data(), proto_trace_data_.size());
+  proto_trace_data_.clear();
+
+  return proto_reader_.Parse(TraceBlobView(std::move(perfetto_blob)));
+}
+
+StringId FuchsiaTraceTokenizer::IdForOutgoingThreadState(uint32_t state) {
+  switch (state) {
+    case kThreadNew:
+    case kThreadRunning:
+      return runnable_string_id_;
+    case kThreadBlocked:
+      return blocked_string_id_;
+    case kThreadSuspended:
+      return suspended_string_id_;
+    case kThreadDying:
+      return exit_dying_string_id_;
+    case kThreadDead:
+      return exit_dead_string_id_;
+    default:
+      return kNullStringId;
+  }
 }
 
 // Most record types are read and recorded in |TraceStorage| here directly.
@@ -259,7 +293,7 @@
     case kThread: {
       uint32_t index = fuchsia_trace_utils::ReadField<uint32_t>(header, 16, 23);
       if (index != 0) {
-        fuchsia_trace_utils::ThreadInfo tinfo;
+        FuchsiaThreadInfo tinfo;
         if (!cursor.ReadInlineThread(&tinfo)) {
           context_->storage->IncrementStats(stats::fuchsia_invalid_event);
           return;
@@ -360,6 +394,36 @@
       sorter->PushFuchsiaRecord(ts, std::move(record));
       break;
     }
+    case kBlob: {
+      constexpr uint32_t kPerfettoBlob = 3;
+      uint32_t blob_type =
+          fuchsia_trace_utils::ReadField<uint32_t>(header, 48, 55);
+      if (blob_type == kPerfettoBlob) {
+        FuchsiaRecord record(std::move(tbv));
+        uint32_t blob_size =
+            fuchsia_trace_utils::ReadField<uint32_t>(header, 32, 46);
+        uint32_t name_ref =
+            fuchsia_trace_utils::ReadField<uint32_t>(header, 16, 31);
+
+        // We don't need the name, but we still need to parse it in case it is
+        // inline
+        if (fuchsia_trace_utils::IsInlineString(name_ref)) {
+          base::StringView name_view;
+          if (!cursor.ReadInlineString(name_ref, &name_view)) {
+            storage->IncrementStats(stats::fuchsia_invalid_event);
+            return;
+          }
+        }
+
+        // Append the Blob into the embedded perfetto bytes -- we'll parse them
+        // all after the main pass is done.
+        if (!cursor.ReadBlob(blob_size, proto_trace_data_)) {
+          storage->IncrementStats(stats::fuchsia_invalid_event);
+          return;
+        }
+      }
+      break;
+    }
     case kKernelObject: {
       uint32_t obj_type =
           fuchsia_trace_utils::ReadField<uint32_t>(header, 16, 23);
@@ -437,7 +501,8 @@
             cursor.SetWordIndex(arg_base + arg_size);
           }
 
-          pid_table_[obj_id] = pid;
+          Thread& thread = GetThread(obj_id);
+          thread.info.pid = pid;
 
           UniqueTid utid = procs->UpdateThread(static_cast<uint32_t>(obj_id),
                                                static_cast<uint32_t>(pid));
@@ -459,10 +524,12 @@
           fuchsia_trace_utils::ReadField<uint32_t>(header, 24, 27);
       uint32_t outgoing_thread_ref =
           fuchsia_trace_utils::ReadField<uint32_t>(header, 28, 35);
-      uint32_t incoming_thread_ref =
-          fuchsia_trace_utils::ReadField<uint32_t>(header, 36, 43);
       int32_t outgoing_priority =
           fuchsia_trace_utils::ReadField<int32_t>(header, 44, 51);
+      uint32_t incoming_thread_ref =
+          fuchsia_trace_utils::ReadField<uint32_t>(header, 36, 43);
+      int32_t incoming_priority =
+          fuchsia_trace_utils::ReadField<int32_t>(header, 52, 59);
 
       int64_t ts;
       if (!cursor.ReadTimestamp(current_provider_->ticks_per_second, &ts)) {
@@ -474,85 +541,117 @@
         return;
       }
 
-      fuchsia_trace_utils::ThreadInfo outgoing_thread;
+      FuchsiaThreadInfo outgoing_thread_info;
       if (fuchsia_trace_utils::IsInlineThread(outgoing_thread_ref)) {
-        if (!cursor.ReadInlineThread(&outgoing_thread)) {
+        if (!cursor.ReadInlineThread(&outgoing_thread_info)) {
           context_->storage->IncrementStats(stats::fuchsia_invalid_event);
           return;
         }
       } else {
-        outgoing_thread = current_provider_->thread_table[outgoing_thread_ref];
+        outgoing_thread_info =
+            current_provider_->thread_table[outgoing_thread_ref];
       }
+      Thread& outgoing_thread = GetThread(outgoing_thread_info.tid);
 
-      fuchsia_trace_utils::ThreadInfo incoming_thread;
+      FuchsiaThreadInfo incoming_thread_info;
       if (fuchsia_trace_utils::IsInlineThread(incoming_thread_ref)) {
-        if (!cursor.ReadInlineThread(&incoming_thread)) {
+        if (!cursor.ReadInlineThread(&incoming_thread_info)) {
           context_->storage->IncrementStats(stats::fuchsia_invalid_event);
           return;
         }
       } else {
-        incoming_thread = current_provider_->thread_table[incoming_thread_ref];
+        incoming_thread_info =
+            current_provider_->thread_table[incoming_thread_ref];
       }
+      Thread& incoming_thread = GetThread(incoming_thread_info.tid);
 
-      // A thread with priority 0 represents an idle CPU
-      if (cpu_threads_.count(cpu) != 0 && outgoing_priority != 0) {
-        // TODO(bhamrick): Some early events will fail to associate with their
-        // pid because the kernel object info event hasn't been processed yet.
-        if (pid_table_.count(outgoing_thread.tid) > 0) {
-          outgoing_thread.pid = pid_table_[outgoing_thread.tid];
+      // Idle threads are identified by pid == 0 and prio == 0.
+      const bool incoming_is_idle =
+          incoming_thread.info.pid == 0 && incoming_priority == 0;
+      const bool outgoing_is_idle =
+          outgoing_thread.info.pid == 0 && outgoing_priority == 0;
+
+      // Handle switching away from the currently running thread.
+      if (!outgoing_is_idle) {
+        UniqueTid utid = procs->UpdateThread(
+            static_cast<uint32_t>(outgoing_thread.info.tid),
+            static_cast<uint32_t>(outgoing_thread.info.pid));
+
+        StringId state = IdForOutgoingThreadState(outgoing_state);
+
+        const auto duration = ts - outgoing_thread.last_ts;
+        outgoing_thread.last_ts = ts;
+
+        // Close the slice record if one is open for this thread.
+        if (outgoing_thread.last_slice_row.has_value()) {
+          auto row_ref = outgoing_thread.last_slice_row->ToRowReference(
+              storage->mutable_sched_slice_table());
+          row_ref.set_dur(duration);
+          row_ref.set_end_state(state);
+          row_ref.set_priority(outgoing_priority);
+          outgoing_thread.last_slice_row.reset();
         }
 
-        UniqueTid utid =
-            procs->UpdateThread(static_cast<uint32_t>(outgoing_thread.tid),
-                                static_cast<uint32_t>(outgoing_thread.pid));
-        RunningThread previous_thread = cpu_threads_[cpu];
-
-        ftrace_utils::TaskState end_state;
-        switch (outgoing_state) {
-          case kThreadNew:
-          case kThreadRunning: {
-            end_state =
-                ftrace_utils::TaskState(ftrace_utils::TaskState::kRunnable);
-            break;
-          }
-          case kThreadBlocked: {
-            end_state = ftrace_utils::TaskState(
-                ftrace_utils::TaskState::kInterruptibleSleep);
-            break;
-          }
-          case kThreadSuspended: {
-            end_state =
-                ftrace_utils::TaskState(ftrace_utils::TaskState::kStopped);
-            break;
-          }
-          case kThreadDying: {
-            end_state =
-                ftrace_utils::TaskState(ftrace_utils::TaskState::kExitZombie);
-            break;
-          }
-          case kThreadDead: {
-            end_state =
-                ftrace_utils::TaskState(ftrace_utils::TaskState::kExitDead);
-            break;
-          }
-          default: {
-            break;
-          }
+        // Close the state record if one is open for this thread.
+        if (outgoing_thread.last_state_row.has_value()) {
+          auto row_ref = outgoing_thread.last_state_row->ToRowReference(
+              storage->mutable_thread_state_table());
+          row_ref.set_dur(duration);
+          outgoing_thread.last_state_row.reset();
         }
 
-        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});
+        // Open a new state record to track the duration of the outgoing state.
+        tables::ThreadStateTable::Row state_row;
+        state_row.ts = ts;
+        state_row.cpu = cpu;
+        state_row.dur = -1;
+        state_row.state = state;
+        state_row.utid = utid;
+        auto state_row_number =
+            storage->mutable_thread_state_table()->Insert(state_row).row_number;
+        outgoing_thread.last_state_row = state_row_number;
       }
 
-      RunningThread new_running;
-      new_running.info = incoming_thread;
-      new_running.start_ts = ts;
-      cpu_threads_[cpu] = new_running;
+      // Handle switching to the new currently running thread.
+      if (!incoming_is_idle) {
+        UniqueTid utid = procs->UpdateThread(
+            static_cast<uint32_t>(incoming_thread.info.tid),
+            static_cast<uint32_t>(incoming_thread.info.pid));
+
+        const auto duration = ts - incoming_thread.last_ts;
+        incoming_thread.last_ts = ts;
+
+        // Close the state record if one is open for this thread.
+        if (incoming_thread.last_state_row.has_value()) {
+          auto row_ref = incoming_thread.last_state_row->ToRowReference(
+              storage->mutable_thread_state_table());
+          row_ref.set_dur(duration);
+          incoming_thread.last_state_row.reset();
+        }
+
+        // Open a new slice record for this thread.
+        tables::SchedSliceTable::Row slice_row;
+        slice_row.ts = ts;
+        slice_row.cpu = cpu;
+        slice_row.dur = -1;
+        slice_row.utid = utid;
+        slice_row.priority = incoming_priority;
+        auto slice_row_number =
+            storage->mutable_sched_slice_table()->Insert(slice_row).row_number;
+        incoming_thread.last_slice_row = slice_row_number;
+
+        // Open a new state record for this thread.
+        tables::ThreadStateTable::Row state_row;
+        state_row.ts = ts;
+        state_row.cpu = cpu;
+        state_row.dur = -1;
+        state_row.state = running_string_id_;
+        state_row.utid = utid;
+        auto state_row_number =
+            storage->mutable_thread_state_table()->Insert(state_row).row_number;
+        incoming_thread.last_state_row = state_row_number;
+      }
+
       break;
     }
     default: {
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.h b/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.h
index 258a922..35e66f5 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.h
+++ b/src/trace_processor/importers/fuchsia/fuchsia_trace_tokenizer.h
@@ -19,7 +19,9 @@
 
 #include "src/trace_processor/importers/common/chunked_trace_reader.h"
 #include "src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h"
+#include "src/trace_processor/importers/proto/proto_trace_reader.h"
 #include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/task_state.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -42,29 +44,57 @@
     std::string name;
 
     std::unordered_map<uint64_t, StringId> string_table;
-    std::unordered_map<uint64_t, fuchsia_trace_utils::ThreadInfo> thread_table;
+    std::unordered_map<uint64_t, FuchsiaThreadInfo> thread_table;
 
     uint64_t ticks_per_second = 1000000000;
   };
 
-  struct RunningThread {
-    fuchsia_trace_utils::ThreadInfo info;
-    int64_t start_ts;
+  // Tracks the state for updating sched slice and thread state tables.
+  struct Thread {
+    explicit Thread(uint64_t tid) : info{0, tid} {}
+
+    FuchsiaThreadInfo info;
+    int64_t last_ts{0};
+    base::Optional<tables::SchedSliceTable::RowNumber> last_slice_row;
+    base::Optional<tables::ThreadStateTable::RowNumber> last_state_row;
   };
 
+  // Allocates or returns an existing Thread instance for the given tid.
+  Thread& GetThread(uint64_t tid) {
+    auto search = threads_.find(tid);
+    if (search != threads_.end()) {
+      return search->second;
+    }
+    auto result = threads_.emplace(tid, tid);
+    return result.first->second;
+  }
+
   void ParseRecord(TraceBlobView);
   void RegisterProvider(uint32_t, std::string);
+  StringId IdForOutgoingThreadState(uint32_t state);
 
   TraceProcessorContext* const context_;
   std::vector<uint8_t> leftover_bytes_;
 
-  // Map from tid to pid. Used because in some places we do not get pid info.
-  // Fuchsia tids are never reused.
-  std::unordered_map<uint64_t, uint64_t> pid_table_;
+  // Proto reader creates state that the blobs it emits reference, so the
+  // proto_reader needs to live for as long as the tokenizer.
+  ProtoTraceReader proto_reader_;
+  std::vector<uint8_t> proto_trace_data_;
+
   std::unordered_map<uint32_t, std::unique_ptr<ProviderInfo>> providers_;
   ProviderInfo* current_provider_;
 
-  std::unordered_map<uint32_t, RunningThread> cpu_threads_;
+  // Interned string ids for the relevant thread states.
+  StringId running_string_id_;
+  StringId runnable_string_id_;
+  StringId preempted_string_id_;
+  StringId blocked_string_id_;
+  StringId suspended_string_id_;
+  StringId exit_dying_string_id_;
+  StringId exit_dead_string_id_;
+
+  // Map from tid to Thread.
+  std::unordered_map<uint64_t, Thread> threads_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.cc b/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.cc
index 8b02c87..d7879be 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.cc
+++ b/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.cc
@@ -126,7 +126,7 @@
   return true;
 }
 
-bool RecordCursor::ReadInlineThread(ThreadInfo* thread_out) {
+bool RecordCursor::ReadInlineThread(FuchsiaThreadInfo* thread_out) {
   const uint8_t* thread_data;
   if (!ReadWords(2, &thread_data)) {
     return false;
@@ -173,6 +173,18 @@
   return true;
 }
 
+bool RecordCursor::ReadBlob(size_t num_bytes,
+                            std::vector<uint8_t>& append_buffer) {
+  const uint8_t* data = begin_ + sizeof(uint64_t) * word_index_;
+  auto num_words = (num_bytes + 7) / 8;
+  if (data + sizeof(uint64_t) * num_words > end_) {
+    return false;
+  }
+  word_index_ += num_words;
+  append_buffer.insert(append_buffer.end(), data, data + num_bytes);
+  return true;
+}
+
 bool RecordCursor::ReadWords(size_t num_words, const uint8_t** data_out) {
   const uint8_t* data = begin_ + sizeof(uint64_t) * word_index_;
   // This addition is unconditional so that callers with data_out == nullptr do
diff --git a/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h b/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h
index 518be4b..f4ae231 100644
--- a/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h
+++ b/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h
@@ -23,17 +23,13 @@
 
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/trace_processor/trace_blob_view.h"
+#include "src/trace_processor/importers/fuchsia/fuchsia_record.h"
 #include "src/trace_processor/storage/trace_storage.h"
 
 namespace perfetto {
 namespace trace_processor {
 namespace fuchsia_trace_utils {
 
-struct ThreadInfo {
-  uint64_t pid;
-  uint64_t tid;
-};
-
 template <class T>
 T ReadField(uint64_t word, size_t begin, size_t end) {
   return static_cast<T>((word >> begin) &
@@ -204,11 +200,12 @@
   bool ReadTimestamp(uint64_t ticks_per_second, int64_t* ts_out);
   bool ReadInlineString(uint32_t string_ref_or_len,
                         base::StringView* string_out);
-  bool ReadInlineThread(ThreadInfo* thread_out);
+  bool ReadInlineThread(FuchsiaThreadInfo* thread_out);
 
   bool ReadInt64(int64_t* out);
   bool ReadUint64(uint64_t* out);
   bool ReadDouble(double* out);
+  bool ReadBlob(size_t num_bytes, std::vector<uint8_t>& out);
 
  private:
   bool ReadWords(size_t num_words, const uint8_t** data_out);
diff --git a/src/trace_processor/importers/gzip/BUILD.gn b/src/trace_processor/importers/gzip/BUILD.gn
new file mode 100644
index 0000000..d2fad21
--- /dev/null
+++ b/src/trace_processor/importers/gzip/BUILD.gn
@@ -0,0 +1,28 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+source_set("full") {
+  sources = [
+    "gzip_trace_parser.cc",
+    "gzip_trace_parser.h",
+  ]
+  deps = [
+    "../..:storage_minimal",
+    "../../../../gn:default_deps",
+    "../../../base",
+    "../../util",
+    "../../util:gzip",
+    "../common",
+  ]
+}
diff --git a/src/trace_processor/importers/i2c/BUILD.gn b/src/trace_processor/importers/i2c/BUILD.gn
new file mode 100644
index 0000000..6b47a4b
--- /dev/null
+++ b/src/trace_processor/importers/i2c/BUILD.gn
@@ -0,0 +1,27 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+source_set("full") {
+  sources = [
+    "i2c_tracker.cc",
+    "i2c_tracker.h",
+  ]
+  deps = [
+    "../../../../gn:default_deps",
+    "../../sorter",
+    "../../storage",
+    "../../types",
+    "../common",
+  ]
+}
diff --git a/src/trace_processor/importers/json/BUILD.gn b/src/trace_processor/importers/json/BUILD.gn
new file mode 100644
index 0000000..7488cd2
--- /dev/null
+++ b/src/trace_processor/importers/json/BUILD.gn
@@ -0,0 +1,69 @@
+# Copyright (C) 2022 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("minimal") {
+  sources = [
+    "json_utils.cc",
+    "json_utils.h",
+  ]
+  deps = [
+    "../../../../gn:default_deps",
+    "../common",
+  ]
+  if (enable_perfetto_trace_processor_json) {
+    public_deps = [ "../../../../gn:jsoncpp" ]
+  }
+}
+
+source_set("full") {
+  sources = [
+    "json_trace_parser.cc",
+    "json_trace_parser.h",
+    "json_trace_tokenizer.cc",
+    "json_trace_tokenizer.h",
+  ]
+  deps = [
+    ":minimal",
+    "../../../../gn:default_deps",
+    "../../sorter",
+    "../../storage",
+    "../../types",
+    "../common",
+    "../systrace:full",
+    "../systrace:systrace_line",
+  ]
+  if (enable_perfetto_trace_processor_json) {
+    public_deps = [ "../../../../gn:jsoncpp" ]
+  }
+}
+
+if (enable_perfetto_trace_processor_json) {
+  perfetto_unittest_source_set("unittests") {
+    testonly = true
+    sources = [
+      "json_trace_tokenizer_unittest.cc",
+      "json_utils_unittest.cc",
+    ]
+    deps = [
+      ":full",
+      ":minimal",
+      "../../../../gn:default_deps",
+      "../../../../gn:gtest_and_gmock",
+      "../../types",
+    ]
+  }
+}
diff --git a/src/trace_processor/importers/json/json_trace_parser.cc b/src/trace_processor/importers/json/json_trace_parser.cc
index f9a26eb..bb32312 100644
--- a/src/trace_processor/importers/json/json_trace_parser.cc
+++ b/src/trace_processor/importers/json/json_trace_parser.cc
@@ -24,14 +24,13 @@
 #include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/string_view.h"
-#include "perfetto/ext/base/utils.h"
 #include "src/trace_processor/importers/common/event_tracker.h"
 #include "src/trace_processor/importers/common/flow_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_utils.h"
-#include "src/trace_processor/tables/slice_tables.h"
+#include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/types/trace_processor_context.h"
 
 namespace perfetto {
@@ -62,25 +61,16 @@
 
 JsonTraceParser::~JsonTraceParser() = default;
 
-void JsonTraceParser::ParseFtracePacket(uint32_t,
-                                        int64_t,
-                                        TimestampedTracePiece) {
-  PERFETTO_FATAL("Json Trace Parser cannot handle ftrace packets.");
+void JsonTraceParser::ParseSystraceLine(int64_t, SystraceLine line) {
+  systrace_line_parser_.ParseLine(line);
 }
 
-void JsonTraceParser::ParseTracePacket(int64_t timestamp,
-                                       TimestampedTracePiece ttp) {
+void JsonTraceParser::ParseJsonPacket(int64_t timestamp,
+                                      std::string string_value) {
   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;
-  }
-
-  auto opt_value = json::ParseJsonString(base::StringView(ttp.json_value));
+  auto opt_value = json::ParseJsonString(base::StringView(string_value));
   if (!opt_value) {
     context_->storage->IncrementStats(stats::json_parser_failure);
     return;
@@ -107,20 +97,19 @@
 
   uint32_t pid = opt_pid.value_or(0);
   uint32_t tid = opt_tid.value_or(pid);
+  UniqueTid utid = procs->UpdateThread(tid, pid);
+
+  std::string id = value.isMember("id") ? value["id"].asString() : "";
 
   base::StringView cat = value.isMember("cat")
                              ? base::StringView(value["cat"].asCString())
                              : base::StringView();
+  StringId cat_id = storage->InternString(cat);
+
   base::StringView name = value.isMember("name")
                               ? base::StringView(value["name"].asCString())
                               : base::StringView();
-  base::StringView id = value.isMember("id")
-                            ? base::StringView(value["id"].asCString())
-                            : base::StringView();
-
-  StringId cat_id = storage->InternString(cat);
-  StringId name_id = storage->InternString(name);
-  UniqueTid utid = procs->UpdateThread(tid, pid);
+  StringId name_id = name.empty() ? kNullStringId : storage->InternString(name);
 
   auto args_inserter = [this, &value](ArgsTracker::BoundInserter* inserter) {
     if (value.isMember("args")) {
@@ -133,8 +122,8 @@
   // Only used for 'B', 'E', and 'X' events so wrap in lambda so it gets
   // ignored in other cases. This lambda is only safe to call within the
   // scope of this function due to the capture by reference.
-  auto make_thread_slice_row = [&](TrackId track_id) {
-    tables::ThreadSliceTable::Row row;
+  auto make_slice_row = [&](TrackId track_id) {
+    tables::SliceTable::Row row;
     row.ts = timestamp;
     row.track_id = track_id;
     row.category = cat_id;
@@ -151,8 +140,8 @@
   switch (phase) {
     case 'B': {  // TRACE_EVENT_BEGIN.
       TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
-      slice_tracker->BeginTyped(storage->mutable_thread_slice_table(),
-                                make_thread_slice_row(track_id), args_inserter);
+      slice_tracker->BeginTyped(storage->mutable_slice_table(),
+                                make_slice_row(track_id), args_inserter);
       MaybeAddFlow(track_id, value);
       break;
     }
@@ -163,13 +152,12 @@
       // Now try to update thread_dur if we have a tts field.
       auto opt_tts = json::CoerceToTs(value["tts"]);
       if (opt_slice_id.has_value() && opt_tts) {
-        auto* thread_slice = storage->mutable_thread_slice_table();
-        auto maybe_row = thread_slice->id().IndexOf(*opt_slice_id);
+        auto* slice = storage->mutable_slice_table();
+        auto maybe_row = slice->id().IndexOf(*opt_slice_id);
         PERFETTO_DCHECK(maybe_row.has_value());
-        auto start_tts = thread_slice->thread_ts()[*maybe_row];
+        auto start_tts = slice->thread_ts()[*maybe_row];
         if (start_tts) {
-          thread_slice->mutable_thread_dur()->Set(*maybe_row,
-                                                  *opt_tts - *start_tts);
+          slice->mutable_thread_dur()->Set(*maybe_row, *opt_tts - *start_tts);
         }
       }
       break;
@@ -182,17 +170,14 @@
         break;
       }
       UniquePid upid = context_->process_tracker->GetOrCreateProcess(pid);
-      // TODO(hjd): base::Hash::Combine should work on StringViews
-      int64_t cookie =
-          static_cast<int64_t>(base::Hash::Combine(id.ToStdString().c_str()));
+      int64_t cookie = static_cast<int64_t>(base::Hasher::Combine(id.c_str()));
       StringId scope = kNullStringId;
       TrackId track_id = context_->track_tracker->InternLegacyChromeAsyncTrack(
           name_id, upid, cookie, true /* source_id_is_process_scoped */, scope);
 
       if (phase == 'b') {
-        slice_tracker->BeginTyped(storage->mutable_thread_slice_table(),
-                                  make_thread_slice_row(track_id),
-                                  args_inserter);
+        slice_tracker->BeginTyped(storage->mutable_slice_table(),
+                                  make_slice_row(track_id), args_inserter);
         MaybeAddFlow(track_id, value);
       } else if (phase == 'e') {
         slice_tracker->End(timestamp, track_id, cat_id, name_id, args_inserter);
@@ -210,10 +195,10 @@
       if (!opt_dur.has_value())
         return;
       TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
-      auto row = make_thread_slice_row(track_id);
+      auto row = make_slice_row(track_id);
       row.dur = opt_dur.value();
-      slice_tracker->ScopedTyped(storage->mutable_thread_slice_table(),
-                                 std::move(row), args_inserter);
+      slice_tracker->ScopedTyped(storage->mutable_slice_table(), std::move(row),
+                                 args_inserter);
       MaybeAddFlow(track_id, value);
       break;
     }
@@ -226,7 +211,7 @@
 
       std::string counter_name_prefix = name.ToStdString();
       if (!id.empty()) {
-        counter_name_prefix += " id: " + id.ToStdString();
+        counter_name_prefix += " id: " + id;
       }
 
       for (auto it = args.begin(); it != args.end(); ++it) {
@@ -279,13 +264,13 @@
           break;
         }
         track_id = context_->track_tracker->InternThreadTrack(utid);
-        auto row = make_thread_slice_row(track_id);
+        auto row = make_slice_row(track_id);
         row.dur = 0;
         if (row.thread_ts) {
           // Only set thread_dur to zero if we have a thread_ts.
           row.thread_dur = 0;
         }
-        slice_tracker->ScopedTyped(storage->mutable_thread_slice_table(),
+        slice_tracker->ScopedTyped(storage->mutable_slice_table(),
                                    std::move(row), args_inserter);
         break;
       } else {
@@ -355,8 +340,8 @@
   }
 #else
   perfetto::base::ignore_result(timestamp);
-  perfetto::base::ignore_result(ttp);
   perfetto::base::ignore_result(context_);
+  perfetto::base::ignore_result(string_value);
   PERFETTO_ELOG("Cannot parse JSON trace due to missing JSON support");
 #endif  // PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
 }
@@ -381,6 +366,9 @@
       context_->storage->IncrementStats(stats::flow_without_direction);
     }
   }
+#else
+  perfetto::base::ignore_result(track_id);
+  perfetto::base::ignore_result(event);
 #endif  // PERFETTO_BUILDFLAG(PERFETTO_TP_JSON)
 }
 
diff --git a/src/trace_processor/importers/json/json_trace_parser.h b/src/trace_processor/importers/json/json_trace_parser.h
index 20d1e85..ff1ad63 100644
--- a/src/trace_processor/importers/json/json_trace_parser.h
+++ b/src/trace_processor/importers/json/json_trace_parser.h
@@ -23,8 +23,8 @@
 #include <tuple>
 
 #include "src/trace_processor/importers/common/trace_parser.h"
+#include "src/trace_processor/importers/systrace/systrace_line.h"
 #include "src/trace_processor/importers/systrace/systrace_line_parser.h"
-#include "src/trace_processor/timestamped_trace_piece.h"
 
 namespace Json {
 class Value;
@@ -43,8 +43,8 @@
   ~JsonTraceParser() override;
 
   // TraceParser implementation.
-  void ParseTracePacket(int64_t timestamp, TimestampedTracePiece) override;
-  void ParseFtracePacket(uint32_t, int64_t, TimestampedTracePiece) override;
+  void ParseJsonPacket(int64_t timestamp, std::string string_value) override;
+  void ParseSystraceLine(int64_t timestamp, SystraceLine line) override;
 
  private:
   TraceProcessorContext* const context_;
diff --git a/src/trace_processor/importers/json/json_trace_tokenizer.cc b/src/trace_processor/importers/json/json_trace_tokenizer.cc
index c9f00e6..e8d1b13 100644
--- a/src/trace_processor/importers/json/json_trace_tokenizer.cc
+++ b/src/trace_processor/importers/json/json_trace_tokenizer.cc
@@ -23,8 +23,8 @@
 
 #include "perfetto/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/importers/json/json_utils.h"
+#include "src/trace_processor/sorter/trace_sorter.h"
 #include "src/trace_processor/storage/stats.h"
-#include "src/trace_processor/trace_sorter.h"
 #include "src/trace_processor/util/status_macros.h"
 
 namespace perfetto {
@@ -32,7 +32,7 @@
 
 namespace {
 
-util::Status AppendUnescapedCharacter(char c,
+base::Status AppendUnescapedCharacter(char c,
                                       bool is_escaping,
                                       std::string* key) {
   if (is_escaping) {
@@ -63,12 +63,12 @@
         key->append("\\u");
         break;
       default:
-        return util::ErrStatus("Illegal character in JSON");
+        return base::ErrStatus("Illegal character in JSON");
     }
   } else if (c != '\\') {
     key->push_back(c);
   }
-  return util::OkStatus();
+  return base::OkStatus();
 }
 
 enum class ReadStringRes {
@@ -80,8 +80,15 @@
                                 const char* end,
                                 std::string* key,
                                 const char** next) {
+  if (start == end) {
+    return ReadStringRes::kNeedsMoreData;
+  }
+  if (*start != '"') {
+    return ReadStringRes::kFatalError;
+  }
+
   bool is_escaping = false;
-  for (const char* s = start; s < end; s++) {
+  for (const char* s = start + 1; s < end; s++) {
     // Control characters are not allowed in JSON strings.
     if (iscntrl(*s))
       return ReadStringRes::kFatalError;
@@ -92,7 +99,7 @@
       return ReadStringRes::kEndOfString;
     }
 
-    util::Status status = AppendUnescapedCharacter(*s, is_escaping, key);
+    base::Status status = AppendUnescapedCharacter(*s, is_escaping, key);
     if (!status.ok())
       return ReadStringRes::kFatalError;
 
@@ -103,6 +110,71 @@
   return ReadStringRes::kNeedsMoreData;
 }
 
+enum class SkipValueRes {
+  kEndOfValue,
+  kNeedsMoreData,
+  kFatalError,
+};
+SkipValueRes SkipOneJsonValue(const char* start,
+                              const char* end,
+                              const char** next) {
+  uint32_t brace_count = 0;
+  uint32_t bracket_count = 0;
+  for (const char* s = start; s < end; s++) {
+    if (*s == '"') {
+      // Because strings can contain {}[] characters, handle them separately
+      // before anything else.
+      std::string ignored;
+      const char* str_next = nullptr;
+      switch (ReadOneJsonString(s, end, &ignored, &str_next)) {
+        case ReadStringRes::kFatalError:
+          return SkipValueRes::kFatalError;
+        case ReadStringRes::kNeedsMoreData:
+          return SkipValueRes::kNeedsMoreData;
+        case ReadStringRes::kEndOfString:
+          // -1 as the loop body will +1 getting to the correct place.
+          s = str_next - 1;
+          break;
+      }
+      continue;
+    }
+    if (brace_count == 0 && bracket_count == 0 && (*s == ',' || *s == '}')) {
+      // Regardless of a comma or brace, this will be skipped by the caller so
+      // just set it to this character.
+      *next = s;
+      return SkipValueRes::kEndOfValue;
+    }
+    if (*s == '[') {
+      ++bracket_count;
+      continue;
+    }
+    if (*s == ']') {
+      if (bracket_count == 0) {
+        return SkipValueRes::kFatalError;
+      }
+      --bracket_count;
+      continue;
+    }
+    if (*s == '{') {
+      ++brace_count;
+      continue;
+    }
+    if (*s == '}') {
+      if (brace_count == 0) {
+        return SkipValueRes::kFatalError;
+      }
+      --brace_count;
+      continue;
+    }
+  }
+  return SkipValueRes::kNeedsMoreData;
+}
+
+base::Status SetOutAndReturn(const char* ptr, const char** out) {
+  *out = ptr;
+  return base::OkStatus();
+}
+
 }  // namespace
 
 ReadDictRes ReadOneJsonDict(const char* start,
@@ -192,12 +264,7 @@
         if (*s == ',')
           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);
+        auto res = ReadOneJsonString(s, end, key, &s);
         if (res == ReadStringRes::kFatalError)
           return ReadKeyRes::kFatalError;
         if (res == ReadStringRes::kNeedsMoreData)
@@ -229,7 +296,7 @@
   return ReadKeyRes::kNeedsMoreData;
 }
 
-util::Status ExtractValueForJsonKey(base::StringView dict,
+base::Status ExtractValueForJsonKey(base::StringView dict,
                                     const std::string& key,
                                     base::Optional<std::string>* value) {
   PERFETTO_DCHECK(dict.size() >= 2);
@@ -256,11 +323,11 @@
         state = kInsideDict;
         continue;
       }
-      return util::ErrStatus("Unexpected character before JSON dict");
+      return base::ErrStatus("Unexpected character before JSON dict");
     }
 
     if (state == kAfterDict)
-      return util::ErrStatus("Unexpected character after JSON dict");
+      return base::ErrStatus("Unexpected character after JSON dict");
 
     PERFETTO_DCHECK(state == kInsideDict);
     PERFETTO_DCHECK(s < end);
@@ -276,17 +343,20 @@
     if (res == ReadKeyRes::kEndOfDictionary)
       break;
 
-    if (res == ReadKeyRes::kFatalError)
-      return util::ErrStatus("Failure parsing JSON: encountered fatal error");
+    if (res == ReadKeyRes::kFatalError) {
+      return base::ErrStatus(
+          "Failure parsing JSON: encountered fatal error while parsing key for "
+          "value");
+    }
 
     if (res == ReadKeyRes::kNeedsMoreData) {
-      return util::ErrStatus("Failure parsing JSON: partial JSON dictionary");
+      return base::ErrStatus("Failure parsing JSON: partial JSON dictionary");
     }
 
     PERFETTO_DCHECK(res == ReadKeyRes::kFoundKey);
 
     if (*s == '[') {
-      return util::ErrStatus(
+      return base::ErrStatus(
           "Failure parsing JSON: unsupported JSON dictionary with array");
     }
 
@@ -297,15 +367,15 @@
       if (dict_res == ReadDictRes::kNeedsMoreData ||
           dict_res == ReadDictRes::kEndOfArray ||
           dict_res == ReadDictRes::kEndOfTrace) {
-        return util::ErrStatus(
+        return base::ErrStatus(
             "Failure parsing JSON: unable to parse dictionary");
       }
       value_str = dict_str.ToStdString();
     } else if (*s == '"') {
-      auto str_res = ReadOneJsonString(s + 1, end, &value_str, &s);
+      auto str_res = ReadOneJsonString(s, end, &value_str, &s);
       if (str_res == ReadStringRes::kNeedsMoreData ||
           str_res == ReadStringRes::kFatalError) {
-        return util::ErrStatus("Failure parsing JSON: unable to parse string");
+        return base::ErrStatus("Failure parsing JSON: unable to parse string");
       }
     } else {
       const char* value_start = s;
@@ -321,15 +391,15 @@
 
     if (key == current_key) {
       *value = value_str;
-      return util::OkStatus();
+      return base::OkStatus();
     }
   }
 
   if (state != kAfterDict)
-    return util::ErrStatus("Failure parsing JSON: malformed dictionary");
+    return base::ErrStatus("Failure parsing JSON: malformed dictionary");
 
   *value = base::nullopt;
-  return util::OkStatus();
+  return base::OkStatus();
 }
 
 ReadSystemLineRes ReadOneSystemTraceLine(const char* start,
@@ -352,7 +422,7 @@
       return ReadSystemLineRes::kFoundLine;
     }
 
-    util::Status status = AppendUnescapedCharacter(*s, is_escaping, line);
+    base::Status status = AppendUnescapedCharacter(*s, is_escaping, line);
     if (!status.ok())
       return ReadSystemLineRes::kFatalError;
 
@@ -367,7 +437,7 @@
     : context_(ctx) {}
 JsonTraceTokenizer::~JsonTraceTokenizer() = default;
 
-util::Status JsonTraceTokenizer::Parse(TraceBlobView blob) {
+base::Status JsonTraceTokenizer::Parse(TraceBlobView blob) {
   PERFETTO_DCHECK(json::IsJsonSupported());
 
   buffer_.insert(buffer_.end(), blob.data(), blob.data() + blob.size());
@@ -381,7 +451,7 @@
       next++;
     }
     if (next == end) {
-      return util::ErrStatus(
+      return base::ErrStatus(
           "Failure parsing JSON: first chunk has only whitespace");
     }
 
@@ -390,7 +460,7 @@
     // { "traceEvents": [{
     // [{
     if (*next != '{' && *next != '[') {
-      return util::ErrStatus(
+      return base::ErrStatus(
           "Failure parsing JSON: first non-whitespace character is not [ or {");
     }
 
@@ -405,175 +475,194 @@
     // Set our current position based on the format of the trace.
     position_ = format_ == TraceFormat::kOuterDictionary
                     ? TracePosition::kDictionaryKey
-                    : TracePosition::kTraceEventsArray;
+                    : TracePosition::kInsideTraceEventsArray;
   }
-
-  auto status = ParseInternal(next, end, &next);
-  if (!status.ok())
-    return status;
+  RETURN_IF_ERROR(ParseInternal(next, end, &next));
 
   offset_ += static_cast<uint64_t>(next - buf);
   buffer_.erase(buffer_.begin(), buffer_.begin() + (next - buf));
-  return util::OkStatus();
+  return base::OkStatus();
 }
 
-util::Status JsonTraceTokenizer::ParseInternal(const char* start,
+base::Status JsonTraceTokenizer::ParseInternal(const char* start,
                                                const char* end,
                                                const char** out) {
   PERFETTO_DCHECK(json::IsJsonSupported());
-  auto* trace_sorter = context_->sorter.get();
 
-  const char* next = start;
   switch (position_) {
-    case TracePosition::kDictionaryKey: {
-      if (format_ != TraceFormat::kOuterDictionary) {
-        return util::ErrStatus(
-            "Failure parsing JSON: illegal format when parsing dictionary key");
-      }
-
-      std::string key;
-      auto res = ReadOneJsonKey(start, end, &key, &next);
-      if (res == ReadKeyRes::kFatalError)
-        return util::ErrStatus("Failure parsing JSON: encountered fatal error");
-
-      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 (key == "displayTimeUnit") {
-        std::string time_unit;
-        auto result = ReadOneJsonString(next + 1, end, &time_unit, &next);
-        if (result == ReadStringRes::kFatalError)
-          return util::ErrStatus("Could not parse displayTimeUnit");
-        context_->storage->IncrementStats(stats::json_display_time_unit);
-        return ParseInternal(next, end, out);
-      } else if (key == "otherData") {
-        base::StringView unparsed;
-        const auto other = ReadOneJsonDict(next, end, &unparsed, &next);
-        if (other == ReadDictRes::kEndOfArray)
-          return util::ErrStatus(
-              "Failure parsing JSON: Missing ] in otherData");
-        if (other == ReadDictRes::kEndOfTrace)
-          return util::ErrStatus(
-              "Failure parsing JSON: Failed parsing otherData");
-        if (other == ReadDictRes::kNeedsMoreData)
-          return util::ErrStatus("Failure parsing JSON: otherData too large");
-        return ParseInternal(next, 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(
-            "Failure parsing JSON: illegal 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(
-              "Failure parsing JSON: encountered fatal error");
-
-        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;
-
-        SystraceLine line;
-        util::Status status =
-            systrace_line_tokenizer_.Tokenize(raw_line, &line);
-        if (!status.ok())
-          return status;
-        trace_sorter->PushSystraceLine(std::move(line));
-      }
-      break;
-    }
-    case TracePosition::kWaitingForMetadataDictionary: {
-      if (format_ != TraceFormat::kOuterDictionary) {
-        return util::ErrStatus(
-            "Failure parsing JSON: illegal format when parsing metadata");
-      }
-
-      base::StringView unparsed;
-      const auto res = ReadOneJsonDict(next, end, &unparsed, &next);
-      if (res == ReadDictRes::kEndOfArray)
-        return util::ErrStatus("Failure parsing JSON: encountered fatal error");
-      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) {
-        base::StringView unparsed;
-        const auto res = ReadOneJsonDict(next, end, &unparsed, &next);
-        if (res == ReadDictRes::kEndOfTrace ||
-            res == ReadDictRes::kNeedsMoreData) {
-          break;
-        }
-
-        if (res == ReadDictRes::kEndOfArray) {
-          position_ = format_ == TraceFormat::kOuterDictionary
-                          ? TracePosition::kDictionaryKey
-                          : TracePosition::kEof;
-          break;
-        }
-
-        base::Optional<std::string> opt_raw_ts;
-        RETURN_IF_ERROR(ExtractValueForJsonKey(unparsed, "ts", &opt_raw_ts));
-        base::Optional<int64_t> opt_ts =
-            opt_raw_ts ? json::CoerceToTs(*opt_raw_ts) : base::nullopt;
-        int64_t ts = 0;
-        if (opt_ts.has_value()) {
-          ts = opt_ts.value();
-        } else {
-          // Metadata events may omit ts. In all other cases error:
-          base::Optional<std::string> opt_raw_ph;
-          RETURN_IF_ERROR(ExtractValueForJsonKey(unparsed, "ph", &opt_raw_ph));
-          if (!opt_raw_ph || *opt_raw_ph != "M") {
-            context_->storage->IncrementStats(stats::json_tokenizer_failure);
-            continue;
-          }
-        }
-        trace_sorter->PushJsonValue(ts, unparsed.ToStdString());
-      }
-      break;
-    }
+    case TracePosition::kDictionaryKey:
+      return HandleDictionaryKey(start, end, out);
+    case TracePosition::kInsideSystemTraceEventsString:
+      return HandleSystemTraceEvent(start, end, out);
+    case TracePosition::kInsideTraceEventsArray:
+      return HandleTraceEvent(start, end, out);
     case TracePosition::kEof: {
-      break;
+      return start == end
+                 ? base::OkStatus()
+                 : base::ErrStatus(
+                       "Failure parsing JSON: tried to parse data after EOF");
     }
   }
-  *out = next;
-  return util::OkStatus();
+  PERFETTO_FATAL("For GCC");
 }
 
-void JsonTraceTokenizer::NotifyEndOfFile() {}
+base::Status JsonTraceTokenizer::HandleTraceEvent(const char* start,
+                                                  const char* end,
+                                                  const char** out) {
+  for (const char* next = start; next < end;) {
+    base::StringView unparsed;
+    switch (ReadOneJsonDict(next, end, &unparsed, &next)) {
+      case ReadDictRes::kEndOfArray: {
+        if (format_ == TraceFormat::kOnlyTraceEvents) {
+          position_ = TracePosition::kEof;
+          return SetOutAndReturn(next, out);
+        }
+
+        position_ = TracePosition::kDictionaryKey;
+        return ParseInternal(next, end, out);
+      }
+      case ReadDictRes::kEndOfTrace:
+        position_ = TracePosition::kEof;
+        return SetOutAndReturn(next, out);
+      case ReadDictRes::kNeedsMoreData:
+        return SetOutAndReturn(next, out);
+      case ReadDictRes::kFoundDict:
+        break;
+    }
+
+    base::Optional<std::string> opt_raw_ts;
+    RETURN_IF_ERROR(ExtractValueForJsonKey(unparsed, "ts", &opt_raw_ts));
+    base::Optional<int64_t> opt_ts =
+        opt_raw_ts ? json::CoerceToTs(*opt_raw_ts) : base::nullopt;
+    int64_t ts = 0;
+    if (opt_ts.has_value()) {
+      ts = opt_ts.value();
+    } else {
+      // Metadata events may omit ts. In all other cases error:
+      base::Optional<std::string> opt_raw_ph;
+      RETURN_IF_ERROR(ExtractValueForJsonKey(unparsed, "ph", &opt_raw_ph));
+      if (!opt_raw_ph || *opt_raw_ph != "M") {
+        context_->storage->IncrementStats(stats::json_tokenizer_failure);
+        continue;
+      }
+    }
+    context_->sorter->PushJsonValue(ts, unparsed.ToStdString());
+  }
+  return base::OkStatus();
+}
+
+base::Status JsonTraceTokenizer::HandleDictionaryKey(const char* start,
+                                                     const char* end,
+                                                     const char** out) {
+  if (format_ != TraceFormat::kOuterDictionary) {
+    return base::ErrStatus(
+        "Failure parsing JSON: illegal format when parsing dictionary key");
+  }
+
+  const char* next = start;
+  std::string key;
+  switch (ReadOneJsonKey(start, end, &key, &next)) {
+    case ReadKeyRes::kFatalError:
+      return base::ErrStatus(
+          "Failure parsing JSON: encountered fatal error while parsing key");
+    case ReadKeyRes::kEndOfDictionary:
+      position_ = TracePosition::kEof;
+      return SetOutAndReturn(next, out);
+    case ReadKeyRes::kNeedsMoreData:
+      return SetOutAndReturn(next, out);
+    case ReadKeyRes::kFoundKey:
+      break;
+  }
+
+  // ReadOneJsonKey should ensure that the first character of the value is
+  // available.
+  PERFETTO_CHECK(next < end);
+
+  if (key == "traceEvents") {
+    // Skip the [ character opening the array.
+    if (*next != '[') {
+      return base::ErrStatus(
+          "Failure parsing JSON: traceEvents is not an array.");
+    }
+    next++;
+
+    position_ = TracePosition::kInsideTraceEventsArray;
+    return ParseInternal(next, end, out);
+  }
+
+  if (key == "systemTraceEvents") {
+    // Skip the " character opening the string.
+    if (*next != '"') {
+      return base::ErrStatus(
+          "Failure parsing JSON: systemTraceEvents is not an string.");
+    }
+    next++;
+
+    position_ = TracePosition::kInsideSystemTraceEventsString;
+    return ParseInternal(next, end, out);
+  }
+
+  if (key == "displayTimeUnit") {
+    std::string time_unit;
+    auto result = ReadOneJsonString(next, end, &time_unit, &next);
+    if (result == ReadStringRes::kFatalError)
+      return base::ErrStatus("Could not parse displayTimeUnit");
+    context_->storage->IncrementStats(stats::json_display_time_unit);
+    return ParseInternal(next, end, out);
+  }
+
+  // If we don't know the key for this JSON value just skip it.
+  switch (SkipOneJsonValue(next, end, &next)) {
+    case SkipValueRes::kFatalError:
+      return base::ErrStatus(
+          "Failure parsing JSON: error while parsing value for key %s",
+          key.c_str());
+    case SkipValueRes::kNeedsMoreData:
+      return SetOutAndReturn(next, out);
+    case SkipValueRes::kEndOfValue:
+      return ParseInternal(next, end, out);
+  }
+  PERFETTO_FATAL("For GCC");
+}
+
+base::Status JsonTraceTokenizer::HandleSystemTraceEvent(const char* start,
+                                                        const char* end,
+                                                        const char** out) {
+  if (format_ != TraceFormat::kOuterDictionary) {
+    return base::ErrStatus(
+        "Failure parsing JSON: illegal format when parsing system events");
+  }
+
+  const char* next = start;
+  while (next < end) {
+    std::string raw_line;
+    switch (ReadOneSystemTraceLine(next, end, &raw_line, &next)) {
+      case ReadSystemLineRes::kFatalError:
+        return base::ErrStatus(
+            "Failure parsing JSON: encountered fatal error while parsing "
+            "event inside trace event string");
+      case ReadSystemLineRes::kNeedsMoreData:
+        return SetOutAndReturn(next, out);
+      case ReadSystemLineRes::kEndOfSystemTrace:
+        position_ = TracePosition::kDictionaryKey;
+        return ParseInternal(next, end, out);
+      case ReadSystemLineRes::kFoundLine:
+        break;
+    }
+
+    if (base::StartsWith(raw_line, "#") || raw_line.empty())
+      continue;
+
+    SystraceLine line;
+    RETURN_IF_ERROR(systrace_line_tokenizer_.Tokenize(raw_line, &line));
+    context_->sorter->PushSystraceLine(std::move(line));
+  }
+  return SetOutAndReturn(next, out);
+}
+
+void JsonTraceTokenizer::NotifyEndOfFile() {
+  PERFETTO_DCHECK(position_ == TracePosition::kEof);
+}
 
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/json/json_trace_tokenizer.h b/src/trace_processor/importers/json/json_trace_tokenizer.h
index 8074b61..3713636 100644
--- a/src/trace_processor/importers/json/json_trace_tokenizer.h
+++ b/src/trace_processor/importers/json/json_trace_tokenizer.h
@@ -20,7 +20,6 @@
 #include <stdint.h>
 
 #include "src/trace_processor/importers/common/chunked_trace_reader.h"
-#include "src/trace_processor/importers/json/json_utils.h"
 #include "src/trace_processor/importers/systrace/systrace_line_tokenizer.h"
 #include "src/trace_processor/storage/trace_storage.h"
 
@@ -78,7 +77,7 @@
 // which have arrays as JSON values because current users of this method
 // do not require this.
 // Visible for testing.
-util::Status ExtractValueForJsonKey(base::StringView dict,
+base::Status ExtractValueForJsonKey(base::StringView dict,
                                     const std::string& key,
                                     base::Optional<std::string>* value);
 
@@ -101,7 +100,7 @@
   ~JsonTraceTokenizer() override;
 
   // ChunkedTraceReader implementation.
-  util::Status Parse(TraceBlobView) override;
+  base::Status Parse(TraceBlobView) override;
   void NotifyEndOfFile() override;
 
  private:
@@ -125,22 +124,30 @@
 
     // 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,
+    kInsideSystemTraceEventsString,
 
     // This indicates where are inside the traceEvents array.
-    kTraceEventsArray,
+    kInsideTraceEventsArray,
 
     // This indicates we cannot parse any more data in the trace.
     kEof,
   };
 
-  util::Status ParseInternal(const char* start,
+  base::Status ParseInternal(const char* start,
                              const char* end,
-                             const char** next);
+                             const char** out);
+
+  base::Status HandleTraceEvent(const char* start,
+                                const char* end,
+                                const char** out);
+
+  base::Status HandleDictionaryKey(const char* start,
+                                   const char* end,
+                                   const char** out);
+
+  base::Status HandleSystemTraceEvent(const char* start,
+                                      const char* end,
+                                      const char** out);
 
   TraceProcessorContext* const context_;
 
diff --git a/src/trace_processor/importers/memory_tracker/BUILD.gn b/src/trace_processor/importers/memory_tracker/BUILD.gn
index 4ac6669..0fc30b8 100644
--- a/src/trace_processor/importers/memory_tracker/BUILD.gn
+++ b/src/trace_processor/importers/memory_tracker/BUILD.gn
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 import("../../../../gn/perfetto.gni")
+import("../../../../gn/test.gni")
 
 source_set("graph_processor") {
   deps = [ "../../../../gn:default_deps" ]
@@ -29,3 +30,18 @@
     "raw_process_memory_node.cc",
   ]
 }
+
+perfetto_unittest_source_set("unittests") {
+  testonly = true
+  sources = [
+    "graph_processor_unittest.cc",
+    "graph_unittest.cc",
+    "raw_process_memory_node_unittest.cc",
+  ]
+  deps = [
+    ":graph_processor",
+    "../../../../gn:default_deps",
+    "../../../../gn:gtest_and_gmock",
+    "../../../base",
+  ]
+}
diff --git a/src/trace_processor/importers/ninja/BUILD.gn b/src/trace_processor/importers/ninja/BUILD.gn
new file mode 100644
index 0000000..f64cf41
--- /dev/null
+++ b/src/trace_processor/importers/ninja/BUILD.gn
@@ -0,0 +1,26 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+source_set("ninja") {
+  sources = [
+    "ninja_log_parser.cc",
+    "ninja_log_parser.h",
+  ]
+  deps = [
+    "../../../../gn:default_deps",
+    "../../importers/common",
+    "../../sorter",
+    "../../storage",
+  ]
+}
diff --git a/src/trace_processor/importers/ninja/ninja_log_parser.cc b/src/trace_processor/importers/ninja/ninja_log_parser.cc
index 0b7be37..232590f 100644
--- a/src/trace_processor/importers/ninja/ninja_log_parser.cc
+++ b/src/trace_processor/importers/ninja/ninja_log_parser.cc
@@ -21,8 +21,8 @@
 #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/sorter/trace_sorter.h"
 #include "src/trace_processor/storage/trace_storage.h"
-#include "src/trace_processor/trace_sorter.h"
 
 namespace perfetto {
 namespace trace_processor {
diff --git a/src/trace_processor/importers/proto/BUILD.gn b/src/trace_processor/importers/proto/BUILD.gn
index 732e002..2026e4a 100644
--- a/src/trace_processor/importers/proto/BUILD.gn
+++ b/src/trace_processor/importers/proto/BUILD.gn
@@ -12,56 +12,258 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-source_set("storage_minimal") {
+import("../../../../gn/perfetto_cc_proto_descriptor.gni")
+
+source_set("minimal") {
   sources = [
+    "active_chrome_processes_tracker.cc",
+    "active_chrome_processes_tracker.h",
+    "chrome_string_lookup.cc",
+    "chrome_string_lookup.h",
+    "chrome_system_probes_module.cc",
+    "chrome_system_probes_module.h",
+    "chrome_system_probes_parser.cc",
+    "chrome_system_probes_parser.h",
+    "default_modules.cc",
+    "default_modules.h",
     "heap_profile_tracker.cc",
     "heap_profile_tracker.h",
+    "memory_tracker_snapshot_module.cc",
+    "memory_tracker_snapshot_module.h",
+    "memory_tracker_snapshot_parser.cc",
+    "memory_tracker_snapshot_parser.h",
+    "metadata_minimal_module.cc",
+    "metadata_minimal_module.h",
+    "metadata_tracker.cc",
+    "metadata_tracker.h",
+    "packet_analyzer.cc",
+    "packet_analyzer.h",
+    "packet_sequence_state.h",
+    "packet_sequence_state_generation.cc",
+    "perf_sample_tracker.cc",
+    "perf_sample_tracker.h",
+    "profile_module.cc",
+    "profile_module.h",
+    "profile_packet_utils.cc",
+    "profile_packet_utils.h",
     "profiler_util.cc",
     "profiler_util.h",
+    "proto_incremental_state.h",
+    "proto_trace_parser.cc",
+    "proto_trace_parser.h",
+    "proto_trace_reader.cc",
+    "proto_trace_reader.h",
+    "proto_trace_tokenizer.cc",
+    "proto_trace_tokenizer.h",
     "stack_profile_tracker.cc",
     "stack_profile_tracker.h",
+    "track_event_module.cc",
+    "track_event_module.h",
+    "track_event_parser.cc",
+    "track_event_parser.h",
+    "track_event_tokenizer.cc",
+    "track_event_tokenizer.h",
+    "track_event_tracker.cc",
+    "track_event_tracker.h",
   ]
+  public_deps = [ ":proto_importer_module" ]
   deps = [
+    ":packet_sequence_state_generation_hdr",
     "../../../../gn:default_deps",
+    "../../../../protos/perfetto/common:zero",
+    "../../../../protos/perfetto/config:zero",
+    "../../../../protos/perfetto/trace:zero",
+    "../../../../protos/perfetto/trace:zero",
+    "../../../../protos/perfetto/trace/android:zero",
+    "../../../../protos/perfetto/trace/chrome:zero",
+    "../../../../protos/perfetto/trace/ftrace:zero",
+    "../../../../protos/perfetto/trace/interned_data:zero",
+    "../../../../protos/perfetto/trace/perfetto:zero",
+    "../../../../protos/perfetto/trace/power:zero",
     "../../../../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/perfetto/trace/translation:zero",
     "../../../base",
+    "../../../protozero",
+    "../../containers",
+    "../../sorter",
     "../../storage",
     "../../tables",
     "../../types",
+    "../../util:gzip",
     "../../util:stack_traces_util",
     "../common",
+    "../common:parser_types",
+    "../ftrace:minimal",
+    "../json:minimal",
+    "../memory_tracker:graph_processor",
   ]
 }
 
-source_set("storage_full") {
+source_set("full") {
   sources = [
+    "additional_modules.cc",
+    "additional_modules.h",
+    "android_camera_event_module.cc",
+    "android_camera_event_module.h",
+    "android_probes_module.cc",
+    "android_probes_module.h",
+    "android_probes_parser.cc",
+    "android_probes_parser.h",
+    "android_probes_tracker.cc",
+    "android_probes_tracker.h",
+    "content_analyzer.cc",
+    "content_analyzer.h",
+    "frame_timeline_event_parser.cc",
+    "frame_timeline_event_parser.h",
+    "gpu_event_parser.cc",
+    "gpu_event_parser.h",
+    "graphics_event_module.cc",
+    "graphics_event_module.h",
+    "graphics_frame_event_parser.cc",
+    "graphics_frame_event_parser.h",
+    "heap_graph_module.cc",
+    "heap_graph_module.h",
     "heap_graph_tracker.cc",
     "heap_graph_tracker.h",
+    "metadata_module.cc",
+    "metadata_module.h",
+    "statsd_module.cc",
+    "statsd_module.h",
+    "system_probes_module.cc",
+    "system_probes_module.h",
+    "system_probes_parser.cc",
+    "system_probes_parser.h",
+    "translation_table_module.cc",
+    "translation_table_module.h",
+    "vulkan_memory_tracker.cc",
+    "vulkan_memory_tracker.h",
   ]
   deps = [
-    ":storage_minimal",
+    ":gen_cc_config_descriptor",
+    ":gen_cc_statsd_atoms_descriptor",
+    ":gen_cc_trace_descriptor",
+    ":minimal",
     "../../../../gn:default_deps",
+    "../../../../include/perfetto/ext/traced:sys_stats_counters",
+    "../../../../protos/perfetto/common:zero",
+    "../../../../protos/perfetto/config:zero",
+    "../../../../protos/perfetto/config:zero",
     "../../../../protos/perfetto/trace:zero",
+    "../../../../protos/perfetto/trace:zero",
+    "../../../../protos/perfetto/trace/android:zero",
+    "../../../../protos/perfetto/trace/gpu:zero",
+    "../../../../protos/perfetto/trace/interned_data:zero",
+    "../../../../protos/perfetto/trace/power:zero",
     "../../../../protos/perfetto/trace/profiling:zero",
+    "../../../../protos/perfetto/trace/profiling:zero",
+    "../../../../protos/perfetto/trace/ps:zero",
+    "../../../../protos/perfetto/trace/statsd:zero",
+    "../../../../protos/perfetto/trace/sys_stats:zero",
+    "../../../../protos/perfetto/trace/system_info:zero",
+    "../../../../protos/perfetto/trace/translation:zero",
     "../../../base",
+    "../../../protozero",
+    "../../sorter",
     "../../storage",
     "../../tables",
     "../../types",
+    "../../util:descriptors",
+    "../../util:proto_profiler",
+    "../../util:proto_to_args_parser",
+    "../common",
+    "../common:parser_types",
+    "../ftrace:full",
+    "../syscalls:full",
   ]
 }
 
+source_set("proto_importer_module") {
+  sources = [
+    "proto_importer_module.cc",
+    "proto_importer_module.h",
+  ]
+  deps = [
+    "../../../../gn:default_deps",
+    "../../../base",
+    "../../types",
+    "../common:trace_parser_hdr",
+  ]
+}
+
+source_set("packet_sequence_state_generation_hdr") {
+  sources = [ "packet_sequence_state_generation.h" ]
+  deps = [
+    "../../../../gn:default_deps",
+    "../../../../include/perfetto/ext/base",
+    "../../../../protos/perfetto/trace:non_minimal_zero",
+    "../../../../protos/perfetto/trace/track_event:zero",
+    "../../util:interned_message_view",
+  ]
+}
+
+perfetto_cc_proto_descriptor("gen_cc_statsd_atoms_descriptor") {
+  descriptor_name = "atoms.descriptor"
+  descriptor_path = "atoms.descriptor"
+}
+
+perfetto_cc_proto_descriptor("gen_cc_trace_descriptor") {
+  descriptor_name = "trace.descriptor"
+  descriptor_target = "../../../../protos/perfetto/trace:descriptor"
+}
+
+perfetto_cc_proto_descriptor("gen_cc_track_event_descriptor") {
+  descriptor_name = "track_event.descriptor"
+  descriptor_target = "../../../../protos/perfetto/trace/track_event:descriptor"
+}
+
+perfetto_cc_proto_descriptor("gen_cc_chrome_track_event_descriptor") {
+  descriptor_name = "chrome_track_event.descriptor"
+  descriptor_target = "../../../../protos/third_party/chromium:descriptor"
+}
+
+perfetto_cc_proto_descriptor("gen_cc_config_descriptor") {
+  descriptor_name = "config.descriptor"
+  descriptor_target = "../../../../protos/perfetto/config:descriptor"
+}
+
 source_set("unittests") {
   testonly = true
   sources = [
+    "active_chrome_processes_tracker_unittest.cc",
     "heap_graph_tracker_unittest.cc",
     "heap_profile_tracker_unittest.cc",
+    "perf_sample_tracker_unittest.cc",
+    "proto_trace_parser_unittest.cc",
   ]
   deps = [
-    ":storage_full",
-    ":storage_minimal",
+    ":full",
+    ":minimal",
     "../../../../gn:default_deps",
     "../../../../gn:gtest_and_gmock",
+    "../../../../protos/perfetto/common:cpp",
+    "../../../../protos/perfetto/common:zero",
+    "../../../../protos/perfetto/config:zero",
+    "../../../../protos/perfetto/trace:non_minimal_cpp",
+    "../../../../protos/perfetto/trace:zero",
+    "../../../../protos/perfetto/trace/android:zero",
+    "../../../../protos/perfetto/trace/chrome:zero",
+    "../../../../protos/perfetto/trace/ftrace:zero",
+    "../../../../protos/perfetto/trace/interned_data:zero",
+    "../../../../protos/perfetto/trace/profiling:cpp",
+    "../../../../protos/perfetto/trace/profiling:zero",
+    "../../../../protos/perfetto/trace/ps:zero",
+    "../../../../protos/perfetto/trace/sys_stats:zero",
+    "../../../../protos/perfetto/trace/track_event:zero",
+    "../../../protozero",
+    "../../sorter",
+    "../../storage",
     "../../types",
+    "../../util:descriptors",
     "../common",
+    "../ftrace:full",
   ]
 }
diff --git a/src/trace_processor/importers/proto/active_chrome_processes_tracker.cc b/src/trace_processor/importers/proto/active_chrome_processes_tracker.cc
new file mode 100644
index 0000000..1c20836
--- /dev/null
+++ b/src/trace_processor/importers/proto/active_chrome_processes_tracker.cc
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2022 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/active_chrome_processes_tracker.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+void ActiveChromeProcessesTracker::AddActiveProcessMetadata(int64_t timestamp,
+                                                            UniquePid upid) {
+  process_data_[upid].metadata_timestamps.insert(timestamp);
+  global_metadata_timestamps_.insert(timestamp);
+}
+
+std::vector<ProcessWithDataLoss>
+ActiveChromeProcessesTracker::GetProcessesWithDataLoss() const {
+  std::vector<ProcessWithDataLoss> processes_with_data_loss;
+  for (auto it = process_data_.GetIterator(); it; ++it) {
+    UniquePid upid = it.key();
+    const auto& process_data = it.value();
+    base::Optional<int64_t> last_loss_moment;
+    base::Optional<int64_t> next_no_loss_moment;
+    for (int64_t metadata_ts : process_data.metadata_timestamps) {
+      // Looks for a matching process descriptor in the [t - 0.2s, t + 0.2s]
+      // window. The window size is somewhat arbitrary, and can be changed in
+      // the future. It should be smaller than the incremental state reset
+      // interval, which is 5s for Chromium traces.
+      constexpr int64_t kMaxTimestampDiff = 200 * 1000 * 1000;
+      auto descriptor_it = process_data.descriptor_timestamps.lower_bound(
+          metadata_ts - kMaxTimestampDiff);
+      if (descriptor_it != process_data.descriptor_timestamps.end()) {
+        if (*descriptor_it > metadata_ts + kMaxTimestampDiff) {
+          // There's no matching descriptor, but there's a descriptor at some
+          // point in the future.
+          last_loss_moment = metadata_ts;
+          next_no_loss_moment = *descriptor_it;
+        }
+      } else {
+        // There's no matching descriptor, and there're no descriptors in the
+        // future.
+        last_loss_moment = metadata_ts;
+        auto global_metadata_it =
+            global_metadata_timestamps_.upper_bound(metadata_ts);
+        if (global_metadata_it != global_metadata_timestamps_.end()) {
+          // The process terminated before the next incremental state reset.
+          // So it has no data loss from the next reset until the end of the
+          // trace.
+          next_no_loss_moment = *global_metadata_it;
+        } else {
+          next_no_loss_moment = base::nullopt;
+        }
+      }
+    }
+    if (last_loss_moment) {
+      processes_with_data_loss.push_back({upid, next_no_loss_moment});
+    }
+  }
+  return processes_with_data_loss;
+}
+
+void ActiveChromeProcessesTracker::NotifyEndOfFile() {
+  const auto processes = GetProcessesWithDataLoss();
+  for (const auto& p : processes) {
+    tables::ExpMissingChromeProcTable::Row row;
+    row.upid = p.upid;
+    row.reliable_from = p.reliable_from;
+    context_->storage->mutable_experimental_missing_chrome_processes_table()
+        ->Insert(row);
+  }
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/active_chrome_processes_tracker.h b/src/trace_processor/importers/proto/active_chrome_processes_tracker.h
new file mode 100644
index 0000000..3dd1499
--- /dev/null
+++ b/src/trace_processor/importers/proto/active_chrome_processes_tracker.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 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_ACTIVE_CHROME_PROCESSES_TRACKER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_ACTIVE_CHROME_PROCESSES_TRACKER_H_
+
+#include <set>
+#include <vector>
+#include "perfetto/ext/base/optional.h"
+
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+struct ProcessWithDataLoss {
+  UniquePid upid;
+  // If not nullopt, the process data is reliable from this point until
+  // the end of the trace.
+  base::Optional<int64_t> reliable_from;
+};
+
+// Tracks ActiveProcesses metadata packets from ChromeTrackEvent,
+// and process descriptors.
+// Computes a list of processes with missing data based on this information.
+class ActiveChromeProcessesTracker {
+ public:
+  explicit ActiveChromeProcessesTracker(TraceProcessorContext* context)
+      : context_(context) {}
+
+  void AddActiveProcessMetadata(int64_t timestamp, UniquePid upid);
+  void AddProcessDescriptor(int64_t timestamp, UniquePid upid) {
+    process_data_[upid].descriptor_timestamps.insert(timestamp);
+  }
+  std::vector<ProcessWithDataLoss> GetProcessesWithDataLoss() const;
+  void NotifyEndOfFile();
+
+ private:
+  struct ProcessData {
+    std::set<int64_t> metadata_timestamps;
+    std::set<int64_t> descriptor_timestamps;
+  };
+
+  TraceProcessorContext* context_;
+  base::FlatHashMap<UniquePid, ProcessData> process_data_;
+  // Metadata timestamps across all processes.
+  std::set<int64_t> global_metadata_timestamps_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_ACTIVE_CHROME_PROCESSES_TRACKER_H_
diff --git a/src/trace_processor/importers/proto/active_chrome_processes_tracker_unittest.cc b/src/trace_processor/importers/proto/active_chrome_processes_tracker_unittest.cc
new file mode 100644
index 0000000..8917eb5
--- /dev/null
+++ b/src/trace_processor/importers/proto/active_chrome_processes_tracker_unittest.cc
@@ -0,0 +1,112 @@
+/*
+ * 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/active_chrome_processes_tracker.h"
+
+#include "perfetto/base/logging.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+static bool operator==(const ProcessWithDataLoss& lhs,
+                       const ProcessWithDataLoss& rhs) {
+  return lhs.upid == rhs.upid && lhs.reliable_from == rhs.reliable_from;
+}
+
+namespace {
+
+using ::testing::IsEmpty;
+using ::testing::UnorderedElementsAre;
+
+constexpr int64_t kNanosecondsInSecond = 1000 * 1000 * 1000;
+
+TEST(ActiveChromeProcessesTrackerTest, NoMetadataAndNoDescriptors) {
+  // No metadata and no descriptor = no processes are missing.
+  ActiveChromeProcessesTracker tracker(nullptr);
+  EXPECT_THAT(tracker.GetProcessesWithDataLoss(), IsEmpty());
+}
+
+TEST(ActiveChromeProcessesTrackerTest, NoDescriptors) {
+  ActiveChromeProcessesTracker tracker(nullptr);
+  tracker.AddActiveProcessMetadata(/*timestamp=*/10, /*upid=*/1);
+  tracker.AddActiveProcessMetadata(/*timestamp=*/10, /*upid=*/2);
+  EXPECT_THAT(tracker.GetProcessesWithDataLoss(),
+              UnorderedElementsAre(ProcessWithDataLoss{1, base::nullopt},
+                                   ProcessWithDataLoss{2, base::nullopt}));
+}
+
+TEST(ActiveChromeProcessesTrackerTest, InexactMatch) {
+  ActiveChromeProcessesTracker tracker(nullptr);
+  tracker.AddActiveProcessMetadata(/*timestamp=*/10 * kNanosecondsInSecond,
+                                   /*upid=*/1);
+  tracker.AddActiveProcessMetadata(/*timestamp=*/15 * kNanosecondsInSecond,
+                                   /*upid=*/1);
+  tracker.AddProcessDescriptor(
+      /*timestamp=*/10 * kNanosecondsInSecond - 200 * 1000 * 1000, /*upid=*/1);
+  tracker.AddProcessDescriptor(
+      /*timestamp=*/15 * kNanosecondsInSecond + 200 * 1000 * 1000, /*upid=*/1);
+  EXPECT_THAT(tracker.GetProcessesWithDataLoss(), IsEmpty());
+}
+
+TEST(ActiveChromeProcessesTrackerTest, InexactMatchTooBigDiff) {
+  ActiveChromeProcessesTracker tracker(nullptr);
+  tracker.AddActiveProcessMetadata(/*timestamp=*/10 * kNanosecondsInSecond,
+                                   /*upid=*/1);
+  tracker.AddActiveProcessMetadata(/*timestamp=*/15 * kNanosecondsInSecond,
+                                   /*upid=*/1);
+  tracker.AddProcessDescriptor(
+      /*timestamp=*/10 * kNanosecondsInSecond - 200 * 1000 * 1000 - 1,
+      /*upid=*/1);
+  tracker.AddProcessDescriptor(
+      /*timestamp=*/15 * kNanosecondsInSecond + 200 * 1000 * 1000 + 1,
+      /*upid=*/1);
+  EXPECT_THAT(tracker.GetProcessesWithDataLoss(),
+              UnorderedElementsAre(ProcessWithDataLoss{
+                  1, 15 * kNanosecondsInSecond + 200 * 1000 * 1000 + 1}));
+}
+
+TEST(ActiveChromeProcessesTrackerTest, ExtraDescriptor) {
+  // There're more descriptors than metadata packets - this is OK.
+  ActiveChromeProcessesTracker tracker(nullptr);
+  tracker.AddActiveProcessMetadata(/*timestamp=*/15 * kNanosecondsInSecond,
+                                   /*upid=*/1);
+  tracker.AddProcessDescriptor(/*timestamp=*/10 * kNanosecondsInSecond,
+                               /*upid=*/1);
+  tracker.AddProcessDescriptor(/*timestamp=*/15 * kNanosecondsInSecond,
+                               /*upid=*/1);
+  EXPECT_THAT(tracker.GetProcessesWithDataLoss(), IsEmpty());
+}
+
+TEST(ActiveChromeProcessesTracker, TemrinatedProcess) {
+  ActiveChromeProcessesTracker tracker(nullptr);
+  // First metadata packet - two processes.
+  tracker.AddActiveProcessMetadata(/*timestamp=*/10, /*upid=*/1);
+  tracker.AddActiveProcessMetadata(/*timestamp=*/10, /*upid=*/2);
+  // Second metadata packet - only one process, the first process terminated.
+  tracker.AddActiveProcessMetadata(/*timestamp=*/15, /*upid=*/2);
+
+  // The first process is reliable since the second snapshot - it terminated,
+  // so it has no data loss.
+  // The second process has data loss till the end of the trace.
+  EXPECT_THAT(tracker.GetProcessesWithDataLoss(),
+              UnorderedElementsAre(ProcessWithDataLoss{1, 15},
+                                   ProcessWithDataLoss{2, base::nullopt}));
+}
+
+}  // namespace
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/additional_modules.cc b/src/trace_processor/importers/proto/additional_modules.cc
new file mode 100644
index 0000000..c9799b0
--- /dev/null
+++ b/src/trace_processor/importers/proto/additional_modules.cc
@@ -0,0 +1,49 @@
+/*
+ * 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/importers/proto/additional_modules.h"
+#include "src/trace_processor/importers/ftrace/ftrace_module_impl.h"
+#include "src/trace_processor/importers/proto/android_camera_event_module.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/metadata_module.h"
+#include "src/trace_processor/importers/proto/statsd_module.h"
+#include "src/trace_processor/importers/proto/system_probes_module.h"
+#include "src/trace_processor/importers/proto/translation_table_module.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+void RegisterAdditionalModules(TraceProcessorContext* context) {
+  context->modules.emplace_back(new AndroidProbesModule(context));
+  context->modules.emplace_back(new GraphicsEventModule(context));
+  context->modules.emplace_back(new HeapGraphModule(context));
+  context->modules.emplace_back(new SystemProbesModule(context));
+  context->modules.emplace_back(new TranslationTableModule(context));
+  context->modules.emplace_back(new StatsdModule(context));
+  context->modules.emplace_back(new AndroidCameraEventModule(context));
+  context->modules.emplace_back(new MetadataModule(context));
+
+  // 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->modules.emplace_back(new FtraceModuleImpl(context));
+  context->ftrace_module =
+      static_cast<FtraceModule*>(context->modules.back().get());
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/additional_modules.h b/src/trace_processor/importers/proto/additional_modules.h
new file mode 100644
index 0000000..164332a
--- /dev/null
+++ b/src/trace_processor/importers/proto/additional_modules.h
@@ -0,0 +1,30 @@
+/*
+ * 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_ADDITIONAL_MODULES_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_ADDITIONAL_MODULES_H_
+
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+void RegisterAdditionalModules(TraceProcessorContext*);
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_ADDITIONAL_MODULES_H_
diff --git a/src/trace_processor/importers/proto/android_camera_event_module.cc b/src/trace_processor/importers/proto/android_camera_event_module.cc
index 7082310..1fdddd1 100644
--- a/src/trace_processor/importers/proto/android_camera_event_module.cc
+++ b/src/trace_processor/importers/proto/android_camera_event_module.cc
@@ -13,17 +13,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #include "src/trace_processor/importers/proto/android_camera_event_module.h"
 
 #include "perfetto/ext/base/string_utils.h"
 #include "protos/perfetto/trace/android/camera_event.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "src/trace_processor/importers/common/async_track_set_tracker.h"
+#include "src/trace_processor/importers/common/parser_types.h"
 #include "src/trace_processor/importers/common/slice_tracker.h"
 #include "src/trace_processor/importers/common/track_tracker.h"
-#include "src/trace_processor/importers/proto/async_track_set_tracker.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state.h"
+#include "src/trace_processor/sorter/trace_sorter.h"
 #include "src/trace_processor/storage/trace_storage.h"
-#include "src/trace_processor/timestamped_trace_piece.h"
-#include "src/trace_processor/trace_sorter.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -51,14 +53,16 @@
       protos::pbzero::AndroidCameraFrameEvent::Decoder(
           decoder.android_camera_frame_event());
   context_->sorter->PushTracePacket(
-      android_camera_frame_event.request_processing_started_ns(), state,
-      std::move(*packet));
+      android_camera_frame_event.request_processing_started_ns(),
+      state->current_generation(), std::move(*packet));
   return ModuleResult::Handled();
 }
 
-void AndroidCameraEventModule::ParsePacket(const TracePacket::Decoder& decoder,
-                                           const TimestampedTracePiece& /*ttp*/,
-                                           uint32_t field_id) {
+void AndroidCameraEventModule::ParseTracePacketData(
+    const TracePacket::Decoder& decoder,
+    int64_t /*ts*/,
+    const TracePacketData&,
+    uint32_t field_id) {
   if (field_id != TracePacket::kAndroidCameraFrameEventFieldNumber) {
     return;
   }
diff --git a/src/trace_processor/importers/proto/android_camera_event_module.h b/src/trace_processor/importers/proto/android_camera_event_module.h
index 7145445..d6d70af 100644
--- a/src/trace_processor/importers/proto/android_camera_event_module.h
+++ b/src/trace_processor/importers/proto/android_camera_event_module.h
@@ -22,6 +22,7 @@
 
 #include "perfetto/ext/base/optional.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "src/trace_processor/importers/common/parser_types.h"
 #include "src/trace_processor/importers/proto/proto_importer_module.h"
 #include "src/trace_processor/tables/slice_tables.h"
 #include "src/trace_processor/tables/track_tables.h"
@@ -43,9 +44,10 @@
       PacketSequenceState* state,
       uint32_t field_id) override;
 
-  void ParsePacket(const protos::pbzero::TracePacket::Decoder& decoder,
-                   const TimestampedTracePiece& ttp,
-                   uint32_t field_id) override;
+  void ParseTracePacketData(const protos::pbzero::TracePacket::Decoder& decoder,
+                            int64_t ts,
+                            const TracePacketData&,
+                            uint32_t field_id) override;
 
  private:
   void InsertCameraFrameSlice(protozero::ConstBytes bytes);
diff --git a/src/trace_processor/importers/proto/android_probes_module.cc b/src/trace_processor/importers/proto/android_probes_module.cc
index 23c25b4..17f57ee 100644
--- a/src/trace_processor/importers/proto/android_probes_module.cc
+++ b/src/trace_processor/importers/proto/android_probes_module.cc
@@ -22,10 +22,12 @@
 #include "src/trace_processor/importers/common/track_tracker.h"
 #include "src/trace_processor/importers/proto/android_probes_parser.h"
 #include "src/trace_processor/importers/proto/android_probes_tracker.h"
-#include "src/trace_processor/timestamped_trace_piece.h"
-#include "src/trace_processor/trace_sorter.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state.h"
+#include "src/trace_processor/sorter/trace_sorter.h"
 
+#include "protos/perfetto/common/android_energy_consumer_descriptor.pbzero.h"
 #include "protos/perfetto/config/trace_config.pbzero.h"
+#include "protos/perfetto/trace/power/android_energy_estimation_breakdown.pbzero.h"
 #include "protos/perfetto/trace/power/power_rails.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 
@@ -84,12 +86,15 @@
           context->storage->InternString("subsystem_name")) {
   RegisterForField(TracePacket::kBatteryFieldNumber, context);
   RegisterForField(TracePacket::kPowerRailsFieldNumber, context);
+  RegisterForField(TracePacket::kAndroidEnergyEstimationBreakdownFieldNumber,
+                   context);
   RegisterForField(TracePacket::kAndroidLogFieldNumber, context);
   RegisterForField(TracePacket::kPackagesListFieldNumber, context);
   RegisterForField(TracePacket::kAndroidGameInterventionListFieldNumber,
                    context);
   RegisterForField(TracePacket::kInitialDisplayStateFieldNumber, context);
   RegisterForField(TracePacket::kAndroidSystemPropertyFieldNumber, context);
+  RegisterForField(TracePacket::kNetworkPacketFieldNumber, context);
 }
 
 ModuleResult AndroidProbesModule::TokenizePacket(
@@ -98,8 +103,18 @@
     int64_t packet_timestamp,
     PacketSequenceState* state,
     uint32_t field_id) {
-  if (field_id != TracePacket::kPowerRailsFieldNumber)
+  protos::pbzero::TracePacket::Decoder decoder(packet->data(),
+                                               packet->length());
+
+  // The energy descriptor packet does not have a timestamp so needs to be
+  // handled at the tokenization phase.
+  if (field_id == TracePacket::kAndroidEnergyEstimationBreakdownFieldNumber) {
+    return ParseEnergyDescriptor(decoder.android_energy_estimation_breakdown());
+  }
+
+  if (field_id != TracePacket::kPowerRailsFieldNumber) {
     return ModuleResult::Ignored();
+  }
 
   // Power rails are similar to ftrace in that they have many events, each with
   // their own timestamp, packed inside a single TracePacket. This means that,
@@ -109,9 +124,6 @@
   // a lot of machinery to shepherd these events through the sorting queues
   // in a special way. Therefore, we just forge new packets and sort them as if
   // they came from the underlying trace.
-
-  protos::pbzero::TracePacket::Decoder decoder(packet->data(),
-                                               packet->length());
   auto power_rails = decoder.power_rails();
   protos::pbzero::PowerRails::Decoder evt(power_rails.data, power_rails.size);
 
@@ -168,22 +180,28 @@
 
     std::vector<uint8_t> vec = data_packet.SerializeAsArray();
     TraceBlob blob = TraceBlob::CopyFrom(vec.data(), vec.size());
-    context_->sorter->PushTracePacket(actual_ts, state,
+    context_->sorter->PushTracePacket(actual_ts, state->current_generation(),
                                       TraceBlobView(std::move(blob)));
   }
 
   return ModuleResult::Handled();
 }
 
-void AndroidProbesModule::ParsePacket(const TracePacket::Decoder& decoder,
-                                      const TimestampedTracePiece& ttp,
-                                      uint32_t field_id) {
+void AndroidProbesModule::ParseTracePacketData(
+    const TracePacket::Decoder& decoder,
+    int64_t ts,
+    const TracePacketData&,
+    uint32_t field_id) {
   switch (field_id) {
     case TracePacket::kBatteryFieldNumber:
-      parser_.ParseBatteryCounters(ttp.timestamp, decoder.battery());
+      parser_.ParseBatteryCounters(ts, decoder.battery());
       return;
     case TracePacket::kPowerRailsFieldNumber:
-      parser_.ParsePowerRails(ttp.timestamp, decoder.power_rails());
+      parser_.ParsePowerRails(ts, decoder.power_rails());
+      return;
+    case TracePacket::kAndroidEnergyEstimationBreakdownFieldNumber:
+      parser_.ParseEnergyBreakdown(
+          ts, decoder.android_energy_estimation_breakdown());
       return;
     case TracePacket::kAndroidLogFieldNumber:
       parser_.ParseAndroidLogPacket(decoder.android_log());
@@ -196,12 +214,13 @@
           decoder.android_game_intervention_list());
       return;
     case TracePacket::kInitialDisplayStateFieldNumber:
-      parser_.ParseInitialDisplayState(ttp.timestamp,
-                                       decoder.initial_display_state());
+      parser_.ParseInitialDisplayState(ts, decoder.initial_display_state());
       return;
     case TracePacket::kAndroidSystemPropertyFieldNumber:
-      parser_.ParseAndroidSystemProperty(ttp.timestamp,
-                                         decoder.android_system_property());
+      parser_.ParseAndroidSystemProperty(ts, decoder.android_system_property());
+      return;
+    case TracePacket::kNetworkPacketFieldNumber:
+      parser_.ParseNetworkPacketEvent(ts, decoder.network_packet());
       return;
   }
 }
@@ -213,5 +232,30 @@
   }
 }
 
+ModuleResult AndroidProbesModule::ParseEnergyDescriptor(
+    protozero::ConstBytes blob) {
+  protos::pbzero::AndroidEnergyEstimationBreakdown::Decoder event(blob);
+  if (!event.has_energy_consumer_descriptor())
+    return ModuleResult::Ignored();
+
+  protos::pbzero::AndroidEnergyConsumerDescriptor::Decoder descriptor(
+      event.energy_consumer_descriptor());
+
+  for (auto it = descriptor.energy_consumers(); it; ++it) {
+    protos::pbzero::AndroidEnergyConsumer::Decoder consumer(*it);
+
+    if (!consumer.has_energy_consumer_id()) {
+      context_->storage->IncrementStats(stats::energy_descriptor_invalid);
+      continue;
+    }
+
+    AndroidProbesTracker::GetOrCreate(context_)->SetEnergyBreakdownDescriptor(
+        consumer.energy_consumer_id(),
+        context_->storage->InternString(consumer.name()),
+        context_->storage->InternString(consumer.type()), consumer.ordinal());
+  }
+  return ModuleResult::Handled();
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/android_probes_module.h b/src/trace_processor/importers/proto/android_probes_module.h
index 1a466ad..cd0b40a 100644
--- a/src/trace_processor/importers/proto/android_probes_module.h
+++ b/src/trace_processor/importers/proto/android_probes_module.h
@@ -20,7 +20,6 @@
 #include "perfetto/base/build_config.h"
 #include "src/trace_processor/importers/proto/android_probes_parser.h"
 #include "src/trace_processor/importers/proto/proto_importer_module.h"
-#include "src/trace_processor/timestamped_trace_piece.h"
 
 #include "protos/perfetto/config/trace_config.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
@@ -38,13 +37,15 @@
                               PacketSequenceState*,
                               uint32_t field_id) override;
 
-  void ParsePacket(const protos::pbzero::TracePacket::Decoder& decoder,
-                   const TimestampedTracePiece& ttp,
-                   uint32_t field_id) override;
-
+  void ParseTracePacketData(const protos::pbzero::TracePacket_Decoder& decoder,
+                            int64_t ts,
+                            const TracePacketData&,
+                            uint32_t field_id) override;
   void ParseTraceConfig(
       const protos::pbzero::TraceConfig::Decoder& decoder) override;
 
+  ModuleResult ParseEnergyDescriptor(protozero::ConstBytes blob);
+
  private:
   AndroidProbesParser parser_;
   TraceProcessorContext* context_ = nullptr;
diff --git a/src/trace_processor/importers/proto/android_probes_parser.cc b/src/trace_processor/importers/proto/android_probes_parser.cc
index fc114ce..8712c19 100644
--- a/src/trace_processor/importers/proto/android_probes_parser.cc
+++ b/src/trace_processor/importers/proto/android_probes_parser.cc
@@ -16,19 +16,20 @@
 
 #include "src/trace_processor/importers/proto/android_probes_parser.h"
 
-#include "perfetto/base/logging.h"
 #include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/traced/sys_stats_counters.h"
 #include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/async_track_set_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/async_track_set_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/tcp_state.h"
 #include "src/trace_processor/types/trace_processor_context.h"
 
+#include "protos/perfetto/common/android_energy_consumer_descriptor.pbzero.h"
 #include "protos/perfetto/common/android_log_constants.pbzero.h"
 #include "protos/perfetto/common/builtin_clock.pbzero.h"
 #include "protos/perfetto/config/trace_config.pbzero.h"
@@ -36,7 +37,9 @@
 #include "protos/perfetto/trace/android/android_log.pbzero.h"
 #include "protos/perfetto/trace/android/android_system_property.pbzero.h"
 #include "protos/perfetto/trace/android/initial_display_state.pbzero.h"
+#include "protos/perfetto/trace/android/network_trace.pbzero.h"
 #include "protos/perfetto/trace/android/packages_list.pbzero.h"
+#include "protos/perfetto/trace/power/android_energy_estimation_breakdown.pbzero.h"
 #include "protos/perfetto/trace/power/battery_counters.pbzero.h"
 #include "protos/perfetto/trace/power/power_rails.pbzero.h"
 #include "protos/perfetto/trace/ps/process_stats.pbzero.h"
@@ -48,6 +51,22 @@
 
 namespace perfetto {
 namespace trace_processor {
+namespace {
+// Convert the bitmask into a string where '.' indicates an unset bit
+// and each bit gets a unique letter if set. The letters correspond to
+// the bitfields in tcphdr (fin, syn, rst, etc).
+base::StackString<12> GetTcpFlagMask(uint32_t tcp_flags) {
+  static constexpr char kBitNames[] = "fsrpauec";
+  static constexpr int kBitCount = 8;
+
+  char flags[kBitCount + 1] = {'\0'};
+  for (int f = 0; f < kBitCount; f++) {
+    flags[f] = (tcp_flags & (1 << f)) ? kBitNames[f] : '.';
+  }
+
+  return base::StackString<12>("%s", flags);
+}
+}  // namespace
 
 AndroidProbesParser::AndroidProbesParser(TraceProcessorContext* context)
     : context_(context),
@@ -57,31 +76,54 @@
       batt_current_avg_id_(
           context->storage->InternString("batt.current.avg_ua")),
       screen_state_id_(context->storage->InternString("ScreenState")),
-      device_state_id_(context->storage->InternString("DeviceStateChanged")) {}
+      device_state_id_(context->storage->InternString("DeviceStateChanged")),
+      net_arg_length_(context->storage->InternString("packet_length")),
+      net_arg_ip_proto_(context->storage->InternString("packet_transport")),
+      net_arg_tcp_flags_(context->storage->InternString("packet_tcp_flags")),
+      net_arg_tag_(context->storage->InternString("socket_tag")),
+      net_arg_local_port_(context->storage->InternString("local_port")),
+      net_arg_remote_port_(context->storage->InternString("remote_port")),
+      net_ipproto_tcp_(context->storage->InternString("IPPROTO_TCP")),
+      net_ipproto_udp_(context->storage->InternString("IPPROTO_UDP")) {}
 
 void AndroidProbesParser::ParseBatteryCounters(int64_t ts, ConstBytes blob) {
   protos::pbzero::BatteryCounters::Decoder evt(blob.data, blob.size);
+  StringId batt_charge_id = batt_charge_id_;
+  StringId batt_capacity_id = batt_capacity_id_;
+  StringId batt_current_id = batt_current_id_;
+  StringId batt_current_avg_id = batt_current_avg_id_;
+  if (evt.has_name()) {
+    std::string batt_name = evt.name().ToStdString();
+    batt_charge_id = context_->storage->InternString(base::StringView(
+        std::string("batt.").append(batt_name).append(".charge_uah")));
+    batt_capacity_id = context_->storage->InternString(base::StringView(
+        std::string("batt.").append(batt_name).append(".capacity_pct")));
+    batt_current_id = context_->storage->InternString(base::StringView(
+        std::string("batt.").append(batt_name).append(".current_ua")));
+    batt_current_avg_id = context_->storage->InternString(base::StringView(
+        std::string("batt.").append(batt_name).append(".current.avg_ua")));
+  }
   if (evt.has_charge_counter_uah()) {
     TrackId track =
-        context_->track_tracker->InternGlobalCounterTrack(batt_charge_id_);
+        context_->track_tracker->InternGlobalCounterTrack(batt_charge_id);
     context_->event_tracker->PushCounter(
         ts, static_cast<double>(evt.charge_counter_uah()), track);
   }
   if (evt.has_capacity_percent()) {
     TrackId track =
-        context_->track_tracker->InternGlobalCounterTrack(batt_capacity_id_);
+        context_->track_tracker->InternGlobalCounterTrack(batt_capacity_id);
     context_->event_tracker->PushCounter(
         ts, static_cast<double>(evt.capacity_percent()), track);
   }
   if (evt.has_current_ua()) {
     TrackId track =
-        context_->track_tracker->InternGlobalCounterTrack(batt_current_id_);
+        context_->track_tracker->InternGlobalCounterTrack(batt_current_id);
     context_->event_tracker->PushCounter(
         ts, static_cast<double>(evt.current_ua()), track);
   }
   if (evt.has_current_avg_ua()) {
     TrackId track =
-        context_->track_tracker->InternGlobalCounterTrack(batt_current_avg_id_);
+        context_->track_tracker->InternGlobalCounterTrack(batt_current_avg_id);
     context_->event_tracker->PushCounter(
         ts, static_cast<double>(evt.current_avg_ua()), track);
   }
@@ -117,6 +159,53 @@
   PERFETTO_DCHECK(!++it);
 }
 
+void AndroidProbesParser::ParseEnergyBreakdown(int64_t ts, ConstBytes blob) {
+  protos::pbzero::AndroidEnergyEstimationBreakdown::Decoder event(blob.data,
+                                                                  blob.size);
+
+  if (!event.has_energy_consumer_id() || !event.has_energy_uws()) {
+    context_->storage->IncrementStats(stats::energy_breakdown_missing_values);
+    return;
+  }
+
+  auto consumer_id = event.energy_consumer_id();
+  auto* tracker = AndroidProbesTracker::GetOrCreate(context_);
+  auto energy_consumer_specs =
+      tracker->GetEnergyBreakdownDescriptor(consumer_id);
+
+  if (!energy_consumer_specs) {
+    context_->storage->IncrementStats(stats::energy_breakdown_missing_values);
+    return;
+  }
+
+  auto total_energy = static_cast<double>(event.energy_uws());
+  auto consumer_name = energy_consumer_specs->name;
+  auto consumer_type = energy_consumer_specs->type;
+  auto ordinal = energy_consumer_specs->ordinal;
+
+  TrackId energy_track = context_->track_tracker->InternEnergyCounterTrack(
+      consumer_name, consumer_id, consumer_type, ordinal);
+  context_->event_tracker->PushCounter(ts, total_energy, energy_track);
+
+  // Consumers providing per-uid energy breakdown
+  for (auto it = event.per_uid_breakdown(); it; ++it) {
+    protos::pbzero::AndroidEnergyEstimationBreakdown_EnergyUidBreakdown::Decoder
+        breakdown(*it);
+
+    if (!breakdown.has_uid() || !breakdown.has_energy_uws()) {
+      context_->storage->IncrementStats(
+          stats::energy_uid_breakdown_missing_values);
+      continue;
+    }
+
+    TrackId energy_uid_track =
+        context_->track_tracker->InternEnergyPerUidCounterTrack(
+            consumer_name, consumer_id, breakdown.uid());
+    context_->event_tracker->PushCounter(
+        ts, static_cast<double>(breakdown.energy_uws()), energy_uid_track);
+  }
+}
+
 void AndroidProbesParser::ParseAndroidLogPacket(ConstBytes blob) {
   protos::pbzero::AndroidLogPacket::Decoder packet(blob.data, blob.size);
   for (auto it = packet.events(); it; ++it)
@@ -318,7 +407,8 @@
                                                             blob.size);
   for (auto it = properties.values(); it; ++it) {
     protos::pbzero::AndroidSystemProperty::PropertyValue::Decoder kv(*it);
-    if (base::StringView(kv.name()) == "debug.tracing.screen_state") {
+    base::StringView name(kv.name());
+    if (name == "debug.tracing.screen_state") {
       base::Optional<int32_t> state =
           base::StringToInt32(kv.value().ToStdString());
       if (state) {
@@ -326,7 +416,7 @@
             context_->track_tracker->InternGlobalCounterTrack(screen_state_id_);
         context_->event_tracker->PushCounter(ts, *state, track);
       }
-    } else if (base::StringView(kv.name()) == "debug.tracing.device_state") {
+    } else if (name == "debug.tracing.device_state") {
       auto state = kv.value();
 
       StringId state_id = context_->storage->InternString(state);
@@ -336,9 +426,85 @@
       TrackId track_id =
           context_->async_track_set_tracker->Scoped(track_set_id, ts, 0);
       context_->slice_tracker->Scoped(ts, track_id, kNullStringId, state_id, 0);
+    } else if (name.StartsWith("debug.tracing.battery_stats.") ||
+               name == "debug.tracing.mcc" || name == "debug.tracing.mnc") {
+      StringId name_id = context_->storage->InternString(
+          name.substr(strlen("debug.tracing.")));
+      base::Optional<int32_t> state =
+          base::StringToInt32(kv.value().ToStdString());
+      if (state) {
+        TrackId track =
+            context_->track_tracker->InternGlobalCounterTrack(name_id);
+        context_->event_tracker->PushCounter(ts, *state, track);
+      }
     }
   }
 }
 
+void AndroidProbesParser::ParseNetworkPacketEvent(int64_t ts, ConstBytes blob) {
+  using protos::pbzero::NetworkPacketEvent;
+  using protos::pbzero::TrafficDirection;
+  NetworkPacketEvent::Decoder evt(blob);
+
+  // Tracks are per interface and per direction.
+  const char* track_suffix =
+      evt.direction() == TrafficDirection::DIR_INGRESS  ? " Received"
+      : evt.direction() == TrafficDirection::DIR_EGRESS ? " Transmitted"
+                                                        : " DIR_UNKNOWN";
+
+  base::StackString<64> name("%.*s%s", static_cast<int>(evt.interface().size),
+                             evt.interface().data, track_suffix);
+  StringId name_id = context_->storage->InternString(name.string_view());
+
+  // Event titles are the package name, if available.
+  StringId title_id = kNullStringId;
+  if (evt.uid() > 0) {
+    const auto& package_list = context_->storage->package_list_table();
+    base::Optional<uint32_t> pkg_row = package_list.uid().IndexOf(evt.uid());
+    if (pkg_row) {
+      title_id = package_list.package_name()[*pkg_row];
+    }
+  }
+
+  // If the above fails, fall back to the uid.
+  if (title_id == kNullStringId) {
+    base::StackString<32> title_str("uid=%" PRIu32, evt.uid());
+    title_id = context_->storage->InternString(title_str.string_view());
+  }
+
+  TrackId track_id = context_->async_track_set_tracker->Scoped(
+      context_->async_track_set_tracker->InternGlobalTrackSet(name_id), ts, 0);
+
+  context_->slice_tracker->Scoped(
+      ts, track_id, name_id, title_id, 0, [&](ArgsTracker::BoundInserter* i) {
+        i->AddArg(net_arg_length_, Variadic::Integer(evt.length()));
+
+        StringId ip_proto;
+        if (evt.ip_proto() == kIpprotoTcp) {
+          ip_proto = net_ipproto_tcp_;
+        } else if (evt.ip_proto() == kIpprotoUdp) {
+          ip_proto = net_ipproto_udp_;
+        } else {
+          base::StackString<32> proto("IPPROTO (%d)", evt.ip_proto());
+          ip_proto = context_->storage->InternString(proto.string_view());
+        }
+
+        i->AddArg(net_arg_ip_proto_, Variadic::String(ip_proto));
+
+        base::StackString<16> tag("0x%x", evt.tag());
+        i->AddArg(net_arg_tag_,
+                  Variadic::String(
+                      context_->storage->InternString(tag.string_view())));
+
+        base::StackString<12> flags = GetTcpFlagMask(evt.tcp_flags());
+        i->AddArg(net_arg_tcp_flags_,
+                  Variadic::String(
+                      context_->storage->InternString(flags.string_view())));
+
+        i->AddArg(net_arg_local_port_, Variadic::Integer(evt.local_port()));
+        i->AddArg(net_arg_remote_port_, Variadic::Integer(evt.remote_port()));
+      });
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/android_probes_parser.h b/src/trace_processor/importers/proto/android_probes_parser.h
index 3464866..001a8e8 100644
--- a/src/trace_processor/importers/proto/android_probes_parser.h
+++ b/src/trace_processor/importers/proto/android_probes_parser.h
@@ -35,6 +35,7 @@
 
   void ParseBatteryCounters(int64_t ts, ConstBytes);
   void ParsePowerRails(int64_t ts, ConstBytes);
+  void ParseEnergyBreakdown(int64_t ts, ConstBytes);
   void ParseAndroidLogPacket(ConstBytes);
   void ParseAndroidLogEvent(ConstBytes);
   void ParseAndroidLogStats(ConstBytes);
@@ -43,6 +44,7 @@
   void ParseInitialDisplayState(int64_t ts, ConstBytes);
   void ParseAndroidSystemProperty(int64_t ts, ConstBytes);
   void ParseAndroidGameIntervention(ConstBytes);
+  void ParseNetworkPacketEvent(int64_t ts, ConstBytes);
 
  private:
   TraceProcessorContext* const context_;
@@ -53,6 +55,15 @@
   const StringId batt_current_avg_id_;
   const StringId screen_state_id_;
   const StringId device_state_id_;
+
+  const StringId net_arg_length_;
+  const StringId net_arg_ip_proto_;
+  const StringId net_arg_tcp_flags_;
+  const StringId net_arg_tag_;
+  const StringId net_arg_local_port_;
+  const StringId net_arg_remote_port_;
+  const StringId net_ipproto_tcp_;
+  const StringId net_ipproto_udp_;
 };
 }  // 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
index c199e7c..1180872 100644
--- a/src/trace_processor/importers/proto/android_probes_tracker.h
+++ b/src/trace_processor/importers/proto/android_probes_tracker.h
@@ -34,6 +34,13 @@
   explicit AndroidProbesTracker(TraceStorage*);
   ~AndroidProbesTracker() override;
 
+  // For EnergyBreakdown Descriptor specifications
+  struct EnergyConsumerSpecs {
+    StringId name;
+    StringId type;
+    int32_t ordinal;
+  };
+
   static AndroidProbesTracker* GetOrCreate(TraceProcessorContext* context) {
     if (!context->android_probes_tracker) {
       context->android_probes_tracker.reset(
@@ -66,9 +73,35 @@
     power_rail_tracks_[index] = track_id;
   }
 
+  base::Optional<EnergyConsumerSpecs> GetEnergyBreakdownDescriptor(
+      int32_t consumer_id) {
+    auto it = energy_consumer_descriptors_.find(consumer_id);
+    // Didn't receive the descriptor
+    if (it == energy_consumer_descriptors_.end()) {
+      return base::nullopt;
+    }
+    return it->second;
+  }
+
+  void SetEnergyBreakdownDescriptor(int32_t consumer_id,
+                                    StringId name,
+                                    StringId type,
+                                    int32_t ordinal) {
+    auto it_consumer_descriptor =
+        energy_consumer_descriptors_.find(consumer_id);
+
+    // Either descriptor was repeated or it came after per uid data.
+    if (it_consumer_descriptor != energy_consumer_descriptors_.end())
+      return;
+
+    energy_consumer_descriptors_[consumer_id] =
+        EnergyConsumerSpecs{name, type, ordinal};
+  }
+
  private:
   std::set<std::string> seen_packages_;
   std::vector<TrackId> power_rail_tracks_;
+  std::unordered_map<int32_t, EnergyConsumerSpecs> energy_consumer_descriptors_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/async_track_set_tracker.cc b/src/trace_processor/importers/proto/async_track_set_tracker.cc
deleted file mode 100644
index 2593a12..0000000
--- a/src/trace_processor/importers/proto/async_track_set_tracker.cc
+++ /dev/null
@@ -1,202 +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/importers/proto/async_track_set_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 {
-
-AsyncTrackSetTracker::AsyncTrackSetTracker(TraceProcessorContext* context)
-    : android_source_(context->storage->InternString("android")),
-      context_(context) {}
-
-AsyncTrackSetTracker::TrackSetId AsyncTrackSetTracker::InternGlobalTrackSet(
-    StringId name) {
-  auto it = global_track_set_ids_.find(name);
-  if (it != global_track_set_ids_.end()) {
-    return it->second;
-  }
-
-  uint32_t id = static_cast<uint32_t>(track_sets_.size());
-  TrackSet set;
-  set.global_track_name = name;
-  set.type = TrackSetType::kGlobal;
-  set.nesting_behaviour = NestingBehaviour::kUnnestable;
-  track_sets_.emplace_back(set);
-
-  return global_track_set_ids_[name] = id;
-}
-
-AsyncTrackSetTracker::TrackSetId AsyncTrackSetTracker::InternProcessTrackSet(
-    UniquePid upid,
-    StringId name) {
-  ProcessTuple tuple{upid, name};
-
-  auto it = process_track_set_ids_.find(tuple);
-  if (it != process_track_set_ids_.end())
-    return it->second;
-
-  uint32_t id = static_cast<uint32_t>(track_sets_.size());
-
-  TrackSet set;
-  set.process_tuple = tuple;
-  set.type = TrackSetType::kProcess;
-  set.nesting_behaviour = NestingBehaviour::kUnnestable;
-  track_sets_.emplace_back(set);
-
-  process_track_set_ids_[tuple] = id;
-  return id;
-}
-
-AsyncTrackSetTracker::TrackSetId
-AsyncTrackSetTracker::InternAndroidLegacyUnnestableTrackSet(UniquePid upid,
-                                                            StringId name) {
-  ProcessTuple tuple{upid, name};
-
-  auto it = android_legacy_unnestable_track_set_ids_.find(tuple);
-  if (it != android_legacy_unnestable_track_set_ids_.end())
-    return it->second;
-
-  uint32_t id = static_cast<uint32_t>(track_sets_.size());
-
-  TrackSet set;
-  set.process_tuple = tuple;
-  set.type = TrackSetType::kAndroidLegacyUnnestable;
-  set.nesting_behaviour = NestingBehaviour::kLegacySaturatingUnnestable;
-  track_sets_.emplace_back(set);
-
-  android_legacy_unnestable_track_set_ids_[tuple] = id;
-  return id;
-}
-
-TrackId AsyncTrackSetTracker::Begin(TrackSetId id, int64_t cookie) {
-  PERFETTO_DCHECK(id < track_sets_.size());
-
-  TrackSet& set = track_sets_[id];
-  TrackState& state = GetOrCreateTrackForCookie(set, cookie);
-  switch (set.nesting_behaviour) {
-    case NestingBehaviour::kLegacySaturatingUnnestable:
-      PERFETTO_DCHECK(state.nest_count <= 1);
-      state.nest_count = 1;
-      break;
-    case NestingBehaviour::kUnnestable:
-      PERFETTO_DCHECK(state.nest_count == 0);
-      state.nest_count++;
-      break;
-  }
-  return state.id;
-}
-
-TrackId AsyncTrackSetTracker::End(TrackSetId id, int64_t cookie) {
-  PERFETTO_DCHECK(id < track_sets_.size());
-
-  TrackSet& set = track_sets_[id];
-  TrackState& state = GetOrCreateTrackForCookie(set, cookie);
-
-  // It's possible to have a nest count of 0 even when we know about the track.
-  // Suppose the following sequence of events for some |id| and |cookie|:
-  //   Begin
-  //   (trace starts)
-  //   Begin
-  //   End
-  //   End <- nest count == 0 here even though we have a record of this track.
-  if (state.nest_count > 0)
-    state.nest_count--;
-  return state.id;
-}
-
-TrackId AsyncTrackSetTracker::Scoped(TrackSetId id, int64_t ts, int64_t dur) {
-  PERFETTO_DCHECK(id < track_sets_.size());
-
-  TrackSet& set = track_sets_[id];
-  PERFETTO_DCHECK(set.nesting_behaviour == NestingBehaviour::kUnnestable);
-
-  auto it = std::find_if(
-      set.tracks.begin(), set.tracks.end(), [ts](const TrackState& state) {
-        return state.slice_type == TrackState::SliceType::kTimestamp &&
-               state.ts_end <= ts;
-      });
-  if (it != set.tracks.end()) {
-    it->ts_end = ts + dur;
-    return it->id;
-  }
-
-  TrackState state;
-  state.slice_type = TrackState::SliceType::kTimestamp;
-  state.ts_end = ts + dur;
-  state.id = CreateTrackForSet(set);
-  set.tracks.emplace_back(state);
-
-  return state.id;
-}
-
-AsyncTrackSetTracker::TrackState&
-AsyncTrackSetTracker::GetOrCreateTrackForCookie(TrackSet& set, int64_t cookie) {
-  auto it = std::find_if(
-      set.tracks.begin(), set.tracks.end(), [cookie](const TrackState& state) {
-        return state.slice_type == TrackState::SliceType::kCookie &&
-               state.cookie == cookie;
-      });
-  if (it != set.tracks.end())
-    return *it;
-
-  it = std::find_if(
-      set.tracks.begin(), set.tracks.end(), [](const TrackState& state) {
-        return state.slice_type == TrackState::SliceType::kCookie &&
-               state.nest_count == 0;
-      });
-  if (it != set.tracks.end()) {
-    // Adopt this track for the cookie to make sure future slices with this
-    // cookie also get associated to this track.
-    it->cookie = cookie;
-    return *it;
-  }
-
-  TrackState state;
-  state.id = CreateTrackForSet(set);
-  state.slice_type = TrackState::SliceType::kCookie;
-  state.cookie = cookie;
-  state.nest_count = 0;
-  set.tracks.emplace_back(state);
-
-  return set.tracks.back();
-}
-
-TrackId AsyncTrackSetTracker::CreateTrackForSet(const TrackSet& set) {
-  switch (set.type) {
-    case TrackSetType::kGlobal:
-      // TODO(lalitm): propogate source from callers rather than just passing
-      // kNullStringId here.
-      return context_->track_tracker->CreateGlobalAsyncTrack(
-          set.global_track_name, kNullStringId);
-    case TrackSetType::kProcess:
-      // TODO(lalitm): propogate source from callers rather than just passing
-      // kNullStringId here.
-      return context_->track_tracker->CreateProcessAsyncTrack(
-          set.process_tuple.name, set.process_tuple.upid, kNullStringId);
-    case TrackSetType::kAndroidLegacyUnnestable:
-      return context_->track_tracker->CreateProcessAsyncTrack(
-          set.process_tuple.name, set.process_tuple.upid, android_source_);
-  }
-  PERFETTO_FATAL("For GCC");
-}
-
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/async_track_set_tracker.h b/src/trace_processor/importers/proto/async_track_set_tracker.h
deleted file mode 100644
index 83fd95c..0000000
--- a/src/trace_processor/importers/proto/async_track_set_tracker.h
+++ /dev/null
@@ -1,185 +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_IMPORTERS_PROTO_ASYNC_TRACK_SET_TRACKER_H_
-#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_ASYNC_TRACK_SET_TRACKER_H_
-
-#include "src/trace_processor/storage/trace_storage.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-class TraceProcessorContext;
-class AsyncTrackSetTrackerUnittest;
-
-// Tracker used to reduce the number of trace processor tracks corresponding
-// to a single "UI track".
-//
-// UIs using trace processor want to display all slices in the same context
-// (e.g. same upid) and same name into a single track. However, because trace
-// processor does not allow parallel slices on a single track (because it breaks
-// things like span join, self time computation etc.), at the trace processor
-// level these parallel slices are put on different tracks.
-//
-// Creating a new track for every event, however, leads to an explosion of
-// tracks which is undesirable. This class exists to multiplex slices so that
-// n events correspond to a single track in a way which minimises the number of
-// tracks which needs to be merged by the UI.
-//
-// The intended usage of this class is for callers to first call one of the
-// Intern* methods to obtain a TrackSetId followed by Begin/End just before
-// calling into SliceTracker's Begin/End respectively. For example:
-//  TrackSetId set_id = track_set_tracker->InternProcessTrackSet(upid, name);
-//  if (event.begin) {
-//    TrackId id = track_set_tracker->Begin(set_id, cookie);
-//    slice_tracker->Begin(ts, id, ...)
-//  } else {
-//    ... (same thing with end)
-//  }
-// Alternatively, instead of Begin/End, Scoped can also be called if supported
-// by the track type.
-class AsyncTrackSetTracker {
- public:
-  using TrackSetId = uint32_t;
-
-  explicit AsyncTrackSetTracker(TraceProcessorContext* context);
-  ~AsyncTrackSetTracker() = default;
-
-  // Interns a set of global async slice tracks associated with the given name.
-  TrackSetId InternGlobalTrackSet(StringId name);
-
-  // Interns a set of process async slice tracks associated with the given name
-  // and upid.
-  TrackSetId InternProcessTrackSet(UniquePid, StringId name);
-
-  // Interns a set of Android legacy unnesteable async slice tracks
-  // associated with the given upid and name.
-  // Scoped is *not* supported for this track set type.
-  TrackSetId InternAndroidLegacyUnnestableTrackSet(UniquePid, StringId name);
-
-  // Starts a new slice on the given async track set which has the given cookie.
-  TrackId Begin(TrackSetId id, int64_t cookie);
-
-  // Ends a new slice on the given async track set which has the given cookie.
-  TrackId End(TrackSetId id, int64_t cookie);
-
-  // Creates a scoped slice on the given async track set.
-  // This method makes sure that any other slice in this track set does
-  // not happen simultaneously on the returned track.
-  // Only supported on selected track set types; read the documentation for
-  // the Intern* method for your track type to check if supported.
-  TrackId Scoped(TrackSetId id, int64_t ts, int64_t dur);
-
- private:
-  friend class AsyncTrackSetTrackerUnittest;
-
-  struct ProcessTuple {
-    UniquePid upid;
-    StringId name;
-
-    friend bool operator<(const ProcessTuple& l, const ProcessTuple& r) {
-      return std::tie(l.upid, l.name) < std::tie(r.upid, r.name);
-    }
-  };
-
-  // Indicates the nesting behaviour of slices associated to a single slice
-  // stack.
-  enum class NestingBehaviour {
-    // Indicates that slices are unnestable; that is, it is an error
-    // to call Begin -> Begin with a single cookie without End inbetween.
-    // This pattern should be the default behaviour that most async slices
-    // should use.
-    kUnnestable,
-
-    // Indicates that slices are unnestable but also saturating; that is
-    // calling Begin -> Begin only causes a single Begin to be recorded.
-    // This is only really useful for Android async slices which have this
-    // behaviour for legacy reasons. See the comment in
-    // SystraceParser::ParseSystracePoint for information on why
-    // this behaviour exists.
-    kLegacySaturatingUnnestable,
-  };
-
-  enum class TrackSetType {
-    kGlobal,
-    kProcess,
-    kAndroidLegacyUnnestable,
-  };
-
-  struct TrackState {
-    TrackId id;
-
-    enum class SliceType { kCookie, kTimestamp };
-    SliceType slice_type;
-
-    union {
-      // Only valid for |slice_type| == |SliceType::kCookie|.
-      int64_t cookie;
-
-      // Only valid for |slice_type| == |SliceType::kTimestamp|.
-      int64_t ts_end;
-    };
-
-    // Only used for |slice_type| == |SliceType::kCookie|.
-    uint32_t nest_count;
-  };
-
-  struct TrackSet {
-    TrackSetType type;
-    union {
-      // Only set when |type| == |TrackSetType::kGlobal|.
-      StringId global_track_name;
-      // Only set when |type| == |TrackSetType::kFrameTimeline| or
-      // |TrackSetType::kAndroidLegacyUnnestable|.
-      ProcessTuple process_tuple;
-    };
-    NestingBehaviour nesting_behaviour;
-    std::vector<TrackState> tracks;
-  };
-
-  TrackSetId CreateUnnestableTrackSetForTesting(UniquePid upid, StringId name) {
-    AsyncTrackSetTracker::TrackSet set;
-    set.process_tuple = ProcessTuple{upid, name};
-    set.type = AsyncTrackSetTracker::TrackSetType::kAndroidLegacyUnnestable;
-    set.nesting_behaviour = NestingBehaviour::kUnnestable;
-    track_sets_.emplace_back(set);
-    return static_cast<TrackSetId>(track_sets_.size() - 1);
-  }
-
-  // Returns the state for a track using the following algorithm:
-  // 1. If a track exists with the given cookie in the track set, returns
-  //    that track.
-  // 2. Otherwise, looks for any track in the set which is "open" (i.e.
-  //    does not have another slice currently scheduled).
-  // 3. Otherwise, creates a new track and associates it with the set.
-  TrackState& GetOrCreateTrackForCookie(TrackSet& set, int64_t cookie);
-
-  TrackId CreateTrackForSet(const TrackSet& set);
-
-  std::map<StringId, TrackSetId> global_track_set_ids_;
-  std::map<ProcessTuple, TrackSetId> process_track_set_ids_;
-  std::map<ProcessTuple, TrackSetId> android_legacy_unnestable_track_set_ids_;
-  std::vector<TrackSet> track_sets_;
-
-  const StringId android_source_ = kNullStringId;
-
-  TraceProcessorContext* const context_;
-};
-
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_ASYNC_TRACK_SET_TRACKER_H_
diff --git a/src/trace_processor/importers/proto/async_track_set_tracker_unittest.cc b/src/trace_processor/importers/proto/async_track_set_tracker_unittest.cc
deleted file mode 100644
index 7d26b04..0000000
--- a/src/trace_processor/importers/proto/async_track_set_tracker_unittest.cc
+++ /dev/null
@@ -1,168 +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/importers/proto/async_track_set_tracker.h"
-
-#include "src/trace_processor/importers/common/args_tracker.h"
-#include "src/trace_processor/importers/common/global_args_tracker.h"
-#include "src/trace_processor/importers/common/track_tracker.h"
-#include "src/trace_processor/types/trace_processor_context.h"
-#include "test/gtest_and_gmock.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-class AsyncTrackSetTrackerUnittest : public testing::Test {
- public:
-  AsyncTrackSetTrackerUnittest() {
-    context_.storage.reset(new TraceStorage());
-    context_.global_args_tracker.reset(
-        new GlobalArgsTracker(context_.storage.get()));
-    context_.args_tracker.reset(new ArgsTracker(&context_));
-    context_.track_tracker.reset(new TrackTracker(&context_));
-    context_.async_track_set_tracker.reset(new AsyncTrackSetTracker(&context_));
-
-    storage_ = context_.storage.get();
-    tracker_ = context_.async_track_set_tracker.get();
-
-    unnestable_id_ = tracker_->CreateUnnestableTrackSetForTesting(
-        1, storage_->InternString("test"));
-    legacy_unnestable_id_ = tracker_->InternAndroidLegacyUnnestableTrackSet(
-        2, storage_->InternString("test"));
-  }
-
- protected:
-  TraceStorage* storage_ = nullptr;
-  AsyncTrackSetTracker* tracker_ = nullptr;
-
-  AsyncTrackSetTracker::TrackSetId unnestable_id_;
-  AsyncTrackSetTracker::TrackSetId legacy_unnestable_id_;
-
- private:
-  TraceProcessorContext context_;
-};
-
-namespace {
-
-TEST_F(AsyncTrackSetTrackerUnittest, Smoke) {
-  auto set_id = tracker_->InternAndroidLegacyUnnestableTrackSet(
-      1, storage_->InternString("test"));
-
-  auto begin = tracker_->Begin(set_id, 1);
-  auto end = tracker_->End(set_id, 1);
-
-  ASSERT_EQ(begin, end);
-
-  uint32_t row = *storage_->process_track_table().id().IndexOf(begin);
-  ASSERT_EQ(storage_->process_track_table().upid()[row], 1u);
-  ASSERT_EQ(storage_->process_track_table().name()[row],
-            storage_->InternString("test"));
-}
-
-TEST_F(AsyncTrackSetTrackerUnittest, EndFirst) {
-  auto end = tracker_->End(unnestable_id_, 1);
-
-  uint32_t row = *storage_->process_track_table().id().IndexOf(end);
-  ASSERT_EQ(storage_->process_track_table().upid()[row], 1u);
-  ASSERT_EQ(storage_->process_track_table().name()[row],
-            storage_->InternString("test"));
-}
-
-TEST_F(AsyncTrackSetTrackerUnittest, LegacySaturating) {
-  auto begin = tracker_->Begin(legacy_unnestable_id_, 1);
-  auto begin_2 = tracker_->Begin(legacy_unnestable_id_, 1);
-
-  ASSERT_EQ(begin, begin_2);
-}
-
-TEST_F(AsyncTrackSetTrackerUnittest, Unnestable) {
-  auto begin = tracker_->Begin(unnestable_id_, 1);
-  auto end = tracker_->End(unnestable_id_, 1);
-  auto begin_2 = tracker_->Begin(unnestable_id_, 1);
-
-  ASSERT_EQ(begin, end);
-  ASSERT_EQ(begin, begin_2);
-}
-
-TEST_F(AsyncTrackSetTrackerUnittest, UnnestableMultipleEndAfterBegin) {
-  auto begin = tracker_->Begin(unnestable_id_, 1);
-  auto end = tracker_->End(unnestable_id_, 1);
-  auto end_2 = tracker_->End(unnestable_id_, 1);
-
-  ASSERT_EQ(begin, end);
-  ASSERT_EQ(end, end_2);
-}
-
-TEST_F(AsyncTrackSetTrackerUnittest, OnlyScoped) {
-  TrackId a = tracker_->Scoped(unnestable_id_, 100, 10);
-  TrackId b = tracker_->Scoped(unnestable_id_, 105, 2);
-  TrackId c = tracker_->Scoped(unnestable_id_, 107, 3);
-  TrackId d = tracker_->Scoped(unnestable_id_, 110, 5);
-
-  ASSERT_NE(a, b);
-  ASSERT_EQ(b, c);
-  ASSERT_EQ(a, d);
-}
-
-TEST_F(AsyncTrackSetTrackerUnittest, MixScopedAndBeginEnd) {
-  TrackId a = tracker_->Scoped(unnestable_id_, 100, 10);
-
-  TrackId begin = tracker_->Begin(unnestable_id_, 777);
-  TrackId end = tracker_->End(unnestable_id_, 777);
-
-  TrackId b = tracker_->Scoped(unnestable_id_, 105, 2);
-
-  ASSERT_NE(a, begin);
-  ASSERT_NE(b, begin);
-  ASSERT_EQ(begin, end);
-}
-
-TEST_F(AsyncTrackSetTrackerUnittest, DifferentTracksInterleave) {
-  TrackId b1 = tracker_->Begin(unnestable_id_, 666);
-  TrackId b2 = tracker_->Begin(legacy_unnestable_id_, 777);
-  TrackId e1 = tracker_->End(unnestable_id_, 666);
-  TrackId e2 = tracker_->End(legacy_unnestable_id_, 777);
-
-  ASSERT_EQ(b1, e1);
-  ASSERT_EQ(b2, e2);
-  ASSERT_NE(b1, b2);
-}
-
-TEST_F(AsyncTrackSetTrackerUnittest, DifferentCookieInterleave) {
-  TrackId b1 = tracker_->Begin(legacy_unnestable_id_, 666);
-  TrackId b2 = tracker_->Begin(legacy_unnestable_id_, 777);
-  TrackId e1 = tracker_->End(legacy_unnestable_id_, 666);
-  TrackId e2 = tracker_->End(legacy_unnestable_id_, 777);
-
-  ASSERT_EQ(b1, e1);
-  ASSERT_EQ(b2, e2);
-  ASSERT_NE(b1, b2);
-}
-
-TEST_F(AsyncTrackSetTrackerUnittest, DifferentCookieSequential) {
-  TrackId b1 = tracker_->Begin(legacy_unnestable_id_, 666);
-  TrackId e1 = tracker_->End(legacy_unnestable_id_, 666);
-  TrackId b2 = tracker_->Begin(legacy_unnestable_id_, 777);
-  TrackId e2 = tracker_->End(legacy_unnestable_id_, 777);
-
-  ASSERT_EQ(b1, e1);
-  ASSERT_EQ(b1, b2);
-  ASSERT_EQ(b2, e2);
-}
-
-}  // namespace
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/chrome_system_probes_module.cc b/src/trace_processor/importers/proto/chrome_system_probes_module.cc
index 775d64f..5920f3c 100644
--- a/src/trace_processor/importers/proto/chrome_system_probes_module.cc
+++ b/src/trace_processor/importers/proto/chrome_system_probes_module.cc
@@ -17,7 +17,6 @@
 #include "src/trace_processor/importers/proto/chrome_system_probes_module.h"
 #include "perfetto/base/build_config.h"
 #include "src/trace_processor/importers/proto/chrome_system_probes_parser.h"
-#include "src/trace_processor/timestamped_trace_piece.h"
 
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 
@@ -32,12 +31,14 @@
   RegisterForField(TracePacket::kProcessStatsFieldNumber, context);
 }
 
-void ChromeSystemProbesModule::ParsePacket(const TracePacket::Decoder& decoder,
-                                           const TimestampedTracePiece& ttp,
-                                           uint32_t field_id) {
+void ChromeSystemProbesModule::ParseTracePacketData(
+    const TracePacket::Decoder& decoder,
+    int64_t ts,
+    const TracePacketData&,
+    uint32_t field_id) {
   switch (field_id) {
     case TracePacket::kProcessStatsFieldNumber:
-      parser_.ParseProcessStats(ttp.timestamp, decoder.process_stats());
+      parser_.ParseProcessStats(ts, decoder.process_stats());
       return;
   }
 }
diff --git a/src/trace_processor/importers/proto/chrome_system_probes_module.h b/src/trace_processor/importers/proto/chrome_system_probes_module.h
index 0fc4615..2c830dd 100644
--- a/src/trace_processor/importers/proto/chrome_system_probes_module.h
+++ b/src/trace_processor/importers/proto/chrome_system_probes_module.h
@@ -32,9 +32,10 @@
  public:
   explicit ChromeSystemProbesModule(TraceProcessorContext* context);
 
-  void ParsePacket(const protos::pbzero::TracePacket::Decoder& decoder,
-                   const TimestampedTracePiece& ttp,
-                   uint32_t field_id) override;
+  void ParseTracePacketData(const protos::pbzero::TracePacket_Decoder& decoder,
+                            int64_t ts,
+                            const TracePacketData&,
+                            uint32_t field_id) override;
 
  private:
   ChromeSystemProbesParser parser_;
diff --git a/src/trace_processor/importers/proto/content_analyzer.cc b/src/trace_processor/importers/proto/content_analyzer.cc
new file mode 100644
index 0000000..a6e97bc
--- /dev/null
+++ b/src/trace_processor/importers/proto/content_analyzer.cc
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2022 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/content_analyzer.h"
+
+#include "perfetto/ext/base/string_utils.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/proto/content_analyzer.h"
+#include "src/trace_processor/storage/trace_storage.h"
+
+#include "src/trace_processor/importers/proto/trace.descriptor.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+ProtoContentAnalyzer::ProtoContentAnalyzer(TraceProcessorContext* context)
+    : context_(context),
+      pool_([]() {
+        DescriptorPool pool;
+        base::Status status = pool.AddFromFileDescriptorSet(
+            kTraceDescriptor.data(), kTraceDescriptor.size());
+        if (!status.ok()) {
+          PERFETTO_ELOG("Could not add TracePacket proto descriptor %s",
+                        status.c_message());
+        }
+        return pool;
+      }()),
+      computer_(&pool_, ".perfetto.protos.TracePacket") {}
+
+ProtoContentAnalyzer::~ProtoContentAnalyzer() = default;
+
+void ProtoContentAnalyzer::ProcessPacket(
+    const TraceBlobView& packet,
+    const SampleAnnotation& packet_annotations) {
+  auto& map = aggregated_samples_[packet_annotations];
+  computer_.Reset(packet.data(), packet.length());
+  for (auto sample = computer_.GetNext(); sample.has_value();
+       sample = computer_.GetNext()) {
+    auto* value = map.Find(computer_.GetPath());
+    if (value)
+      *value += *sample;
+    else
+      map.Insert(computer_.GetPath(), *sample);
+  }
+}
+
+void ProtoContentAnalyzer::NotifyEndOfFile() {
+  // TODO(kraskevich): consider generating a flamegraph-compatable table once
+  // Perfetto UI supports custom flamegraphs (b/227644078).
+  for (auto annotated_map = aggregated_samples_.GetIterator(); annotated_map;
+       ++annotated_map) {
+    base::FlatHashMap<util::SizeProfileComputer::FieldPath,
+                      tables::ExperimentalProtoPathTable::Id,
+                      util::SizeProfileComputer::FieldPathHasher>
+        path_ids;
+    for (auto sample = annotated_map.value().GetIterator(); sample; ++sample) {
+      std::string path_string;
+      base::Optional<tables::ExperimentalProtoPathTable::Id> previous_path_id;
+      util::SizeProfileComputer::FieldPath path;
+      for (const auto& field : sample.key()) {
+        if (field.has_field_name()) {
+          if (!path_string.empty()) {
+            path_string += '.';
+          }
+          path_string.append(field.field_name());
+        }
+        if (!path_string.empty()) {
+          path_string += '.';
+        }
+        path_string.append(field.type_name());
+
+        path.push_back(field);
+        // Reuses existing path from |path_ids| if possible.
+        {
+          auto* path_id = path_ids.Find(path);
+          if (path_id) {
+            previous_path_id = *path_id;
+            continue;
+          }
+        }
+        // Create a new row in experimental_proto_path.
+        tables::ExperimentalProtoPathTable::Row path_row;
+        if (field.has_field_name()) {
+          path_row.field_name = context_->storage->InternString(
+              base::StringView(field.field_name()));
+        }
+        path_row.field_type = context_->storage->InternString(
+            base::StringView(field.type_name()));
+        if (previous_path_id.has_value())
+          path_row.parent_id = *previous_path_id;
+
+        auto path_id =
+            context_->storage->mutable_experimental_proto_path_table()
+                ->Insert(path_row)
+                .id;
+        if (!previous_path_id.has_value()) {
+          // Add annotations to the current row as an args set.
+          auto inserter = context_->args_tracker->AddArgsTo(path_id);
+          for (auto& annotation : annotated_map.key()) {
+            inserter.AddArg(annotation.first,
+                            Variadic::String(annotation.second));
+          }
+        }
+        previous_path_id = path_id;
+        path_ids[path] = path_id;
+      }
+
+      // Add a content row referring to |previous_path_id|.
+      tables::ExperimentalProtoContentTable::Row content_row;
+      content_row.path =
+          context_->storage->InternString(base::StringView(path_string));
+      content_row.path_id = *previous_path_id;
+      content_row.total_size = static_cast<int64_t>(sample.value());
+      content_row.size = static_cast<int64_t>(sample.value());
+      context_->storage->mutable_experimental_proto_content_table()->Insert(
+          content_row);
+    }
+  }
+  aggregated_samples_.Clear();
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/content_analyzer.h b/src/trace_processor/importers/proto/content_analyzer.h
new file mode 100644
index 0000000..27ccded
--- /dev/null
+++ b/src/trace_processor/importers/proto/content_analyzer.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 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_CONTENT_ANALYZER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_CONTENT_ANALYZER_H_
+
+#include <utility>
+#include <vector>
+
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
+#include "src/trace_processor/importers/proto/packet_analyzer.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/proto_profiler.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Interface for a module that processes track event information.
+class ProtoContentAnalyzer : public PacketAnalyzer {
+ public:
+  using PathToSamplesMap =
+      base::FlatHashMap<util::SizeProfileComputer::FieldPath,
+                        size_t,
+                        util::SizeProfileComputer::FieldPathHasher>;
+  using SampleAnnotation = PacketAnalyzer::SampleAnnotation;
+
+  struct SampleAnnotationHasher {
+    using argument_type = SampleAnnotation;
+    using result_type = size_t;
+
+    result_type operator()(const argument_type& p) const {
+      base::Hasher hash;
+      for (auto v : p) {
+        hash.Update(v.first.raw_id());
+        hash.Update(v.second.raw_id());
+      }
+      return static_cast<size_t>(hash.digest());
+    }
+  };
+  using AnnotatedSamplesMap = base::
+      FlatHashMap<SampleAnnotation, PathToSamplesMap, SampleAnnotationHasher>;
+
+  ProtoContentAnalyzer(TraceProcessorContext* context);
+  ~ProtoContentAnalyzer() override;
+
+  void ProcessPacket(const TraceBlobView& packet,
+                     const SampleAnnotation& annotation) override;
+
+  void NotifyEndOfFile() override;
+
+ private:
+  TraceProcessorContext* context_;
+  DescriptorPool pool_;
+  util::SizeProfileComputer computer_;
+  AnnotatedSamplesMap aggregated_samples_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_CONTENT_ANALYZER_H_
diff --git a/src/trace_processor/importers/proto/default_modules.cc b/src/trace_processor/importers/proto/default_modules.cc
new file mode 100644
index 0000000..c24b0f0
--- /dev/null
+++ b/src/trace_processor/importers/proto/default_modules.cc
@@ -0,0 +1,46 @@
+/*
+ * 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/default_modules.h"
+#include "src/trace_processor/importers/ftrace/ftrace_module.h"
+#include "src/trace_processor/importers/proto/chrome_system_probes_module.h"
+#include "src/trace_processor/importers/proto/memory_tracker_snapshot_module.h"
+#include "src/trace_processor/importers/proto/metadata_minimal_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->track_module =
+      static_cast<TrackEventModule*>(context->modules.back().get());
+
+  context->modules.emplace_back(new MemoryTrackerSnapshotModule(context));
+  context->modules.emplace_back(new ChromeSystemProbesModule(context));
+  context->modules.emplace_back(new ProfileModule(context));
+  context->modules.emplace_back(new MetadataMinimalModule(context));
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/default_modules.h b/src/trace_processor/importers/proto/default_modules.h
new file mode 100644
index 0000000..8428b44
--- /dev/null
+++ b/src/trace_processor/importers/proto/default_modules.h
@@ -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.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_DEFAULT_MODULES_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_DEFAULT_MODULES_H_
+
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+void RegisterDefaultModules(TraceProcessorContext*);
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_DEFAULT_MODULES_H_
diff --git a/src/trace_processor/importers/proto/frame_timeline_event_parser.h b/src/trace_processor/importers/proto/frame_timeline_event_parser.h
index c4e3ee8..9c4e75e 100644
--- a/src/trace_processor/importers/proto/frame_timeline_event_parser.h
+++ b/src/trace_processor/importers/proto/frame_timeline_event_parser.h
@@ -19,7 +19,7 @@
 
 #include "perfetto/protozero/field.h"
 #include "src/trace_processor/importers/common/args_tracker.h"
-#include "src/trace_processor/importers/proto/async_track_set_tracker.h"
+#include "src/trace_processor/importers/common/async_track_set_tracker.h"
 #include "src/trace_processor/importers/proto/proto_incremental_state.h"
 #include "src/trace_processor/storage/trace_storage.h"
 
diff --git a/src/trace_processor/importers/proto/gpu_event_parser.cc b/src/trace_processor/importers/proto/gpu_event_parser.cc
index c771525..377b5eb 100644
--- a/src/trace_processor/importers/proto/gpu_event_parser.cc
+++ b/src/trace_processor/importers/proto/gpu_event_parser.cc
@@ -198,17 +198,7 @@
       // Check missing counter_id
       if (gpu_counter_track_ids_.find(counter_id) ==
           gpu_counter_track_ids_.end()) {
-        char buffer[64];
-        base::StringWriter writer(buffer, sizeof(buffer));
-        writer.AppendString("gpu_counter(");
-        writer.AppendUnsignedInt(counter_id);
-        writer.AppendString(")");
-        auto name_id = context_->storage->InternString(writer.GetStringView());
-
-        TrackId track = context_->track_tracker->CreateGpuCounterTrack(
-            name_id, 0 /* gpu_id */);
-        gpu_counter_track_ids_.emplace(counter_id, track);
-        context_->storage->IncrementStats(stats::gpu_counters_missing_spec);
+        continue;
       }
       double counter_val = counter.has_int_value()
                                ? static_cast<double>(counter.int_value())
diff --git a/src/trace_processor/importers/proto/graphics_event_module.cc b/src/trace_processor/importers/proto/graphics_event_module.cc
index 46d8838..1445668 100644
--- a/src/trace_processor/importers/proto/graphics_event_module.cc
+++ b/src/trace_processor/importers/proto/graphics_event_module.cc
@@ -15,6 +15,7 @@
  */
 
 #include "src/trace_processor/importers/proto/graphics_event_module.h"
+#include "src/trace_processor/importers/common/trace_parser.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -37,40 +38,38 @@
 
 GraphicsEventModule::~GraphicsEventModule() = default;
 
-void GraphicsEventModule::ParsePacket(const TracePacket::Decoder& decoder,
-                                      const TimestampedTracePiece& ttp,
-                                      uint32_t field_id) {
+void GraphicsEventModule::ParseTracePacketData(
+    const TracePacket::Decoder& decoder,
+    int64_t ts,
+    const TracePacketData& data,
+    uint32_t field_id) {
   switch (field_id) {
     case TracePacket::kFrameTimelineEventFieldNumber:
       frame_timeline_parser_.ParseFrameTimelineEvent(
-          ttp.timestamp, decoder.frame_timeline_event());
+          ts, decoder.frame_timeline_event());
       return;
     case TracePacket::kGpuCounterEventFieldNumber:
-      parser_.ParseGpuCounterEvent(ttp.timestamp, decoder.gpu_counter_event());
+      parser_.ParseGpuCounterEvent(ts, decoder.gpu_counter_event());
       return;
     case TracePacket::kGpuRenderStageEventFieldNumber:
-      parser_.ParseGpuRenderStageEvent(ttp.timestamp,
-                                       ttp.packet_data.sequence_state.get(),
+      parser_.ParseGpuRenderStageEvent(ts, data.sequence_state.get(),
                                        decoder.gpu_render_stage_event());
       return;
     case TracePacket::kGpuLogFieldNumber:
-      parser_.ParseGpuLog(ttp.timestamp, decoder.gpu_log());
+      parser_.ParseGpuLog(ts, decoder.gpu_log());
       return;
     case TracePacket::kGraphicsFrameEventFieldNumber:
-      frame_parser_.ParseGraphicsFrameEvent(ttp.timestamp,
-                                            decoder.graphics_frame_event());
+      frame_parser_.ParseGraphicsFrameEvent(ts, decoder.graphics_frame_event());
       return;
     case TracePacket::kVulkanMemoryEventFieldNumber:
-      PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kTracePacket);
-      parser_.ParseVulkanMemoryEvent(ttp.packet_data.sequence_state.get(),
+      parser_.ParseVulkanMemoryEvent(data.sequence_state.get(),
                                      decoder.vulkan_memory_event());
       return;
     case TracePacket::kVulkanApiEventFieldNumber:
-      parser_.ParseVulkanApiEvent(ttp.timestamp, decoder.vulkan_api_event());
+      parser_.ParseVulkanApiEvent(ts, decoder.vulkan_api_event());
       return;
     case TracePacket::kGpuMemTotalEventFieldNumber:
-      parser_.ParseGpuMemTotalEvent(ttp.timestamp,
-                                    decoder.gpu_mem_total_event());
+      parser_.ParseGpuMemTotalEvent(ts, decoder.gpu_mem_total_event());
       return;
   }
 }
diff --git a/src/trace_processor/importers/proto/graphics_event_module.h b/src/trace_processor/importers/proto/graphics_event_module.h
index b550382..324cec5 100644
--- a/src/trace_processor/importers/proto/graphics_event_module.h
+++ b/src/trace_processor/importers/proto/graphics_event_module.h
@@ -17,12 +17,13 @@
 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_GRAPHICS_EVENT_MODULE_H_
 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_GRAPHICS_EVENT_MODULE_H_
 
+#include <cstdint>
 #include "perfetto/base/build_config.h"
+#include "src/trace_processor/importers/common/parser_types.h"
 #include "src/trace_processor/importers/proto/frame_timeline_event_parser.h"
 #include "src/trace_processor/importers/proto/gpu_event_parser.h"
 #include "src/trace_processor/importers/proto/graphics_frame_event_parser.h"
 #include "src/trace_processor/importers/proto/proto_importer_module.h"
-#include "src/trace_processor/timestamped_trace_piece.h"
 
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 
@@ -35,9 +36,10 @@
 
   ~GraphicsEventModule() override;
 
-  void ParsePacket(const protos::pbzero::TracePacket::Decoder&,
-                   const TimestampedTracePiece&,
-                   uint32_t field_id) override;
+  void ParseTracePacketData(const protos::pbzero::TracePacket::Decoder&,
+                            int64_t ts,
+                            const TracePacketData&,
+                            uint32_t field_id) override;
 
  private:
   GpuEventParser parser_;
diff --git a/src/trace_processor/importers/proto/heap_graph_module.cc b/src/trace_processor/importers/proto/heap_graph_module.cc
index c53306a1..0fe7506 100644
--- a/src/trace_processor/importers/proto/heap_graph_module.cc
+++ b/src/trace_processor/importers/proto/heap_graph_module.cc
@@ -16,6 +16,7 @@
 
 #include "src/trace_processor/importers/proto/heap_graph_module.h"
 
+#include "src/trace_processor/importers/common/parser_types.h"
 #include "src/trace_processor/importers/common/process_tracker.h"
 #include "src/trace_processor/importers/proto/heap_graph_tracker.h"
 #include "src/trace_processor/importers/proto/profiler_util.h"
@@ -24,6 +25,7 @@
 
 #include "protos/perfetto/trace/profiling/deobfuscation.pbzero.h"
 #include "protos/perfetto/trace/profiling/heap_graph.pbzero.h"
+#include "protos/perfetto/trace/profiling/profile_common.pbzero.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -132,13 +134,14 @@
   RegisterForField(TracePacket::kDeobfuscationMappingFieldNumber, context);
 }
 
-void HeapGraphModule::ParsePacket(
+void HeapGraphModule::ParseTracePacketData(
     const protos::pbzero::TracePacket::Decoder& decoder,
-    const TimestampedTracePiece& ttp,
+    int64_t ts,
+    const TracePacketData&,
     uint32_t field_id) {
   switch (field_id) {
     case TracePacket::kHeapGraphFieldNumber:
-      ParseHeapGraph(decoder.trusted_packet_sequence_id(), ttp.timestamp,
+      ParseHeapGraph(decoder.trusted_packet_sequence_id(), ts,
                      decoder.heap_graph());
       return;
     case TracePacket::kDeobfuscationMappingFieldNumber:
diff --git a/src/trace_processor/importers/proto/heap_graph_module.h b/src/trace_processor/importers/proto/heap_graph_module.h
index 76f4146..502eab1 100644
--- a/src/trace_processor/importers/proto/heap_graph_module.h
+++ b/src/trace_processor/importers/proto/heap_graph_module.h
@@ -18,9 +18,9 @@
 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_HEAP_GRAPH_MODULE_H_
 
 #include "perfetto/base/build_config.h"
+#include "src/trace_processor/importers/common/parser_types.h"
 #include "src/trace_processor/importers/proto/heap_graph_tracker.h"
 #include "src/trace_processor/importers/proto/proto_importer_module.h"
-#include "src/trace_processor/timestamped_trace_piece.h"
 
 #include "protos/perfetto/trace/profiling/deobfuscation.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
@@ -32,9 +32,10 @@
  public:
   explicit HeapGraphModule(TraceProcessorContext* context);
 
-  void ParsePacket(const protos::pbzero::TracePacket::Decoder& decoder,
-                   const TimestampedTracePiece& ttp,
-                   uint32_t field_id) override;
+  void ParseTracePacketData(const protos::pbzero::TracePacket_Decoder& decoder,
+                            int64_t ts,
+                            const TracePacketData&,
+                            uint32_t field_id) override;
 
   void NotifyEndOfFile() override;
 
diff --git a/src/trace_processor/importers/proto/memory_tracker_snapshot_module.cc b/src/trace_processor/importers/proto/memory_tracker_snapshot_module.cc
index cd4dde2..b428ab6 100644
--- a/src/trace_processor/importers/proto/memory_tracker_snapshot_module.cc
+++ b/src/trace_processor/importers/proto/memory_tracker_snapshot_module.cc
@@ -15,6 +15,7 @@
  */
 
 #include "src/trace_processor/importers/proto/memory_tracker_snapshot_module.h"
+#include "src/trace_processor/importers/common/parser_types.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -29,14 +30,14 @@
 
 MemoryTrackerSnapshotModule::~MemoryTrackerSnapshotModule() = default;
 
-void MemoryTrackerSnapshotModule::ParsePacket(
+void MemoryTrackerSnapshotModule::ParseTracePacketData(
     const TracePacket::Decoder& decoder,
-    const TimestampedTracePiece& ttp,
+    int64_t ts,
+    const TracePacketData&,
     uint32_t field_id) {
   switch (field_id) {
     case TracePacket::kMemoryTrackerSnapshotFieldNumber:
-      parser_.ParseMemoryTrackerSnapshot(ttp.timestamp,
-                                         decoder.memory_tracker_snapshot());
+      parser_.ParseMemoryTrackerSnapshot(ts, decoder.memory_tracker_snapshot());
       return;
   }
 }
diff --git a/src/trace_processor/importers/proto/memory_tracker_snapshot_module.h b/src/trace_processor/importers/proto/memory_tracker_snapshot_module.h
index 730b7e3..f6016b3 100644
--- a/src/trace_processor/importers/proto/memory_tracker_snapshot_module.h
+++ b/src/trace_processor/importers/proto/memory_tracker_snapshot_module.h
@@ -19,7 +19,6 @@
 
 #include "src/trace_processor/importers/proto/memory_tracker_snapshot_parser.h"
 #include "src/trace_processor/importers/proto/proto_importer_module.h"
-#include "src/trace_processor/timestamped_trace_piece.h"
 
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 
@@ -32,9 +31,10 @@
 
   ~MemoryTrackerSnapshotModule() override;
 
-  void ParsePacket(const protos::pbzero::TracePacket::Decoder&,
-                   const TimestampedTracePiece&,
-                   uint32_t field_id) override;
+  void ParseTracePacketData(const protos::pbzero::TracePacket_Decoder& decoder,
+                            int64_t ts,
+                            const TracePacketData&,
+                            uint32_t field_id) override;
 
   void NotifyEndOfFile() override;
 
diff --git a/src/trace_processor/importers/proto/metadata_minimal_module.cc b/src/trace_processor/importers/proto/metadata_minimal_module.cc
new file mode 100644
index 0000000..08ee096
--- /dev/null
+++ b/src/trace_processor/importers/proto/metadata_minimal_module.cc
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2021 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/metadata_minimal_module.h"
+
+#include "perfetto/ext/base/base64.h"
+#include "src/trace_processor/importers/proto/metadata_tracker.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+#include "protos/perfetto/trace/chrome/chrome_benchmark_metadata.pbzero.h"
+#include "protos/perfetto/trace/chrome/chrome_metadata.pbzero.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+using perfetto::protos::pbzero::TracePacket;
+
+MetadataMinimalModule::MetadataMinimalModule(TraceProcessorContext* context)
+    : context_(context) {
+  RegisterForField(TracePacket::kChromeMetadataFieldNumber, context);
+  RegisterForField(TracePacket::kChromeBenchmarkMetadataFieldNumber, context);
+}
+
+ModuleResult MetadataMinimalModule::TokenizePacket(
+    const protos::pbzero::TracePacket::Decoder& decoder,
+    TraceBlobView*,
+    int64_t,
+    PacketSequenceState*,
+    uint32_t field_id) {
+  switch (field_id) {
+    case TracePacket::kChromeMetadataFieldNumber: {
+      ParseChromeMetadataPacket(decoder.chrome_metadata());
+      return ModuleResult::Handled();
+    }
+    case TracePacket::kChromeBenchmarkMetadataFieldNumber: {
+      ParseChromeBenchmarkMetadata(decoder.chrome_benchmark_metadata());
+      return ModuleResult::Handled();
+    }
+  }
+  return ModuleResult::Ignored();
+}
+
+void MetadataMinimalModule::ParseChromeBenchmarkMetadata(ConstBytes blob) {
+  TraceStorage* storage = context_->storage.get();
+  MetadataTracker* metadata = context_->metadata_tracker.get();
+
+  protos::pbzero::ChromeBenchmarkMetadata::Decoder packet(blob.data, blob.size);
+  if (packet.has_benchmark_name()) {
+    auto benchmark_name_id = storage->InternString(packet.benchmark_name());
+    metadata->SetMetadata(metadata::benchmark_name,
+                          Variadic::String(benchmark_name_id));
+  }
+  if (packet.has_benchmark_description()) {
+    auto benchmark_description_id =
+        storage->InternString(packet.benchmark_description());
+    metadata->SetMetadata(metadata::benchmark_description,
+                          Variadic::String(benchmark_description_id));
+  }
+  if (packet.has_label()) {
+    auto label_id = storage->InternString(packet.label());
+    metadata->SetMetadata(metadata::benchmark_label,
+                          Variadic::String(label_id));
+  }
+  if (packet.has_story_name()) {
+    auto story_name_id = storage->InternString(packet.story_name());
+    metadata->SetMetadata(metadata::benchmark_story_name,
+                          Variadic::String(story_name_id));
+  }
+  for (auto it = packet.story_tags(); it; ++it) {
+    auto story_tag_id = storage->InternString(*it);
+    metadata->AppendMetadata(metadata::benchmark_story_tags,
+                             Variadic::String(story_tag_id));
+  }
+  if (packet.has_benchmark_start_time_us()) {
+    metadata->SetMetadata(metadata::benchmark_start_time_us,
+                          Variadic::Integer(packet.benchmark_start_time_us()));
+  }
+  if (packet.has_story_run_time_us()) {
+    metadata->SetMetadata(metadata::benchmark_story_run_time_us,
+                          Variadic::Integer(packet.story_run_time_us()));
+  }
+  if (packet.has_story_run_index()) {
+    metadata->SetMetadata(metadata::benchmark_story_run_index,
+                          Variadic::Integer(packet.story_run_index()));
+  }
+  if (packet.has_had_failures()) {
+    metadata->SetMetadata(metadata::benchmark_had_failures,
+                          Variadic::Integer(packet.had_failures()));
+  }
+}
+
+void MetadataMinimalModule::ParseChromeMetadataPacket(ConstBytes blob) {
+  TraceStorage* storage = context_->storage.get();
+  MetadataTracker* metadata = context_->metadata_tracker.get();
+
+  // Typed chrome metadata proto. The untyped metadata is parsed below in
+  // ParseChromeEvents().
+  protos::pbzero::ChromeMetadataPacket::Decoder packet(blob.data, blob.size);
+
+  if (packet.has_background_tracing_metadata()) {
+    auto background_tracing_metadata = packet.background_tracing_metadata();
+    std::string base64 = base::Base64Encode(background_tracing_metadata.data,
+                                            background_tracing_metadata.size);
+    metadata->SetDynamicMetadata(
+        storage->InternString("cr-background_tracing_metadata"),
+        Variadic::String(storage->InternString(base::StringView(base64))));
+  }
+
+  if (packet.has_chrome_version_code()) {
+    metadata->SetDynamicMetadata(
+        storage->InternString("cr-playstore_version_code"),
+        Variadic::Integer(packet.chrome_version_code()));
+  }
+  if (packet.has_enabled_categories()) {
+    auto categories_id = storage->InternString(packet.enabled_categories());
+    metadata->SetDynamicMetadata(storage->InternString("cr-enabled_categories"),
+                                 Variadic::String(categories_id));
+  }
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/metadata_minimal_module.h b/src/trace_processor/importers/proto/metadata_minimal_module.h
new file mode 100644
index 0000000..b12eed9
--- /dev/null
+++ b/src/trace_processor/importers/proto/metadata_minimal_module.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 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_METADATA_MINIMAL_MODULE_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_METADATA_MINIMAL_MODULE_H_
+
+#include "src/trace_processor/importers/common/trace_parser.h"
+#include "src/trace_processor/importers/proto/proto_importer_module.h"
+
+#include "src/trace_processor/storage/trace_storage.h"
+
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class MetadataMinimalModule : public ProtoImporterModule {
+ public:
+  using ConstBytes = protozero::ConstBytes;
+  explicit MetadataMinimalModule(TraceProcessorContext* context);
+
+  ModuleResult TokenizePacket(
+      const protos::pbzero::TracePacket::Decoder& decoder,
+      TraceBlobView* packet,
+      int64_t packet_timestamp,
+      PacketSequenceState* state,
+      uint32_t field_id) override;
+
+ private:
+  void ParseChromeBenchmarkMetadata(ConstBytes);
+  void ParseChromeMetadataPacket(ConstBytes);
+
+  TraceProcessorContext* context_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_METADATA_MINIMAL_MODULE_H_
diff --git a/src/trace_processor/importers/proto/metadata_module.cc b/src/trace_processor/importers/proto/metadata_module.cc
index 3388b28..81a4ce6 100644
--- a/src/trace_processor/importers/proto/metadata_module.cc
+++ b/src/trace_processor/importers/proto/metadata_module.cc
@@ -17,12 +17,17 @@
 #include "src/trace_processor/importers/proto/metadata_module.h"
 
 #include "perfetto/ext/base/base64.h"
+#include "perfetto/ext/base/uuid.h"
 #include "src/trace_processor/importers/common/slice_tracker.h"
 #include "src/trace_processor/importers/common/track_tracker.h"
+#include "src/trace_processor/importers/proto/config.descriptor.h"
 #include "src/trace_processor/importers/proto/metadata_tracker.h"
+#include "src/trace_processor/util/descriptors.h"
+#include "src/trace_processor/util/protozero_to_text.h"
 
-#include "protos/perfetto/trace/chrome/chrome_benchmark_metadata.pbzero.h"
-#include "protos/perfetto/trace/chrome/chrome_metadata.pbzero.h"
+#include "protos/perfetto/config/trace_config.pbzero.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "protos/perfetto/trace/trace_uuid.pbzero.h"
 #include "protos/perfetto/trace/trigger.pbzero.h"
 
 namespace perfetto {
@@ -36,9 +41,8 @@
       trusted_producer_uid_key_id_(
           context_->storage->InternString("trusted_producer_uid")) {
   RegisterForField(TracePacket::kUiStateFieldNumber, context);
-  RegisterForField(TracePacket::kChromeMetadataFieldNumber, context);
-  RegisterForField(TracePacket::kChromeBenchmarkMetadataFieldNumber, context);
   RegisterForField(TracePacket::kTriggerFieldNumber, context);
+  RegisterForField(TracePacket::kTraceUuidFieldNumber, context);
 }
 
 ModuleResult MetadataModule::TokenizePacket(
@@ -56,110 +60,37 @@
                                               Variadic::String(id));
       return ModuleResult::Handled();
     }
-    case TracePacket::kChromeMetadataFieldNumber: {
-      ParseChromeMetadataPacket(decoder.chrome_metadata());
-      // Metadata packets may also contain untyped metadata due to a bug in
-      // Chrome <M92.
-      // TODO(crbug.com/1194914): Replace this with Handled() once the
-      // Chrome-side fix has propagated into all release channels.
-      return ModuleResult::Ignored();
-    }
-    case TracePacket::kChromeBenchmarkMetadataFieldNumber: {
-      ParseChromeBenchmarkMetadata(decoder.chrome_benchmark_metadata());
+    case TracePacket::kTraceUuidFieldNumber: {
+      // If both the TraceUuid packet and TraceConfig.trace_uuid_msb/lsb are
+      // set, the former (which is emitted first) takes precedence. This is
+      // because the UUID can change throughout the lifecycle of a tracing
+      // session if gap-less snapshots are used. Each trace file has at most one
+      // TraceUuid packet (i has if it comes from an older version of the
+      // tracing service < v32)
+      protos::pbzero::TraceUuid::Decoder uuid_packet(decoder.trace_uuid());
+      if (uuid_packet.msb() != 0 || uuid_packet.lsb() != 0) {
+        base::Uuid uuid(uuid_packet.lsb(), uuid_packet.msb());
+        std::string str = uuid.ToPrettyString();
+        StringId id = context_->storage->InternString(base::StringView(str));
+        context_->metadata_tracker->SetMetadata(metadata::trace_uuid,
+                                                Variadic::String(id));
+        context_->uuid_found_in_trace = true;
+      }
       return ModuleResult::Handled();
     }
   }
   return ModuleResult::Ignored();
 }
 
-void MetadataModule::ParsePacket(
+void MetadataModule::ParseTracePacketData(
     const protos::pbzero::TracePacket::Decoder& decoder,
-    const TimestampedTracePiece& ttp,
+    int64_t ts,
+    const TracePacketData&,
     uint32_t field_id) {
-  switch (field_id) {
-    case TracePacket::kTriggerFieldNumber:
-      // We handle triggers at parse time rather at tokenization because
-      // we add slices to tables which need to happen post-sorting.
-      ParseTrigger(ttp.timestamp, decoder.trigger());
-      break;
-  }
-}
-
-void MetadataModule::ParseChromeBenchmarkMetadata(ConstBytes blob) {
-  TraceStorage* storage = context_->storage.get();
-  MetadataTracker* metadata = context_->metadata_tracker.get();
-
-  protos::pbzero::ChromeBenchmarkMetadata::Decoder packet(blob.data, blob.size);
-  if (packet.has_benchmark_name()) {
-    auto benchmark_name_id = storage->InternString(packet.benchmark_name());
-    metadata->SetMetadata(metadata::benchmark_name,
-                          Variadic::String(benchmark_name_id));
-  }
-  if (packet.has_benchmark_description()) {
-    auto benchmark_description_id =
-        storage->InternString(packet.benchmark_description());
-    metadata->SetMetadata(metadata::benchmark_description,
-                          Variadic::String(benchmark_description_id));
-  }
-  if (packet.has_label()) {
-    auto label_id = storage->InternString(packet.label());
-    metadata->SetMetadata(metadata::benchmark_label,
-                          Variadic::String(label_id));
-  }
-  if (packet.has_story_name()) {
-    auto story_name_id = storage->InternString(packet.story_name());
-    metadata->SetMetadata(metadata::benchmark_story_name,
-                          Variadic::String(story_name_id));
-  }
-  for (auto it = packet.story_tags(); it; ++it) {
-    auto story_tag_id = storage->InternString(*it);
-    metadata->AppendMetadata(metadata::benchmark_story_tags,
-                             Variadic::String(story_tag_id));
-  }
-  if (packet.has_benchmark_start_time_us()) {
-    metadata->SetMetadata(metadata::benchmark_start_time_us,
-                          Variadic::Integer(packet.benchmark_start_time_us()));
-  }
-  if (packet.has_story_run_time_us()) {
-    metadata->SetMetadata(metadata::benchmark_story_run_time_us,
-                          Variadic::Integer(packet.story_run_time_us()));
-  }
-  if (packet.has_story_run_index()) {
-    metadata->SetMetadata(metadata::benchmark_story_run_index,
-                          Variadic::Integer(packet.story_run_index()));
-  }
-  if (packet.has_had_failures()) {
-    metadata->SetMetadata(metadata::benchmark_had_failures,
-                          Variadic::Integer(packet.had_failures()));
-  }
-}
-
-void MetadataModule::ParseChromeMetadataPacket(ConstBytes blob) {
-  TraceStorage* storage = context_->storage.get();
-  MetadataTracker* metadata = context_->metadata_tracker.get();
-
-  // Typed chrome metadata proto. The untyped metadata is parsed below in
-  // ParseChromeEvents().
-  protos::pbzero::ChromeMetadataPacket::Decoder packet(blob.data, blob.size);
-
-  if (packet.has_background_tracing_metadata()) {
-    auto background_tracing_metadata = packet.background_tracing_metadata();
-    std::string base64 = base::Base64Encode(background_tracing_metadata.data,
-                                            background_tracing_metadata.size);
-    metadata->SetDynamicMetadata(
-        storage->InternString("cr-background_tracing_metadata"),
-        Variadic::String(storage->InternString(base::StringView(base64))));
-  }
-
-  if (packet.has_chrome_version_code()) {
-    metadata->SetDynamicMetadata(
-        storage->InternString("cr-playstore_version_code"),
-        Variadic::Integer(packet.chrome_version_code()));
-  }
-  if (packet.has_enabled_categories()) {
-    auto categories_id = storage->InternString(packet.enabled_categories());
-    metadata->SetDynamicMetadata(storage->InternString("cr-enabled_categories"),
-                                 Variadic::String(categories_id));
+  if (field_id == TracePacket::kTriggerFieldNumber) {
+    // We handle triggers at parse time rather at tokenization because
+    // we add slices to tables which need to happen post-sorting.
+    ParseTrigger(ts, decoder.trigger());
   }
 }
 
@@ -185,5 +116,57 @@
       });
 }
 
+void MetadataModule::ParseTraceUuid(ConstBytes blob) {
+  // If both the TraceUuid packet and TraceConfig.trace_uuid_msb/lsb are set,
+  // the former (which is emitted first) takes precedence. This is because the
+  // UUID can change throughout the lifecycle of a tracing session if gap-less
+  // snapshots are used. Each trace file has at most one TraceUuid packet (i
+  // has if it comes from an older version of the tracing service < v32)
+  protos::pbzero::TraceUuid::Decoder uuid_packet(blob.data, blob.size);
+  if (uuid_packet.msb() != 0 || uuid_packet.lsb() != 0) {
+    base::Uuid uuid(uuid_packet.lsb(), uuid_packet.msb());
+    std::string str = uuid.ToPrettyString();
+    StringId id = context_->storage->InternString(base::StringView(str));
+    context_->metadata_tracker->SetMetadata(metadata::trace_uuid,
+                                            Variadic::String(id));
+    context_->uuid_found_in_trace = true;
+  }
+}
+
+void MetadataModule::ParseTraceConfig(
+    const protos::pbzero::TraceConfig_Decoder& trace_config) {
+  int64_t uuid_msb = trace_config.trace_uuid_msb();
+  int64_t uuid_lsb = trace_config.trace_uuid_lsb();
+  if (!context_->uuid_found_in_trace && (uuid_msb != 0 || uuid_lsb != 0)) {
+    base::Uuid uuid(uuid_lsb, uuid_msb);
+    std::string str = uuid.ToPrettyString();
+    StringId id = context_->storage->InternString(base::StringView(str));
+    context_->metadata_tracker->SetMetadata(metadata::trace_uuid,
+                                            Variadic::String(id));
+    context_->uuid_found_in_trace = true;
+  }
+
+  if (trace_config.has_unique_session_name()) {
+    StringId id = context_->storage->InternString(
+        base::StringView(trace_config.unique_session_name()));
+    context_->metadata_tracker->SetMetadata(metadata::unique_session_name,
+                                            Variadic::String(id));
+  }
+
+  DescriptorPool pool;
+  pool.AddFromFileDescriptorSet(kConfigDescriptor.data(),
+                                kConfigDescriptor.size());
+
+  std::string text = protozero_to_text::ProtozeroToText(
+      pool, ".perfetto.protos.TraceConfig",
+      protozero::ConstBytes{
+          trace_config.begin(),
+          static_cast<uint32_t>(trace_config.end() - trace_config.begin())},
+      protozero_to_text::kIncludeNewLines);
+  StringId id = context_->storage->InternString(base::StringView(text));
+  context_->metadata_tracker->SetMetadata(metadata::trace_config_pbtxt,
+                                          Variadic::String(id));
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/metadata_module.h b/src/trace_processor/importers/proto/metadata_module.h
index 38a6a56..49e0384 100644
--- a/src/trace_processor/importers/proto/metadata_module.h
+++ b/src/trace_processor/importers/proto/metadata_module.h
@@ -17,12 +17,11 @@
 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_METADATA_MODULE_H_
 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_METADATA_MODULE_H_
 
-#include "perfetto/base/build_config.h"
+#include "src/trace_processor/importers/common/trace_parser.h"
 #include "src/trace_processor/importers/proto/proto_importer_module.h"
-#include "src/trace_processor/timestamped_trace_piece.h"
 
-#include "protos/perfetto/trace/profiling/deobfuscation.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "src/trace_processor/storage/trace_storage.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -39,14 +38,16 @@
       PacketSequenceState* state,
       uint32_t field_id) override;
 
-  void ParsePacket(const protos::pbzero::TracePacket::Decoder& decoder,
-                   const TimestampedTracePiece& ttp,
-                   uint32_t field_id) override;
+  void ParseTracePacketData(const protos::pbzero::TracePacket::Decoder& decoder,
+                            int64_t ts,
+                            const TracePacketData&,
+                            uint32_t field_id) override;
+
+  void ParseTraceConfig(const protos::pbzero::TraceConfig_Decoder&) override;
 
  private:
-  void ParseChromeBenchmarkMetadata(ConstBytes);
-  void ParseChromeMetadataPacket(ConstBytes);
   void ParseTrigger(int64_t ts, ConstBytes);
+  void ParseTraceUuid(ConstBytes);
 
   TraceProcessorContext* context_;
   StringId producer_name_key_id_ = kNullStringId;
diff --git a/src/trace_processor/importers/proto/packet_analyzer.cc b/src/trace_processor/importers/proto/packet_analyzer.cc
new file mode 100644
index 0000000..d3ad6b9
--- /dev/null
+++ b/src/trace_processor/importers/proto/packet_analyzer.cc
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 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/packet_analyzer.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+PacketAnalyzer::~PacketAnalyzer() = default;
+
+}
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/packet_analyzer.h b/src/trace_processor/importers/proto/packet_analyzer.h
new file mode 100644
index 0000000..b60412c
--- /dev/null
+++ b/src/trace_processor/importers/proto/packet_analyzer.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 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_PACKET_ANALYZER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PACKET_ANALYZER_H_
+
+#include <utility>
+#include <vector>
+
+#include "perfetto/trace_processor/trace_blob_view.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 {
+
+// Interface for processing packet information.
+class PacketAnalyzer : public Destructible {
+ public:
+  using SampleAnnotation = std::vector<std::pair<StringId, StringId>>;
+
+  PacketAnalyzer() = default;
+  ~PacketAnalyzer() override;
+
+  static PacketAnalyzer* Get(TraceProcessorContext* context) {
+    if (!context->content_analyzer)
+      return nullptr;
+    return static_cast<PacketAnalyzer*>(context->content_analyzer.get());
+  }
+
+  virtual void ProcessPacket(const TraceBlobView& packet,
+                             const SampleAnnotation& packet_annotation) = 0;
+
+  virtual void NotifyEndOfFile() = 0;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PACKET_ANALYZER_H_
diff --git a/src/trace_processor/importers/proto/packet_sequence_state.cc b/src/trace_processor/importers/proto/packet_sequence_state.cc
deleted file mode 100644
index 2eb2350..0000000
--- a/src/trace_processor/importers/proto/packet_sequence_state.cc
+++ /dev/null
@@ -1,71 +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/importers/proto/packet_sequence_state.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-void PacketSequenceStateGeneration::InternMessage(uint32_t field_id,
-                                                  TraceBlobView message) {
-  constexpr auto kIidFieldNumber = 1;
-
-  uint64_t iid = 0;
-  auto message_start = message.data();
-  auto message_size = message.length();
-  protozero::ProtoDecoder decoder(message_start, message_size);
-
-  auto field = decoder.FindField(kIidFieldNumber);
-  if (PERFETTO_UNLIKELY(!field)) {
-    PERFETTO_DLOG("Interned message without interning_id");
-    state_->context()->storage->IncrementStats(
-        stats::interned_data_tokenizer_errors);
-    return;
-  }
-  iid = field.as_uint64();
-
-  auto res = interned_data_[field_id].emplace(
-      iid, InternedMessageView(std::move(message)));
-
-  // If a message with this ID is already interned in the same generation,
-  // its data should not have changed (this is forbidden by the InternedData
-  // proto).
-  // TODO(eseckler): This DCHECK assumes that the message is encoded the
-  // same way if it is re-emitted.
-  PERFETTO_DCHECK(res.second ||
-                  (res.first->second.message().length() == message_size &&
-                   memcmp(res.first->second.message().data(), message_start,
-                          message_size) == 0));
-}
-
-InternedMessageView* PacketSequenceStateGeneration::GetInternedMessageView(
-    uint32_t field_id,
-    uint64_t iid) {
-  auto field_it = interned_data_.find(field_id);
-  if (field_it != interned_data_.end()) {
-    auto* message_map = &field_it->second;
-    auto it = message_map->find(iid);
-    if (it != message_map->end()) {
-      return &it->second;
-    }
-  }
-  state_->context()->storage->IncrementStats(
-      stats::interned_data_tokenizer_errors);
-  return nullptr;
-}
-
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/packet_sequence_state.h b/src/trace_processor/importers/proto/packet_sequence_state.h
index d985572..d95fccc 100644
--- a/src/trace_processor/importers/proto/packet_sequence_state.h
+++ b/src/trace_processor/importers/proto/packet_sequence_state.h
@@ -23,100 +23,15 @@
 #include <vector>
 
 #include "perfetto/base/compiler.h"
-#include "perfetto/protozero/proto_decoder.h"
-#include "perfetto/trace_processor/ref_counted.h"
-#include "perfetto/trace_processor/trace_blob_view.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
 #include "src/trace_processor/importers/proto/stack_profile_tracker.h"
 #include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/types/trace_processor_context.h"
 #include "src/trace_processor/util/interned_message_view.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 {
 
-using InternedMessageMap =
-    std::unordered_map<uint64_t /*iid*/, InternedMessageView>;
-using InternedFieldMap =
-    std::unordered_map<uint32_t /*field_id*/, InternedMessageMap>;
-
-class PacketSequenceState;
-
-class PacketSequenceStateGeneration : public RefCounted {
- public:
-  // Returns |nullptr| if the message with the given |iid| was not found (also
-  // records a stat in this case).
-  template <uint32_t FieldId, typename MessageType>
-  typename MessageType::Decoder* LookupInternedMessage(uint64_t iid);
-
-  InternedMessageView* GetInternedMessageView(uint32_t field_id, uint64_t iid);
-  // 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.
-  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_; }
-  size_t generation_index() const { return generation_index_; }
-
- private:
-  friend class PacketSequenceState;
-
-  PacketSequenceStateGeneration(PacketSequenceState* state,
-                                size_t generation_index)
-      : state_(state), generation_index_(generation_index) {}
-
-  PacketSequenceStateGeneration(PacketSequenceState* state,
-                                size_t generation_index,
-                                InternedFieldMap interned_data,
-                                TraceBlobView defaults)
-      : state_(state),
-        generation_index_(generation_index),
-        interned_data_(interned_data),
-        trace_packet_defaults_(InternedMessageView(std::move(defaults))) {}
-
-  void InternMessage(uint32_t field_id, TraceBlobView message);
-
-  void SetTracePacketDefaults(TraceBlobView defaults) {
-    // Defaults should only be set once per generation.
-    PERFETTO_DCHECK(!trace_packet_defaults_);
-    trace_packet_defaults_ = InternedMessageView(std::move(defaults));
-  }
-
-  PacketSequenceState* state_;
-  size_t generation_index_;
-  InternedFieldMap interned_data_;
-  base::Optional<InternedMessageView> trace_packet_defaults_;
-};
-
 class PacketSequenceState {
  public:
   explicit PacketSequenceState(TraceProcessorContext* context)
diff --git a/src/trace_processor/importers/proto/packet_sequence_state_generation.cc b/src/trace_processor/importers/proto/packet_sequence_state_generation.cc
new file mode 100644
index 0000000..6e8e98d
--- /dev/null
+++ b/src/trace_processor/importers/proto/packet_sequence_state_generation.cc
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/importers/proto/packet_sequence_state_generation.h"
+
+#include "src/trace_processor/importers/proto/packet_sequence_state.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+void PacketSequenceStateGeneration::InternMessage(uint32_t field_id,
+                                                  TraceBlobView message) {
+  constexpr auto kIidFieldNumber = 1;
+
+  uint64_t iid = 0;
+  auto message_start = message.data();
+  auto message_size = message.length();
+  protozero::ProtoDecoder decoder(message_start, message_size);
+
+  auto field = decoder.FindField(kIidFieldNumber);
+  if (PERFETTO_UNLIKELY(!field)) {
+    PERFETTO_DLOG("Interned message without interning_id");
+    state_->context()->storage->IncrementStats(
+        stats::interned_data_tokenizer_errors);
+    return;
+  }
+  iid = field.as_uint64();
+
+  auto res = interned_data_[field_id].emplace(
+      iid, InternedMessageView(std::move(message)));
+
+  // If a message with this ID is already interned in the same generation,
+  // its data should not have changed (this is forbidden by the InternedData
+  // proto).
+  // TODO(eseckler): This DCHECK assumes that the message is encoded the
+  // same way if it is re-emitted.
+  PERFETTO_DCHECK(res.second ||
+                  (res.first->second.message().length() == message_size &&
+                   memcmp(res.first->second.message().data(), message_start,
+                          message_size) == 0));
+}
+
+InternedMessageView* PacketSequenceStateGeneration::GetInternedMessageView(
+    uint32_t field_id,
+    uint64_t iid) {
+  auto field_it = interned_data_.find(field_id);
+  if (field_it != interned_data_.end()) {
+    auto* message_map = &field_it->second;
+    auto it = message_map->find(iid);
+    if (it != message_map->end()) {
+      return &it->second;
+    }
+  }
+  state_->context()->storage->IncrementStats(
+      stats::interned_data_tokenizer_errors);
+  return nullptr;
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/packet_sequence_state_generation.h b/src/trace_processor/importers/proto/packet_sequence_state_generation.h
new file mode 100644
index 0000000..c3dda05
--- /dev/null
+++ b/src/trace_processor/importers/proto/packet_sequence_state_generation.h
@@ -0,0 +1,115 @@
+/*
+ * 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_PACKET_SEQUENCE_STATE_GENERATION_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PACKET_SEQUENCE_STATE_GENERATION_H_
+
+#include <unordered_map>
+
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/trace_processor/ref_counted.h"
+#include "src/trace_processor/util/interned_message_view.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 {
+
+using InternedMessageMap =
+    std::unordered_map<uint64_t /*iid*/, InternedMessageView>;
+using InternedFieldMap =
+    std::unordered_map<uint32_t /*field_id*/, InternedMessageMap>;
+
+class PacketSequenceState;
+
+class PacketSequenceStateGeneration : public RefCounted {
+ public:
+  // Returns |nullptr| if the message with the given |iid| was not found (also
+  // records a stat in this case).
+  template <uint32_t FieldId, typename MessageType>
+  typename MessageType::Decoder* LookupInternedMessage(uint64_t iid);
+
+  InternedMessageView* GetInternedMessageView(uint32_t field_id, uint64_t iid);
+  // 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.
+  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_; }
+  size_t generation_index() const { return generation_index_; }
+
+ private:
+  friend class PacketSequenceState;
+
+  PacketSequenceStateGeneration(PacketSequenceState* state,
+                                size_t generation_index)
+      : state_(state), generation_index_(generation_index) {}
+
+  PacketSequenceStateGeneration(PacketSequenceState* state,
+                                size_t generation_index,
+                                InternedFieldMap interned_data,
+                                TraceBlobView defaults)
+      : state_(state),
+        generation_index_(generation_index),
+        interned_data_(interned_data),
+        trace_packet_defaults_(InternedMessageView(std::move(defaults))) {}
+
+  void InternMessage(uint32_t field_id, TraceBlobView message);
+
+  void SetTracePacketDefaults(TraceBlobView defaults) {
+    // Defaults should only be set once per generation.
+    PERFETTO_DCHECK(!trace_packet_defaults_);
+    trace_packet_defaults_ = InternedMessageView(std::move(defaults));
+  }
+
+  PacketSequenceState* state_;
+  size_t generation_index_;
+  InternedFieldMap interned_data_;
+  base::Optional<InternedMessageView> trace_packet_defaults_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PACKET_SEQUENCE_STATE_GENERATION_H_
diff --git a/src/trace_processor/importers/proto/profile_module.cc b/src/trace_processor/importers/proto/profile_module.cc
index a8cf943..d5b9be3 100644
--- a/src/trace_processor/importers/proto/profile_module.cc
+++ b/src/trace_processor/importers/proto/profile_module.cc
@@ -18,9 +18,11 @@
 #include <string>
 
 #include "perfetto/base/logging.h"
+#include "perfetto/ext/base/flat_hash_map.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "src/trace_processor/importers/common/args_translation_table.h"
 #include "src/trace_processor/importers/common/clock_tracker.h"
+#include "src/trace_processor/importers/common/deobfuscation_mapping_table.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/heap_profile_tracker.h"
@@ -29,11 +31,10 @@
 #include "src/trace_processor/importers/proto/profile_packet_utils.h"
 #include "src/trace_processor/importers/proto/profiler_util.h"
 #include "src/trace_processor/importers/proto/stack_profile_tracker.h"
+#include "src/trace_processor/sorter/trace_sorter.h"
 #include "src/trace_processor/storage/stats.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 "src/trace_processor/util/stack_traces_util.h"
 
@@ -76,41 +77,34 @@
   return ModuleResult::Ignored();
 }
 
-void ProfileModule::ParsePacket(const TracePacket::Decoder& decoder,
-                                const TimestampedTracePiece& ttp,
-                                uint32_t field_id) {
+void ProfileModule::ParseTracePacketData(
+    const protos::pbzero::TracePacket::Decoder& decoder,
+    int64_t ts,
+    const TracePacketData& data,
+    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.get(),
+      ParseStreamingProfilePacket(ts, data.sequence_state.get(),
                                   decoder.streaming_profile_packet());
       return;
     case TracePacket::kPerfSampleFieldNumber:
-      PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kTracePacket);
-      ParsePerfSample(ttp.timestamp, ttp.packet_data.sequence_state.get(),
-                      decoder);
+      ParsePerfSample(ts, data.sequence_state.get(), decoder);
       return;
     case TracePacket::kProfilePacketFieldNumber:
-      PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kTracePacket);
-      ParseProfilePacket(ttp.timestamp, ttp.packet_data.sequence_state.get(),
+      ParseProfilePacket(ts, data.sequence_state.get(),
                          decoder.trusted_packet_sequence_id(),
                          decoder.profile_packet());
       return;
     case TracePacket::kModuleSymbolsFieldNumber:
-      PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kTracePacket);
       ParseModuleSymbols(decoder.module_symbols());
       return;
     case TracePacket::kDeobfuscationMappingFieldNumber:
-      PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kTracePacket);
-      ParseDeobfuscationMapping(ttp.timestamp,
-                                ttp.packet_data.sequence_state.get(),
+      ParseDeobfuscationMapping(ts, data.sequence_state.get(),
                                 decoder.trusted_packet_sequence_id(),
                                 decoder.deobfuscation_mapping());
       return;
     case TracePacket::kSmapsPacketFieldNumber:
-      PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kTracePacket);
-      ParseSmapsPacket(ttp.timestamp, decoder.smaps_packet());
+      ParseSmapsPacket(ts, decoder.smaps_packet());
       return;
   }
 }
@@ -144,8 +138,8 @@
     sequence_state->IncrementAndGetTrackEventTimeNs(*timestamp_it * 1000);
   }
 
-  context_->sorter->PushTracePacket(packet_ts, sequence_state,
-                                    std::move(*packet));
+  context_->sorter->PushTracePacket(
+      packet_ts, sequence_state->current_generation(), std::move(*packet));
   return ModuleResult::Handled();
 }
 
@@ -511,6 +505,7 @@
                                               PacketSequenceStateGeneration*,
                                               uint32_t /* seq_id */,
                                               ConstBytes blob) {
+  DeobfuscationMappingTable deobfuscation_mapping_table;
   protos::pbzero::DeobfuscationMapping::Decoder deobfuscation_mapping(
       blob.data, blob.size);
   if (deobfuscation_mapping.package_name().size == 0)
@@ -525,6 +520,7 @@
   for (auto class_it = deobfuscation_mapping.obfuscated_classes(); class_it;
        ++class_it) {
     protos::pbzero::ObfuscatedClass::Decoder cls(*class_it);
+    base::FlatHashMap<StringId, StringId> obfuscated_to_deobfuscated_members;
     for (auto member_it = cls.obfuscated_methods(); member_it; ++member_it) {
       protos::pbzero::ObfuscatedMember::Decoder member(*member_it);
       std::string merged_obfuscated = cls.obfuscated_name().ToStdString() +
@@ -564,8 +560,21 @@
             context_->storage->InternString(
                 base::StringView(merged_deobfuscated)));
       }
+      obfuscated_to_deobfuscated_members[context_->storage->InternString(
+          member.obfuscated_name())] =
+          context_->storage->InternString(member.deobfuscated_name());
     }
+    // Members can contain a class name (e.g "ClassA.FunctionF")
+    deobfuscation_mapping_table.AddClassTranslation(
+        DeobfuscationMappingTable::PackageId{
+            deobfuscation_mapping.package_name().ToStdString(),
+            deobfuscation_mapping.version_code()},
+        context_->storage->InternString(cls.obfuscated_name()),
+        context_->storage->InternString(cls.deobfuscated_name()),
+        std::move(obfuscated_to_deobfuscated_members));
   }
+  context_->args_translation_table->AddDeobfuscationMappingTable(
+      std::move(deobfuscation_mapping_table));
 }
 
 void ProfileModule::ParseSmapsPacket(int64_t ts, ConstBytes blob) {
diff --git a/src/trace_processor/importers/proto/profile_module.h b/src/trace_processor/importers/proto/profile_module.h
index acc2794..e6674d7 100644
--- a/src/trace_processor/importers/proto/profile_module.h
+++ b/src/trace_processor/importers/proto/profile_module.h
@@ -40,9 +40,10 @@
       PacketSequenceState* state,
       uint32_t field_id) override;
 
-  void ParsePacket(const protos::pbzero::TracePacket::Decoder& decoder,
-                   const TimestampedTracePiece& ttp,
-                   uint32_t field_id) override;
+  void ParseTracePacketData(const protos::pbzero::TracePacket::Decoder& decoder,
+                            int64_t ts,
+                            const TracePacketData& data,
+                            uint32_t field_id) override;
 
   void NotifyEndOfFile() override;
 
diff --git a/src/trace_processor/importers/proto/profile_packet_utils.h b/src/trace_processor/importers/proto/profile_packet_utils.h
index 08c8a46..683348c 100644
--- a/src/trace_processor/importers/proto/profile_packet_utils.h
+++ b/src/trace_processor/importers/proto/profile_packet_utils.h
@@ -119,6 +119,8 @@
         return "maps_parse";
       case Profiling::UNWIND_ERROR_INVALID_PARAMETER:
         return "invalid_parameter";
+      case Profiling::UNWIND_ERROR_PTRACE_CALL:
+        return "ptrace_call";
     }
     return "unknown";  // switch should be complete, but gcc needs a hint
   }
diff --git a/src/trace_processor/importers/proto/proto_importer_module.cc b/src/trace_processor/importers/proto/proto_importer_module.cc
index c171360..c79c05d 100644
--- a/src/trace_processor/importers/proto/proto_importer_module.cc
+++ b/src/trace_processor/importers/proto/proto_importer_module.cc
@@ -16,7 +16,6 @@
 
 #include "src/trace_processor/importers/proto/proto_importer_module.h"
 
-#include "src/trace_processor/timestamped_trace_piece.h"
 #include "src/trace_processor/types/trace_processor_context.h"
 
 namespace perfetto {
@@ -35,9 +34,10 @@
   return ModuleResult::Ignored();
 }
 
-void ProtoImporterModule::ParsePacket(
+void ProtoImporterModule::ParseTracePacketData(
     const protos::pbzero::TracePacket_Decoder&,
-    const TimestampedTracePiece&,
+    int64_t /*ts*/,
+    const TracePacketData&,
     uint32_t /*field_id*/) {}
 
 void ProtoImporterModule::ParseTraceConfig(
@@ -51,5 +51,9 @@
   context->modules_by_field[field_id].push_back(this);
 }
 
+void ProtoImporterModule::RegisterForAllFields(TraceProcessorContext* context) {
+  context->modules_for_all_fields.push_back(this);
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/proto_importer_module.h b/src/trace_processor/importers/proto/proto_importer_module.h
index c56f034..3ff56aa 100644
--- a/src/trace_processor/importers/proto/proto_importer_module.h
+++ b/src/trace_processor/importers/proto/proto_importer_module.h
@@ -17,8 +17,9 @@
 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROTO_IMPORTER_MODULE_H_
 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_PROTO_IMPORTER_MODULE_H_
 
+#include "perfetto/base/status.h"
 #include "perfetto/ext/base/optional.h"
-#include "perfetto/trace_processor/status.h"
+#include "src/trace_processor/importers/common/trace_parser.h"
 
 namespace perfetto {
 
@@ -32,7 +33,6 @@
 namespace trace_processor {
 
 class PacketSequenceState;
-struct TimestampedTracePiece;
 class TraceBlobView;
 class TraceProcessorContext;
 
@@ -52,7 +52,7 @@
 class ModuleResult {
  public:
   // Allow auto conversion from util::Status to Handled / Error result.
-  ModuleResult(util::Status status)
+  ModuleResult(base::Status status)
       : ignored_(false),
         error_(status.ok() ? base::nullopt
                            : base::make_optional(status.message())) {}
@@ -75,11 +75,11 @@
   bool ok() const { return !error_.has_value(); }
   const std::string& message() const { return *error_; }
 
-  util::Status ToStatus() const {
+  base::Status ToStatus() const {
     PERFETTO_DCHECK(!ignored_);
     if (error_)
-      return util::Status(*error_);
-    return util::OkStatus();
+      return base::Status(*error_);
+    return base::OkStatus();
   }
 
  private:
@@ -117,11 +117,19 @@
   // packet sequence.
   virtual void OnIncrementalStateCleared(uint32_t /* packet_sequence_id */) {}
 
-  // Called by ProtoTraceParser after the sorting stage for each non-ftrace
-  // TracePacket that contains fields for which the module was registered.
-  virtual void ParsePacket(const protos::pbzero::TracePacket_Decoder&,
-                           const TimestampedTracePiece&,
-                           uint32_t field_id);
+  // Called by ProtoTraceReader during the tokenization stage i.e. before
+  // sorting. Indicates that sequence with id |packet_sequence_id| has a packet
+  // with first_packet_on_sequence = true. This implies that there was no data
+  // loss, including ring buffer overwrittes, on this sequence.
+  virtual void OnFirstPacketOnSequence(uint32_t /* packet_sequence_id */) {}
+
+  // ParsePacket functions are called by ProtoTraceParser after the sorting
+  // stage for each non-ftrace TracePacket that contains fields for which the
+  // module was registered.
+  virtual void ParseTracePacketData(const protos::pbzero::TracePacket_Decoder&,
+                                    int64_t ts,
+                                    const TracePacketData&,
+                                    uint32_t /*field_id*/);
 
   // Called by ProtoTraceParser for trace config packets after the sorting
   // stage, on all existing modules.
@@ -131,6 +139,10 @@
 
  protected:
   void RegisterForField(uint32_t field_id, TraceProcessorContext*);
+  // Primarily intended for special modules that need to get all TracePacket's,
+  // for example for trace proto content analysis. Most modules need to register
+  // for specific fields using the method above.
+  void RegisterForAllFields(TraceProcessorContext*);
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/proto_trace_parser.cc b/src/trace_processor/importers/proto/proto_trace_parser.cc
index 08170a6..600b886 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser.cc
+++ b/src/trace_processor/importers/proto/proto_trace_parser.cc
@@ -23,37 +23,30 @@
 
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/metatrace_events.h"
+#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/utils.h"
 #include "perfetto/ext/base/uuid.h"
-#include "perfetto/trace_processor/status.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/parser_types.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/config.descriptor.h"
 #include "src/trace_processor/importers/ftrace/ftrace_module.h"
 #include "src/trace_processor/importers/proto/metadata_tracker.h"
 #include "src/trace_processor/importers/proto/packet_sequence_state.h"
+#include "src/trace_processor/importers/proto/track_event_module.h"
 #include "src/trace_processor/storage/metadata.h"
 #include "src/trace_processor/storage/stats.h"
-#include "src/trace_processor/timestamped_trace_piece.h"
 #include "src/trace_processor/types/trace_processor_context.h"
 #include "src/trace_processor/types/variadic.h"
-#include "src/trace_processor/util/descriptors.h"
-#include "src/trace_processor/util/protozero_to_text.h"
 
-#include "protos/perfetto/common/builtin_clock.pbzero.h"
 #include "protos/perfetto/common/trace_stats.pbzero.h"
 #include "protos/perfetto/config/trace_config.pbzero.h"
 #include "protos/perfetto/trace/chrome/chrome_trace_event.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/trace.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 
 namespace perfetto {
@@ -68,56 +61,25 @@
       raw_chrome_legacy_system_trace_event_id_(
           context->storage->InternString("chrome_event.legacy_system_trace")),
       raw_chrome_legacy_user_trace_event_id_(
-          context->storage->InternString("chrome_event.legacy_user_trace")) {
-  // TODO(140860736): Once we support null values for
-  // stack_profile_frame.symbol_set_id remove this hack
-  context_->storage->mutable_symbol_table()->Insert(
-      {0, kNullStringId, kNullStringId, 0});
-}
+          context->storage->InternString("chrome_event.legacy_user_trace")),
+      missing_metatrace_interned_string_id_(
+          context->storage->InternString("MISSING STRING")) {}
 
 ProtoTraceParser::~ProtoTraceParser() = default;
 
-void ProtoTraceParser::ParseTracePacket(int64_t ts, TimestampedTracePiece ttp) {
-  const TracePacketData* data = nullptr;
-  if (ttp.type == TimestampedTracePiece::Type::kTracePacket) {
-    data = &ttp.packet_data;
-  } else {
-    PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kTrackEvent);
-    data = &ttp.track_event_data;
-  }
-
-  const TraceBlobView& blob = data->packet;
+void ProtoTraceParser::ParseTracePacket(int64_t ts, TracePacketData data) {
+  const TraceBlobView& blob = data.packet;
   protos::pbzero::TracePacket::Decoder packet(blob.data(), blob.length());
-
-  ParseTracePacketImpl(ts, ttp, data->sequence_state.get(), packet);
-
-  // TODO(lalitm): maybe move this to the flush method in the trace processor
-  // once we have it. This may reduce performance in the ArgsTracker though so
-  // needs to be handled carefully.
-  context_->args_tracker->Flush();
-  PERFETTO_DCHECK(!packet.bytes_left());
-}
-
-void ProtoTraceParser::ParseTracePacketImpl(
-    int64_t ts,
-    const TimestampedTracePiece& ttp,
-    PacketSequenceStateGeneration* /*sequence_state*/,
-    const protos::pbzero::TracePacket::Decoder& packet) {
-  // Chrome doesn't honor the one-of in TracePacket for this field and sets it
-  // together with chrome_metadata, which is handled by a module. Thus, we have
-  // to parse this field before the modules get to parse other fields.
-  // TODO(crbug/1194914): Move this back after the modules (or into a separate
-  // module) once the Chrome-side fix has propagated into all release channels.
-  if (packet.has_chrome_events()) {
-    ParseChromeEvents(ts, packet.chrome_events());
-  }
-
   // TODO(eseckler): Propagate statuses from modules.
   auto& modules = context_->modules_by_field;
   for (uint32_t field_id = 1; field_id < modules.size(); ++field_id) {
     if (!modules[field_id].empty() && packet.Get(field_id).valid()) {
+      for (ProtoImporterModule* global_module :
+           context_->modules_for_all_fields) {
+        global_module->ParseTracePacketData(packet, ts, data, field_id);
+      }
       for (ProtoImporterModule* module : modules[field_id])
-        module->ParsePacket(packet, ttp, field_id);
+        module->ParseTracePacketData(packet, ts, data, field_id);
       return;
     }
   }
@@ -125,23 +87,59 @@
   if (packet.has_trace_stats())
     ParseTraceStats(packet.trace_stats());
 
+  if (packet.has_chrome_events()) {
+    ParseChromeEvents(ts, packet.chrome_events());
+  }
+
   if (packet.has_perfetto_metatrace()) {
     ParseMetatraceEvent(ts, packet.perfetto_metatrace());
   }
 
   if (packet.has_trace_config()) {
-    ParseTraceConfig(packet.trace_config());
+    // TODO(eseckler): Propagate statuses from modules.
+    protos::pbzero::TraceConfig::Decoder config(packet.trace_config());
+    for (auto& module : context_->modules) {
+      module->ParseTraceConfig(config);
+    }
   }
 }
 
-void ProtoTraceParser::ParseFtracePacket(uint32_t cpu,
-                                         int64_t /*ts*/,
-                                         TimestampedTracePiece ttp) {
-  PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kFtraceEvent ||
-                  ttp.type == TimestampedTracePiece::Type::kInlineSchedSwitch ||
-                  ttp.type == TimestampedTracePiece::Type::kInlineSchedWaking);
+void ProtoTraceParser::ParseTrackEvent(int64_t ts, TrackEventData data) {
+  const TraceBlobView& blob = data.trace_packet_data.packet;
+  protos::pbzero::TracePacket::Decoder packet(blob.data(), blob.length());
+  context_->track_module->ParseTrackEventData(packet, ts, data);
+  context_->args_tracker->Flush();
+}
+
+void ProtoTraceParser::ParseFtraceEvent(uint32_t cpu,
+                                        int64_t ts,
+                                        TracePacketData data) {
   PERFETTO_DCHECK(context_->ftrace_module);
-  context_->ftrace_module->ParseFtracePacket(cpu, ttp);
+  context_->ftrace_module->ParseFtraceEventData(cpu, ts, data);
+
+  // TODO(lalitm): maybe move this to the flush method in the trace processor
+  // once we have it. This may reduce performance in the ArgsTracker though so
+  // needs to be handled carefully.
+  context_->args_tracker->Flush();
+}
+
+void ProtoTraceParser::ParseInlineSchedSwitch(uint32_t cpu,
+                                              int64_t ts,
+                                              InlineSchedSwitch data) {
+  PERFETTO_DCHECK(context_->ftrace_module);
+  context_->ftrace_module->ParseInlineSchedSwitch(cpu, ts, data);
+
+  // TODO(lalitm): maybe move this to the flush method in the trace processor
+  // once we have it. This may reduce performance in the ArgsTracker though so
+  // needs to be handled carefully.
+  context_->args_tracker->Flush();
+}
+
+void ProtoTraceParser::ParseInlineSchedWaking(uint32_t cpu,
+                                              int64_t ts,
+                                              InlineSchedWaking data) {
+  PERFETTO_DCHECK(context_->ftrace_module);
+  context_->ftrace_module->ParseInlineSchedWaking(cpu, ts, data);
 
   // TODO(lalitm): maybe move this to the flush method in the trace processor
   // once we have it. This may reduce performance in the ArgsTracker though so
@@ -154,6 +152,8 @@
   auto* storage = context_->storage.get();
   storage->SetStats(stats::traced_producers_connected,
                     static_cast<int64_t>(evt.producers_connected()));
+  storage->SetStats(stats::traced_producers_seen,
+                    static_cast<int64_t>(evt.producers_seen()));
   storage->SetStats(stats::traced_data_sources_registered,
                     static_cast<int64_t>(evt.data_sources_registered()));
   storage->SetStats(stats::traced_data_sources_seen,
@@ -319,7 +319,14 @@
 
   StringId cat_id = metatrace_id_;
   StringId name_id = kNullStringId;
-  char fallback[64];
+
+  for (auto it = event.interned_strings(); it; ++it) {
+    protos::pbzero::PerfettoMetatrace::InternedString::Decoder interned_string(
+        it->data(), it->size());
+    metatrace_interned_strings_.Insert(
+        interned_string.iid(),
+        context_->storage->InternString(interned_string.value()));
+  }
 
   // This function inserts the args from the proto into the args table.
   // Args inserted with the same key multiple times are treated as an array:
@@ -331,8 +338,18 @@
     std::vector<Arg> interned;
     for (auto it = event.args(); it; ++it) {
       protos::pbzero::PerfettoMetatrace::Arg::Decoder arg_proto(*it);
-      StringId key = context_->storage->InternString(arg_proto.key());
-      StringId value = context_->storage->InternString(arg_proto.value());
+      StringId key;
+      if (arg_proto.has_key_iid()) {
+        key = GetMetatraceInternedString(arg_proto.key_iid());
+      } else {
+        key = context_->storage->InternString(arg_proto.key());
+      }
+      StringId value;
+      if (arg_proto.has_value_iid()) {
+        value = GetMetatraceInternedString(arg_proto.value_iid());
+      } else {
+        value = context_->storage->InternString(arg_proto.value());
+      }
       interned.emplace_back(key, value);
     }
 
@@ -377,15 +394,18 @@
     }
   };
 
-  if (event.has_event_id() || event.has_event_name()) {
+  if (event.has_event_id() || event.has_event_name() ||
+      event.has_event_name_iid()) {
     if (event.has_event_id()) {
       auto eid = event.event_id();
       if (eid < metatrace::EVENTS_MAX) {
         name_id = context_->storage->InternString(metatrace::kEventNames[eid]);
       } else {
-        sprintf(fallback, "Event %d", eid);
-        name_id = context_->storage->InternString(fallback);
+        base::StackString<64> fallback("Event %d", eid);
+        name_id = context_->storage->InternString(fallback.string_view());
       }
+    } else if (event.has_event_name_iid()) {
+      name_id = GetMetatraceInternedString(event.event_name_iid());
     } else {
       name_id = context_->storage->InternString(event.event_name());
     }
@@ -400,8 +420,8 @@
         name_id =
             context_->storage->InternString(metatrace::kCounterNames[cid]);
       } else {
-        sprintf(fallback, "Counter %d", cid);
-        name_id = context_->storage->InternString(fallback);
+        base::StackString<64> fallback("Counter %d", cid);
+        name_id = context_->storage->InternString(fallback.string_view());
       }
     } else {
       name_id = context_->storage->InternString(event.counter_name());
@@ -420,42 +440,11 @@
     context_->storage->IncrementStats(stats::metatrace_overruns);
 }
 
-void ProtoTraceParser::ParseTraceConfig(ConstBytes blob) {
-  protos::pbzero::TraceConfig::Decoder trace_config(blob.data, blob.size);
-
-  // TODO(eseckler): Propagate statuses from modules.
-  for (auto& module : context_->modules) {
-    module->ParseTraceConfig(trace_config);
-  }
-
-  int64_t uuid_msb = trace_config.trace_uuid_msb();
-  int64_t uuid_lsb = trace_config.trace_uuid_lsb();
-  if (uuid_msb != 0 || uuid_lsb != 0) {
-    base::Uuid uuid(uuid_lsb, uuid_msb);
-    std::string str = uuid.ToPrettyString();
-    StringId id = context_->storage->InternString(base::StringView(str));
-    context_->metadata_tracker->SetMetadata(metadata::trace_uuid,
-                                            Variadic::String(id));
-    context_->uuid_found_in_trace = true;
-  }
-
-  if (trace_config.has_unique_session_name()) {
-    StringId id = context_->storage->InternString(
-        base::StringView(trace_config.unique_session_name()));
-    context_->metadata_tracker->SetMetadata(metadata::unique_session_name,
-                                            Variadic::String(id));
-  }
-
-  DescriptorPool pool;
-  pool.AddFromFileDescriptorSet(kConfigDescriptor.data(),
-                                kConfigDescriptor.size());
-
-  std::string text = protozero_to_text::ProtozeroToText(
-      pool, ".perfetto.protos.TraceConfig", blob,
-      protozero_to_text::kIncludeNewLines);
-  StringId id = context_->storage->InternString(base::StringView(text));
-  context_->metadata_tracker->SetMetadata(metadata::trace_config_pbtxt,
-                                          Variadic::String(id));
+StringId ProtoTraceParser::GetMetatraceInternedString(uint64_t iid) {
+  StringId* maybe_id = metatrace_interned_strings_.Find(iid);
+  if (!maybe_id)
+    return missing_metatrace_interned_string_id_;
+  return *maybe_id;
 }
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/proto_trace_parser.h b/src/trace_processor/importers/proto/proto_trace_parser.h
index 0061d49..bfe5602 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser.h
+++ b/src/trace_processor/importers/proto/proto_trace_parser.h
@@ -22,13 +22,10 @@
 #include <array>
 #include <memory>
 
-#include "perfetto/ext/base/optional.h"
-#include "perfetto/ext/base/string_view.h"
 #include "perfetto/protozero/field.h"
-#include "perfetto/trace_processor/trace_blob_view.h"
+#include "src/trace_processor/importers/common/parser_types.h"
 #include "src/trace_processor/importers/common/trace_parser.h"
 #include "src/trace_processor/storage/trace_storage.h"
-#include "src/trace_processor/timestamped_trace_piece.h"
 
 namespace perfetto {
 
@@ -49,23 +46,28 @@
   explicit ProtoTraceParser(TraceProcessorContext*);
   ~ProtoTraceParser() override;
 
-  // TraceParser implementation.
-  void ParseTracePacket(int64_t timestamp, TimestampedTracePiece) override;
-  void ParseFtracePacket(uint32_t cpu,
-                         int64_t timestamp,
-                         TimestampedTracePiece) override;
+  void ParseTrackEvent(int64_t ts, TrackEventData data) override;
+  void ParseTracePacket(int64_t ts, TracePacketData data) override;
 
-  void ParseTracePacketImpl(int64_t ts,
-                            const TimestampedTracePiece&,
-                            PacketSequenceStateGeneration*,
-                            const protos::pbzero::TracePacket_Decoder&);
+  void ParseFtraceEvent(uint32_t cpu,
+                        int64_t /*ts*/,
+                        TracePacketData data) override;
+
+  void ParseInlineSchedSwitch(uint32_t cpu,
+                              int64_t /*ts*/,
+                              InlineSchedSwitch data) override;
+
+  void ParseInlineSchedWaking(uint32_t cpu,
+                              int64_t /*ts*/,
+                              InlineSchedWaking data) override;
 
   void ParseTraceStats(ConstBytes);
   void ParseChromeEvents(int64_t ts, ConstBytes);
   void ParseMetatraceEvent(int64_t ts, ConstBytes);
-  void ParseTraceConfig(ConstBytes);
 
  private:
+  StringId GetMetatraceInternedString(uint64_t iid);
+
   TraceProcessorContext* context_;
 
   const StringId metatrace_id_;
@@ -73,6 +75,9 @@
   const StringId raw_chrome_metadata_event_id_;
   const StringId raw_chrome_legacy_system_trace_event_id_;
   const StringId raw_chrome_legacy_user_trace_event_id_;
+  const StringId missing_metatrace_interned_string_id_;
+
+  base::FlatHashMap<uint64_t, StringId> metatrace_interned_strings_;
 };
 
 }  // namespace trace_processor
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 cbb42cc..5b907a3 100644
--- a/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
+++ b/src/trace_processor/importers/proto/proto_trace_parser_unittest.cc
@@ -20,7 +20,6 @@
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/protozero/scattered_heap_buffer.h"
 #include "perfetto/trace_processor/trace_blob.h"
-#include "src/trace_processor/importers/additional_modules.h"
 #include "src/trace_processor/importers/common/args_tracker.h"
 #include "src/trace_processor/importers/common/args_translation_table.h"
 #include "src/trace_processor/importers/common/clock_tracker.h"
@@ -29,14 +28,15 @@
 #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/additional_modules.h"
+#include "src/trace_processor/importers/proto/default_modules.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/stack_profile_tracker.h"
+#include "src/trace_processor/sorter/trace_sorter.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/util/descriptors.h"
 #include "test/gtest_and_gmock.h"
 
@@ -45,7 +45,6 @@
 #include "protos/perfetto/config/trace_config.pbzero.h"
 #include "protos/perfetto/trace/android/packages_list.pbzero.h"
 #include "protos/perfetto/trace/chrome/chrome_benchmark_metadata.pbzero.h"
-#include "protos/perfetto/trace/chrome/chrome_metadata.pbzero.h"
 #include "protos/perfetto/trace/chrome/chrome_trace_event.pbzero.h"
 #include "protos/perfetto/trace/clock_snapshot.pbzero.h"
 #include "protos/perfetto/trace/ftrace/ftrace.pbzero.h"
@@ -61,6 +60,7 @@
 #include "protos/perfetto/trace/sys_stats/sys_stats.pbzero.h"
 #include "protos/perfetto/trace/trace.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "protos/perfetto/trace/trace_uuid.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/debug_annotation.pbzero.h"
@@ -603,10 +603,14 @@
   context_.sorter->ExtractEventsForced();
 
   EXPECT_EQ(context_.storage->track_table().row_count(), 2u);
-  EXPECT_EQ(context_.storage->track_table().name().GetString(0),
-            "CPU 0 Freq in kHz");
-  EXPECT_EQ(context_.storage->track_table().name().GetString(1),
-            "CPU 1 Freq in kHz");
+  EXPECT_EQ(context_.storage->cpu_counter_track_table().row_count(), 2u);
+
+  auto row = context_.storage->cpu_counter_track_table().FindById(TrackId(0));
+  EXPECT_EQ(context_.storage->GetString(row->name()), "cpufreq");
+  EXPECT_EQ(row->cpu(), 0u);
+
+  row = context_.storage->cpu_counter_track_table().FindById(TrackId(1));
+  EXPECT_EQ(row->cpu(), 1u);
 }
 
 TEST_F(ProtoTraceParserTest, LoadMemInfo) {
@@ -869,15 +873,15 @@
 
   context_.sorter->ExtractEventsForced();
 
-  EXPECT_EQ(storage_->thread_slice_table().row_count(), 2u);
-  auto id_0 = storage_->thread_slice_table().id().IndexOf(SliceId(0u));
+  EXPECT_EQ(storage_->slice_table().row_count(), 2u);
+  auto id_0 = storage_->slice_table().id().IndexOf(SliceId(0u));
   EXPECT_TRUE(id_0);
-  EXPECT_EQ(storage_->thread_slice_table().thread_ts()[*id_0], 2003000);
-  EXPECT_EQ(storage_->thread_slice_table().thread_dur()[*id_0], 12000);
-  auto id_1 = storage_->thread_slice_table().id().IndexOf(SliceId(1u));
+  EXPECT_EQ(storage_->slice_table().thread_ts()[*id_0], 2003000);
+  EXPECT_EQ(storage_->slice_table().thread_dur()[*id_0], 12000);
+  auto id_1 = storage_->slice_table().id().IndexOf(SliceId(1u));
   EXPECT_TRUE(id_1);
-  EXPECT_EQ(storage_->thread_slice_table().thread_ts()[*id_1], 2005000);
-  EXPECT_EQ(storage_->thread_slice_table().thread_dur()[*id_1], 5000);
+  EXPECT_EQ(storage_->slice_table().thread_ts()[*id_1], 2005000);
+  EXPECT_EQ(storage_->slice_table().thread_dur()[*id_1], 5000);
 }
 
 TEST_F(ProtoTraceParserTest, TrackEventWithoutInternedDataWithTypes) {
@@ -958,15 +962,15 @@
 
   context_.sorter->ExtractEventsForced();
 
-  EXPECT_EQ(storage_->thread_slice_table().row_count(), 2u);
-  auto id_0 = storage_->thread_slice_table().id().IndexOf(SliceId(0u));
+  EXPECT_EQ(storage_->slice_table().row_count(), 2u);
+  auto id_0 = storage_->slice_table().id().IndexOf(SliceId(0u));
   EXPECT_TRUE(id_0);
-  EXPECT_EQ(storage_->thread_slice_table().thread_ts()[*id_0], 2005000);
-  EXPECT_EQ(storage_->thread_slice_table().thread_dur()[*id_0], 5000);
-  auto id_1 = storage_->thread_slice_table().id().IndexOf(SliceId(1u));
+  EXPECT_EQ(storage_->slice_table().thread_ts()[*id_0], 2005000);
+  EXPECT_EQ(storage_->slice_table().thread_dur()[*id_0], 5000);
+  auto id_1 = storage_->slice_table().id().IndexOf(SliceId(1u));
   EXPECT_TRUE(id_1);
-  EXPECT_EQ(storage_->thread_slice_table().thread_ts()[*id_1], 2007000);
-  EXPECT_EQ(storage_->thread_slice_table().thread_dur()[*id_1], 0);
+  EXPECT_EQ(storage_->slice_table().thread_ts()[*id_1], 2007000);
+  EXPECT_EQ(storage_->slice_table().thread_dur()[*id_1], 0);
 }
 
 TEST_F(ProtoTraceParserTest, TrackEventWithInternedData) {
@@ -1157,31 +1161,25 @@
 
   context_.sorter->ExtractEventsForced();
 
-  EXPECT_EQ(storage_->thread_slice_table().row_count(), 3u);
-  auto id_0 = storage_->thread_slice_table().id().IndexOf(SliceId(0u));
+  EXPECT_EQ(storage_->slice_table().row_count(), 3u);
+  auto id_0 = storage_->slice_table().id().IndexOf(SliceId(0u));
   EXPECT_TRUE(id_0);
-  EXPECT_EQ(storage_->thread_slice_table().thread_ts()[*id_0], 2003000);
-  EXPECT_EQ(storage_->thread_slice_table().thread_dur()[*id_0], 12000);
-  EXPECT_EQ(storage_->thread_slice_table().thread_instruction_count()[*id_0],
-            3010);
-  EXPECT_EQ(storage_->thread_slice_table().thread_instruction_delta()[*id_0],
-            50);
-  auto id_1 = storage_->thread_slice_table().id().IndexOf(SliceId(1u));
+  EXPECT_EQ(storage_->slice_table().thread_ts()[*id_0], 2003000);
+  EXPECT_EQ(storage_->slice_table().thread_dur()[*id_0], 12000);
+  EXPECT_EQ(storage_->slice_table().thread_instruction_count()[*id_0], 3010);
+  EXPECT_EQ(storage_->slice_table().thread_instruction_delta()[*id_0], 50);
+  auto id_1 = storage_->slice_table().id().IndexOf(SliceId(1u));
   EXPECT_TRUE(id_1);
-  EXPECT_EQ(storage_->thread_slice_table().thread_ts()[*id_1], 2005000);
-  EXPECT_EQ(storage_->thread_slice_table().thread_dur()[*id_1], 5000);
-  EXPECT_EQ(storage_->thread_slice_table().thread_instruction_count()[*id_1],
-            3020);
-  EXPECT_EQ(storage_->thread_slice_table().thread_instruction_delta()[*id_1],
-            20);
-  auto id_2 = storage_->thread_slice_table().id().IndexOf(SliceId(2u));
+  EXPECT_EQ(storage_->slice_table().thread_ts()[*id_1], 2005000);
+  EXPECT_EQ(storage_->slice_table().thread_dur()[*id_1], 5000);
+  EXPECT_EQ(storage_->slice_table().thread_instruction_count()[*id_1], 3020);
+  EXPECT_EQ(storage_->slice_table().thread_instruction_delta()[*id_1], 20);
+  auto id_2 = storage_->slice_table().id().IndexOf(SliceId(2u));
   EXPECT_TRUE(id_2);
-  EXPECT_EQ(storage_->thread_slice_table().thread_ts()[*id_2], 2030000);
-  EXPECT_EQ(storage_->thread_slice_table().thread_dur()[*id_2], 0);
-  EXPECT_EQ(storage_->thread_slice_table().thread_instruction_count()[*id_2],
-            3100);
-  EXPECT_EQ(storage_->thread_slice_table().thread_instruction_delta()[*id_2],
-            0);
+  EXPECT_EQ(storage_->slice_table().thread_ts()[*id_2], 2030000);
+  EXPECT_EQ(storage_->slice_table().thread_dur()[*id_2], 0);
+  EXPECT_EQ(storage_->slice_table().thread_instruction_count()[*id_2], 3100);
+  EXPECT_EQ(storage_->slice_table().thread_instruction_delta()[*id_2], 0);
 }
 
 TEST_F(ProtoTraceParserTest, TrackEventAsyncEvents) {
@@ -1520,24 +1518,20 @@
   EXPECT_EQ(storage_->virtual_track_slices().thread_instruction_deltas()[0],
             20);
 
-  EXPECT_EQ(storage_->thread_slice_table().row_count(), 2u);
-  auto id_0 = storage_->thread_slice_table().id().IndexOf(SliceId(0u));
+  EXPECT_EQ(storage_->slice_table().row_count(), 2u);
+  auto id_0 = storage_->slice_table().id().IndexOf(SliceId(0u));
   EXPECT_TRUE(id_0);
-  EXPECT_EQ(storage_->thread_slice_table().thread_ts()[*id_0], 2007000);
-  EXPECT_EQ(storage_->thread_slice_table().thread_dur()[*id_0], 0);
+  EXPECT_EQ(storage_->slice_table().thread_ts()[*id_0], 2007000);
+  EXPECT_EQ(storage_->slice_table().thread_dur()[*id_0], 0);
   // There was no thread instructions in the packets above.
-  EXPECT_FALSE(
-      storage_->thread_slice_table().thread_instruction_count()[*id_0]);
-  EXPECT_FALSE(
-      storage_->thread_slice_table().thread_instruction_delta()[*id_0]);
-  auto id_1 = storage_->thread_slice_table().id().IndexOf(SliceId(1u));
+  EXPECT_FALSE(storage_->slice_table().thread_instruction_count()[*id_0]);
+  EXPECT_FALSE(storage_->slice_table().thread_instruction_delta()[*id_0]);
+  auto id_1 = storage_->slice_table().id().IndexOf(SliceId(1u));
   EXPECT_TRUE(id_1);
-  EXPECT_EQ(storage_->thread_slice_table().thread_ts()[*id_1], 2008000);
-  EXPECT_EQ(storage_->thread_slice_table().thread_dur()[*id_1], 0);
-  EXPECT_FALSE(
-      storage_->thread_slice_table().thread_instruction_count()[*id_1]);
-  EXPECT_FALSE(
-      storage_->thread_slice_table().thread_instruction_delta()[*id_1]);
+  EXPECT_EQ(storage_->slice_table().thread_ts()[*id_1], 2008000);
+  EXPECT_EQ(storage_->slice_table().thread_dur()[*id_1], 0);
+  EXPECT_FALSE(storage_->slice_table().thread_instruction_count()[*id_1]);
+  EXPECT_FALSE(storage_->slice_table().thread_instruction_delta()[*id_1]);
 }
 
 TEST_F(ProtoTraceParserTest, TrackEventWithResortedCounterDescriptor) {
@@ -1633,11 +1627,11 @@
   EXPECT_EQ(storage_->thread_track_table().utid()[0], 1u);
 
   // Counter values should also be imported into thread slices.
-  EXPECT_EQ(storage_->thread_slice_table().row_count(), 1u);
-  auto id_0 = storage_->thread_slice_table().id().IndexOf(SliceId(0u));
+  EXPECT_EQ(storage_->slice_table().row_count(), 1u);
+  auto id_0 = storage_->slice_table().id().IndexOf(SliceId(0u));
   EXPECT_TRUE(id_0);
-  EXPECT_EQ(storage_->thread_slice_table().thread_ts()[*id_0], 1000000);
-  EXPECT_EQ(storage_->thread_slice_table().thread_dur()[*id_0], 10000);
+  EXPECT_EQ(storage_->slice_table().thread_ts()[*id_0], 1000000);
+  EXPECT_EQ(storage_->slice_table().thread_dur()[*id_0], 10000);
 }
 
 TEST_F(ProtoTraceParserTest, TrackEventWithoutIncrementalStateReset) {
@@ -2250,6 +2244,9 @@
   storage_->mutable_thread_table()->Insert(row);
 
   StringId body_1 = storage_->InternString("body1");
+  StringId file_1 = storage_->InternString("file1");
+  StringId func_1 = storage_->InternString("func1");
+  StringId source_location_id = storage_->InternString("file1:1");
 
   constexpr TrackId track{0};
   InSequence in_sequence;  // Below slices should be sorted by timestamp.
@@ -2261,12 +2258,16 @@
 
   // Call with logMessageBody (body1 in this case).
   EXPECT_CALL(inserter, AddArg(_, _, Variadic::String(body_1), _));
+  EXPECT_CALL(inserter, AddArg(_, _, Variadic::String(file_1), _));
+  EXPECT_CALL(inserter, AddArg(_, _, Variadic::String(func_1), _));
+  EXPECT_CALL(inserter, AddArg(_, _, Variadic::Integer(1), _));
 
   context_.sorter->ExtractEventsForced();
 
   EXPECT_GT(context_.storage->android_log_table().row_count(), 0u);
   EXPECT_EQ(context_.storage->android_log_table().ts()[0], 1010000);
   EXPECT_EQ(context_.storage->android_log_table().msg()[0], body_1);
+  EXPECT_EQ(context_.storage->android_log_table().tag()[0], source_location_id);
 }
 
 TEST_F(ProtoTraceParserTest, TrackEventParseLegacyEventIntoRawTable) {
@@ -2469,51 +2470,6 @@
                      Variadic::Integer(kIntValue)));
 }
 
-// TODO(crbug.com/1194914): Remove this test once the Chrome-side fix has
-// propagated into all release channels.
-TEST_F(ProtoTraceParserTest, ParseChromeCombinedMetadataPacket) {
-  static const char kStringName[] = "string_name";
-  static const char kStringValue[] = "string_value";
-
-  {
-    auto* packet = trace_->add_packet();
-    packet->set_timestamp(1000);
-    packet->set_timestamp_clock_id(3);
-    packet->set_trusted_packet_sequence_id(1);
-    auto* chrome_metadata = packet->set_chrome_metadata();
-    chrome_metadata->set_chrome_version_code(123);
-    auto* bundle = packet->set_chrome_events();
-    auto* metadata = bundle->add_metadata();
-    metadata->set_name(kStringName);
-    metadata->set_string_value(kStringValue);
-  }
-
-  Tokenize();
-  context_.sorter->ExtractEventsForced();
-
-  // Typed metadata should be in metadata table.
-  bool found = false;
-  for (uint32_t row = 0; row < storage_->metadata_table().row_count(); row++) {
-    if (storage_->metadata_table().name()[row] ==
-        storage_->InternString("cr-playstore_version_code")) {
-      found = true;
-      EXPECT_EQ(storage_->metadata_table().int_value()[0], 123);
-    }
-  }
-  EXPECT_TRUE(found);
-
-  // Untyped metadata should be in raw table.
-  const auto& raw_table = storage_->raw_table();
-  EXPECT_EQ(raw_table.row_count(), 1u);
-  EXPECT_EQ(raw_table.name()[0],
-            storage_->InternString("chrome_event.metadata"));
-  EXPECT_EQ(raw_table.arg_set_id()[0], 1u);
-
-  EXPECT_EQ(storage_->arg_table().row_count(), 1u);
-  EXPECT_TRUE(HasArg(1u, storage_->InternString(kStringName),
-                     Variadic::String(storage_->InternString(kStringValue))));
-}
-
 TEST_F(ProtoTraceParserTest, ParseChromeLegacyFtraceIntoRawTable) {
   static const char kDataPart0[] = "aaa";
   static const char kDataPart1[] = "bbb";
@@ -2925,6 +2881,38 @@
   ASSERT_TRUE(context_.uuid_found_in_trace);
 }
 
+TEST_F(ProtoTraceParserTest, PacketUuid) {
+  auto* uuid = trace_->add_packet()->set_trace_uuid();
+  uuid->set_lsb(1);
+  uuid->set_msb(2);
+
+  ASSERT_TRUE(Tokenize().ok());
+  context_.sorter->ExtractEventsForced();
+
+  SqlValue value = context_.metadata_tracker->GetMetadata(metadata::trace_uuid);
+  EXPECT_STREQ(value.string_value, "00000000-0000-0002-0000-000000000001");
+  ASSERT_TRUE(context_.uuid_found_in_trace);
+}
+
+// If both the TraceConfig and TracePacket.trace_uuid are present, the latter
+// is considered the source of truth.
+TEST_F(ProtoTraceParserTest, PacketAndConfigUuid) {
+  auto* uuid = trace_->add_packet()->set_trace_uuid();
+  uuid->set_lsb(1);
+  uuid->set_msb(2);
+
+  auto* config = trace_->add_packet()->set_trace_config();
+  config->set_trace_uuid_lsb(42);
+  config->set_trace_uuid_msb(42);
+
+  ASSERT_TRUE(Tokenize().ok());
+  context_.sorter->ExtractEventsForced();
+
+  SqlValue value = context_.metadata_tracker->GetMetadata(metadata::trace_uuid);
+  EXPECT_STREQ(value.string_value, "00000000-0000-0002-0000-000000000001");
+  ASSERT_TRUE(context_.uuid_found_in_trace);
+}
+
 TEST_F(ProtoTraceParserTest, ConfigPbtxt) {
   auto* config = trace_->add_packet()->set_trace_config();
   config->add_buffers()->set_size_kb(42);
diff --git a/src/trace_processor/importers/proto/proto_trace_reader.cc b/src/trace_processor/importers/proto/proto_trace_reader.cc
index 9362756..f955e6e 100644
--- a/src/trace_processor/importers/proto/proto_trace_reader.cc
+++ b/src/trace_processor/importers/proto/proto_trace_reader.cc
@@ -31,11 +31,12 @@
 #include "src/trace_processor/importers/common/track_tracker.h"
 #include "src/trace_processor/importers/ftrace/ftrace_module.h"
 #include "src/trace_processor/importers/proto/metadata_tracker.h"
+#include "src/trace_processor/importers/proto/packet_analyzer.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/sorter/trace_sorter.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/util/descriptors.h"
 #include "src/trace_processor/util/gzip_utils.h"
 
@@ -52,7 +53,10 @@
 namespace trace_processor {
 
 ProtoTraceReader::ProtoTraceReader(TraceProcessorContext* ctx)
-    : context_(ctx) {}
+    : context_(ctx),
+      skipped_packet_key_id_(ctx->storage->InternString("skipped_packet")),
+      invalid_incremental_state_key_id_(
+          ctx->storage->InternString("invalid_incremental_state")) {}
 ProtoTraceReader::~ProtoTraceReader() = default;
 
 util::Status ProtoTraceReader::Parse(TraceBlobView blob) {
@@ -85,6 +89,10 @@
   const uint32_t seq_id = decoder.trusted_packet_sequence_id();
   auto* state = GetIncrementalStateForPacketSequence(seq_id);
 
+  if (decoder.first_packet_on_sequence()) {
+    HandleFirstPacketOnSequence(seq_id);
+  }
+
   uint32_t sequence_flags = decoder.sequence_flags();
 
   if (decoder.incremental_state_cleared() ||
@@ -132,6 +140,14 @@
     }
 
     if (!state->IsIncrementalStateValid()) {
+      if (context_->content_analyzer) {
+        // Account for the skipped packet for trace proto content analysis,
+        // with a special annotation.
+        PacketAnalyzer::SampleAnnotation annotation;
+        annotation.push_back(
+            {skipped_packet_key_id_, invalid_incremental_state_key_id_});
+        PacketAnalyzer::Get(context_)->ProcessPacket(packet, annotation);
+      }
       context_->storage->IncrementStats(stats::tokenizer_skipped_packets);
       return util::OkStatus();
     }
@@ -195,9 +211,20 @@
   }
   latest_timestamp_ = std::max(timestamp, latest_timestamp_);
 
+  if (context_->content_analyzer && !decoder.has_track_event()) {
+    PacketAnalyzer::Get(context_)->ProcessPacket(packet, {});
+  }
+
   auto& modules = context_->modules_by_field;
   for (uint32_t field_id = 1; field_id < modules.size(); ++field_id) {
     if (!modules[field_id].empty() && decoder.Get(field_id).valid()) {
+      for (ProtoImporterModule* global_module :
+           context_->modules_for_all_fields) {
+        ModuleResult res = global_module->TokenizePacket(
+            decoder, &packet, timestamp, state, field_id);
+        if (!res.ignored())
+          return res.ToStatus();
+      }
       for (ProtoImporterModule* module : modules[field_id]) {
         ModuleResult res = module->TokenizePacket(decoder, &packet, timestamp,
                                                   state, field_id);
@@ -213,7 +240,8 @@
 
   // Use parent data and length because we want to parse this again
   // later to get the exact type of the packet.
-  context_->sorter->PushTracePacket(timestamp, state, std::move(packet));
+  context_->sorter->PushTracePacket(timestamp, state->current_generation(),
+                                    std::move(packet));
 
   return util::OkStatus();
 }
@@ -246,6 +274,13 @@
   }
 }
 
+void ProtoTraceReader::HandleFirstPacketOnSequence(
+    uint32_t packet_sequence_id) {
+  for (auto& module : context_->modules) {
+    module->OnFirstPacketOnSequence(packet_sequence_id);
+  }
+}
+
 void ProtoTraceReader::HandlePreviousPacketDropped(
     const protos::pbzero::TracePacket::Decoder& packet_decoder) {
   if (PERFETTO_UNLIKELY(!packet_decoder.has_trusted_packet_sequence_id())) {
diff --git a/src/trace_processor/importers/proto/proto_trace_reader.h b/src/trace_processor/importers/proto/proto_trace_reader.h
index ed3e43e..471503a 100644
--- a/src/trace_processor/importers/proto/proto_trace_reader.h
+++ b/src/trace_processor/importers/proto/proto_trace_reader.h
@@ -67,6 +67,7 @@
   util::Status ParseClockSnapshot(ConstBytes blob, uint32_t seq_id);
   void HandleIncrementalStateCleared(
       const protos::pbzero::TracePacket_Decoder&);
+  void HandleFirstPacketOnSequence(uint32_t packet_sequence_id);
   void HandlePreviousPacketDropped(const protos::pbzero::TracePacket_Decoder&);
   void ParseTracePacketDefaults(const protos::pbzero::TracePacket_Decoder&,
                                 TraceBlobView trace_packet_defaults);
@@ -95,6 +96,9 @@
   // Stores incremental state and references to interned data, e.g. for track
   // event protos.
   std::unique_ptr<ProtoIncrementalState> incremental_state;
+
+  StringId skipped_packet_key_id_;
+  StringId invalid_incremental_state_key_id_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/stack_profile_tracker.cc b/src/trace_processor/importers/proto/stack_profile_tracker.cc
index 2451142..cfe90d2 100644
--- a/src/trace_processor/importers/proto/stack_profile_tracker.cc
+++ b/src/trace_processor/importers/proto/stack_profile_tracker.cc
@@ -58,6 +58,11 @@
       break;
     path += "/" + *opt_str;
   }
+  // When path strings just have single full path(like Chrome does), the mapping
+  // path gets an extra '/' prepended, strip the extra '/'.
+  if(base::StartsWith(path, "//")) {
+    path = path.substr(1);
+  }
 
   auto opt_build_id = FindAndInternString(mapping.build_id, intern_lookup,
                                           InternedStringType::kBuildId);
diff --git a/src/trace_processor/importers/proto/statsd_module.cc b/src/trace_processor/importers/proto/statsd_module.cc
index 7b2d8aa..555ccaa 100644
--- a/src/trace_processor/importers/proto/statsd_module.cc
+++ b/src/trace_processor/importers/proto/statsd_module.cc
@@ -18,15 +18,14 @@
 #include "perfetto/ext/base/string_utils.h"
 #include "protos/perfetto/trace/statsd/statsd_atom.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "src/trace_processor/importers/common/async_track_set_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/proto/async_track_set_tracker.h"
+#include "src/trace_processor/sorter/trace_sorter.h"
 #include "src/trace_processor/storage/trace_storage.h"
-#include "src/trace_processor/timestamped_trace_piece.h"
-#include "src/trace_processor/trace_sorter.h"
 #include "src/trace_processor/util/descriptors.h"
 
-#include "src/trace_processor/importers/atoms.descriptor.h"
+#include "src/trace_processor/importers/proto/atoms.descriptor.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -102,14 +101,8 @@
     inserter_.AddArg(flat_key_id, key_id, variadic_val);
   }
 
-  bool AddJson(const Key& key, const protozero::ConstChars& value) override {
-    auto json_value = json::ParseJsonString(value);
-    if (!json_value) {
-      return false;
-    }
-    return json::AddJsonValueToArgs(*json_value, base::StringView(key.flat_key),
-                                    base::StringView(key.key), &storage_,
-                                    &inserter_);
+  bool AddJson(const Key&, const protozero::ConstChars&) override {
+    PERFETTO_FATAL("Unexpected JSON value when parsing statsd data");
   }
 
   void AddNull(const Key& key) override {
@@ -170,13 +163,13 @@
 
 StatsdModule::~StatsdModule() = default;
 
-void StatsdModule::ParsePacket(const TracePacket::Decoder& decoder,
-                               const TimestampedTracePiece& ttp,
-                               uint32_t field_id) {
+void StatsdModule::ParseTracePacketData(const TracePacket::Decoder& decoder,
+                                        int64_t ts,
+                                        const TracePacketData&,
+                                        uint32_t field_id) {
   if (field_id != TracePacket::kStatsdAtomFieldNumber) {
     return;
   }
-  int64_t ts = ttp.timestamp;
   const auto& atoms_wrapper =
       protos::pbzero::StatsdAtom::Decoder(decoder.statsd_atom());
   for (auto it = atoms_wrapper.nested(); it; ++it) {
diff --git a/src/trace_processor/importers/proto/statsd_module.h b/src/trace_processor/importers/proto/statsd_module.h
index 50184c0..b01559e 100644
--- a/src/trace_processor/importers/proto/statsd_module.h
+++ b/src/trace_processor/importers/proto/statsd_module.h
@@ -22,7 +22,8 @@
 #include "perfetto/ext/base/flat_hash_map.h"
 #include "perfetto/ext/base/optional.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
-#include "src/trace_processor/importers/proto/async_track_set_tracker.h"
+#include "src/trace_processor/importers/common/async_track_set_tracker.h"
+#include "src/trace_processor/importers/common/trace_parser.h"
 #include "src/trace_processor/importers/proto/proto_importer_module.h"
 #include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/tables/slice_tables.h"
@@ -61,9 +62,10 @@
 
   ~StatsdModule() override;
 
-  void ParsePacket(const protos::pbzero::TracePacket::Decoder& decoder,
-                   const TimestampedTracePiece& ttp,
-                   uint32_t field_id) override;
+  void ParseTracePacketData(const protos::pbzero::TracePacket_Decoder& decoder,
+                            int64_t ts,
+                            const TracePacketData&,
+                            uint32_t field_id) override;
 
  private:
   void ParseAtom(int64_t ts, protozero::ConstBytes bytes);
diff --git a/src/trace_processor/importers/proto/system_probes_module.cc b/src/trace_processor/importers/proto/system_probes_module.cc
index 1b21eca..b451f96 100644
--- a/src/trace_processor/importers/proto/system_probes_module.cc
+++ b/src/trace_processor/importers/proto/system_probes_module.cc
@@ -17,7 +17,6 @@
 #include "src/trace_processor/importers/proto/system_probes_module.h"
 #include "perfetto/base/build_config.h"
 #include "src/trace_processor/importers/proto/system_probes_parser.h"
-#include "src/trace_processor/timestamped_trace_piece.h"
 
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 
@@ -52,18 +51,20 @@
   return ModuleResult::Ignored();
 }
 
-void SystemProbesModule::ParsePacket(const TracePacket::Decoder& decoder,
-                                     const TimestampedTracePiece& ttp,
-                                     uint32_t field_id) {
+void SystemProbesModule::ParseTracePacketData(
+    const TracePacket::Decoder& decoder,
+    int64_t ts,
+    const TracePacketData&,
+    uint32_t field_id) {
   switch (field_id) {
     case TracePacket::kProcessTreeFieldNumber:
       parser_.ParseProcessTree(decoder.process_tree());
       return;
     case TracePacket::kProcessStatsFieldNumber:
-      parser_.ParseProcessStats(ttp.timestamp, decoder.process_stats());
+      parser_.ParseProcessStats(ts, decoder.process_stats());
       return;
     case TracePacket::kSysStatsFieldNumber:
-      parser_.ParseSysStats(ttp.timestamp, decoder.sys_stats());
+      parser_.ParseSysStats(ts, decoder.sys_stats());
       return;
   }
 }
diff --git a/src/trace_processor/importers/proto/system_probes_module.h b/src/trace_processor/importers/proto/system_probes_module.h
index 8fcd94b..b781ecc 100644
--- a/src/trace_processor/importers/proto/system_probes_module.h
+++ b/src/trace_processor/importers/proto/system_probes_module.h
@@ -18,6 +18,7 @@
 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_SYSTEM_PROBES_MODULE_H_
 
 #include "perfetto/base/build_config.h"
+#include "src/trace_processor/importers/common/trace_parser.h"
 #include "src/trace_processor/importers/proto/proto_importer_module.h"
 #include "src/trace_processor/importers/proto/system_probes_parser.h"
 
@@ -36,9 +37,10 @@
                               PacketSequenceState*,
                               uint32_t field_id) override;
 
-  void ParsePacket(const protos::pbzero::TracePacket::Decoder& decoder,
-                   const TimestampedTracePiece& ttp,
-                   uint32_t field_id) override;
+  void ParseTracePacketData(const protos::pbzero::TracePacket::Decoder& decoder,
+                            int64_t ts,
+                            const TracePacketData&,
+                            uint32_t field_id) override;
 
  private:
   SystemProbesParser parser_;
diff --git a/src/trace_processor/importers/proto/system_probes_parser.cc b/src/trace_processor/importers/proto/system_probes_parser.cc
index 56469af..a16ca4a 100644
--- a/src/trace_processor/importers/proto/system_probes_parser.cc
+++ b/src/trace_processor/importers/proto/system_probes_parser.cc
@@ -32,10 +32,17 @@
 
 #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 {
+
+bool IsSupportedDiskStatDevice(const std::string& device_name) {
+  return device_name == "sda";  // Primary SCSI disk device name
+}
+
+}  // namespace
+
 namespace perfetto {
 namespace trace_processor {
 
@@ -119,7 +126,7 @@
       cpu_times_softirq_ns_id_(
           context->storage->InternString("cpu.times.softirq_ns")),
       oom_score_adj_id_(context->storage->InternString("oom_score_adj")),
-      cpu_freq_id_(context_->storage->InternString("freq")) {
+      cpu_freq_id_(context_->storage->InternString("cpufreq")) {
   for (const auto& name : BuildMeminfoCounterNames()) {
     meminfo_strs_id_.emplace_back(context->storage->InternString(name));
   }
@@ -148,6 +155,82 @@
       oom_score_adj_id_;
 }
 
+void SystemProbesParser::ParseDiskStats(int64_t ts, ConstBytes blob) {
+  protos::pbzero::SysStats::DiskStat::Decoder ds(blob.data, blob.size);
+  static constexpr double SECTORS_PER_MB = 2048.0;
+  static constexpr double MS_PER_SEC = 1000.0;
+  std::string device_name = ds.device_name().ToStdString();
+  if (!IsSupportedDiskStatDevice(device_name)) {
+    return;
+  }
+
+  base::StackString<512> tag_prefix("diskstat.[%s]", device_name.c_str());
+  auto push_counter = [this, ts, tag_prefix](const char* counter_name,
+                                             double value) {
+    base::StackString<512> track_name("%s.%s", tag_prefix.c_str(),
+                                      counter_name);
+    StringId string_id = context_->storage->InternString(track_name.c_str());
+    TrackId track =
+        context_->track_tracker->InternGlobalCounterTrack(string_id);
+    context_->event_tracker->PushCounter(ts, value, track);
+  };
+
+  auto calculate_throughput = [](double amount, int64_t diff) {
+    return diff == 0 ? 0 : amount * MS_PER_SEC / static_cast<double>(diff);
+  };
+
+  int64_t cur_read_amount = static_cast<int64_t>(ds.read_sectors());
+  int64_t cur_write_amount = static_cast<int64_t>(ds.write_sectors());
+  int64_t cur_discard_amount = static_cast<int64_t>(ds.discard_sectors());
+  int64_t cur_flush_count = static_cast<int64_t>(ds.flush_count());
+  int64_t cur_read_time = static_cast<int64_t>(ds.read_time_ms());
+  int64_t cur_write_time = static_cast<int64_t>(ds.write_time_ms());
+  int64_t cur_discard_time = static_cast<int64_t>(ds.discard_time_ms());
+  int64_t cur_flush_time = static_cast<int64_t>(ds.flush_time_ms());
+
+  if (prev_read_amount != -1) {
+    double read_amount =
+        static_cast<double>(cur_read_amount - prev_read_amount) /
+        SECTORS_PER_MB;
+    double write_amount =
+        static_cast<double>(cur_write_amount - prev_write_amount) /
+        SECTORS_PER_MB;
+    double discard_amount =
+        static_cast<double>(cur_discard_amount - prev_discard_amount) /
+        SECTORS_PER_MB;
+    double flush_count =
+        static_cast<double>(cur_flush_count - prev_flush_count);
+    int64_t read_time_diff = cur_read_time - prev_read_time;
+    int64_t write_time_diff = cur_write_time - prev_write_time;
+    int64_t discard_time_diff = cur_discard_time - prev_discard_time;
+    double flush_time_diff =
+        static_cast<double>(cur_flush_time - prev_flush_time);
+
+    double read_thpt = calculate_throughput(read_amount, read_time_diff);
+    double write_thpt = calculate_throughput(write_amount, write_time_diff);
+    double discard_thpt =
+        calculate_throughput(discard_amount, discard_time_diff);
+
+    push_counter("read_amount(mg)", read_amount);
+    push_counter("read_throughput(mg/s)", read_thpt);
+    push_counter("write_amount(mg)", write_amount);
+    push_counter("write_throughput(mg/s)", write_thpt);
+    push_counter("discard_amount(mg)", discard_amount);
+    push_counter("discard_throughput(mg/s)", discard_thpt);
+    push_counter("flush_amount(count)", flush_count);
+    push_counter("flush_time(ms)", flush_time_diff);
+  }
+
+  prev_read_amount = cur_read_amount;
+  prev_write_amount = cur_write_amount;
+  prev_discard_amount = cur_discard_amount;
+  prev_flush_count = cur_flush_count;
+  prev_read_time = cur_read_time;
+  prev_write_time = cur_write_time;
+  prev_discard_time = cur_discard_time;
+  prev_flush_time = cur_flush_time;
+}
+
 void SystemProbesParser::ParseSysStats(int64_t ts, ConstBytes blob) {
   protos::pbzero::SysStats::Decoder sys_stats(blob.data, blob.size);
 
@@ -181,11 +264,10 @@
                                          track);
   }
 
-  int c = 0;
+  uint32_t c = 0;
   for (auto it = sys_stats.cpufreq_khz(); it; ++it, ++c) {
-    base::StackString<255> counter_name("CPU %d Freq in kHz", c);
-    StringId name = context_->storage->InternString(counter_name.string_view());
-    TrackId track = context_->track_tracker->InternGlobalCounterTrack(name);
+    TrackId track =
+        context_->track_tracker->InternCpuCounterTrack(cpu_freq_id_, c);
     context_->event_tracker->PushCounter(ts, static_cast<double>(*it), track);
   }
 
@@ -304,6 +386,10 @@
       order++;
     }
   }
+
+  for (auto it = sys_stats.disk_stat(); it; ++it) {
+    ParseDiskStats(ts, *it);
+  }
 }
 
 void SystemProbesParser::ParseProcessTree(ConstBytes blob) {
@@ -398,6 +484,10 @@
         ParseThreadStats(ts, pid, fld.as_bytes());
         continue;
       }
+      if (fld.id() == protos::pbzero::ProcessStats::Process::kFdsFieldNumber) {
+        ParseProcessFds(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) {
@@ -447,6 +537,21 @@
                                           pid);
 }
 
+void SystemProbesParser::ParseProcessFds(int64_t ts,
+                                         uint32_t pid,
+                                         ConstBytes blob) {
+  protos::pbzero::ProcessStats::FDInfo::Decoder fd_info(blob.data, blob.size);
+
+  tables::FiledescriptorTable::Row row;
+  row.fd = static_cast<int64_t>(fd_info.fd());
+  row.ts = ts;
+  row.path = context_->storage->InternString(fd_info.path());
+  row.upid = context_->process_tracker->GetOrCreateProcess(pid);
+
+  auto* fd_table = context_->storage->mutable_filedescriptor_table();
+  fd_table->Insert(row);
+}
+
 void SystemProbesParser::ParseSystemInfo(ConstBytes blob) {
   protos::pbzero::SystemInfo::Decoder packet(blob.data, blob.size);
   if (packet.has_utsname()) {
diff --git a/src/trace_processor/importers/proto/system_probes_parser.h b/src/trace_processor/importers/proto/system_probes_parser.h
index 55c5002..77721d5 100644
--- a/src/trace_processor/importers/proto/system_probes_parser.h
+++ b/src/trace_processor/importers/proto/system_probes_parser.h
@@ -22,6 +22,7 @@
 #include <vector>
 
 #include "perfetto/protozero/field.h"
+#include "protos/perfetto/trace/sys_stats/sys_stats.pbzero.h"
 #include "src/trace_processor/storage/trace_storage.h"
 
 namespace perfetto {
@@ -44,6 +45,8 @@
 
  private:
   void ParseThreadStats(int64_t timestamp, uint32_t pid, ConstBytes);
+  void ParseDiskStats(int64_t ts, ConstBytes blob);
+  void ParseProcessFds(int64_t ts, uint32_t pid, ConstBytes);
 
   TraceProcessorContext* const context_;
 
@@ -74,6 +77,15 @@
 
   uint64_t ms_per_tick_ = 0;
   uint32_t page_size_ = 0;
+
+  int64_t prev_read_amount = -1;
+  int64_t prev_write_amount = -1;
+  int64_t prev_discard_amount = -1;
+  int64_t prev_flush_count = -1;
+  int64_t prev_read_time = -1;
+  int64_t prev_write_time = -1;
+  int64_t prev_discard_time = -1;
+  int64_t prev_flush_time = -1;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/track_event.descriptor.h b/src/trace_processor/importers/proto/track_event.descriptor.h
deleted file mode 100644
index 47ab150..0000000
--- a/src/trace_processor/importers/proto/track_event.descriptor.h
+++ /dev/null
@@ -1,1864 +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_TRACK_EVENT_DESCRIPTOR_H_
-#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_TRACK_EVENT_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)
-// e5c244903aa00cad06faf3d126918306a7fe811e
-// SHA1(protos/perfetto/trace/track_event/track_event.proto)
-// 0c3776094b678fe0d7981a20564efbc002e321d9
-
-// 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, 21868> kTrackEventDescriptor{
-    {0x0a, 0x96, 0x08, 0x0a, 0x38, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f,
-     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x74, 0x72, 0x61,
-     0x63, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x65, 0x76, 0x65,
-     0x6e, 0x74, 0x2f, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x61, 0x6e, 0x6e,
-     0x6f, 0x74, 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, 0x8b, 0x07, 0x0a, 0x0f, 0x44,
-     0x65, 0x62, 0x75, 0x67, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69,
-     0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x08, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x69,
-     0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x07,
-     0x6e, 0x61, 0x6d, 0x65, 0x49, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x04, 0x6e,
-     0x61, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52,
-     0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0a, 0x62, 0x6f, 0x6f,
-     0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
-     0x08, 0x48, 0x01, 0x52, 0x09, 0x62, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c,
-     0x75, 0x65, 0x12, 0x1f, 0x0a, 0x0a, 0x75, 0x69, 0x6e, 0x74, 0x5f, 0x76,
-     0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x48, 0x01,
-     0x52, 0x09, 0x75, 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12,
-     0x1d, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65,
-     0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x48, 0x01, 0x52, 0x08, 0x69, 0x6e,
-     0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x23, 0x0a, 0x0c, 0x64, 0x6f,
-     0x75, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x05,
-     0x20, 0x01, 0x28, 0x01, 0x48, 0x01, 0x52, 0x0b, 0x64, 0x6f, 0x75, 0x62,
-     0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x23, 0x0a, 0x0c, 0x73,
-     0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
-     0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0b, 0x73, 0x74, 0x72,
-     0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x25, 0x0a, 0x0d,
-     0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75,
-     0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x48, 0x01, 0x52, 0x0c, 0x70,
-     0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12,
-     0x51, 0x0a, 0x0c, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x76, 0x61,
-     0x6c, 0x75, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 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, 0x2e, 0x4e, 0x65, 0x73, 0x74,
-     0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x01, 0x52, 0x0b, 0x6e,
-     0x65, 0x73, 0x74, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x2c,
-     0x0a, 0x11, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x6a, 0x73, 0x6f,
-     0x6e, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28,
-     0x09, 0x48, 0x01, 0x52, 0x0f, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x4a,
-     0x73, 0x6f, 0x6e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0xda, 0x03, 0x0a,
-     0x0b, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65,
-     0x12, 0x58, 0x0a, 0x0b, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 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, 0x44, 0x65, 0x62, 0x75, 0x67, 0x41, 0x6e, 0x6e,
-     0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4e, 0x65, 0x73, 0x74,
-     0x65, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x2e, 0x4e, 0x65, 0x73, 0x74,
-     0x65, 0x64, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x6e, 0x65, 0x73, 0x74,
-     0x65, 0x64, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x69,
-     0x63, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
-     0x09, 0x52, 0x08, 0x64, 0x69, 0x63, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x12,
-     0x4d, 0x0a, 0x0b, 0x64, 0x69, 0x63, 0x74, 0x5f, 0x76, 0x61, 0x6c, 0x75,
-     0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 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, 0x2e, 0x4e, 0x65, 0x73, 0x74, 0x65,
-     0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0a, 0x64, 0x69, 0x63, 0x74,
-     0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x4f, 0x0a, 0x0c, 0x61, 0x72,
-     0x72, 0x61, 0x79, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x04,
-     0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 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, 0x2e, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x56, 0x61, 0x6c,
-     0x75, 0x65, 0x52, 0x0b, 0x61, 0x72, 0x72, 0x61, 0x79, 0x56, 0x61, 0x6c,
-     0x75, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x6e, 0x74, 0x5f, 0x76,
-     0x61, 0x6c, 0x75, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08,
-     0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x21, 0x0a, 0x0c,
-     0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65,
-     0x18, 0x06, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x64, 0x6f, 0x75, 0x62,
-     0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x62,
-     0x6f, 0x6f, 0x6c, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x07, 0x20,
-     0x01, 0x28, 0x08, 0x52, 0x09, 0x62, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c,
-     0x75, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
-     0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09,
-     0x52, 0x0b, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75,
-     0x65, 0x22, 0x32, 0x0a, 0x0a, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x54,
-     0x79, 0x70, 0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x4e, 0x53, 0x50, 0x45,
-     0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04,
-     0x44, 0x49, 0x43, 0x54, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x52,
-     0x52, 0x41, 0x59, 0x10, 0x02, 0x42, 0x0c, 0x0a, 0x0a, 0x6e, 0x61, 0x6d,
-     0x65, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x42, 0x07, 0x0a, 0x05, 0x76,
-     0x61, 0x6c, 0x75, 0x65, 0x22, 0x3b, 0x0a, 0x13, 0x44, 0x65, 0x62, 0x75,
-     0x67, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 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, 0x0a, 0xd7, 0x01, 0x0a, 0x33, 0x70, 0x72,
-     0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74,
-     0x6f, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x63,
-     0x6b, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2f, 0x6c, 0x6f, 0x67, 0x5f,
-     0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
-     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0x57, 0x0a, 0x0a, 0x4c, 0x6f,
-     0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2e, 0x0a, 0x13,
-     0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74,
-     0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
-     0x04, 0x52, 0x11, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x6f, 0x63,
-     0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08,
-     0x62, 0x6f, 0x64, 0x79, 0x5f, 0x69, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01,
-     0x28, 0x04, 0x52, 0x07, 0x62, 0x6f, 0x64, 0x79, 0x49, 0x69, 0x64, 0x22,
-     0x36, 0x0a, 0x0e, 0x4c, 0x6f, 0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67,
-     0x65, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x69, 0x69, 0x64,
-     0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x69, 0x69, 0x64, 0x12,
-     0x12, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28,
-     0x09, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x0a, 0x82, 0x01, 0x0a, 0x36,
-     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65,
-     0x74, 0x74, 0x6f, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x65, 0x2f, 0x74, 0x72,
-     0x61, 0x63, 0x6b, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x2f, 0x74, 0x61,
-     0x73, 0x6b, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e,
-     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66,
-     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22,
-     0x37, 0x0a, 0x0d, 0x54, 0x61, 0x73, 0x6b, 0x45, 0x78, 0x65, 0x63, 0x75,
-     0x74, 0x69, 0x6f, 0x6e, 0x12, 0x26, 0x0a, 0x0f, 0x70, 0x6f, 0x73, 0x74,
-     0x65, 0x64, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x69, 0x69, 0x64, 0x18,
-     0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x70, 0x6f, 0x73, 0x74, 0x65,
-     0x64, 0x46, 0x72, 0x6f, 0x6d, 0x49, 0x69, 0x64, 0x0a, 0xdb, 0x03, 0x0a,
-     0x45, 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, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63,
-     0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 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, 0x80, 0x03, 0x0a, 0x1a, 0x43, 0x68, 0x72, 0x6f,
-     0x6d, 0x65, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
-     0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x6f,
-     0x0a, 0x11, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
-     0x6e, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
-     0x0e, 0x32, 0x42, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
-     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f,
-     0x6d, 0x65, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
-     0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x43,
-     0x68, 0x72, 0x6f, 0x6d, 0x65, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61,
-     0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x10, 0x61,
-     0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74,
-     0x61, 0x74, 0x65, 0x22, 0xf0, 0x01, 0x0a, 0x16, 0x43, 0x68, 0x72, 0x6f,
-     0x6d, 0x65, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f,
-     0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x19, 0x41, 0x50,
-     0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54,
-     0x41, 0x54, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10,
-     0x00, 0x12, 0x2c, 0x0a, 0x28, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41,
-     0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x48,
-     0x41, 0x53, 0x5f, 0x52, 0x55, 0x4e, 0x4e, 0x49, 0x4e, 0x47, 0x5f, 0x41,
-     0x43, 0x54, 0x49, 0x56, 0x49, 0x54, 0x49, 0x45, 0x53, 0x10, 0x01, 0x12,
-     0x2b, 0x0a, 0x27, 0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49,
-     0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x48, 0x41, 0x53,
-     0x5f, 0x50, 0x41, 0x55, 0x53, 0x45, 0x44, 0x5f, 0x41, 0x43, 0x54, 0x49,
-     0x56, 0x49, 0x54, 0x49, 0x45, 0x53, 0x10, 0x02, 0x12, 0x2c, 0x0a, 0x28,
-     0x41, 0x50, 0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f,
-     0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x48, 0x41, 0x53, 0x5f, 0x53, 0x54,
-     0x4f, 0x50, 0x50, 0x45, 0x44, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x49,
-     0x54, 0x49, 0x45, 0x53, 0x10, 0x03, 0x12, 0x2e, 0x0a, 0x2a, 0x41, 0x50,
-     0x50, 0x4c, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53, 0x54,
-     0x41, 0x54, 0x45, 0x5f, 0x48, 0x41, 0x53, 0x5f, 0x44, 0x45, 0x53, 0x54,
-     0x52, 0x4f, 0x59, 0x45, 0x44, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x49,
-     0x54, 0x49, 0x45, 0x53, 0x10, 0x04, 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, 0x0a, 0xb1, 0x01, 0x0a, 0x4a, 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, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x74,
-     0x74, 0x69, 0x6e, 0x67, 0x73, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 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, 0x52, 0x0a, 0x1e, 0x43, 0x68, 0x72, 0x6f, 0x6d,
-     0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x74, 0x74,
-     0x69, 0x6e, 0x67, 0x73, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66,
-     0x6f, 0x12, 0x30, 0x0a, 0x14, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f,
-     0x6f, 0x66, 0x5f, 0x65, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6f, 0x6e,
-     0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x6e, 0x75, 0x6d,
-     0x62, 0x65, 0x72, 0x4f, 0x66, 0x45, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69,
-     0x6f, 0x6e, 0x73, 0x0a, 0xe0, 0x04, 0x0a, 0x3d, 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, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x72, 0x65, 0x70, 0x6f, 0x72,
-     0x74, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70,
-     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x73, 0x22, 0x8d, 0x04, 0x0a, 0x13, 0x43, 0x68, 0x72, 0x6f, 0x6d,
-     0x65, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74,
-     0x65, 0x72, 0x12, 0x40, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18,
-     0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2a, 0x2e, 0x70, 0x65, 0x72, 0x66,
-     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
-     0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52,
-     0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x72, 0x2e, 0x53, 0x74, 0x61, 0x74,
-     0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x4c, 0x0a, 0x06,
-     0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e,
-     0x32, 0x34, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e,
-     0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d,
-     0x65, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74,
-     0x65, 0x72, 0x2e, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x44, 0x72, 0x6f, 0x70,
-     0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73,
-     0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f,
-     0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04,
-     0x52, 0x0b, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63,
-     0x65, 0x12, 0x25, 0x0a, 0x0e, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x73,
-     0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28,
-     0x04, 0x52, 0x0d, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x53, 0x65, 0x71, 0x75,
-     0x65, 0x6e, 0x63, 0x65, 0x12, 0x2d, 0x0a, 0x12, 0x61, 0x66, 0x66, 0x65,
-     0x63, 0x74, 0x73, 0x5f, 0x73, 0x6d, 0x6f, 0x6f, 0x74, 0x68, 0x6e, 0x65,
-     0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x61, 0x66,
-     0x66, 0x65, 0x63, 0x74, 0x73, 0x53, 0x6d, 0x6f, 0x6f, 0x74, 0x68, 0x6e,
-     0x65, 0x73, 0x73, 0x22, 0x6d, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65,
-     0x12, 0x1b, 0x0a, 0x17, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x4e, 0x4f,
-     0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x44, 0x45, 0x53, 0x49,
-     0x52, 0x45, 0x44, 0x10, 0x00, 0x12, 0x17, 0x0a, 0x13, 0x53, 0x54, 0x41,
-     0x54, 0x45, 0x5f, 0x50, 0x52, 0x45, 0x53, 0x45, 0x4e, 0x54, 0x45, 0x44,
-     0x5f, 0x41, 0x4c, 0x4c, 0x10, 0x01, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x54,
-     0x41, 0x54, 0x45, 0x5f, 0x50, 0x52, 0x45, 0x53, 0x45, 0x4e, 0x54, 0x45,
-     0x44, 0x5f, 0x50, 0x41, 0x52, 0x54, 0x49, 0x41, 0x4c, 0x10, 0x02, 0x12,
-     0x11, 0x0a, 0x0d, 0x53, 0x54, 0x41, 0x54, 0x45, 0x5f, 0x44, 0x52, 0x4f,
-     0x50, 0x50, 0x45, 0x44, 0x10, 0x03, 0x22, 0x7e, 0x0a, 0x0f, 0x46, 0x72,
-     0x61, 0x6d, 0x65, 0x44, 0x72, 0x6f, 0x70, 0x52, 0x65, 0x61, 0x73, 0x6f,
-     0x6e, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f,
-     0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10,
-     0x00, 0x12, 0x1d, 0x0a, 0x19, 0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f,
-     0x44, 0x49, 0x53, 0x50, 0x4c, 0x41, 0x59, 0x5f, 0x43, 0x4f, 0x4d, 0x50,
-     0x4f, 0x53, 0x49, 0x54, 0x4f, 0x52, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12,
-     0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x4d, 0x41, 0x49, 0x4e, 0x5f,
-     0x54, 0x48, 0x52, 0x45, 0x41, 0x44, 0x10, 0x02, 0x12, 0x1c, 0x0a, 0x18,
-     0x52, 0x45, 0x41, 0x53, 0x4f, 0x4e, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e,
-     0x54, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x4f, 0x52,
-     0x10, 0x03, 0x0a, 0x86, 0x02, 0x0a, 0x3f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-     0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x74,
-     0x72, 0x61, 0x63, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x65,
-     0x76, 0x65, 0x6e, 0x74, 0x2f, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x5f,
-     0x68, 0x69, 0x73, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x6d, 0x5f, 0x73, 0x61,
-     0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f,
-     0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
-     0x74, 0x6f, 0x73, 0x22, 0x35, 0x0a, 0x0d, 0x48, 0x69, 0x73, 0x74, 0x6f,
-     0x67, 0x72, 0x61, 0x6d, 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, 0x22, 0x7b,
-     0x0a, 0x15, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x48, 0x69, 0x73, 0x74,
-     0x6f, 0x67, 0x72, 0x61, 0x6d, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x12,
-     0x1b, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x68, 0x61, 0x73, 0x68,
-     0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6e, 0x61, 0x6d, 0x65,
-     0x48, 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
-     0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
-     0x12, 0x16, 0x0a, 0x06, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x18, 0x03,
-     0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65,
-     0x12, 0x19, 0x0a, 0x08, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x69, 0x69, 0x64,
-     0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x6e, 0x61, 0x6d, 0x65,
-     0x49, 0x69, 0x64, 0x0a, 0x79, 0x0a, 0x3c, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-     0x73, 0x2f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2f, 0x74,
-     0x72, 0x61, 0x63, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x63, 0x6b, 0x5f, 0x65,
-     0x76, 0x65, 0x6e, 0x74, 0x2f, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x5f,
-     0x6b, 0x65, 0x79, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
-     0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72,
-     0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
-     0x22, 0x28, 0x0a, 0x12, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x4b, 0x65,
-     0x79, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x12,
-     0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
-     0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x0a, 0x89, 0x0d, 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, 0xb8, 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, 0x12, 0x2a, 0x0a, 0x11, 0x67, 0x65,
-     0x73, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x73, 0x63, 0x72, 0x6f, 0x6c, 0x6c,
-     0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x67,
-     0x65, 0x73, 0x74, 0x75, 0x72, 0x65, 0x53, 0x63, 0x72, 0x6f, 0x6c, 0x6c,
-     0x49, 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, 0x74, 0x2f, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x5f,
-     0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x69, 0x70, 0x63, 0x2e, 0x70,
-     0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
-     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x22, 0xe9, 0x07,
-     0x0a, 0x0f, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x4c, 0x65, 0x67, 0x61,
-     0x63, 0x79, 0x49, 0x70, 0x63, 0x12, 0x52, 0x0a, 0x0d, 0x6d, 0x65, 0x73,
-     0x73, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x01,
-     0x20, 0x01, 0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65,
-     0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43,
-     0x68, 0x72, 0x6f, 0x6d, 0x65, 0x4c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x49,
-     0x70, 0x63, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x43, 0x6c,
-     0x61, 0x73, 0x73, 0x52, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
-     0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x65, 0x73,
-     0x73, 0x61, 0x67, 0x65, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x02, 0x20,
-     0x01, 0x28, 0x0d, 0x52, 0x0b, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
-     0x4c, 0x69, 0x6e, 0x65, 0x22, 0xde, 0x06, 0x0a, 0x0c, 0x4d, 0x65, 0x73,
-     0x73, 0x61, 0x67, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x15, 0x0a,
-     0x11, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45,
-     0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10,
-     0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x41, 0x55, 0x54, 0x4f, 0x4d, 0x41,
-     0x54, 0x49, 0x4f, 0x4e, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x4c,
-     0x41, 0x53, 0x53, 0x5f, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x10, 0x02, 0x12,
-     0x0e, 0x0a, 0x0a, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x50, 0x41, 0x47,
-     0x45, 0x10, 0x03, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x4c, 0x41, 0x53, 0x53,
-     0x5f, 0x56, 0x49, 0x45, 0x57, 0x10, 0x04, 0x12, 0x10, 0x0a, 0x0c, 0x43,
-     0x4c, 0x41, 0x53, 0x53, 0x5f, 0x57, 0x49, 0x44, 0x47, 0x45, 0x54, 0x10,
-     0x05, 0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x49,
-     0x4e, 0x50, 0x55, 0x54, 0x10, 0x06, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x4c,
-     0x41, 0x53, 0x53, 0x5f, 0x54, 0x45, 0x53, 0x54, 0x10, 0x07, 0x12, 0x10,
-     0x0a, 0x0c, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x57, 0x4f, 0x52, 0x4b,
-     0x45, 0x52, 0x10, 0x08, 0x12, 0x0e, 0x0a, 0x0a, 0x43, 0x4c, 0x41, 0x53,
-     0x53, 0x5f, 0x4e, 0x41, 0x43, 0x4c, 0x10, 0x09, 0x12, 0x15, 0x0a, 0x11,
-     0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x47, 0x50, 0x55, 0x5f, 0x43, 0x48,
-     0x41, 0x4e, 0x4e, 0x45, 0x4c, 0x10, 0x0a, 0x12, 0x0f, 0x0a, 0x0b, 0x43,
-     0x4c, 0x41, 0x53, 0x53, 0x5f, 0x4d, 0x45, 0x44, 0x49, 0x41, 0x10, 0x0b,
-     0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x50, 0x50,
-     0x41, 0x50, 0x49, 0x10, 0x0c, 0x12, 0x10, 0x0a, 0x0c, 0x43, 0x4c, 0x41,
-     0x53, 0x53, 0x5f, 0x43, 0x48, 0x52, 0x4f, 0x4d, 0x45, 0x10, 0x0d, 0x12,
-     0x0e, 0x0a, 0x0a, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x44, 0x52, 0x41,
-     0x47, 0x10, 0x0e, 0x12, 0x0f, 0x0a, 0x0b, 0x43, 0x4c, 0x41, 0x53, 0x53,
-     0x5f, 0x50, 0x52, 0x49, 0x4e, 0x54, 0x10, 0x0f, 0x12, 0x13, 0x0a, 0x0f,
-     0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x45, 0x58, 0x54, 0x45, 0x4e, 0x53,
-     0x49, 0x4f, 0x4e, 0x10, 0x10, 0x12, 0x1b, 0x0a, 0x17, 0x43, 0x4c, 0x41,
-     0x53, 0x53, 0x5f, 0x54, 0x45, 0x58, 0x54, 0x5f, 0x49, 0x4e, 0x50, 0x55,
-     0x54, 0x5f, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x10, 0x11, 0x12, 0x14,
-     0x0a, 0x10, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x42, 0x4c, 0x49, 0x4e,
-     0x4b, 0x5f, 0x54, 0x45, 0x53, 0x54, 0x10, 0x12, 0x12, 0x17, 0x0a, 0x13,
-     0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x41, 0x43, 0x43, 0x45, 0x53, 0x53,
-     0x49, 0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x10, 0x13, 0x12, 0x13, 0x0a,
-     0x0f, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x50, 0x52, 0x45, 0x52, 0x45,
-     0x4e, 0x44, 0x45, 0x52, 0x10, 0x14, 0x12, 0x14, 0x0a, 0x10, 0x43, 0x4c,
-     0x41, 0x53, 0x53, 0x5f, 0x43, 0x48, 0x52, 0x4f, 0x4d, 0x4f, 0x54, 0x49,
-     0x4e, 0x47, 0x10, 0x15, 0x12, 0x18, 0x0a, 0x14, 0x43, 0x4c, 0x41, 0x53,
-     0x53, 0x5f, 0x42, 0x52, 0x4f, 0x57, 0x53, 0x45, 0x52, 0x5f, 0x50, 0x4c,
-     0x55, 0x47, 0x49, 0x4e, 0x10, 0x16, 0x12, 0x1a, 0x0a, 0x16, 0x43, 0x4c,
-     0x41, 0x53, 0x53, 0x5f, 0x41, 0x4e, 0x44, 0x52, 0x4f, 0x49, 0x44, 0x5f,
-     0x57, 0x45, 0x42, 0x5f, 0x56, 0x49, 0x45, 0x57, 0x10, 0x17, 0x12, 0x13,
-     0x0a, 0x0f, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x4e, 0x41, 0x43, 0x4c,
-     0x5f, 0x48, 0x4f, 0x53, 0x54, 0x10, 0x18, 0x12, 0x19, 0x0a, 0x15, 0x43,
-     0x4c, 0x41, 0x53, 0x53, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59, 0x50, 0x54,
-     0x45, 0x44, 0x5f, 0x4d, 0x45, 0x44, 0x49, 0x41, 0x10, 0x19, 0x12, 0x0e,
-     0x0a, 0x0a, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x43, 0x41, 0x53, 0x54,
-     0x10, 0x1a, 0x12, 0x19, 0x0a, 0x15, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f,
-     0x47, 0x49, 0x4e, 0x5f, 0x4a, 0x41, 0x56, 0x41, 0x5f, 0x42, 0x52, 0x49,
-     0x44, 0x47, 0x45, 0x10, 0x1b, 0x12, 0x21, 0x0a, 0x1d, 0x43, 0x4c, 0x41,
-     0x53, 0x53, 0x5f, 0x43, 0x48, 0x52, 0x4f, 0x4d, 0x45, 0x5f, 0x55, 0x54,
-     0x49, 0x4c, 0x49, 0x54, 0x59, 0x5f, 0x50, 0x52, 0x49, 0x4e, 0x54, 0x49,
-     0x4e, 0x47, 0x10, 0x1c, 0x12, 0x13, 0x0a, 0x0f, 0x43, 0x4c, 0x41, 0x53,
-     0x53, 0x5f, 0x4f, 0x5a, 0x4f, 0x4e, 0x45, 0x5f, 0x47, 0x50, 0x55, 0x10,
-     0x1d, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x57,
-     0x45, 0x42, 0x5f, 0x54, 0x45, 0x53, 0x54, 0x10, 0x1e, 0x12, 0x17, 0x0a,
-     0x13, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x4e, 0x45, 0x54, 0x57, 0x4f,
-     0x52, 0x4b, 0x5f, 0x48, 0x49, 0x4e, 0x54, 0x53, 0x10, 0x1f, 0x12, 0x1f,
-     0x0a, 0x1b, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x45, 0x58, 0x54, 0x45,
-     0x4e, 0x53, 0x49, 0x4f, 0x4e, 0x53, 0x5f, 0x47, 0x55, 0x45, 0x53, 0x54,
-     0x5f, 0x56, 0x49, 0x45, 0x57, 0x10, 0x20, 0x12, 0x14, 0x0a, 0x10, 0x43,
-     0x4c, 0x41, 0x53, 0x53, 0x5f, 0x47, 0x55, 0x45, 0x53, 0x54, 0x5f, 0x56,
-     0x49, 0x45, 0x57, 0x10, 0x21, 0x12, 0x1f, 0x0a, 0x1b, 0x43, 0x4c, 0x41,
-     0x53, 0x53, 0x5f, 0x4d, 0x45, 0x44, 0x49, 0x41, 0x5f, 0x50, 0x4c, 0x41,
-     0x59, 0x45, 0x52, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x47, 0x41, 0x54, 0x45,
-     0x10, 0x22, 0x12, 0x1a, 0x0a, 0x16, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f,
-     0x45, 0x58, 0x54, 0x45, 0x4e, 0x53, 0x49, 0x4f, 0x4e, 0x5f, 0x57, 0x4f,
-     0x52, 0x4b, 0x45, 0x52, 0x10, 0x23, 0x12, 0x1c, 0x0a, 0x18, 0x43, 0x4c,
-     0x41, 0x53, 0x53, 0x5f, 0x53, 0x55, 0x42, 0x52, 0x45, 0x53, 0x4f, 0x55,
-     0x52, 0x43, 0x45, 0x5f, 0x46, 0x49, 0x4c, 0x54, 0x45, 0x52, 0x10, 0x24,
-     0x12, 0x1b, 0x0a, 0x17, 0x43, 0x4c, 0x41, 0x53, 0x53, 0x5f, 0x55, 0x4e,
-     0x46, 0x52, 0x45, 0x45, 0x5a, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x46, 0x52,
-     0x41, 0x4d, 0x45, 0x10, 0x25, 0x0a, 0x98, 0x01, 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, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x70,
-     0x75, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70,
-     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x73, 0x22, 0x48, 0x0a, 0x11, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65,
-     0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x50, 0x75, 0x6d, 0x70, 0x12,
-     0x33, 0x0a, 0x16, 0x73, 0x65, 0x6e, 0x74, 0x5f, 0x6d, 0x65, 0x73, 0x73,
-     0x61, 0x67, 0x65, 0x73, 0x5f, 0x69, 0x6e, 0x5f, 0x71, 0x75, 0x65, 0x75,
-     0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x73, 0x65, 0x6e,
-     0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x49, 0x6e, 0x51,
-     0x75, 0x65, 0x75, 0x65, 0x0a, 0xa9, 0x01, 0x0a, 0x3e, 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, 0x6d, 0x6f, 0x6a, 0x6f, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74,
-     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, 0x56, 0x0a, 0x13, 0x43, 0x68, 0x72, 0x6f,
-     0x6d, 0x65, 0x4d, 0x6f, 0x6a, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x49,
-     0x6e, 0x66, 0x6f, 0x12, 0x3f, 0x0a, 0x1c, 0x77, 0x61, 0x74, 0x63, 0x68,
-     0x65, 0x72, 0x5f, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x5f, 0x69, 0x6e,
-     0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x61, 0x67, 0x18,
-     0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x19, 0x77, 0x61, 0x74, 0x63, 0x68,
-     0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x49, 0x6e, 0x74, 0x65,
-     0x72, 0x66, 0x61, 0x63, 0x65, 0x54, 0x61, 0x67, 0x0a, 0xb7, 0x02, 0x0a,
-     0x47, 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, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72,
-     0x65, 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, 0x22, 0x5c, 0x0a, 0x1c, 0x43, 0x68, 0x72,
-     0x6f, 0x6d, 0x65, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x53,
-     0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74,
-     0x65, 0x12, 0x3c, 0x0a, 0x09, 0x72, 0x61, 0x69, 0x6c, 0x5f, 0x6d, 0x6f,
-     0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1f, 0x2e, 0x70,
-     0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
-     0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x52, 0x41, 0x49,
-     0x4c, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x08, 0x72, 0x61, 0x69, 0x6c, 0x4d,
-     0x6f, 0x64, 0x65, 0x2a, 0x7d, 0x0a, 0x0e, 0x43, 0x68, 0x72, 0x6f, 0x6d,
-     0x65, 0x52, 0x41, 0x49, 0x4c, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a,
-     0x0e, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x4e,
-     0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x52, 0x41, 0x49,
-     0x4c, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x53, 0x50, 0x4f,
-     0x4e, 0x53, 0x45, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x41, 0x49,
-     0x4c, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x41, 0x4e, 0x49, 0x4d, 0x41,
-     0x54, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x41,
-     0x49, 0x4c, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x49, 0x44, 0x4c, 0x45,
-     0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x41, 0x49, 0x4c, 0x5f, 0x4d,
-     0x4f, 0x44, 0x45, 0x5f, 0x4c, 0x4f, 0x41, 0x44, 0x10, 0x04, 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, 0xaa, 0x01,
-     0x0a, 0x47, 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, 0x77, 0x69, 0x6e, 0x64, 0x6f,
-     0x77, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x5f, 0x65, 0x76, 0x65,
-     0x6e, 0x74, 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, 0x4e, 0x0a, 0x1b, 0x43, 0x68,
-     0x72, 0x6f, 0x6d, 0x65, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x48, 0x61,
-     0x6e, 0x64, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66,
-     0x6f, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x70, 0x69, 0x18, 0x01, 0x20, 0x01,
-     0x28, 0x0d, 0x52, 0x03, 0x64, 0x70, 0x69, 0x12, 0x1d, 0x0a, 0x0a, 0x6d,
-     0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20,
-     0x01, 0x28, 0x0d, 0x52, 0x09, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
-     0x49, 0x64, 0x0a, 0xdb, 0x26, 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,
-     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, 0x45, 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, 0x61, 0x70, 0x70,
-     0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x61,
-     0x74, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 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, 0x4a, 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, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69,
-     0x6e, 0x67, 0x73, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e,
-     0x66, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x3d, 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, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x72, 0x65, 0x70,
-     0x6f, 0x72, 0x74, 0x65, 0x72, 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, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x5f, 0x69, 0x70, 0x63,
-     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, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x70, 0x75, 0x6d,
-     0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x3e, 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, 0x6d, 0x6f, 0x6a, 0x6f, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74,
-     0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a,
-     0x47, 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, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72,
-     0x65, 0x72, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72,
-     0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 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, 0x1a,
-     0x47, 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, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77,
-     0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x5f, 0x65, 0x76, 0x65, 0x6e,
-     0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-     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, 0xfa, 0x1b,
-     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, 0x19, 0x0a, 0x08, 0x66, 0x6c, 0x6f, 0x77, 0x5f,
-     0x69, 0x64, 0x73, 0x18, 0x24, 0x20, 0x03, 0x28, 0x04, 0x52, 0x07, 0x66,
-     0x6c, 0x6f, 0x77, 0x49, 0x64, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x74, 0x65,
-     0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x66, 0x6c,
-     0x6f, 0x77, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x2a, 0x20, 0x03, 0x28, 0x04,
-     0x52, 0x12, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6e,
-     0x67, 0x46, 0x6c, 0x6f, 0x77, 0x49, 0x64, 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, 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, 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, 0x58, 0x0a, 0x15, 0x63, 0x68,
-     0x72, 0x6f, 0x6d, 0x65, 0x5f, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x5f, 0x72,
-     0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x72, 0x18, 0x20, 0x20, 0x01, 0x28,
-     0x0b, 0x32, 0x24, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74, 0x74, 0x6f,
-     0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f,
-     0x6d, 0x65, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72,
-     0x74, 0x65, 0x72, 0x52, 0x13, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x46,
-     0x72, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x72,
-     0x12, 0x6e, 0x0a, 0x1d, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x5f, 0x61,
-     0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73,
-     0x74, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x27, 0x20,
-     0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, 0x65, 0x72, 0x66, 0x65, 0x74,
-     0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x43, 0x68,
-     0x72, 0x6f, 0x6d, 0x65, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74,
-     0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x49, 0x6e, 0x66, 0x6f,
-     0x52, 0x1a, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x41, 0x70, 0x70, 0x6c,
-     0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x65,
-     0x49, 0x6e, 0x66, 0x6f, 0x12, 0x74, 0x0a, 0x1f, 0x63, 0x68, 0x72, 0x6f,
-     0x6d, 0x65, 0x5f, 0x72, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x5f,
-     0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x5f, 0x73, 0x74,
-     0x61, 0x74, 0x65, 0x18, 0x28, 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, 0x52, 0x65,
-     0x6e, 0x64, 0x65, 0x72, 0x65, 0x72, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75,
-     0x6c, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x1c, 0x63, 0x68,
-     0x72, 0x6f, 0x6d, 0x65, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x65, 0x72,
-     0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x53, 0x74, 0x61,
-     0x74, 0x65, 0x12, 0x72, 0x0a, 0x1f, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65,
-     0x5f, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x5f, 0x68, 0x61, 0x6e, 0x64,
-     0x6c, 0x65, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x66,
-     0x6f, 0x18, 0x29, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x70, 0x65,
-     0x72, 0x66, 0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
-     0x73, 0x2e, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x57, 0x69, 0x6e, 0x64,
-     0x6f, 0x77, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e,
-     0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x1b, 0x63, 0x68, 0x72, 0x6f, 0x6d,
-     0x65, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x48, 0x61, 0x6e, 0x64, 0x6c,
-     0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x7b,
-     0x0a, 0x22, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x5f, 0x63, 0x6f, 0x6e,
-     0x74, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
-     0x73, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f,
-     0x18, 0x2b, 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, 0x6e, 0x74, 0x65,
-     0x6e, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x45, 0x76,
-     0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x1e, 0x63, 0x68, 0x72,
-     0x6f, 0x6d, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x53, 0x65,
-     0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x49,
-     0x6e, 0x66, 0x6f, 0x12, 0x4a, 0x0a, 0x0f, 0x73, 0x6f, 0x75, 0x72, 0x63,
-     0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x21,
-     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, 0x01, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c,
-     0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x13, 0x73,
-     0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69,
-     0x6f, 0x6e, 0x5f, 0x69, 0x69, 0x64, 0x18, 0x22, 0x20, 0x01, 0x28, 0x04,
-     0x48, 0x01, 0x52, 0x11, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x6f,
-     0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x69, 0x64, 0x12, 0x52, 0x0a,
-     0x13, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x5f, 0x6d, 0x65, 0x73, 0x73,
-     0x61, 0x67, 0x65, 0x5f, 0x70, 0x75, 0x6d, 0x70, 0x18, 0x23, 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, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x50, 0x75,
-     0x6d, 0x70, 0x52, 0x11, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x4d, 0x65,
-     0x73, 0x73, 0x61, 0x67, 0x65, 0x50, 0x75, 0x6d, 0x70, 0x12, 0x59, 0x0a,
-     0x16, 0x63, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x5f, 0x6d, 0x6f, 0x6a, 0x6f,
-     0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18,
-     0x26, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x65, 0x72, 0x66,
-     0x65, 0x74, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e,
-     0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x4d, 0x6f, 0x6a, 0x6f, 0x45, 0x76,
-     0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x13, 0x63, 0x68, 0x72,
-     0x6f, 0x6d, 0x65, 0x4d, 0x6f, 0x6a, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74,
-     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, 0x02, 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,
-     0x02, 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, 0x03, 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, 0x03,
-     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, 0x04, 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, 0x04, 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, 0x2a,
-     0x06, 0x08, 0xe8, 0x07, 0x10, 0xac, 0x4d, 0x2a, 0x06, 0x08, 0xac, 0x4d,
-     0x10, 0x91, 0x4e, 0x42, 0x0c, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x5f,
-     0x66, 0x69, 0x65, 0x6c, 0x64, 0x42, 0x17, 0x0a, 0x15, 0x73, 0x6f, 0x75,
-     0x72, 0x63, 0x65, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e,
-     0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x42, 0x0b, 0x0a, 0x09, 0x74, 0x69,
-     0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 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
-
-#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_TRACK_EVENT_DESCRIPTOR_H_
diff --git a/src/trace_processor/importers/proto/track_event_module.cc b/src/trace_processor/importers/proto/track_event_module.cc
index efa0a56..7197046 100644
--- a/src/trace_processor/importers/proto/track_event_module.cc
+++ b/src/trace_processor/importers/proto/track_event_module.cc
@@ -16,10 +16,10 @@
 #include "src/trace_processor/importers/proto/track_event_module.h"
 
 #include "perfetto/base/build_config.h"
+#include "perfetto/base/logging.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "src/trace_processor/importers/common/track_tracker.h"
 #include "src/trace_processor/importers/proto/track_event_tracker.h"
-#include "src/trace_processor/timestamped_trace_piece.h"
 #include "src/trace_processor/types/trace_processor_context.h"
 
 #include "protos/perfetto/config/data_source_config.pbzero.h"
@@ -35,6 +35,7 @@
     : track_event_tracker_(new TrackEventTracker(context)),
       tokenizer_(context, track_event_tracker_.get()),
       parser_(context, track_event_tracker_.get()) {
+  RegisterForField(TracePacket::kTrackEventRangeOfInterestFieldNumber, context);
   RegisterForField(TracePacket::kTrackEventFieldNumber, context);
   RegisterForField(TracePacket::kTrackDescriptorFieldNumber, context);
   RegisterForField(TracePacket::kThreadDescriptorFieldNumber, context);
@@ -50,6 +51,9 @@
     PacketSequenceState* state,
     uint32_t field_id) {
   switch (field_id) {
+    case TracePacket::kTrackEventRangeOfInterestFieldNumber:
+      return tokenizer_.TokenizeRangeOfInterestPacket(state, decoder,
+                                                      packet_timestamp);
     case TracePacket::kTrackDescriptorFieldNumber:
       return tokenizer_.TokenizeTrackDescriptorPacket(state, decoder,
                                                       packet_timestamp);
@@ -64,29 +68,32 @@
   return ModuleResult::Ignored();
 }
 
-void TrackEventModule::ParsePacket(const TracePacket::Decoder& decoder,
-                                   const TimestampedTracePiece& ttp,
-                                   uint32_t field_id) {
+void TrackEventModule::ParseTrackEventData(const TracePacket::Decoder& decoder,
+                                           int64_t ts,
+                                           const TrackEventData& data) {
+  parser_.ParseTrackEvent(ts, &data, decoder.track_event(),
+                          decoder.trusted_packet_sequence_id());
+}
+
+void TrackEventModule::ParseTracePacketData(const TracePacket::Decoder& decoder,
+                                            int64_t ts,
+                                            const TracePacketData&,
+                                            uint32_t field_id) {
   switch (field_id) {
     case TracePacket::kTrackDescriptorFieldNumber:
-      PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kTracePacket);
-      parser_.ParseTrackDescriptor(decoder.track_descriptor());
-      break;
-    case TracePacket::kTrackEventFieldNumber:
-      PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kTrackEvent);
-      parser_.ParseTrackEvent(ttp.timestamp, &ttp.track_event_data,
-                              decoder.track_event());
+      parser_.ParseTrackDescriptor(ts, decoder.track_descriptor(),
+                                   decoder.trusted_packet_sequence_id());
       break;
     case TracePacket::kProcessDescriptorFieldNumber:
       // TODO(eseckler): Remove once Chrome has switched to TrackDescriptors.
-      PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kTracePacket);
-      parser_.ParseProcessDescriptor(decoder.process_descriptor());
+      parser_.ParseProcessDescriptor(ts, decoder.process_descriptor());
       break;
     case TracePacket::kThreadDescriptorFieldNumber:
       // TODO(eseckler): Remove once Chrome has switched to TrackDescriptors.
-      PERFETTO_DCHECK(ttp.type == TimestampedTracePiece::Type::kTracePacket);
       parser_.ParseThreadDescriptor(decoder.thread_descriptor());
       break;
+    case TracePacket::kTrackEventFieldNumber:
+      PERFETTO_DFATAL("Wrong TracePacket number");
   }
 }
 
@@ -94,5 +101,13 @@
   track_event_tracker_->OnIncrementalStateCleared(packet_sequence_id);
 }
 
+void TrackEventModule::OnFirstPacketOnSequence(uint32_t packet_sequence_id) {
+  track_event_tracker_->OnFirstPacketOnSequence(packet_sequence_id);
+}
+
+void TrackEventModule::NotifyEndOfFile() {
+  parser_.NotifyEndOfFile();
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/track_event_module.h b/src/trace_processor/importers/proto/track_event_module.h
index 0632747..2b68a88 100644
--- a/src/trace_processor/importers/proto/track_event_module.h
+++ b/src/trace_processor/importers/proto/track_event_module.h
@@ -41,9 +41,18 @@
 
   void OnIncrementalStateCleared(uint32_t) override;
 
-  void ParsePacket(const protos::pbzero::TracePacket::Decoder& decoder,
-                   const TimestampedTracePiece& ttp,
-                   uint32_t field_id) override;
+  void OnFirstPacketOnSequence(uint32_t) override;
+
+  void ParseTrackEventData(const protos::pbzero::TracePacket::Decoder& decoder,
+                           int64_t ts,
+                           const TrackEventData& data);
+
+  void ParseTracePacketData(const protos::pbzero::TracePacket::Decoder& decoder,
+                            int64_t ts,
+                            const TracePacketData& data,
+                            uint32_t field_id) override;
+
+  void NotifyEndOfFile() override;
 
  private:
   std::unique_ptr<TrackEventTracker> track_event_tracker_;
diff --git a/src/trace_processor/importers/proto/track_event_parser.cc b/src/trace_processor/importers/proto/track_event_parser.cc
index 71ed9fc..ab74bb1 100644
--- a/src/trace_processor/importers/proto/track_event_parser.cc
+++ b/src/trace_processor/importers/proto/track_event_parser.cc
@@ -30,6 +30,7 @@
 #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/packet_analyzer.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/track_event_tracker.h"
@@ -39,6 +40,7 @@
 
 #include "protos/perfetto/trace/extension_descriptor.pbzero.h"
 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
+#include "protos/perfetto/trace/track_event/chrome_active_processes.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_legacy_ipc.pbzero.h"
@@ -70,10 +72,12 @@
 
 class TrackEventArgsParser : public util::ProtoToArgsParser::Delegate {
  public:
-  TrackEventArgsParser(BoundInserter& inserter,
+  TrackEventArgsParser(int64_t packet_timestamp,
+                       BoundInserter& inserter,
                        TraceStorage& storage,
                        PacketSequenceStateGeneration& sequence_state)
-      : inserter_(inserter),
+      : packet_timestamp_(packet_timestamp),
+        inserter_(inserter),
         storage_(storage),
         sequence_state_(sequence_state) {}
 
@@ -146,9 +150,12 @@
     return sequence_state_.GetInternedMessageView(field_id, iid);
   }
 
+  int64_t packet_timestamp() final { return packet_timestamp_; }
+
   PacketSequenceStateGeneration* seq_state() final { return &sequence_state_; }
 
  private:
+  int64_t packet_timestamp_;
   BoundInserter& inserter_;
   TraceStorage& storage_;
   PacketSequenceStateGeneration& sequence_state_;
@@ -229,7 +236,8 @@
   EventImporter(TrackEventParser* parser,
                 int64_t ts,
                 const TrackEventData* event_data,
-                ConstBytes blob)
+                ConstBytes blob,
+                uint32_t packet_sequence_id)
       : context_(parser->context_),
         track_event_tracker_(parser->track_event_tracker_),
         storage_(context_->storage.get()),
@@ -237,13 +245,15 @@
         args_translation_table_(context_->args_translation_table.get()),
         ts_(ts),
         event_data_(event_data),
-        sequence_state_(event_data->sequence_state.get()),
+        sequence_state_(event_data->trace_packet_data.sequence_state.get()),
         blob_(std::move(blob)),
         event_(blob_),
         legacy_event_(event_.legacy_event()),
-        defaults_(event_data->sequence_state->GetTrackEventDefaults()),
+        defaults_(event_data->trace_packet_data.sequence_state
+                      ->GetTrackEventDefaults()),
         thread_timestamp_(event_data->thread_timestamp),
-        thread_instruction_count_(event_data->thread_instruction_count) {}
+        thread_instruction_count_(event_data->thread_instruction_count),
+        packet_sequence_id_(packet_sequence_id) {}
 
   util::Status Import() {
     // TODO(eseckler): This legacy event field will eventually be replaced by
@@ -254,6 +264,14 @@
     category_id_ = ParseTrackEventCategory();
     name_id_ = ParseTrackEventName();
 
+    if (context_->content_analyzer) {
+      PacketAnalyzer::SampleAnnotation annotation;
+      annotation.push_back({parser_->event_category_key_id_, category_id_});
+      annotation.push_back({parser_->event_name_key_id_, name_id_});
+      PacketAnalyzer::Get(context_)->ProcessPacket(
+          event_data_->trace_packet_data.packet, annotation);
+    }
+
     RETURN_IF_ERROR(ParseTrackAssociation());
 
     // Counter-type events don't support arguments (those are on the
@@ -409,13 +427,14 @@
     //   b) a default track.
     if (track_uuid_) {
       base::Optional<TrackId> opt_track_id =
-          track_event_tracker_->GetDescriptorTrack(track_uuid_, name_id_);
+          track_event_tracker_->GetDescriptorTrack(track_uuid_, name_id_,
+                                                   packet_sequence_id_);
       if (!opt_track_id) {
         track_event_tracker_->ReserveDescriptorChildTrack(track_uuid_,
                                                           /*parent_uuid=*/0,
                                                           name_id_);
-        opt_track_id =
-            track_event_tracker_->GetDescriptorTrack(track_uuid_, name_id_);
+        opt_track_id = track_event_tracker_->GetDescriptorTrack(
+            track_uuid_, name_id_, packet_sequence_id_);
       }
       track_id_ = *opt_track_id;
 
@@ -678,8 +697,8 @@
     PERFETTO_DCHECK(track_uuid_it);
     PERFETTO_DCHECK(index < TrackEventData::kMaxNumExtraCounters);
 
-    base::Optional<TrackId> track_id =
-        track_event_tracker_->GetDescriptorTrack(*track_uuid_it);
+    base::Optional<TrackId> track_id = track_event_tracker_->GetDescriptorTrack(
+        *track_uuid_it, kNullStringId, packet_sequence_id_);
     base::Optional<uint32_t> counter_row =
         storage_->counter_track_table().id().IndexOf(*track_id);
 
@@ -704,7 +723,7 @@
           "TrackEvent with phase B without thread association");
     }
 
-    auto* thread_slices = storage_->mutable_thread_slice_table();
+    auto* thread_slices = storage_->mutable_slice_table();
     auto opt_slice_id = context_->slice_tracker->BeginTyped(
         thread_slices, MakeThreadSliceRow(),
         [this](BoundInserter* inserter) { ParseTrackEventArgs(inserter); });
@@ -727,7 +746,7 @@
       return base::OkStatus();
 
     MaybeParseFlowEvents(*opt_slice_id);
-    auto* thread_slices = storage_->mutable_thread_slice_table();
+    auto* thread_slices = storage_->mutable_slice_table();
     auto opt_thread_slice_ref = thread_slices->FindById(*opt_slice_id);
     if (!opt_thread_slice_ref) {
       // This means that the end event did not match a corresponding track event
@@ -737,7 +756,7 @@
       return base::OkStatus();
     }
 
-    tables::ThreadSliceTable::RowReference slice_ref = *opt_thread_slice_ref;
+    tables::SliceTable::RowReference slice_ref = *opt_thread_slice_ref;
     base::Optional<int64_t> tts = slice_ref.thread_ts();
     if (tts) {
       PERFETTO_DCHECK(thread_timestamp_);
@@ -762,8 +781,8 @@
     if (duration_ns < 0)
       return util::ErrStatus("TrackEvent with phase X with negative duration");
 
-    auto* thread_slices = storage_->mutable_thread_slice_table();
-    tables::ThreadSliceTable::Row row = MakeThreadSliceRow();
+    auto* thread_slices = storage_->mutable_slice_table();
+    tables::SliceTable::Row row = MakeThreadSliceRow();
     row.dur = duration_ns;
     if (legacy_event_.has_thread_duration_us()) {
       row.thread_dur = legacy_event_.thread_duration_us() * 1000;
@@ -895,8 +914,8 @@
       }
     };
     if (utid_) {
-      auto* thread_slices = storage_->mutable_thread_slice_table();
-      tables::ThreadSliceTable::Row row = MakeThreadSliceRow();
+      auto* thread_slices = storage_->mutable_slice_table();
+      tables::SliceTable::Row row = MakeThreadSliceRow();
       row.dur = duration_ns;
       if (thread_timestamp_) {
         row.thread_dur = duration_ns;
@@ -1173,8 +1192,16 @@
       log_errors(
           ParseHistogramName(event_.chrome_histogram_sample(), inserter));
     }
+    if (event_.has_chrome_active_processes()) {
+      protos::pbzero::ChromeActiveProcesses::Decoder message(
+          event_.chrome_active_processes());
+      for (auto it = message.pid(); it; ++it) {
+        parser_->AddActiveProcess(ts_, *it);
+      }
+    }
 
-    TrackEventArgsParser args_writer(*inserter, *storage_, *sequence_state_);
+    TrackEventArgsParser args_writer(ts_, *inserter, *storage_,
+                                     *sequence_state_);
     int unknown_extensions = 0;
     log_errors(parser_->args_parser_.ParseMessage(
         blob_, ".perfetto.protos.TrackEvent", &parser_->reflect_fields_,
@@ -1264,28 +1291,47 @@
 
     protos::pbzero::LogMessage::Decoder message(blob);
 
-    StringId log_message_id = kNullStringId;
-
-    auto* decoder = sequence_state_->LookupInternedMessage<
+    auto* body_decoder = sequence_state_->LookupInternedMessage<
         protos::pbzero::InternedData::kLogMessageBodyFieldNumber,
         protos::pbzero::LogMessageBody>(message.body_iid());
-    if (!decoder)
+    if (!body_decoder)
       return util::ErrStatus("LogMessage with invalid body_iid");
 
-    log_message_id = storage_->InternString(decoder->body());
+    const StringId log_message_id =
+        storage_->InternString(body_decoder->body());
+    inserter->AddArg(parser_->log_message_body_key_id_,
+                     Variadic::String(log_message_id));
 
-    // TODO(nicomazz): LogMessage also contains the source of the message (file
-    // and line number). Android logs doesn't support this so far.
+    StringId source_location_id = kNullStringId;
+    if (message.has_source_location_iid()) {
+      auto* source_location_decoder = sequence_state_->LookupInternedMessage<
+          protos::pbzero::InternedData::kSourceLocationsFieldNumber,
+          protos::pbzero::SourceLocation>(message.source_location_iid());
+      if (!source_location_decoder)
+        return util::ErrStatus("LogMessage with invalid source_location_iid");
+      const std::string source_location =
+          source_location_decoder->file_name().ToStdString() + ":" +
+          std::to_string(source_location_decoder->line_number());
+      source_location_id =
+          storage_->InternString(base::StringView(source_location));
+
+      inserter->AddArg(parser_->log_message_source_location_file_name_key_id_,
+                       Variadic::String(storage_->InternString(
+                           source_location_decoder->file_name())));
+      inserter->AddArg(
+          parser_->log_message_source_location_function_name_key_id_,
+          Variadic::String(storage_->InternString(
+              source_location_decoder->function_name())));
+      inserter->AddArg(
+          parser_->log_message_source_location_line_number_key_id_,
+          Variadic::Integer(source_location_decoder->line_number()));
+    }
+
     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});
+         /*tag_id*/ source_location_id, 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();
   }
 
@@ -1311,8 +1357,8 @@
     return util::OkStatus();
   }
 
-  tables::ThreadSliceTable::Row MakeThreadSliceRow() {
-    tables::ThreadSliceTable::Row row;
+  tables::SliceTable::Row MakeThreadSliceRow() {
+    tables::SliceTable::Row row;
     row.ts = ts_;
     row.track_id = track_id_;
     row.category = category_id_;
@@ -1351,6 +1397,8 @@
   // 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_;
+
+  uint32_t packet_sequence_id_;
 };
 
 TrackEventParser::TrackEventParser(TraceProcessorContext* context,
@@ -1370,6 +1418,14 @@
           context->storage->InternString("task.posted_from.line_number")),
       log_message_body_key_id_(
           context->storage->InternString("track_event.log_message")),
+      log_message_source_location_function_name_key_id_(
+          context->storage->InternString(
+              "track_event.log_message.function_name")),
+      log_message_source_location_file_name_key_id_(
+          context->storage->InternString("track_event.log_message.file_name")),
+      log_message_source_location_line_number_key_id_(
+          context->storage->InternString(
+              "track_event.log_message.line_number")),
       source_location_function_name_key_id_(
           context->storage->InternString("source.function_name")),
       source_location_file_name_key_id_(
@@ -1431,10 +1487,13 @@
           context->storage->InternString("chrome.process_label")),
       chrome_process_type_id_(
           context_->storage->InternString("chrome.process_type")),
+      event_category_key_id_(context_->storage->InternString("event.category")),
+      event_name_key_id_(context_->storage->InternString("event.name")),
       chrome_string_lookup_(context->storage.get()),
       counter_unit_ids_{{kNullStringId, context_->storage->InternString("ns"),
                          context_->storage->InternString("count"),
-                         context_->storage->InternString("bytes")}} {
+                         context_->storage->InternString("bytes")}},
+      active_chrome_processes_tracker_(context) {
   args_parser_.AddParsingOverrideForField(
       "chrome_mojo_event_info.mojo_interface_method_iid",
       [](const protozero::Field& field,
@@ -1486,25 +1545,37 @@
         return annotation_parser.Parse(data, delegate);
       });
 
+  args_parser_.AddParsingOverrideForField(
+      "active_processes.pid", [&](const protozero::Field& field,
+                                  util::ProtoToArgsParser::Delegate& delegate) {
+        AddActiveProcess(delegate.packet_timestamp(), field.as_int32());
+        // Fallthrough so that the parser adds pid as a regular arg.
+        return base::nullopt;
+      });
+
   for (uint16_t index : kReflectFields) {
     reflect_fields_.push_back(index);
   }
 }
 
 void TrackEventParser::ParseTrackDescriptor(
-    protozero::ConstBytes track_descriptor) {
+    int64_t packet_timestamp,
+    protozero::ConstBytes track_descriptor,
+    uint32_t packet_sequence_id) {
   protos::pbzero::TrackDescriptor::Decoder decoder(track_descriptor);
 
   // Ensure that the track and its parents are resolved. This may start a new
   // process and/or thread (i.e. new upid/utid).
-  TrackId track_id = *track_event_tracker_->GetDescriptorTrack(decoder.uuid());
+  TrackId track_id = *track_event_tracker_->GetDescriptorTrack(
+      decoder.uuid(), kNullStringId, packet_sequence_id);
 
   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());
+    UniquePid upid =
+        ParseProcessDescriptor(packet_timestamp, decoder.process());
     if (decoder.has_chrome_process())
       ParseChromeProcessDescriptor(upid, decoder.chrome_process());
   } else if (decoder.has_counter()) {
@@ -1520,10 +1591,12 @@
 }
 
 UniquePid TrackEventParser::ParseProcessDescriptor(
+    int64_t packet_timestamp,
     protozero::ConstBytes process_descriptor) {
   protos::pbzero::ProcessDescriptor::Decoder decoder(process_descriptor);
   UniquePid upid = context_->process_tracker->GetOrCreateProcess(
       static_cast<uint32_t>(decoder.pid()));
+  active_chrome_processes_tracker_.AddProcessDescriptor(packet_timestamp, upid);
   if (decoder.has_process_name() && decoder.process_name().size) {
     // Don't override system-provided names.
     context_->process_tracker->SetProcessNameIfUnset(
@@ -1651,14 +1724,38 @@
 
 void TrackEventParser::ParseTrackEvent(int64_t ts,
                                        const TrackEventData* event_data,
-                                       ConstBytes blob) {
+                                       ConstBytes blob,
+                                       uint32_t packet_sequence_id) {
+  const auto range_of_interest_start_us =
+      track_event_tracker_->range_of_interest_start_us();
+  if (context_->config.drop_track_event_data_before ==
+          DropTrackEventDataBefore::kTrackEventRangeOfInterest &&
+      range_of_interest_start_us && ts < *range_of_interest_start_us * 1000) {
+    // The event is outside of the range of interest, and dropping is enabled.
+    // So we drop the event.
+    context_->storage->IncrementStats(
+        stats::track_event_dropped_packets_outside_of_range_of_interest);
+    return;
+  }
   util::Status status =
-      EventImporter(this, ts, event_data, std::move(blob)).Import();
+      EventImporter(this, ts, event_data, std::move(blob), packet_sequence_id)
+          .Import();
   if (!status.ok()) {
     context_->storage->IncrementStats(stats::track_event_parser_errors);
     PERFETTO_DLOG("ParseTrackEvent error: %s", status.c_message());
   }
 }
 
+void TrackEventParser::AddActiveProcess(int64_t packet_timestamp, int32_t pid) {
+  UniquePid upid =
+      context_->process_tracker->GetOrCreateProcess(static_cast<uint32_t>(pid));
+  active_chrome_processes_tracker_.AddActiveProcessMetadata(packet_timestamp,
+                                                            upid);
+}
+
+void TrackEventParser::NotifyEndOfFile() {
+  active_chrome_processes_tracker_.NotifyEndOfFile();
+}
+
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/track_event_parser.h b/src/trace_processor/importers/proto/track_event_parser.h
index d30c002..86e05a9 100644
--- a/src/trace_processor/importers/proto/track_event_parser.h
+++ b/src/trace_processor/importers/proto/track_event_parser.h
@@ -23,10 +23,12 @@
 #include "perfetto/base/build_config.h"
 #include "perfetto/protozero/field.h"
 #include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/parser_types.h"
 #include "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/importers/common/trace_parser.h"
+#include "src/trace_processor/importers/proto/active_chrome_processes_tracker.h"
 #include "src/trace_processor/importers/proto/chrome_string_lookup.h"
 #include "src/trace_processor/storage/trace_storage.h"
-#include "src/trace_processor/timestamped_trace_piece.h"
 #include "src/trace_processor/util/proto_to_args_parser.h"
 
 #include "protos/perfetto/trace/track_event/track_event.pbzero.h"
@@ -43,7 +45,7 @@
 // TODO(ddrone): replace with a predicate on field id to import new fields
 // automatically
 static constexpr uint16_t kReflectFields[] = {24, 25, 26, 27, 28, 29, 32, 33,
-                                              34, 35, 38, 39, 40, 41, 43};
+                                              34, 35, 38, 39, 40, 41, 43, 49};
 
 class PacketSequenceStateGeneration;
 class TraceProcessorContext;
@@ -53,13 +55,19 @@
  public:
   TrackEventParser(TraceProcessorContext*, TrackEventTracker*);
 
-  void ParseTrackDescriptor(protozero::ConstBytes);
-  UniquePid ParseProcessDescriptor(protozero::ConstBytes);
+  void ParseTrackDescriptor(int64_t packet_timestamp,
+                            protozero::ConstBytes,
+                            uint32_t packet_sequence_id);
+  UniquePid ParseProcessDescriptor(int64_t packet_timestamp,
+                                   protozero::ConstBytes);
   UniqueTid ParseThreadDescriptor(protozero::ConstBytes);
 
   void ParseTrackEvent(int64_t ts,
                        const TrackEventData* event_data,
-                       protozero::ConstBytes);
+                       protozero::ConstBytes,
+                       uint32_t packet_sequence_id);
+
+  void NotifyEndOfFile();
 
  private:
   class EventImporter;
@@ -67,6 +75,7 @@
   void ParseChromeProcessDescriptor(UniquePid, protozero::ConstBytes);
   void ParseChromeThreadDescriptor(UniqueTid, protozero::ConstBytes);
   void ParseCounterDescriptor(TrackId, protozero::ConstBytes);
+  void AddActiveProcess(int64_t packet_timestamp, int32_t pid);
 
   // Reflection-based proto TrackEvent field parser.
   util::ProtoToArgsParser args_parser_;
@@ -80,6 +89,9 @@
   const StringId task_function_name_args_key_id_;
   const StringId task_line_number_args_key_id_;
   const StringId log_message_body_key_id_;
+  const StringId log_message_source_location_function_name_key_id_;
+  const StringId log_message_source_location_file_name_key_id_;
+  const StringId log_message_source_location_line_number_key_id_;
   const StringId source_location_function_name_key_id_;
   const StringId source_location_file_name_key_id_;
   const StringId source_location_line_number_key_id_;
@@ -111,11 +123,15 @@
   const StringId chrome_crash_trace_id_name_id_;
   const StringId chrome_process_label_flat_key_id_;
   const StringId chrome_process_type_id_;
+  const StringId event_category_key_id_;
+  const StringId event_name_key_id_;
 
   ChromeStringLookup chrome_string_lookup_;
   std::array<StringId, 4> counter_unit_ids_;
 
   std::vector<uint16_t> reflect_fields_;
+
+  ActiveChromeProcessesTracker active_chrome_processes_tracker_;
 };
 
 }  // 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 1a5acf6..1f6218f 100644
--- a/src/trace_processor/importers/proto/track_event_tokenizer.cc
+++ b/src/trace_processor/importers/proto/track_event_tokenizer.cc
@@ -21,12 +21,13 @@
 #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/metadata_tracker.h"
 #include "src/trace_processor/importers/proto/packet_sequence_state.h"
 #include "src/trace_processor/importers/proto/proto_trace_reader.h"
 #include "src/trace_processor/importers/proto/track_event_tracker.h"
+#include "src/trace_processor/sorter/trace_sorter.h"
 #include "src/trace_processor/storage/stats.h"
 #include "src/trace_processor/storage/trace_storage.h"
-#include "src/trace_processor/trace_sorter.h"
 
 #include "protos/perfetto/common/builtin_clock.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
@@ -34,6 +35,7 @@
 #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/range_of_interest.pbzero.h"
 #include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"
 #include "protos/perfetto/trace/track_event/track_descriptor.pbzero.h"
 #include "protos/perfetto/trace/track_event/track_event.pbzero.h"
@@ -54,6 +56,23 @@
       counter_name_thread_instruction_count_id_(
           context_->storage->InternString("thread_instruction_count")) {}
 
+ModuleResult TrackEventTokenizer::TokenizeRangeOfInterestPacket(
+    PacketSequenceState* /*state*/,
+    const protos::pbzero::TracePacket::Decoder& packet,
+    int64_t /*packet_timestamp*/) {
+  protos::pbzero::TrackEventRangeOfInterest::Decoder range_of_interest(
+      packet.track_event_range_of_interest());
+  if (!range_of_interest.has_start_us()) {
+    context_->storage->IncrementStats(stats::track_event_tokenizer_errors);
+    return ModuleResult::Handled();
+  }
+  track_event_tracker_->SetRangeOfInterestStartUs(range_of_interest.start_us());
+  context_->metadata_tracker->SetMetadata(
+      metadata::range_of_interest_start_us,
+      Variadic::Integer(range_of_interest.start_us()));
+  return ModuleResult::Handled();
+}
+
 ModuleResult TrackEventTokenizer::TokenizeTrackDescriptorPacket(
     PacketSequenceState* state,
     const protos::pbzero::TracePacket::Decoder& packet,
diff --git a/src/trace_processor/importers/proto/track_event_tokenizer.h b/src/trace_processor/importers/proto/track_event_tokenizer.h
index 0dc41f8..010bc87 100644
--- a/src/trace_processor/importers/proto/track_event_tokenizer.h
+++ b/src/trace_processor/importers/proto/track_event_tokenizer.h
@@ -46,6 +46,10 @@
  public:
   explicit TrackEventTokenizer(TraceProcessorContext*, TrackEventTracker*);
 
+  ModuleResult TokenizeRangeOfInterestPacket(
+      PacketSequenceState* state,
+      const protos::pbzero::TracePacket_Decoder&,
+      int64_t packet_timestamp);
   ModuleResult TokenizeTrackDescriptorPacket(
       PacketSequenceState* state,
       const protos::pbzero::TracePacket_Decoder&,
diff --git a/src/trace_processor/importers/proto/track_event_tracker.cc b/src/trace_processor/importers/proto/track_event_tracker.cc
index 43c9a0d..c37eccd 100644
--- a/src/trace_processor/importers/proto/track_event_tracker.cc
+++ b/src/trace_processor/importers/proto/track_event_tracker.cc
@@ -24,14 +24,18 @@
 namespace perfetto {
 namespace trace_processor {
 
+#if !PERFETTO_IS_AT_LEAST_CPP17()
 // static
 constexpr uint64_t TrackEventTracker::kDefaultDescriptorTrackUuid;
+#endif
 
 TrackEventTracker::TrackEventTracker(TraceProcessorContext* context)
     : source_key_(context->storage->InternString("source")),
       source_id_key_(context->storage->InternString("source_id")),
       is_root_in_scope_key_(context->storage->InternString("is_root_in_scope")),
       category_key_(context->storage->InternString("category")),
+      has_first_packet_on_sequence_key_id_(
+          context->storage->InternString("has_first_packet_on_sequence")),
       descriptor_source_(context->storage->InternString("descriptor")),
       default_descriptor_track_name_(
           context->storage->InternString("Default Track")),
@@ -161,8 +165,10 @@
 
 base::Optional<TrackId> TrackEventTracker::GetDescriptorTrack(
     uint64_t uuid,
-    StringId event_name) {
-  base::Optional<TrackId> track_id = GetDescriptorTrackImpl(uuid);
+    StringId event_name,
+    base::Optional<uint32_t> packet_sequence_id) {
+  base::Optional<TrackId> track_id =
+      GetDescriptorTrackImpl(uuid, packet_sequence_id);
   if (!track_id || event_name.is_null())
     return track_id;
 
@@ -186,7 +192,8 @@
 }
 
 base::Optional<TrackId> TrackEventTracker::GetDescriptorTrackImpl(
-    uint64_t uuid) {
+    uint64_t uuid,
+    base::Optional<uint32_t> packet_sequence_id) {
   auto it = descriptor_tracks_.find(uuid);
   if (it != descriptor_tracks_.end())
     return it->second;
@@ -220,6 +227,11 @@
               Variadic::Boolean(resolved_track->is_root_in_scope()));
   if (!reservation.category.is_null())
     args.AddArg(category_key_, Variadic::String(reservation.category));
+  if (packet_sequence_id &&
+      sequences_with_first_packet_.find(*packet_sequence_id) !=
+          sequences_with_first_packet_.end()) {
+    args.AddArg(has_first_packet_on_sequence_key_id_, Variadic::Boolean(true));
+  }
 
   auto* tracks = context_->storage->mutable_track_table();
   auto row_ref = *tracks->FindById(track_id);
@@ -555,6 +567,10 @@
   }
 }
 
+void TrackEventTracker::OnFirstPacketOnSequence(uint32_t packet_sequence_id) {
+  sequences_with_first_packet_.insert(packet_sequence_id);
+}
+
 TrackEventTracker::ResolvedDescriptorTrack
 TrackEventTracker::ResolvedDescriptorTrack::Process(UniquePid upid,
                                                     bool is_counter,
diff --git a/src/trace_processor/importers/proto/track_event_tracker.h b/src/trace_processor/importers/proto/track_event_tracker.h
index e6af39f..6664599 100644
--- a/src/trace_processor/importers/proto/track_event_tracker.h
+++ b/src/trace_processor/importers/proto/track_event_tracker.h
@@ -17,6 +17,8 @@
 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_TRACK_EVENT_TRACKER_H_
 #define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_TRACK_EVENT_TRACKER_H_
 
+#include <unordered_set>
+
 #include "src/trace_processor/importers/common/args_tracker.h"
 #include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/types/trace_processor_context.h"
@@ -105,7 +107,8 @@
   // TrackTracker.
   base::Optional<TrackId> GetDescriptorTrack(
       uint64_t uuid,
-      StringId event_name = kNullStringId);
+      StringId event_name = kNullStringId,
+      base::Optional<uint32_t> packet_sequence_id = base::nullopt);
 
   // Converts the given counter value to an absolute value in the unit of the
   // counter, applying incremental delta encoding or unit multipliers as
@@ -127,6 +130,16 @@
   // the sequence identified by |packet_sequence_id|.
   void OnIncrementalStateCleared(uint32_t packet_sequence_id);
 
+  void OnFirstPacketOnSequence(uint32_t packet_sequence_id);
+
+  void SetRangeOfInterestStartUs(int64_t range_of_interest_start_us) {
+    range_of_interest_start_us_ = range_of_interest_start_us;
+  }
+
+  base::Optional<int64_t> range_of_interest_start_us() const {
+    return range_of_interest_start_us_;
+  }
+
  private:
   struct DescriptorTrackReservation {
     uint64_t parent_uuid = 0;
@@ -195,7 +208,9 @@
     UniquePid upid_;
   };
 
-  base::Optional<TrackId> GetDescriptorTrackImpl(uint64_t uuid);
+  base::Optional<TrackId> GetDescriptorTrackImpl(
+      uint64_t uuid,
+      base::Optional<uint32_t> packet_sequence_id = base::nullopt);
   TrackId CreateTrackFromResolved(const ResolvedDescriptorTrack&);
   base::Optional<ResolvedDescriptorTrack> ResolveDescriptorTrack(
       uint64_t uuid,
@@ -221,15 +236,20 @@
   std::map<UniquePid, uint64_t /*uuid*/> descriptor_uuids_by_upid_;
   std::map<UniqueTid, uint64_t /*uuid*/> descriptor_uuids_by_utid_;
 
+  std::unordered_set<uint32_t> sequences_with_first_packet_;
+
   const StringId source_key_ = kNullStringId;
   const StringId source_id_key_ = kNullStringId;
   const StringId is_root_in_scope_key_ = kNullStringId;
   const StringId category_key_ = kNullStringId;
+  const StringId has_first_packet_on_sequence_key_id_ = kNullStringId;
 
   const StringId descriptor_source_ = kNullStringId;
 
   const StringId default_descriptor_track_name_ = kNullStringId;
 
+  base::Optional<int64_t> range_of_interest_start_us_;
+
   TraceProcessorContext* const context_;
 };
 
diff --git a/src/trace_processor/importers/syscalls/BUILD.gn b/src/trace_processor/importers/syscalls/BUILD.gn
new file mode 100644
index 0000000..2576f8e
--- /dev/null
+++ b/src/trace_processor/importers/syscalls/BUILD.gn
@@ -0,0 +1,41 @@
+# Copyright (C) 2022 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("full") {
+  sources = [
+    "syscall_tracker.cc",
+    "syscall_tracker.h",
+  ]
+  deps = [
+    "../../../../gn:default_deps",
+    "../../../kernel_utils:syscall_table",
+    "../../containers",
+    "../../storage",
+    "../../types",
+    "../common",
+  ]
+}
+
+perfetto_unittest_source_set("unittests") {
+  testonly = true
+  sources = [ "syscall_tracker_unittest.cc" ]
+  deps = [
+    ":full",
+    "../../../../gn:default_deps",
+    "../../../../gn:gtest_and_gmock",
+    "../common",
+  ]
+}
diff --git a/src/trace_processor/importers/syscalls/syscall_tracker.cc b/src/trace_processor/importers/syscalls/syscall_tracker.cc
index f092455..37be70e 100644
--- a/src/trace_processor/importers/syscalls/syscall_tracker.cc
+++ b/src/trace_processor/importers/syscalls/syscall_tracker.cc
@@ -20,6 +20,7 @@
 #include <type_traits>
 #include <utility>
 
+#include "perfetto/ext/base/string_utils.h"
 #include "src/kernel_utils/syscall_table.h"
 #include "src/trace_processor/storage/stats.h"
 
@@ -50,9 +51,8 @@
       if (!strcmp(name, "sys_write"))
         sys_write_string_id_ = id;
     } else {
-      char unknown_str[64];
-      sprintf(unknown_str, "sys_%zu", i);
-      id = context_->storage->InternString(unknown_str);
+      base::StackString<64> unknown_str("sys_%zu", i);
+      id = context_->storage->InternString(unknown_str.string_view());
     }
     arch_syscall_to_string_id_[i] = id;
   }
diff --git a/src/trace_processor/importers/syscalls/syscall_tracker.h b/src/trace_processor/importers/syscalls/syscall_tracker.h
index 72bbd9d..218256b 100644
--- a/src/trace_processor/importers/syscalls/syscall_tracker.h
+++ b/src/trace_processor/importers/syscalls/syscall_tracker.h
@@ -22,6 +22,8 @@
 
 #include "perfetto/ext/base/string_view.h"
 #include "src/kernel_utils/syscall_table.h"
+#include "src/trace_processor/containers/bit_vector.h"
+#include "src/trace_processor/importers/common/event_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"
@@ -45,21 +47,66 @@
 
   void SetArchitecture(Architecture architecture);
 
-  void Enter(int64_t ts, UniqueTid utid, uint32_t syscall_num) {
+  void Enter(int64_t ts,
+             UniqueTid utid,
+             uint32_t syscall_num,
+             EventTracker::SetArgsCallback args_callback =
+                 EventTracker::SetArgsCallback()) {
     StringId name = SyscallNumberToStringId(syscall_num);
-    if (!name.is_null()) {
-      TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
-      context_->slice_tracker->Begin(ts, track_id, kNullStringId /* cat */,
-                                     name);
+    if (name.is_null())
+      return;
+
+    TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
+    context_->slice_tracker->Begin(ts, track_id, kNullStringId /* cat */, name,
+                                   args_callback);
+
+    if (name == sys_write_string_id_) {
+      if (utid >= in_sys_write_.size())
+        in_sys_write_.Resize(utid + 1);
+
+      in_sys_write_.Set(utid);
     }
   }
 
-  void Exit(int64_t ts, UniqueTid utid, uint32_t syscall_num) {
+  void Exit(int64_t ts,
+            UniqueTid utid,
+            uint32_t syscall_num,
+            EventTracker::SetArgsCallback args_callback =
+                EventTracker::SetArgsCallback()) {
     StringId name = SyscallNumberToStringId(syscall_num);
-    if (!name.is_null()) {
-      TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
-      context_->slice_tracker->End(ts, track_id, kNullStringId /* cat */, name);
+    if (name.is_null())
+      return;
+
+    if (name == sys_write_string_id_) {
+      if (utid >= in_sys_write_.size())
+        in_sys_write_.Resize(utid + 1);
+      // Either seeing an exit event without the corresponding entry at the
+      // start of the trace, or the slice was closed by
+      // MaybeTruncateOngoingWriteSlice.
+      if (!in_sys_write_.IsSet(utid))
+        return;
+      in_sys_write_.Clear(utid);
     }
+
+    TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
+    context_->slice_tracker->End(ts, track_id, kNullStringId /* cat */, name,
+                                 args_callback);
+  }
+
+  // Resolves slice nesting issues when the sys_write is for an atrace slice on
+  // android. See callsite for details.
+  void MaybeTruncateOngoingWriteSlice(int64_t ts, UniqueTid utid) {
+    if (utid >= in_sys_write_.size())
+      in_sys_write_.Resize(utid + 1);
+
+    if (!in_sys_write_.IsSet(utid))
+      return;
+    in_sys_write_.Clear(utid);
+    context_->storage->IncrementStats(stats::truncated_sys_write_duration);
+
+    TrackId track_id = context_->track_tracker->InternThreadTrack(utid);
+    context_->slice_tracker->End(ts, track_id, kNullStringId /* cat */,
+                                 sys_write_string_id_);
   }
 
  private:
@@ -70,20 +117,15 @@
   inline StringId SyscallNumberToStringId(uint32_t syscall_num) {
     if (syscall_num > kMaxSyscalls)
       return kNullStringId;
-    // We see two write sys calls around each userspace slice that is going via
-    // trace_marker, this violates the assumption that userspace slices are
-    // perfectly nested. For the moment ignore all write sys calls.
-    // TODO(hjd): Remove this limitation.
-    StringId id = arch_syscall_to_string_id_[syscall_num];
-    if (id == sys_write_string_id_)
-      return kNullStringId;
-    return id;
+    return arch_syscall_to_string_id_[syscall_num];
   }
 
   // This is table from platform specific syscall number directly to
   // the relevant StringId (this avoids having to always do two conversions).
   std::array<StringId, kMaxSyscalls> arch_syscall_to_string_id_{};
   StringId sys_write_string_id_ = std::numeric_limits<StringId>::max();
+  // UniqueTids currently in a sys_write syscall.
+  BitVector in_sys_write_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/syscalls/syscall_tracker_unittest.cc b/src/trace_processor/importers/syscalls/syscall_tracker_unittest.cc
index 7bbc73b..67ef441 100644
--- a/src/trace_processor/importers/syscalls/syscall_tracker_unittest.cc
+++ b/src/trace_processor/importers/syscalls/syscall_tracker_unittest.cc
@@ -79,16 +79,6 @@
   EXPECT_EQ(context.storage->GetString(end_name), "sys_57");
 }
 
-TEST_F(SyscallTrackerTest, IgnoreWriteSyscalls) {
-  SyscallTracker* syscall_tracker = SyscallTracker::GetOrCreate(&context);
-  syscall_tracker->SetArchitecture(kAarch64);
-  EXPECT_CALL(*slice_tracker, Begin(_, _, _, _, _)).Times(0);
-  EXPECT_CALL(*slice_tracker, End(_, _, _, _, _)).Times(0);
-
-  syscall_tracker->Enter(100 /*ts*/, 42 /*utid*/, 64 /*sys_write*/);
-  syscall_tracker->Exit(110 /*ts*/, 42 /*utid*/, 64 /*sys_write*/);
-}
-
 TEST_F(SyscallTrackerTest, Aarch64) {
   constexpr TrackId track{0u};
   StringId begin_name = kNullStringId;
diff --git a/src/trace_processor/importers/systrace/BUILD.gn b/src/trace_processor/importers/systrace/BUILD.gn
new file mode 100644
index 0000000..b322307
--- /dev/null
+++ b/src/trace_processor/importers/systrace/BUILD.gn
@@ -0,0 +1,72 @@
+# Copyright (C) 2022 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("systrace_line") {
+  sources = [ "systrace_line.h" ]
+  deps = [
+    "../../../../gn:default_deps",
+    "../../containers",
+  ]
+}
+
+source_set("systrace_parser") {
+  sources = [
+    "systrace_parser.cc",
+    "systrace_parser.h",
+  ]
+  deps = [
+    "../../../../gn:default_deps",
+    "../../containers",
+    "../../storage",
+    "../../types",
+    "../common",
+  ]
+}
+
+source_set("full") {
+  sources = [
+    "systrace_line_parser.cc",
+    "systrace_line_parser.h",
+    "systrace_line_tokenizer.cc",
+    "systrace_line_tokenizer.h",
+    "systrace_trace_parser.cc",
+    "systrace_trace_parser.h",
+  ]
+  deps = [
+    ":systrace_line",
+    ":systrace_parser",
+    "../..:storage_minimal",
+    "../../../../gn:default_deps",
+    "../../containers",
+    "../../sorter",
+    "../../storage",
+    "../../types",
+    "../common",
+    "../ftrace:full",
+  ]
+}
+
+perfetto_unittest_source_set("unittests") {
+  testonly = true
+  sources = [ "systrace_parser_unittest.cc" ]
+  deps = [
+    ":full",
+    ":systrace_line",
+    ":systrace_parser",
+    "../../../../gn:default_deps",
+    "../../../../gn:gtest_and_gmock",
+  ]
+}
diff --git a/src/trace_processor/importers/systrace/systrace_line_parser.cc b/src/trace_processor/importers/systrace/systrace_line_parser.cc
index fe11db3..150124c 100644
--- a/src/trace_processor/importers/systrace/systrace_line_parser.cc
+++ b/src/trace_processor/importers/systrace/systrace_line_parser.cc
@@ -48,11 +48,19 @@
       sched_blocked_reason_id_(
           ctx->storage->InternString("sched_blocked_reason")),
       io_wait_id_(ctx->storage->InternString("io_wait")),
-      waker_utid_id_(ctx->storage->InternString("waker_utid")) {}
+      waker_utid_id_(ctx->storage->InternString("waker_utid")),
+      unknown_thread_name_id_(ctx->storage->InternString("<...>")) {}
 
 util::Status SystraceLineParser::ParseLine(const SystraceLine& line) {
+  const StringId line_task_id{
+      context_->storage->InternString(base::StringView(line.task))};
   auto utid = context_->process_tracker->UpdateThreadName(
-      line.pid, context_->storage->InternString(base::StringView(line.task)),
+      line.pid,
+      // Ftrace doesn't always know the thread name (see ftrace documentation
+      // for saved_cmdlines) so some lines name a process "<...>". Don't use
+      // this bogus name for thread naming otherwise a real name from a previous
+      // line could be overwritten.
+      line_task_id == unknown_thread_name_id_ ? StringId::Null() : line_task_id,
       ThreadNamePriority::kFtrace);
 
   if (!line.tgid_str.empty() && line.tgid_str != "-----") {
@@ -84,7 +92,8 @@
   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();
+        ftrace_utils::TaskState::FromSystrace(prev_state_str.c_str())
+            .ToRawStateOnlyForSystraceConversions();
 
     auto prev_pid = base::StringToUInt32(args["prev_pid"]);
     auto prev_comm = base::StringView(args["prev_comm"]);
diff --git a/src/trace_processor/importers/systrace/systrace_line_parser.h b/src/trace_processor/importers/systrace/systrace_line_parser.h
index 126fca7..017faba 100644
--- a/src/trace_processor/importers/systrace/systrace_line_parser.h
+++ b/src/trace_processor/importers/systrace/systrace_line_parser.h
@@ -45,6 +45,7 @@
   const StringId sched_blocked_reason_id_ = kNullStringId;
   const StringId io_wait_id_ = kNullStringId;
   const StringId waker_utid_id_ = kNullStringId;
+  const StringId unknown_thread_name_id_ = kNullStringId;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/systrace/systrace_parser.cc b/src/trace_processor/importers/systrace/systrace_parser.cc
index 6f82886..538278d 100644
--- a/src/trace_processor/importers/systrace/systrace_parser.cc
+++ b/src/trace_processor/importers/systrace/systrace_parser.cc
@@ -18,11 +18,11 @@
 
 #include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_utils.h"
+#include "src/trace_processor/importers/common/async_track_set_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/proto/async_track_set_tracker.h"
 #include "src/trace_processor/storage/trace_storage.h"
 
 namespace perfetto {
@@ -281,6 +281,14 @@
         context_->event_tracker->PushCounter(
             ts, static_cast<double>(point.int_value), track);
         return;
+      } else if (point.name.StartsWith("battery_stats.")) {
+        // Promote battery_stats conters to global tracks.
+        StringId name_id = context_->storage->InternString(point.name);
+        TrackId track =
+            context_->track_tracker->InternGlobalCounterTrack(name_id);
+        context_->event_tracker->PushCounter(
+            ts, static_cast<double>(point.int_value), track);
+        return;
       }
 
       // This is per upid on purpose. Some long-standing counters are pushed
diff --git a/src/trace_processor/importers/systrace/systrace_trace_parser.cc b/src/trace_processor/importers/systrace/systrace_trace_parser.cc
index 0fe8781..167b3c3 100644
--- a/src/trace_processor/importers/systrace/systrace_trace_parser.cc
+++ b/src/trace_processor/importers/systrace/systrace_trace_parser.cc
@@ -21,7 +21,7 @@
 #include "perfetto/ext/base/string_utils.h"
 #include "src/trace_processor/forwarding_trace_parser.h"
 #include "src/trace_processor/importers/common/process_tracker.h"
-#include "src/trace_processor/trace_sorter.h"
+#include "src/trace_processor/sorter/trace_sorter.h"
 
 #include <cctype>
 #include <cinttypes>
diff --git a/src/trace_processor/iterator_impl.h b/src/trace_processor/iterator_impl.h
index 68068e6..272d13e 100644
--- a/src/trace_processor/iterator_impl.h
+++ b/src/trace_processor/iterator_impl.h
@@ -24,6 +24,7 @@
 
 #include "perfetto/base/build_config.h"
 #include "perfetto/base/export.h"
+#include "perfetto/ext/base/optional.h"
 #include "perfetto/trace_processor/basic_types.h"
 #include "perfetto/trace_processor/iterator.h"
 #include "perfetto/trace_processor/status.h"
@@ -85,7 +86,9 @@
 
     int ret = sqlite3_step(*stmt_);
     if (PERFETTO_UNLIKELY(ret != SQLITE_ROW && ret != SQLITE_DONE)) {
-      status_ = base::ErrStatus("%s (errcode %d)", sqlite3_errmsg(db_), ret);
+      status_ = base::ErrStatus("%s", sqlite_utils::FormatErrorMessage(
+                                          stmt_.get(), base::nullopt, db_, ret)
+                                          .c_message());
       stmt_.reset();
       return false;
     }
diff --git a/src/trace_processor/metrics/BUILD.gn b/src/trace_processor/metrics/BUILD.gn
index 8731f78..8ac796e 100644
--- a/src/trace_processor/metrics/BUILD.gn
+++ b/src/trace_processor/metrics/BUILD.gn
@@ -16,6 +16,8 @@
 import("../../../gn/perfetto_cc_proto_descriptor.gni")
 import("../../../gn/test.gni")
 
+assert(enable_perfetto_trace_processor_sqlite)
+
 perfetto_cc_proto_descriptor("gen_cc_metrics_descriptor") {
   descriptor_name = "metrics.descriptor"
   descriptor_target = "../../../protos/perfetto/metrics:descriptor"
@@ -26,42 +28,41 @@
   descriptor_target = "../../../protos/perfetto/metrics/chrome:descriptor"
 }
 
-if (enable_perfetto_trace_processor_sqlite) {
-  source_set("metrics") {
-    sources = [
-      "metrics.cc",
-      "metrics.h",
-    ]
-    deps = [
-      "..:metatrace",
-      "../../../gn:default_deps",
-      "../../../gn:sqlite",
-      "../../../include/perfetto/trace_processor",
-      "../../../protos/perfetto/common:zero",
-      "../../../protos/perfetto/trace_processor:metrics_impl_zero",
-      "../../base",
-      "../../protozero:protozero",
-      "../sqlite",
-    ]
-    public_deps = [
-      ":gen_cc_all_chrome_metrics_descriptor",
-      ":gen_cc_metrics_descriptor",
-      "../util",
-      "../util:descriptors",
-      "sql:gen_amalgamated_sql_metrics",
-    ]
-  }
+source_set("metrics") {
+  sources = [
+    "metrics.cc",
+    "metrics.h",
+  ]
+  deps = [
+    "..:metatrace",
+    "../../../gn:default_deps",
+    "../../../gn:sqlite",
+    "../../../include/perfetto/trace_processor",
+    "../../../protos/perfetto/common:zero",
+    "../../../protos/perfetto/trace_processor:metrics_impl_zero",
+    "../../base",
+    "../../protozero:protozero",
+    "../prelude/functions",
+    "../sqlite",
+  ]
+  public_deps = [
+    ":gen_cc_all_chrome_metrics_descriptor",
+    ":gen_cc_metrics_descriptor",
+    "../util",
+    "../util:descriptors",
+    "sql:gen_amalgamated_sql_metrics",
+  ]
+}
 
-  perfetto_unittest_source_set("unittests") {
-    testonly = true
-    sources = [ "metrics_unittest.cc" ]
-    deps = [
-      ":metrics",
-      "..:lib",
-      "../../../gn:default_deps",
-      "../../../gn:gtest_and_gmock",
-      "../../../gn:sqlite",
-      "../../../protos/perfetto/common:zero",
-    ]
-  }
+perfetto_unittest_source_set("unittests") {
+  testonly = true
+  sources = [ "metrics_unittest.cc" ]
+  deps = [
+    ":metrics",
+    "..:lib",
+    "../../../gn:default_deps",
+    "../../../gn:gtest_and_gmock",
+    "../../../gn:sqlite",
+    "../../../protos/perfetto/common:zero",
+  ]
 }
diff --git a/src/trace_processor/metrics/metrics.cc b/src/trace_processor/metrics/metrics.cc
index da372a3..c713532 100644
--- a/src/trace_processor/metrics/metrics.cc
+++ b/src/trace_processor/metrics/metrics.cc
@@ -732,9 +732,9 @@
 
     auto output_query =
         "SELECT * FROM " + sql_metric.output_table_name.value() + ";";
-    PERFETTO_TP_TRACE("COMPUTE_METRIC_QUERY", [&](metatrace::Record* r) {
-      r->AddArg("SQL", output_query);
-    });
+    PERFETTO_TP_TRACE(
+        metatrace::Category::QUERY, "COMPUTE_METRIC_QUERY",
+        [&](metatrace::Record* r) { r->AddArg("SQL", output_query); });
 
     auto it = tp->ExecuteQuery(output_query.c_str());
     auto has_next = it.Next();
diff --git a/src/trace_processor/metrics/metrics.h b/src/trace_processor/metrics/metrics.h
index 090d673..72a10f2 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/sqlite/register_function.h"
+#include "src/trace_processor/prelude/functions/register_function.h"
 #include "src/trace_processor/util/descriptors.h"
 
 #include "protos/perfetto/trace_processor/metrics_impl.pbzero.h"
@@ -178,6 +178,7 @@
     TraceProcessor* tp;
     std::vector<SqlMetricFile>* metrics;
   };
+  static constexpr bool kVoidReturn = true;
   static base::Status Run(Context* ctx,
                           size_t argc,
                           sqlite3_value** argv,
diff --git a/src/trace_processor/metrics/sql/BUILD.gn b/src/trace_processor/metrics/sql/BUILD.gn
index fcc81ba..337b17c 100644
--- a/src/trace_processor/metrics/sql/BUILD.gn
+++ b/src/trace_processor/metrics/sql/BUILD.gn
@@ -13,153 +13,26 @@
 # limitations under the License.
 
 import("../../../../gn/perfetto.gni")
+import("../../../../gn/perfetto_sql.gni")
 
-# Keep this list sorted.
-sql_files = [
-  "android/android_batt.sql",
-  "android/android_binder.sql",
-  "android/android_camera_unagg.sql",
-  "android/android_camera.sql",
-  "android/android_cpu_agg.sql",
-  "android/android_cpu_raw_metrics_per_core.sql",
-  "android/android_cpu.sql",
-  "android/android_dma_heap.sql",
-  "android/android_dvfs.sql",
-  "android/android_fastrpc.sql",
-  "android/android_frame_timeline_metric.sql",
-  "android/android_gpu.sql",
-  "android/android_hwcomposer.sql",
-  "android/android_hwui_metric.sql",
-  "android/android_hwui_threads.sql",
-  "android/android_ion.sql",
-  "android/android_irq_runtime.sql",
-  "android/android_jank_cuj.sql",
-  "android/android_lmk_reason.sql",
-  "android/android_lmk.sql",
-  "android/android_mem_unagg.sql",
-  "android/android_mem.sql",
-  "android/android_multiuser_populator.sql",
-  "android/android_multiuser.sql",
-  "android/android_netperf.sql",
-  "android/android_other_traces.sql",
-  "android/android_package_list.sql",
-  "android/android_powrails.sql",
-  "android/android_proxy_power.sql",
-  "android/android_rt_runtime.sql",
-  "android/android_simpleperf.sql",
-  "android/android_startup.sql",
-  "android/android_surfaceflinger.sql",
-  "android/android_sysui_cuj_jank_query.sql",
-  "android/android_sysui_cuj_surfaceflinger.sql",
-  "android/android_sysui_cuj.sql",
-  "android/android_task_names.sql",
-  "android/android_trace_quality.sql",
-  "android/android_trusty_workqueues.sql",
-  "android/composer_execution.sql",
-  "android/composition_layers.sql",
-  "android/cpu_info.sql",
-  "android/display_metrics.sql",
-  "android/frame_missed.sql",
-  "android/g2d_duration.sql",
-  "android/g2d.sql",
-  "android/global_counter_span_view.sql",
-  "android/gpu_counter_span_view.sql",
-  "android/jank/cujs.sql",
-  "android/jank/cujs_boundaries.sql",
-  "android/jank/frames.sql",
-  "android/jank/internal/counters.sql",
-  "android/jank/internal/derived_events.sql",
-  "android/jank/internal/query_base.sql",
-  "android/jank/internal/query_frame_slice.sql",
-  "android/jank/query_functions.sql",
-  "android/jank/relevant_slices.sql",
-  "android/jank/relevant_threads.sql",
-  "android/jank/slices.sql",
-  "android/java_heap_histogram.sql",
-  "android/java_heap_stats.sql",
-  "android/mem_stats_priority_breakdown.sql",
-  "android/power_drain_in_watts.sql",
-  "android/power_profile_data.sql",
-  "android/process_counter_span_view.sql",
-  "android/process_counter_span_view.sql",
-  "android/process_mem.sql",
-  "android/process_metadata.sql",
-  "android/process_oom_score.sql",
-  "android/process_unagg_mem_view.sql",
-  "android/profiler_smaps.sql",
-  "android/span_view_stats.sql",
-  "android/startup/gc_slices.sql",
-  "android/startup/hsc.sql",
-  "android/startup/launches_maxsdk28.sql",
-  "android/startup/launches_minsdk29.sql",
-  "android/startup/launches_minsdk33.sql",
-  "android/startup/launches.sql",
-  "android/startup/mcycles_per_launch.sql",
-  "android/startup/slice_functions.sql",
-  "android/startup/system_state.sql",
-  "android/startup/thread_state_breakdown.sql",
-  "android/unsymbolized_frames.sql",
-  "chrome/actual_power_by_category.sql",
-  "chrome/actual_power_by_rail_mode.sql",
-  "chrome/chrome_event_metadata.sql",
-  "chrome/chrome_histogram_hashes.sql",
-  "chrome/chrome_input_to_browser_intervals.sql",
-  "chrome/chrome_performance_mark_hashes.sql",
-  "chrome/chrome_processes.sql",
-  "chrome/chrome_scroll_jank_caused_by_scheduling.sql",
-  "chrome/chrome_scroll_inputs_per_frame.sql",
-  "chrome/chrome_slice_names.sql",
-  "chrome/chrome_stack_samples_for_task.sql",
-  "chrome/chrome_unsymbolized_args.sql",
-  "chrome/chrome_tasks.sql",
-  "chrome/chrome_tasks_delaying_input_processing.sql",
-  "chrome/chrome_thread_slice.sql",
-  "chrome/chrome_user_event_hashes.sql",
-  "chrome/cpu_time_by_category.sql",
-  "chrome/cpu_time_by_rail_mode.sql",
-  "chrome/estimated_power_by_category.sql",
-  "chrome/estimated_power_by_rail_mode.sql",
-  "chrome/gesture_flow_event_queuing_delay.sql",
-  "chrome/gesture_flow_event.sql",
-  "chrome/gesture_jank.sql",
-  "chrome/jank_utilities.sql",
-  "chrome/rail_modes.sql",
-  "chrome/scroll_flow_event_queuing_delay.sql",
-  "chrome/scroll_flow_event.sql",
-  "chrome/scroll_jank_cause_blocking_task.sql",
-  "chrome/scroll_jank_cause_blocking_touch_move.sql",
-  "chrome/scroll_jank_cause_get_bitmap.sql",
-  "chrome/scroll_jank_cause_queuing_delay.sql",
-  "chrome/scroll_jank_cause.sql",
-  "chrome/scroll_jank.sql",
-  "chrome/sufficient_chrome_processes.sql",
-  "chrome/test_chrome_metric.sql",
-  "chrome/touch_flow_event_queuing_delay.sql",
-  "chrome/touch_flow_event.sql",
-  "chrome/touch_jank.sql",
-  "experimental/blink_gc_metric.sql",
-  "experimental/chrome_dropped_frames.sql",
-  "experimental/chrome_long_latency.sql",
-  "experimental/frame_times.sql",
-  "experimental/media_metric.sql",
-  "experimental/reported_by_page.sql",
-  "trace_metadata.sql",
-  "trace_stats.sql",
-  "webview/webview_power_usage.sql",
-]
+assert(enable_perfetto_trace_processor_sqlite)
 
-config("gen_config") {
-  include_dirs = [ "${root_gen_dir}/${perfetto_root_path}" ]
+perfetto_sql_source_set("misc_sql") {
+  sources = [
+    "trace_metadata.sql",
+    "trace_stats.sql",
+  ]
 }
 
-action("gen_amalgamated_sql_metrics") {
-  script = "../../../../tools/gen_amalgamated_sql_metrics.py"
-  generated_header = "${target_gen_dir}/amalgamated_sql_metrics.h"
-  args = rebase_path(sql_files, root_build_dir) + [
-           "--cpp_out",
-           rebase_path(generated_header, root_build_dir),
-         ]
-  inputs = sql_files
-  outputs = [ generated_header ]
-  public_configs = [ ":gen_config" ]
+perfetto_amalgamated_sql_header("gen_amalgamated_sql_metrics") {
+  deps = [
+    ":misc_sql",
+    "android",
+    "chrome:chrome_sql",
+    "common",
+    "experimental",
+    "webview",
+  ]
+  generated_header = "amalgamated_sql_metrics.h"
+  namespace = "sql_metrics"
 }
diff --git a/src/trace_processor/metrics/sql/android/BUILD.gn b/src/trace_processor/metrics/sql/android/BUILD.gn
new file mode 100644
index 0000000..833faa7
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/BUILD.gn
@@ -0,0 +1,122 @@
+# Copyright (C) 2022 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/perfetto_sql.gni")
+
+assert(enable_perfetto_trace_processor_sqlite)
+
+perfetto_sql_source_set("android") {
+  sources = [
+    "android_batt.sql",
+    "android_binder.sql",
+    "android_camera.sql",
+    "android_camera_unagg.sql",
+    "android_cpu.sql",
+    "android_cpu_agg.sql",
+    "android_cpu_raw_metrics_per_core.sql",
+    "android_dma_heap.sql",
+    "android_dvfs.sql",
+    "android_fastrpc.sql",
+    "android_frame_timeline_metric.sql",
+    "android_gpu.sql",
+    "android_hwcomposer.sql",
+    "android_hwui_metric.sql",
+    "android_hwui_threads.sql",
+    "android_ion.sql",
+    "android_irq_runtime.sql",
+    "android_jank_cuj.sql",
+    "android_lmk.sql",
+    "android_lmk_reason.sql",
+    "android_mem.sql",
+    "android_mem_unagg.sql",
+    "android_multiuser.sql",
+    "android_multiuser_populator.sql",
+    "android_netperf.sql",
+    "android_other_traces.sql",
+    "android_package_list.sql",
+    "android_powrails.sql",
+    "android_proxy_power.sql",
+    "android_rt_runtime.sql",
+    "android_simpleperf.sql",
+    "android_startup.sql",
+    "android_surfaceflinger.sql",
+    "android_task_names.sql",
+    "android_trace_quality.sql",
+    "android_trusty_workqueues.sql",
+    "composer_execution.sql",
+    "composition_layers.sql",
+    "cpu_info.sql",
+    "display_metrics.sql",
+    "frame_missed.sql",
+    "g2d.sql",
+    "g2d_duration.sql",
+    "global_counter_span_view.sql",
+    "global_counter_span_view_merged.sql",
+    "gpu_counter_span_view.sql",
+    "jank/cujs.sql",
+    "jank/cujs_boundaries.sql",
+    "jank/frames.sql",
+    "jank/internal/counters.sql",
+    "jank/internal/derived_events.sql",
+    "jank/internal/query_base.sql",
+    "jank/internal/query_frame_slice.sql",
+    "jank/params.sql",
+    "jank/query_functions.sql",
+    "jank/relevant_slices.sql",
+    "jank/relevant_threads.sql",
+    "jank/slices.sql",
+    "java_heap_histogram.sql",
+    "java_heap_stats.sql",
+    "mem_stats_priority_breakdown.sql",
+    "p_state.sql",
+    "power_drain_in_watts.sql",
+    "power_profile_data.sql",
+    "power_profile_data/barbet.sql",
+    "power_profile_data/bluejay.sql",
+    "power_profile_data/blueline.sql",
+    "power_profile_data/bonito.sql",
+    "power_profile_data/bramble.sql",
+    "power_profile_data/coral.sql",
+    "power_profile_data/crosshatch.sql",
+    "power_profile_data/flame.sql",
+    "power_profile_data/marlin.sql",
+    "power_profile_data/oriole.sql",
+    "power_profile_data/raven.sql",
+    "power_profile_data/redfin.sql",
+    "power_profile_data/sargo.sql",
+    "power_profile_data/sunfish.sql",
+    "power_profile_data/taimen.sql",
+    "power_profile_data/walleye.sql",
+    "process_counter_span_view.sql",
+    "process_counter_span_view.sql",
+    "process_mem.sql",
+    "process_metadata.sql",
+    "process_oom_score.sql",
+    "process_unagg_mem_view.sql",
+    "profiler_smaps.sql",
+    "span_view_stats.sql",
+    "startup/gc_slices.sql",
+    "startup/hsc.sql",
+    "startup/launches.sql",
+    "startup/launches_maxsdk28.sql",
+    "startup/launches_minsdk29.sql",
+    "startup/launches_minsdk33.sql",
+    "startup/mcycles_per_launch.sql",
+    "startup/slice_functions.sql",
+    "startup/system_state.sql",
+    "startup/thread_state_breakdown.sql",
+    "unsymbolized_frames.sql",
+  ]
+}
diff --git a/src/trace_processor/metrics/sql/android/android_batt.sql b/src/trace_processor/metrics/sql/android/android_batt.sql
index 2ee4530..c9aad7e 100644
--- a/src/trace_processor/metrics/sql/android/android_batt.sql
+++ b/src/trace_processor/metrics/sql/android/android_batt.sql
@@ -13,45 +13,11 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 --
+SELECT IMPORT('android.battery');
+
 DROP VIEW IF EXISTS battery_view;
 CREATE VIEW battery_view AS
-SELECT
-  all_ts.ts as ts,
-  current_avg_ua,
-  capacity_percent,
-  charge_uah,
-  current_ua
-FROM (
-  SELECT distinct(ts) AS ts
-  FROM counter c
-  JOIN counter_track t on c.track_id = t.id
-  WHERE name GLOB 'batt.*'
-) AS all_ts
-LEFT JOIN (
-  SELECT ts, value AS current_avg_ua
-  FROM counter c
-  JOIN counter_track t on c.track_id = t.id
-  WHERE name='batt.current.avg_ua'
-) USING(ts)
-LEFT JOIN (
-  SELECT ts, value AS capacity_percent
-  FROM counter c
-  JOIN counter_track t on c.track_id = t.id
-  WHERE name='batt.capacity_pct'
-) USING(ts)
-LEFT JOIN (
-  SELECT ts, value AS charge_uah
-  FROM counter c
-  JOIN counter_track t on c.track_id = t.id
-  WHERE name='batt.charge_uah'
-) USING(ts)
-LEFT JOIN (
-  SELECT ts, value AS current_ua
-  FROM counter c
-  JOIN counter_track t on c.track_id = t.id
-  WHERE name='batt.current_ua'
-) USING(ts)
-ORDER BY ts;
+SELECT * FROM android_battery_charge;
 
 DROP TABLE IF EXISTS android_batt_wakelocks_merged;
 CREATE TABLE android_batt_wakelocks_merged AS
@@ -60,23 +26,23 @@
   MAX(ts_end) AS ts_end
 FROM (
     SELECT
-        *,
-        SUM(new_group) OVER (ORDER BY ts) AS group_id
+      *,
+      SUM(new_group) OVER (ORDER BY ts) AS group_id
     FROM (
         SELECT
-            ts,
-            ts + dur AS ts_end,
-            -- There is a new group if there was a gap before this wakelock.
-            -- i.e. the max end timestamp of all preceding wakelocks is before
-            -- the start timestamp of this one.
-            -- The null check is for the first row which is always a new group.
-            IFNULL(
-                MAX(ts + dur) OVER (
-                    ORDER BY ts
-                    ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
-                ) < ts,
-                true
-            ) AS new_group
+          ts,
+          ts + dur AS ts_end,
+          -- There is a new group if there was a gap before this wakelock.
+          -- i.e. the max end timestamp of all preceding wakelocks is before
+          -- the start timestamp of this one.
+          -- The null check is for the first row which is always a new group.
+          IFNULL(
+            MAX(ts + dur) OVER (
+              ORDER BY ts
+              ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
+            ) < ts,
+            TRUE
+          ) AS new_group
         FROM slice
         WHERE slice.name GLOB 'WakeLock *' AND dur != -1
     )
@@ -86,36 +52,89 @@
 DROP TABLE IF EXISTS suspend_slice_;
 CREATE TABLE suspend_slice_ AS
 SELECT
-    ts,
-    dur
+  ts,
+  dur
 FROM
-    slice
-    JOIN
-    track
-    ON slice.track_id = track.id
+  slice
+JOIN
+  track
+  ON slice.track_id = track.id
 WHERE
-    track.name = 'Suspend/Resume Latency'
-    AND slice.name = 'syscore_resume(0)'
-    AND dur != -1
-;
+  track.name = 'Suspend/Resume Latency'
+  AND (slice.name = 'syscore_resume(0)' OR slice.name = 'timekeeping_freeze(0)')
+  AND dur != -1;
 
-SELECT RUN_METRIC('android/global_counter_span_view.sql',
+SELECT RUN_METRIC('android/global_counter_span_view_merged.sql',
   'table_name', 'screen_state',
   'counter_name', 'ScreenState');
 
+SELECT RUN_METRIC('android/process_counter_span_view.sql',
+  'table_name', 'doze_light_state',
+  'counter_name', 'DozeLightState');
+
+SELECT RUN_METRIC('android/process_counter_span_view.sql',
+  'table_name', 'doze_deep_state',
+  'counter_name', 'DozeDeepState');
+
 DROP TABLE IF EXISTS screen_state_span_with_suspend;
 CREATE VIRTUAL TABLE screen_state_span_with_suspend
-    USING span_join(screen_state_span, suspend_slice_);
+USING span_join(screen_state_span, suspend_slice_);
 
 DROP VIEW IF EXISTS android_batt_event;
 CREATE VIEW android_batt_event AS
 SELECT
-       ts,
+  ts,
+  dur,
+  'Suspended' AS slice_name,
+  'Suspend / resume' AS track_name,
+  'slice' AS track_type
+FROM suspend_slice_
+UNION ALL
+SELECT ts,
        dur,
-       'Suspended' AS slice_name,
-       'Suspend / resume' AS track_name,
+       CASE screen_state_val
+       WHEN 1 THEN 'Screen off'
+       WHEN 2 THEN 'Screen on'
+       WHEN 3 THEN 'Always-on display (doze)'
+       ELSE 'unknown'
+       END AS slice_name,
+       'Screen state' AS track_name,
        'slice' AS track_type
-FROM suspend_slice_;
+FROM screen_state_span
+UNION ALL
+-- See DeviceIdleController.java for where these states come from and how
+-- they transition.
+SELECT ts,
+       dur,
+       CASE doze_light_state_val
+       WHEN 0 THEN 'active'
+       WHEN 1 THEN 'inactive'
+       WHEN 4 THEN 'idle'
+       WHEN 5 THEN 'waiting_for_network'
+       WHEN 6 THEN 'idle_maintenance'
+       WHEN 7 THEN 'override'
+       ELSE 'unknown'
+       END AS slice_name,
+       'Doze light state' AS track_name,
+       'slice' AS track_type
+FROM doze_light_state_span
+UNION ALL
+SELECT ts,
+       dur,
+       CASE doze_deep_state_val
+       WHEN 0 THEN 'active'
+       WHEN 1 THEN 'inactive'
+       WHEN 2 THEN 'idle_pending'
+       WHEN 3 THEN 'sensing'
+       WHEN 4 THEN 'locating'
+       WHEN 5 THEN 'idle'
+       WHEN 6 THEN 'idle_maintenance'
+       WHEN 7 THEN 'quick_doze_delay'
+       ELSE 'unknown'
+       END AS slice_name,
+       'Doze deep state' AS track_name,
+       'slice' AS track_type
+FROM doze_deep_state_span;
 
 DROP VIEW IF EXISTS android_batt_output;
 CREATE VIEW android_batt_output AS
@@ -130,7 +149,7 @@
         'current_avg_ua', current_avg_ua
       )
     )
-    FROM battery_view
+    FROM android_battery_charge
   ),
   'battery_aggregates', (
     SELECT NULL_IF_EMPTY(AndroidBatteryMetric_BatteryAggregates(
@@ -150,13 +169,13 @@
       SUM(CASE WHEN state = 3.0 AND tbl = 'sleep' THEN dur ELSE 0 END),
       'total_wakelock_ns',
       (SELECT SUM(ts_end - ts) FROM android_batt_wakelocks_merged)
-    ))
+      ))
     FROM (
-         SELECT dur, screen_state_val AS state, 'total' AS tbl
-         FROM screen_state_span
-         UNION ALL
-         SELECT dur, screen_state_val AS state, 'sleep' AS tbl
-         FROM screen_state_span_with_suspend
+      SELECT dur, screen_state_val AS state, 'total' AS tbl
+      FROM screen_state_span
+      UNION ALL
+      SELECT dur, screen_state_val AS state, 'sleep' AS tbl
+      FROM screen_state_span_with_suspend
     )
   ),
   'suspend_period', (
diff --git a/src/trace_processor/metrics/sql/android/android_binder.sql b/src/trace_processor/metrics/sql/android/android_binder.sql
index cfe6fe8..5c2e464 100644
--- a/src/trace_processor/metrics/sql/android/android_binder.sql
+++ b/src/trace_processor/metrics/sql/android/android_binder.sql
@@ -14,23 +14,12 @@
 -- limitations under the License.
 --
 
+SELECT IMPORT('android.binder');
+
 -- Count Binder transactions per process
 DROP VIEW IF EXISTS binder_metrics_by_process;
 CREATE VIEW binder_metrics_by_process AS
-SELECT
-  process.name as process_name,
-  process.pid as pid,
-  slice.name as slice_name,
-  COUNT(*) as event_count
-FROM slice
-  INNER JOIN thread_track ON slice.track_id=thread_track.id
-  INNER JOIN thread ON thread.utid=thread_track.utid
-  INNER JOIN process ON thread.upid=process.upid
-WHERE
-  slice.name glob 'binder*'
-GROUP BY
-  process_name,
-  slice_name;
+SELECT * FROM android_binder_metrics_by_process;
 
 DROP VIEW IF EXISTS android_binder_output;
 CREATE VIEW android_binder_output AS
@@ -44,6 +33,6 @@
         'count', event_count
       )
     )
-    FROM binder_metrics_by_process
+    FROM android_binder_metrics_by_process
   )
 );
diff --git a/src/trace_processor/metrics/sql/android/android_camera.sql b/src/trace_processor/metrics/sql/android/android_camera.sql
index 791c922..606af3b 100644
--- a/src/trace_processor/metrics/sql/android/android_camera.sql
+++ b/src/trace_processor/metrics/sql/android/android_camera.sql
@@ -20,7 +20,7 @@
 -- Compute DMA spans.
 SELECT RUN_METRIC('android/global_counter_span_view.sql',
   'table_name', 'dma',
-   'counter_name', 'mem.dma_heap');
+  'counter_name', 'mem.dma_heap');
 
 -- RSS of GCA.
 DROP VIEW IF EXISTS rss_gca;
@@ -79,10 +79,10 @@
   ts,
   dur,
   CAST(
-    IFNULL(gca_rss_val, 0) +
-    IFNULL(hal_rss_val, 0) +
-    IFNULL(cameraserver_rss_val, 0) +
-    IFNULL(dma_val, 0) AS int) AS rss_and_dma_val
+    IFNULL(gca_rss_val, 0)
+    + IFNULL(hal_rss_val, 0)
+    + IFNULL(cameraserver_rss_val, 0)
+    + IFNULL(dma_val, 0) AS int) AS rss_and_dma_val
 FROM rss_and_dma_all_camera_join;
 
 -- we are dividing and casting to real when calculating avg_value
diff --git a/src/trace_processor/metrics/sql/android/android_camera_unagg.sql b/src/trace_processor/metrics/sql/android/android_camera_unagg.sql
index 29f4ca7..b3a433c 100644
--- a/src/trace_processor/metrics/sql/android/android_camera_unagg.sql
+++ b/src/trace_processor/metrics/sql/android/android_camera_unagg.sql
@@ -24,9 +24,9 @@
     'gc_rss_and_dma', (
       SELECT RepeatedField(
           AndroidCameraUnaggregatedMetric_Value(
-          'ts', ts,
-          'value', CAST(rss_and_dma_val AS real)
-        )
+            'ts', ts,
+            'value', CAST(rss_and_dma_val AS real)
+          )
       )
       FROM rss_and_dma_all_camera_span
     )
diff --git a/src/trace_processor/metrics/sql/android/android_cpu.sql b/src/trace_processor/metrics/sql/android/android_cpu.sql
index 2af9faf..b86cb5d 100644
--- a/src/trace_processor/metrics/sql/android/android_cpu.sql
+++ b/src/trace_processor/metrics/sql/android/android_cpu.sql
@@ -55,7 +55,7 @@
         'avg_freq_khz', avg_freq_khz
       )
     )
-  ) as proto
+  ) AS proto
 FROM raw_metrics_per_core
 GROUP BY utid;
 
@@ -68,7 +68,7 @@
       'type', core_type,
       'metrics', metrics_per_core_type.proto
     )
-  ) as proto
+  ) AS proto
 FROM metrics_per_core_type
 GROUP BY utid;
 
@@ -100,7 +100,7 @@
       'core', core_proto_per_thread.proto,
       'core_type', core_type_proto_per_thread.proto
     )
-  ) as proto
+  ) AS proto
 FROM thread
 LEFT JOIN core_proto_per_thread USING (utid)
 LEFT JOIN core_type_proto_per_thread USING (utid)
@@ -136,7 +136,7 @@
       'id', cpu,
       'metrics', core_metrics_per_process.proto
     )
-  ) as proto
+  ) AS proto
 FROM core_metrics_per_process
 GROUP BY upid;
 
@@ -169,7 +169,7 @@
       'type', core_type,
       'metrics', core_type_metrics_per_process.proto
     )
-  ) as proto
+  ) AS proto
 FROM core_type_metrics_per_process
 GROUP BY upid;
 
diff --git a/src/trace_processor/metrics/sql/android/android_cpu_agg.sql b/src/trace_processor/metrics/sql/android/android_cpu_agg.sql
index d5f145e..c45c22a 100644
--- a/src/trace_processor/metrics/sql/android/android_cpu_agg.sql
+++ b/src/trace_processor/metrics/sql/android/android_cpu_agg.sql
@@ -21,11 +21,11 @@
 SELECT
   cpu,
   ts,
-  LEAD(ts, 1, (SELECT end_ts from trace_bounds))
-    OVER (PARTITION by cpu ORDER BY ts) - ts AS dur,
-  CAST(value AS INT) as freq_khz
+  LEAD(ts, 1, (SELECT end_ts FROM trace_bounds))
+  OVER (PARTITION BY cpu ORDER BY ts) - ts AS dur,
+  CAST(value AS INT) AS freq_khz
 FROM counter
-JOIN cpu_counter_track on counter.track_id = cpu_counter_track.id
+JOIN cpu_counter_track ON counter.track_id = cpu_counter_track.id
 WHERE name = 'cpufreq';
 
 -- View that joins the cpufreq table with the slice table.
diff --git a/src/trace_processor/metrics/sql/android/android_cpu_raw_metrics_per_core.sql b/src/trace_processor/metrics/sql/android/android_cpu_raw_metrics_per_core.sql
index 990bd04..dd53f83 100644
--- a/src/trace_processor/metrics/sql/android/android_cpu_raw_metrics_per_core.sql
+++ b/src/trace_processor/metrics/sql/android/android_cpu_raw_metrics_per_core.sql
@@ -20,7 +20,7 @@
 SELECT
   utid,
   cpu,
-  IFNULL(core_type_per_cpu.core_type, 'unknown') core_type,
+  IFNULL(core_type_per_cpu.core_type, 'unknown') AS core_type,
   -- 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
@@ -38,5 +38,5 @@
   CAST(SUM(dur * freq_khz / 1000) / SUM(dur / 1000) AS INT) AS avg_freq_khz
 FROM {{input_table}}
 LEFT JOIN core_type_per_cpu USING (cpu)
-WHERE utid != 0 and dur != -1
+WHERE utid != 0 AND dur != -1
 GROUP BY utid, cpu;
diff --git a/src/trace_processor/metrics/sql/android/android_dma_heap.sql b/src/trace_processor/metrics/sql/android/android_dma_heap.sql
index afdab08..3a9cfc6 100644
--- a/src/trace_processor/metrics/sql/android/android_dma_heap.sql
+++ b/src/trace_processor/metrics/sql/android/android_dma_heap.sql
@@ -19,7 +19,7 @@
 SELECT
   ts,
   LEAD(ts, 1, (SELECT end_ts FROM trace_bounds))
-    OVER(PARTITION BY track_id ORDER BY ts) - ts AS dur,
+  OVER(PARTITION BY track_id ORDER BY ts) - ts AS dur,
   track_id,
   value
 FROM counter JOIN counter_track
@@ -70,9 +70,9 @@
 DROP VIEW IF EXISTS android_dma_heap_output;
 CREATE VIEW android_dma_heap_output AS
 SELECT AndroidDmaHeapMetric(
-      'avg_size_bytes', avg_size,
-      'min_size_bytes', min_size,
-      'max_size_bytes', max_size,
-      'total_alloc_size_bytes', total_alloc_size_bytes
+  'avg_size_bytes', avg_size,
+  'min_size_bytes', min_size,
+  'max_size_bytes', max_size,
+  'total_alloc_size_bytes', total_alloc_size_bytes
   )
 FROM dma_heap_stats JOIN dma_heap_total_stats;
diff --git a/src/trace_processor/metrics/sql/android/android_dvfs.sql b/src/trace_processor/metrics/sql/android/android_dvfs.sql
index d8162b1..cffb4eb 100644
--- a/src/trace_processor/metrics/sql/android/android_dvfs.sql
+++ b/src/trace_processor/metrics/sql/android/android_dvfs.sql
@@ -16,28 +16,28 @@
 DROP VIEW IF EXISTS freq_slice;
 
 CREATE VIEW freq_slice AS
-  SELECT
-    counter.track_id AS track_id,
-    track.name AS freq_name,
-    ts,
-    value AS freq_value,
-    LEAD(ts, 1, (SELECT end_ts+1 FROM trace_bounds))
-      OVER (PARTITION by track.id ORDER BY ts)  - ts AS duration
-  FROM counter
-  LEFT JOIN track ON counter.track_id = track.id
-  WHERE track.name GLOB "* Frequency"
-  ORDER BY ts;
+SELECT
+  counter.track_id AS track_id,
+  track.name AS freq_name,
+  ts,
+  value AS freq_value,
+  LEAD(ts, 1, (SELECT end_ts + 1 FROM trace_bounds))
+  OVER (PARTITION BY track.id ORDER BY ts) - ts AS duration
+FROM counter
+LEFT JOIN track ON counter.track_id = track.id
+WHERE track.name GLOB "* Frequency"
+ORDER BY ts;
 
 DROP VIEW IF EXISTS freq_total_duration;
 
 CREATE VIEW freq_total_duration AS
-  SELECT
-    track_id,
-    freq_name,
-    SUM(duration) AS total_duration
-  FROM freq_slice
-  WHERE duration > 0
-  GROUP BY track_id, freq_name;
+SELECT
+  track_id,
+  freq_name,
+  SUM(duration) AS total_duration
+FROM freq_slice
+WHERE duration > 0
+GROUP BY track_id, freq_name;
 
 DROP VIEW IF EXISTS dvfs_per_band_view;
 
@@ -57,34 +57,34 @@
   freq_duration.track_id,
   freq_duration.freq_name,
   AndroidDvfsMetric_BandStat(
-     'freq_value', freq_value,
-     'percentage', duration_ns / (total_duration / 1e2),
-     'duration_ns', duration_ns
+    'freq_value', freq_value,
+    'percentage', duration_ns / (total_duration / 1e2),
+    'duration_ns', duration_ns
   ) AS proto
 FROM freq_duration
 LEFT JOIN freq_total_duration
-USING(track_id)
+  USING(track_id)
 ORDER BY freq_duration.freq_name, freq_duration.freq_value;
 
 DROP VIEW IF EXISTS dvfs_per_freq_view;
 CREATE VIEW dvfs_per_freq_view AS
-  SELECT
-    AndroidDvfsMetric_FrequencyResidency(
-      'freq_name', freq_total_duration.freq_name,
-      'band_stat', (
-        SELECT
-          RepeatedField(proto)
-        FROM dvfs_per_band_view
-        WHERE dvfs_per_band_view.track_id = freq_total_duration.track_id
-      )
-    ) AS proto
-  FROM freq_total_duration
-  GROUP BY track_id, freq_name
-  ORDER BY freq_name;
+SELECT
+  AndroidDvfsMetric_FrequencyResidency(
+    'freq_name', freq_total_duration.freq_name,
+    'band_stat', (
+      SELECT
+        RepeatedField(proto)
+      FROM dvfs_per_band_view
+      WHERE dvfs_per_band_view.track_id = freq_total_duration.track_id
+    )
+  ) AS proto
+FROM freq_total_duration
+GROUP BY track_id, freq_name
+ORDER BY freq_name;
 
 DROP VIEW IF EXISTS android_dvfs_output;
 CREATE VIEW android_dvfs_output AS
-  SELECT AndroidDVFSMetric(
+SELECT AndroidDVFSMetric(
     'freq_residencies', (
       SELECT
         RepeatedField(proto)
diff --git a/src/trace_processor/metrics/sql/android/android_fastrpc.sql b/src/trace_processor/metrics/sql/android/android_fastrpc.sql
index e381668..081dd6b 100644
--- a/src/trace_processor/metrics/sql/android/android_fastrpc.sql
+++ b/src/trace_processor/metrics/sql/android/android_fastrpc.sql
@@ -3,7 +3,7 @@
 SELECT
   ts,
   LEAD(ts, 1, (SELECT end_ts FROM trace_bounds))
-    OVER(PARTITION BY track_id ORDER BY ts) - ts AS dur,
+  OVER(PARTITION BY track_id ORDER BY ts) - ts AS dur,
   RTRIM(SUBSTR(name, 13), ']') AS subsystem_name,
   track_id,
   value
diff --git a/src/trace_processor/metrics/sql/android/android_gpu.sql b/src/trace_processor/metrics/sql/android/android_gpu.sql
index f0c0456..d2b6371 100644
--- a/src/trace_processor/metrics/sql/android/android_gpu.sql
+++ b/src/trace_processor/metrics/sql/android/android_gpu.sql
@@ -26,10 +26,10 @@
 CREATE VIEW proc_gpu_memory_view AS
 SELECT
   upid,
-  MAX(proc_gpu_memory_val) as mem_max,
-  MIN(proc_gpu_memory_val) as mem_min,
-  SUM(proc_gpu_memory_val * dur) as mem_valxdur,
-  SUM(dur) as mem_dur
+  MAX(proc_gpu_memory_val) AS mem_max,
+  MIN(proc_gpu_memory_val) AS mem_min,
+  SUM(proc_gpu_memory_val * dur) AS mem_valxdur,
+  SUM(dur) AS mem_dur
 FROM proc_gpu_memory_span
 GROUP BY upid;
 
@@ -37,12 +37,12 @@
 CREATE VIEW agg_proc_gpu_view AS
 SELECT
   name,
-  MAX(mem_max) as mem_max,
-  MIN(mem_min) as mem_min,
-  SUM(mem_valxdur) / SUM(mem_dur) as mem_avg
+  MAX(mem_max) AS mem_max,
+  MIN(mem_min) AS mem_min,
+  SUM(mem_valxdur) / SUM(mem_dur) AS mem_avg
 FROM process
 JOIN proc_gpu_memory_view
-USING(upid)
+  USING(upid)
 GROUP BY name;
 
 DROP VIEW IF EXISTS proc_gpu_view;
@@ -50,9 +50,9 @@
 SELECT
   AndroidGpuMetric_Process(
     'name', name,
-    'mem_max', CAST(mem_max as INT64),
-    'mem_min', CAST(mem_min as INT64),
-    'mem_avg', CAST(mem_avg as INT64)
+    'mem_max', CAST(mem_max AS INT64),
+    'mem_min', CAST(mem_min AS INT64),
+    'mem_avg', CAST(mem_avg AS INT64)
   ) AS proto
 FROM agg_proc_gpu_view;
 
@@ -81,7 +81,7 @@
 SELECT
   gpu_id,
   AndroidGpuMetric_FrequencyMetric_MetricsPerFrequency(
-    'freq', CAST(freq as INT64),
+    'freq', CAST(freq AS INT64),
     'dur_ms', f.dur_ns / 1e6,
     'percentage', f.dur_ns * 100.0 / g.dur_ns
   ) AS proto
@@ -92,11 +92,11 @@
 SELECT
   AndroidGpuMetric_FrequencyMetric(
     'gpu_id', gpu_id,
-    'freq_max', CAST(MAX(gpu_freq_val) as INT64),
-    'freq_min', CAST(MIN(gpu_freq_val) as INT64),
+    'freq_max', CAST(MAX(gpu_freq_val) AS INT64),
+    'freq_min', CAST(MIN(gpu_freq_val) AS INT64),
     'freq_avg', SUM(gpu_freq_val * dur) / SUM(dur),
     'used_freqs', (SELECT RepeatedField(proto) FROM metrics_per_freq_view
-        WHERE metrics_per_freq_view.gpu_id = gpu_freq_span.gpu_id)
+      WHERE metrics_per_freq_view.gpu_id = gpu_freq_span.gpu_id)
   ) AS proto
 FROM gpu_freq_span
 GROUP BY gpu_id;
@@ -105,9 +105,9 @@
 CREATE VIEW android_gpu_output AS
 SELECT AndroidGpuMetric(
   'processes', (SELECT RepeatedField(proto) FROM proc_gpu_view),
-  'mem_max', CAST(MAX(global_gpu_memory_val) as INT64),
-  'mem_min', CAST(MIN(global_gpu_memory_val) as INT64),
-  'mem_avg', CAST(SUM(global_gpu_memory_val * dur) / SUM(dur) as INT64),
+  'mem_max', CAST(MAX(global_gpu_memory_val) AS INT64),
+  'mem_min', CAST(MIN(global_gpu_memory_val) AS INT64),
+  'mem_avg', CAST(SUM(global_gpu_memory_val * dur) / SUM(dur) AS INT64),
   'freq_metrics', (SELECT RepeatedField(proto) FROM gpu_freq_metrics_view)
 )
 FROM global_gpu_memory_span;
diff --git a/src/trace_processor/metrics/sql/android/android_hwcomposer.sql b/src/trace_processor/metrics/sql/android/android_hwcomposer.sql
index d6f24bd..d7da4e5 100644
--- a/src/trace_processor/metrics/sql/android/android_hwcomposer.sql
+++ b/src/trace_processor/metrics/sql/android/android_hwcomposer.sql
@@ -81,23 +81,23 @@
 ) s JOIN process p USING (upid);
 
 -- These systrace counters are coming from dedicated kernel threads, so we can
--- assume pid == tid.
+-- assume pid = tid.
 DROP VIEW IF EXISTS dpu_vote_metrics;
 CREATE VIEW dpu_vote_metrics AS
 SELECT AndroidHwcomposerMetrics_DpuVoteMetrics(
   'tid', pid,
   'avg_dpu_vote_clock',
-      (SELECT SUM(dpu_vote_clock_val * dur) / SUM(dur)
-      FROM dpu_vote_clock_span s WHERE s.upid = p.upid),
+  (SELECT SUM(dpu_vote_clock_val * dur) / SUM(dur)
+    FROM dpu_vote_clock_span s WHERE s.upid = p.upid),
   'avg_dpu_vote_avg_bw',
-      (SELECT SUM(dpu_vote_avg_bw_val * dur) / SUM(dur)
-      FROM dpu_vote_avg_bw_span s WHERE s.upid = p.upid),
+  (SELECT SUM(dpu_vote_avg_bw_val * dur) / SUM(dur)
+    FROM dpu_vote_avg_bw_span s WHERE s.upid = p.upid),
   'avg_dpu_vote_peak_bw',
-      (SELECT SUM(dpu_vote_peak_bw_val * dur) / SUM(dur)
-      FROM dpu_vote_peak_bw_span s WHERE s.upid = p.upid),
+  (SELECT SUM(dpu_vote_peak_bw_val * dur) / SUM(dur)
+    FROM dpu_vote_peak_bw_span s WHERE s.upid = p.upid),
   'avg_dpu_vote_rt_bw',
-      (SELECT SUM(dpu_vote_rt_bw_val * dur) / SUM(dur)
-      FROM dpu_vote_rt_bw_span s WHERE s.upid = p.upid)
+  (SELECT SUM(dpu_vote_rt_bw_val * dur) / SUM(dur)
+    FROM dpu_vote_rt_bw_span s WHERE s.upid = p.upid)
 ) AS proto
 FROM dpu_vote_process p
 ORDER BY pid;
@@ -111,28 +111,28 @@
   'composition_dpu_cached_layers', (SELECT AVG(value) FROM dpu_cached_layers),
   'composition_sf_cached_layers', (SELECT AVG(value) FROM sf_cached_layers),
   'skipped_validation_count',
-      (SELECT COUNT(*) FROM hwc_execution_spans
-      WHERE validation_type = 'skipped_validation'),
+  (SELECT COUNT(*) FROM hwc_execution_spans
+    WHERE validation_type = 'skipped_validation'),
   'unskipped_validation_count',
-      (SELECT COUNT(*) FROM hwc_execution_spans
-      WHERE validation_type = 'unskipped_validation'),
+  (SELECT COUNT(*) FROM hwc_execution_spans
+    WHERE validation_type = 'unskipped_validation'),
   'separated_validation_count',
-      (SELECT COUNT(*) FROM hwc_execution_spans
-      WHERE validation_type = 'separated_validation'),
+  (SELECT COUNT(*) FROM hwc_execution_spans
+    WHERE validation_type = 'separated_validation'),
   'unknown_validation_count',
-      (SELECT COUNT(*) FROM hwc_execution_spans
-      WHERE validation_type = 'unknown'),
+  (SELECT COUNT(*) FROM hwc_execution_spans
+    WHERE validation_type = 'unknown'),
   'avg_all_execution_time_ms',
-      (SELECT AVG(execution_time_ns) / 1e6 FROM hwc_execution_spans
-      WHERE validation_type != 'unknown'),
+  (SELECT AVG(execution_time_ns) / 1e6 FROM hwc_execution_spans
+    WHERE validation_type != 'unknown'),
   'avg_skipped_execution_time_ms',
-      (SELECT AVG(execution_time_ns) / 1e6 FROM hwc_execution_spans
-      WHERE validation_type = 'skipped_validation'),
+  (SELECT AVG(execution_time_ns) / 1e6 FROM hwc_execution_spans
+    WHERE validation_type = 'skipped_validation'),
   'avg_unskipped_execution_time_ms',
-      (SELECT AVG(execution_time_ns) / 1e6 FROM hwc_execution_spans
-      WHERE validation_type = 'unskipped_validation'),
+  (SELECT AVG(execution_time_ns) / 1e6 FROM hwc_execution_spans
+    WHERE validation_type = 'unskipped_validation'),
   'avg_separated_execution_time_ms',
-      (SELECT AVG(execution_time_ns) / 1e6 FROM hwc_execution_spans
-      WHERE validation_type = 'separated_validation'),
+  (SELECT AVG(execution_time_ns) / 1e6 FROM hwc_execution_spans
+    WHERE validation_type = 'separated_validation'),
   'dpu_vote_metrics', (SELECT RepeatedField(proto) FROM dpu_vote_metrics)
 );
diff --git a/src/trace_processor/metrics/sql/android/android_hwui_metric.sql b/src/trace_processor/metrics/sql/android/android_hwui_metric.sql
index 1a33e22..391d7d2 100644
--- a/src/trace_processor/metrics/sql/android/android_hwui_metric.sql
+++ b/src/trace_processor/metrics/sql/android/android_hwui_metric.sql
@@ -19,166 +19,166 @@
 DROP VIEW IF EXISTS hwui_processes;
 CREATE VIEW hwui_processes AS
 SELECT
-  process.name as process_name,
-  process.upid as process_upid,
-  CAST(SUM(sched.dur) / 1e6 as INT64) as rt_cpu_time_ms,
-  thread.utid as render_thread_id
+  process.name AS process_name,
+  process.upid AS process_upid,
+  CAST(SUM(sched.dur) / 1e6 AS INT64) AS rt_cpu_time_ms,
+  thread.utid AS render_thread_id
 FROM sched
-INNER JOIN thread ON (thread.utid = sched.utid AND thread.name='RenderThread')
-INNER JOIN process ON (process.upid = thread.upid)
+JOIN thread ON (thread.utid = sched.utid AND thread.name = 'RenderThread')
+JOIN process ON (process.upid = thread.upid)
 GROUP BY process.name
 ORDER BY rt_cpu_time_ms DESC;
 
 DROP VIEW IF EXISTS hwui_draw_frame;
 CREATE VIEW hwui_draw_frame AS
 SELECT
-  count(*) as draw_frame_count,
-  max(dur) as draw_frame_max,
-  min(dur) as draw_frame_min,
-  avg(dur) as draw_frame_avg,
-  thread_track.utid as render_thread_id
+  count(*) AS draw_frame_count,
+  max(dur) AS draw_frame_max,
+  min(dur) AS draw_frame_min,
+  avg(dur) AS draw_frame_avg,
+  thread_track.utid AS render_thread_id
 FROM slice
-INNER JOIN thread_track ON (thread_track.id = slice.track_id)
+JOIN thread_track ON (thread_track.id = slice.track_id)
 WHERE slice.name GLOB 'DrawFrame*' AND slice.dur >= 0
 GROUP BY thread_track.utid;
 
 DROP VIEW IF EXISTS hwui_flush_commands;
 CREATE VIEW hwui_flush_commands AS
 SELECT
-  count(*) as flush_count,
-  max(dur) as flush_max,
-  min(dur) as flush_min,
-  avg(dur) as flush_avg,
-  thread_track.utid as render_thread_id
+  count(*) AS flush_count,
+  max(dur) AS flush_max,
+  min(dur) AS flush_min,
+  avg(dur) AS flush_avg,
+  thread_track.utid AS render_thread_id
 FROM slice
-INNER JOIN thread_track ON (thread_track.id = slice.track_id)
-WHERE slice.name='flush commands' AND slice.dur >= 0
+JOIN thread_track ON (thread_track.id = slice.track_id)
+WHERE slice.name = 'flush commands' AND slice.dur >= 0
 GROUP BY thread_track.utid;
 
 DROP VIEW IF EXISTS hwui_prepare_tree;
 CREATE VIEW hwui_prepare_tree AS
 SELECT
-  count(*) as prepare_tree_count,
-  max(dur) as prepare_tree_max,
-  min(dur) as prepare_tree_min,
-  avg(dur) as prepare_tree_avg,
-  thread_track.utid as render_thread_id
+  count(*) AS prepare_tree_count,
+  max(dur) AS prepare_tree_max,
+  min(dur) AS prepare_tree_min,
+  avg(dur) AS prepare_tree_avg,
+  thread_track.utid AS render_thread_id
 FROM slice
-INNER JOIN thread_track ON (thread_track.id = slice.track_id)
-WHERE slice.name='prepareTree' AND slice.dur >= 0
+JOIN thread_track ON (thread_track.id = slice.track_id)
+WHERE slice.name = 'prepareTree' AND slice.dur >= 0
 GROUP BY thread_track.utid;
 
 DROP VIEW IF EXISTS hwui_gpu_completion;
 CREATE VIEW hwui_gpu_completion AS
 SELECT
-  count(*) as gpu_completion_count,
-  max(dur) as gpu_completion_max,
-  min(dur) as gpu_completion_min,
-  avg(dur) as gpu_completion_avg,
-  thread.upid as process_upid
+  count(*) AS gpu_completion_count,
+  max(dur) AS gpu_completion_max,
+  min(dur) AS gpu_completion_min,
+  avg(dur) AS gpu_completion_avg,
+  thread.upid AS process_upid
 FROM slice
-INNER JOIN thread_track ON (thread_track.id = slice.track_id)
-INNER JOIN thread ON (thread.name='GPU completion' AND thread.utid = thread_track.utid)
+JOIN thread_track ON (thread_track.id = slice.track_id)
+JOIN thread ON (thread.name = 'GPU completion' AND thread.utid = thread_track.utid)
 WHERE slice.name GLOB 'waiting for GPU completion*' AND slice.dur >= 0
 GROUP BY thread_track.utid;
 
 DROP VIEW IF EXISTS hwui_ui_record;
 CREATE VIEW hwui_ui_record AS
 SELECT
-  count(*) as ui_record_count,
-  max(dur) as ui_record_max,
-  min(dur) as ui_record_min,
-  avg(dur) as ui_record_avg,
-  thread.upid as process_upid
+  count(*) AS ui_record_count,
+  max(dur) AS ui_record_max,
+  min(dur) AS ui_record_min,
+  avg(dur) AS ui_record_avg,
+  thread.upid AS process_upid
 FROM slice
-INNER JOIN thread_track ON (thread_track.id = slice.track_id)
-INNER JOIN thread ON (thread.name=substr(process.name,-15) AND thread.utid = thread_track.utid)
-INNER JOIN process ON (process.upid = thread.upid)
-WHERE slice.name='Record View#draw()' AND slice.dur >= 0
+JOIN thread_track ON (thread_track.id = slice.track_id)
+JOIN thread ON (thread.name = substr(process.name, -15) AND thread.utid = thread_track.utid)
+JOIN process ON (process.upid = thread.upid)
+WHERE slice.name = 'Record View#draw()' AND slice.dur >= 0
 GROUP BY thread_track.utid;
 
 DROP VIEW IF EXISTS hwui_shader_compile;
 CREATE VIEW hwui_shader_compile AS
 SELECT
-  count(*) as shader_compile_count,
-  sum(dur) as shader_compile_time,
-  avg(dur) as shader_compile_avg,
-  thread_track.utid as render_thread_id
+  count(*) AS shader_compile_count,
+  sum(dur) AS shader_compile_time,
+  avg(dur) AS shader_compile_avg,
+  thread_track.utid AS render_thread_id
 FROM slice
-INNER JOIN thread_track ON (thread_track.id = slice.track_id)
-WHERE slice.name='shader_compile' AND slice.dur >= 0
+JOIN thread_track ON (thread_track.id = slice.track_id)
+WHERE slice.name = 'shader_compile' AND slice.dur >= 0
 GROUP BY thread_track.utid;
 
 DROP VIEW IF EXISTS hwui_cache_hit;
 CREATE VIEW hwui_cache_hit AS
 SELECT
-  count(*) as cache_hit_count,
-  sum(dur) as cache_hit_time,
-  avg(dur) as cache_hit_avg,
-  thread_track.utid as render_thread_id
+  count(*) AS cache_hit_count,
+  sum(dur) AS cache_hit_time,
+  avg(dur) AS cache_hit_avg,
+  thread_track.utid AS render_thread_id
 FROM slice
-INNER JOIN thread_track ON (thread_track.id = slice.track_id)
-WHERE slice.name='cache_hit' AND slice.dur >= 0
+JOIN thread_track ON (thread_track.id = slice.track_id)
+WHERE slice.name = 'cache_hit' AND slice.dur >= 0
 GROUP BY thread_track.utid;
 
 DROP VIEW IF EXISTS hwui_cache_miss;
 CREATE VIEW hwui_cache_miss AS
 SELECT
-  count(*) as cache_miss_count,
-  sum(dur) as cache_miss_time,
-  avg(dur) as cache_miss_avg,
-  thread_track.utid as render_thread_id
+  count(*) AS cache_miss_count,
+  sum(dur) AS cache_miss_time,
+  avg(dur) AS cache_miss_avg,
+  thread_track.utid AS render_thread_id
 FROM slice
-INNER JOIN thread_track ON (thread_track.id = slice.track_id)
-WHERE slice.name='cache_miss' AND slice.dur >= 0
+JOIN thread_track ON (thread_track.id = slice.track_id)
+WHERE slice.name = 'cache_miss' AND slice.dur >= 0
 GROUP BY thread_track.utid;
 
 DROP VIEW IF EXISTS hwui_graphics_cpu_mem;
 CREATE VIEW hwui_graphics_cpu_mem AS
 SELECT
-  max(value) as graphics_cpu_mem_max,
-  min(value) as graphics_cpu_mem_min,
-  avg(value) as graphics_cpu_mem_avg,
-  process_counter_track.upid as process_upid
+  max(value) AS graphics_cpu_mem_max,
+  min(value) AS graphics_cpu_mem_min,
+  avg(value) AS graphics_cpu_mem_avg,
+  process_counter_track.upid AS process_upid
 FROM counter
-INNER JOIN process_counter_track ON (counter.track_id = process_counter_track.id)
-WHERE name='HWUI CPU Memory' AND counter.value >= 0
+JOIN process_counter_track ON (counter.track_id = process_counter_track.id)
+WHERE name = 'HWUI CPU Memory' AND counter.value >= 0
 GROUP BY process_counter_track.upid;
 
 DROP VIEW IF EXISTS hwui_graphics_gpu_mem;
 CREATE VIEW hwui_graphics_gpu_mem AS
 SELECT
-  max(value) as graphics_gpu_mem_max,
-  min(value) as graphics_gpu_mem_min,
-  avg(value) as graphics_gpu_mem_avg,
-  process_counter_track.upid as process_upid
+  max(value) AS graphics_gpu_mem_max,
+  min(value) AS graphics_gpu_mem_min,
+  avg(value) AS graphics_gpu_mem_avg,
+  process_counter_track.upid AS process_upid
 FROM counter
-INNER JOIN process_counter_track ON (counter.track_id = process_counter_track.id)
-WHERE name='HWUI Misc Memory' AND counter.value >= 0
+JOIN process_counter_track ON (counter.track_id = process_counter_track.id)
+WHERE name = 'HWUI Misc Memory' AND counter.value >= 0
 GROUP BY process_counter_track.upid;
 
 DROP VIEW IF EXISTS hwui_texture_mem;
 CREATE VIEW hwui_texture_mem AS
 SELECT
-  max(value) as texture_mem_max,
-  min(value) as texture_mem_min,
-  avg(value) as texture_mem_avg,
-  process_counter_track.upid as process_upid
+  max(value) AS texture_mem_max,
+  min(value) AS texture_mem_min,
+  avg(value) AS texture_mem_avg,
+  process_counter_track.upid AS process_upid
 FROM counter
-INNER JOIN process_counter_track ON (counter.track_id = process_counter_track.id)
-WHERE name='HWUI Texture Memory' AND counter.value >= 0
+JOIN process_counter_track ON (counter.track_id = process_counter_track.id)
+WHERE name = 'HWUI Texture Memory' AND counter.value >= 0
 GROUP BY process_counter_track.upid;
 
 DROP VIEW IF EXISTS hwui_all_mem;
 CREATE VIEW hwui_all_mem AS
 SELECT
-  max(value) as all_mem_max,
-  min(value) as all_mem_min,
-  avg(value) as all_mem_avg,
-  process_counter_track.upid as process_upid
+  max(value) AS all_mem_max,
+  min(value) AS all_mem_min,
+  avg(value) AS all_mem_avg,
+  process_counter_track.upid AS process_upid
 FROM counter
-INNER JOIN process_counter_track ON (counter.track_id = process_counter_track.id)
-WHERE name='HWUI All Memory' AND counter.value >= 0
+JOIN process_counter_track ON (counter.track_id = process_counter_track.id)
+WHERE name = 'HWUI All Memory' AND counter.value >= 0
 GROUP BY process_counter_track.upid;
 
 DROP VIEW IF EXISTS android_hwui_metric_output;
@@ -227,20 +227,20 @@
         'cache_miss_time', hwui_cache_miss.cache_miss_time,
         'cache_miss_avg', hwui_cache_miss.cache_miss_avg,
 
-        'graphics_cpu_mem_max', CAST(hwui_graphics_cpu_mem.graphics_cpu_mem_max as INT64),
-        'graphics_cpu_mem_min', CAST(hwui_graphics_cpu_mem.graphics_cpu_mem_min as INT64),
+        'graphics_cpu_mem_max', CAST(hwui_graphics_cpu_mem.graphics_cpu_mem_max AS INT64),
+        'graphics_cpu_mem_min', CAST(hwui_graphics_cpu_mem.graphics_cpu_mem_min AS INT64),
         'graphics_cpu_mem_avg', hwui_graphics_cpu_mem.graphics_cpu_mem_avg,
 
-        'graphics_gpu_mem_max', CAST(hwui_graphics_gpu_mem.graphics_gpu_mem_max as INT64),
-        'graphics_gpu_mem_min', CAST(hwui_graphics_gpu_mem.graphics_gpu_mem_min as INT64),
+        'graphics_gpu_mem_max', CAST(hwui_graphics_gpu_mem.graphics_gpu_mem_max AS INT64),
+        'graphics_gpu_mem_min', CAST(hwui_graphics_gpu_mem.graphics_gpu_mem_min AS INT64),
         'graphics_gpu_mem_avg', hwui_graphics_gpu_mem.graphics_gpu_mem_avg,
 
-        'texture_mem_max', CAST(hwui_texture_mem.texture_mem_max as INT64),
-        'texture_mem_min', CAST(hwui_texture_mem.texture_mem_min as INT64),
+        'texture_mem_max', CAST(hwui_texture_mem.texture_mem_max AS INT64),
+        'texture_mem_min', CAST(hwui_texture_mem.texture_mem_min AS INT64),
         'texture_mem_avg', hwui_texture_mem.texture_mem_avg,
 
-        'all_mem_max', CAST(hwui_all_mem.all_mem_max as INT64),
-        'all_mem_min', CAST(hwui_all_mem.all_mem_min as INT64),
+        'all_mem_max', CAST(hwui_all_mem.all_mem_max AS INT64),
+        'all_mem_min', CAST(hwui_all_mem.all_mem_min AS INT64),
         'all_mem_avg', hwui_all_mem.all_mem_avg
       )
     )
diff --git a/src/trace_processor/metrics/sql/android/android_hwui_threads.sql b/src/trace_processor/metrics/sql/android/android_hwui_threads.sql
index f06a44d..5ab4f77 100644
--- a/src/trace_processor/metrics/sql/android/android_hwui_threads.sql
+++ b/src/trace_processor/metrics/sql/android/android_hwui_threads.sql
@@ -17,110 +17,110 @@
 
 DROP VIEW IF EXISTS {{table_name_prefix}}_main_thread;
 CREATE VIEW {{table_name_prefix}}_main_thread AS
-  SELECT
-    process.name as process_name,
-    thread.utid
-  FROM thread
-  JOIN {{process_allowlist_table}} process_allowlist USING (upid)
-  JOIN process USING (upid)
-  WHERE thread.is_main_thread;
+SELECT
+  process.name AS process_name,
+  thread.utid
+FROM thread
+JOIN {{process_allowlist_table}} process_allowlist USING (upid)
+JOIN process USING (upid)
+WHERE thread.is_main_thread;
 
 DROP VIEW IF EXISTS {{table_name_prefix}}_render_thread;
 CREATE VIEW {{table_name_prefix}}_render_thread AS
-  SELECT
-    process.name as process_name,
-    thread.utid
-  FROM thread
-  JOIN {{process_allowlist_table}} process_allowlist USING (upid)
-  JOIN process USING (upid)
-  WHERE thread.name = 'RenderThread';
+SELECT
+  process.name AS process_name,
+  thread.utid
+FROM thread
+JOIN {{process_allowlist_table}} process_allowlist USING (upid)
+JOIN process USING (upid)
+WHERE thread.name = 'RenderThread';
 
 DROP VIEW IF EXISTS {{table_name_prefix}}_gpu_completion_thread;
 CREATE VIEW {{table_name_prefix}}_gpu_completion_thread AS
-  SELECT
-    process.name as process_name,
-    thread.utid
-  FROM thread
-  JOIN {{process_allowlist_table}} process_allowlist USING (upid)
-  JOIN process USING (upid)
-  WHERE thread.name = 'GPU completion';
+SELECT
+  process.name AS process_name,
+  thread.utid
+FROM thread
+JOIN {{process_allowlist_table}} process_allowlist USING (upid)
+JOIN process USING (upid)
+WHERE thread.name = 'GPU completion';
 
 DROP VIEW IF EXISTS {{table_name_prefix}}_hwc_release_thread;
 CREATE VIEW {{table_name_prefix}}_hwc_release_thread AS
-  SELECT
-    process.name as process_name,
-    thread.utid
-  FROM thread
-  JOIN {{process_allowlist_table}} process_allowlist USING (upid)
-  JOIN process USING (upid)
-  WHERE thread.name = 'HWC release';
+SELECT
+  process.name AS process_name,
+  thread.utid
+FROM thread
+JOIN {{process_allowlist_table}} process_allowlist USING (upid)
+JOIN process USING (upid)
+WHERE thread.name = 'HWC release';
 
 DROP TABLE IF EXISTS {{table_name_prefix}}_main_thread_slices;
 CREATE TABLE {{table_name_prefix}}_main_thread_slices AS
-  SELECT
-    process_name,
-    thread.utid,
-    slice.*,
-    ts + dur AS ts_end
-  FROM slice
-  JOIN thread_track ON slice.track_id = thread_track.id
-  JOIN {{table_name_prefix}}_main_thread thread USING (utid)
-  WHERE dur > 0;
+SELECT
+  process_name,
+  thread.utid,
+  slice.*,
+  ts + dur AS ts_end
+FROM slice
+JOIN thread_track ON slice.track_id = thread_track.id
+JOIN {{table_name_prefix}}_main_thread thread USING (utid)
+WHERE dur > 0;
 
 DROP VIEW IF EXISTS {{table_name_prefix}}_do_frame_slices;
 CREATE VIEW {{table_name_prefix}}_do_frame_slices AS
-  SELECT
-    *,
-    CAST(STR_SPLIT(name, ' ', 1) AS INTEGER) as vsync
-  FROM {{table_name_prefix}}_main_thread_slices
-  WHERE name GLOB 'Choreographer#doFrame*';
+SELECT
+  *,
+  CAST(STR_SPLIT(name, ' ', 1) AS INTEGER) AS vsync
+FROM {{table_name_prefix}}_main_thread_slices
+WHERE name GLOB 'Choreographer#doFrame*';
 
 DROP TABLE IF EXISTS {{table_name_prefix}}_render_thread_slices;
 CREATE TABLE {{table_name_prefix}}_render_thread_slices AS
-  SELECT
-    process_name,
-    thread.utid,
-    slice.*,
-    ts + dur AS ts_end
-  FROM slice
-  JOIN thread_track ON slice.track_id = thread_track.id
-  JOIN {{table_name_prefix}}_render_thread thread USING (utid)
-  WHERE dur > 0;
+SELECT
+  process_name,
+  thread.utid,
+  slice.*,
+  ts + dur AS ts_end
+FROM slice
+JOIN thread_track ON slice.track_id = thread_track.id
+JOIN {{table_name_prefix}}_render_thread thread USING (utid)
+WHERE dur > 0;
 
 DROP VIEW IF EXISTS {{table_name_prefix}}_draw_frame_slices;
 CREATE VIEW {{table_name_prefix}}_draw_frame_slices AS
-  SELECT
-    *,
-    CAST(STR_SPLIT(name, ' ', 1) AS INTEGER) as vsync
-  FROM {{table_name_prefix}}_render_thread_slices
-  WHERE name GLOB 'DrawFrame*';
+SELECT
+  *,
+  CAST(STR_SPLIT(name, ' ', 1) AS INTEGER) AS vsync
+FROM {{table_name_prefix}}_render_thread_slices
+WHERE name GLOB 'DrawFrame*';
 
 DROP VIEW IF EXISTS {{table_name_prefix}}_gpu_completion_slices;
 CREATE VIEW {{table_name_prefix}}_gpu_completion_slices AS
-  SELECT
-    process_name,
-    thread.utid,
-    slice.*,
-    ts + dur AS ts_end,
-    -- Extracts 1234 from 'waiting for GPU completion 1234'
-    CAST(STR_SPLIT(slice.name, ' ', 4) AS INTEGER) as idx
-  FROM slice
-  JOIN thread_track ON slice.track_id = thread_track.id
-  JOIN {{table_name_prefix}}_gpu_completion_thread thread USING (utid)
-  WHERE slice.name GLOB 'waiting for GPU completion *'
+SELECT
+  process_name,
+  thread.utid,
+  slice.*,
+  ts + dur AS ts_end,
+  -- Extracts 1234 from 'waiting for GPU completion 1234'
+  CAST(STR_SPLIT(slice.name, ' ', 4) AS INTEGER) AS idx
+FROM slice
+JOIN thread_track ON slice.track_id = thread_track.id
+JOIN {{table_name_prefix}}_gpu_completion_thread thread USING (utid)
+WHERE slice.name GLOB 'waiting for GPU completion *'
   AND dur > 0;
 
 DROP VIEW IF EXISTS {{table_name_prefix}}_hwc_release_slices;
 CREATE VIEW {{table_name_prefix}}_hwc_release_slices AS
-  SELECT
-    process_name,
-    thread.utid,
-    slice.*,
-    ts + dur as ts_end,
-    -- Extracts 1234 from 'waiting for HWC release 1234'
-    CAST(STR_SPLIT(slice.name, ' ', 4) AS INTEGER) as idx
-  FROM slice
-  JOIN thread_track ON slice.track_id = thread_track.id
-  JOIN {{table_name_prefix}}_hwc_release_thread thread USING (utid)
-  WHERE slice.name GLOB 'waiting for HWC release *'
+SELECT
+  process_name,
+  thread.utid,
+  slice.*,
+  ts + dur AS ts_end,
+  -- Extracts 1234 from 'waiting for HWC release 1234'
+  CAST(STR_SPLIT(slice.name, ' ', 4) AS INTEGER) AS idx
+FROM slice
+JOIN thread_track ON slice.track_id = thread_track.id
+JOIN {{table_name_prefix}}_hwc_release_thread thread USING (utid)
+WHERE slice.name GLOB 'waiting for HWC release *'
   AND dur > 0;
diff --git a/src/trace_processor/metrics/sql/android/android_ion.sql b/src/trace_processor/metrics/sql/android/android_ion.sql
index 4d61843..8ceb2d4 100644
--- a/src/trace_processor/metrics/sql/android/android_ion.sql
+++ b/src/trace_processor/metrics/sql/android/android_ion.sql
@@ -19,7 +19,7 @@
 SELECT
   ts,
   LEAD(ts, 1, (SELECT end_ts FROM trace_bounds))
-    OVER(PARTITION BY track_id ORDER BY ts) - ts AS dur,
+  OVER(PARTITION BY track_id ORDER BY ts) - ts AS dur,
   CASE name
     WHEN 'mem.ion' THEN 'all'
     ELSE SUBSTR(name, 9)
diff --git a/src/trace_processor/metrics/sql/android/android_irq_runtime.sql b/src/trace_processor/metrics/sql/android/android_irq_runtime.sql
index e69683c..4d51383 100644
--- a/src/trace_processor/metrics/sql/android/android_irq_runtime.sql
+++ b/src/trace_processor/metrics/sql/android/android_irq_runtime.sql
@@ -80,8 +80,8 @@
             'anomaly_ratio',
             CAST(
               over_threshold_count AS DOUBLE)
-              / CAST(
-                total_count AS DOUBLE)),
+            / CAST(
+              total_count AS DOUBLE)),
           'longest_irq_slices',
           (
             SELECT
@@ -109,8 +109,8 @@
             'anomaly_ratio',
             CAST(
               over_threshold_count AS DOUBLE)
-              / CAST(
-                total_count AS DOUBLE)),
+            / CAST(
+              total_count AS DOUBLE)),
           'longest_irq_slices',
           (
             SELECT
diff --git a/src/trace_processor/metrics/sql/android/android_jank_cuj.sql b/src/trace_processor/metrics/sql/android/android_jank_cuj.sql
index 394b3bd..caaac19 100644
--- a/src/trace_processor/metrics/sql/android/android_jank_cuj.sql
+++ b/src/trace_processor/metrics/sql/android/android_jank_cuj.sql
@@ -17,6 +17,10 @@
 -- found in the trace.
 SELECT RUN_METRIC('android/jank/cujs.sql');
 
+-- Creates tables that store constant parameters for each CUJ - e.g. parameter
+-- that describes whether Choreographer callbacks run on a dedicated thread.
+SELECT RUN_METRIC('android/jank/params.sql');
+
 -- Create tables to store each CUJs main, render, HWC release,
 -- and GPU completion threads.
 -- Also stores the (not CUJ-specific) threads of SF: main, render engine,
@@ -153,7 +157,7 @@
             FROM android_jank_cuj_sf_frame f
             WHERE f.cuj_id = cuj.cuj_id
             ORDER BY frame_number ASC)
-          ))
+        ))
       FROM android_jank_cuj cuj
       LEFT JOIN android_jank_cuj_boundary boundary USING (cuj_id)
       ORDER BY cuj.cuj_id ASC));
diff --git a/src/trace_processor/metrics/sql/android/android_lmk.sql b/src/trace_processor/metrics/sql/android/android_lmk.sql
index f4b405b..42d0c1c 100644
--- a/src/trace_processor/metrics/sql/android/android_lmk.sql
+++ b/src/trace_processor/metrics/sql/android/android_lmk.sql
@@ -32,33 +32,33 @@
   oom_scores.oom_score_val AS score
 FROM raw_events
 LEFT JOIN oom_score_span oom_scores
-  ON (raw_events.upid = oom_scores.upid AND
-      raw_events.ts >= oom_scores.ts AND
-      raw_events.ts < oom_scores.ts + oom_scores.dur)
+  ON (raw_events.upid = oom_scores.upid
+      AND raw_events.ts >= oom_scores.ts
+      AND raw_events.ts < oom_scores.ts + oom_scores.dur)
 ORDER BY 1;
 
 DROP VIEW IF EXISTS android_lmk_event;
 CREATE VIEW android_lmk_event AS
 WITH raw_events AS (
   SELECT
-      ts,
-      LEAD(ts) OVER (ORDER BY ts) - ts AS dur,
-      CAST(value AS INTEGER) AS pid
-    FROM counter c
-    JOIN counter_track t ON t.id = c.track_id
-    WHERE t.name = 'kill_one_process'
+    ts,
+    LEAD(ts) OVER (ORDER BY ts) - ts AS dur,
+    CAST(value AS INTEGER) AS pid
+  FROM counter c
+  JOIN counter_track t ON t.id = c.track_id
+  WHERE t.name = 'kill_one_process'
   UNION ALL
   SELECT
-      slice.ts,
-      slice.dur,
-      CAST(STR_SPLIT(slice.name, ",", 1) AS INTEGER) AS pid
-    FROM slice
-    WHERE slice.name GLOB 'lmk,*'
+    slice.ts,
+    slice.dur,
+    CAST(STR_SPLIT(slice.name, ",", 1) AS INTEGER) AS pid
+  FROM slice
+  WHERE slice.name GLOB 'lmk,*'
 ),
 lmks_with_proc_name AS (
   SELECT
     *,
-    process.name as process_name
+    process.name AS process_name
   FROM raw_events
   LEFT JOIN process ON
     process.pid = raw_events.pid
@@ -67,15 +67,15 @@
   WHERE raw_events.pid != 0
 )
 SELECT
-  'slice' as track_type,
-  'Low Memory Kills (LMKs)' as track_name,
+  'slice' AS track_type,
+  'Low Memory Kills (LMKs)' AS track_name,
   ts,
   dur,
   CASE
     WHEN process_name IS NULL THEN printf('Process %d', lmk.pid)
     ELSE printf('%s (pid: %d)', process_name, lmk.pid)
   END AS slice_name
-FROM lmks_with_proc_name as lmk;
+FROM lmks_with_proc_name AS lmk;
 
 DROP VIEW IF EXISTS android_lmk_output;
 CREATE VIEW android_lmk_output AS
diff --git a/src/trace_processor/metrics/sql/android/android_lmk_reason.sql b/src/trace_processor/metrics/sql/android/android_lmk_reason.sql
index 0772c30..4adf18f 100644
--- a/src/trace_processor/metrics/sql/android/android_lmk_reason.sql
+++ b/src/trace_processor/metrics/sql/android/android_lmk_reason.sql
@@ -24,11 +24,11 @@
 WITH
 total_ion_name AS (
   SELECT
-  CASE
-    WHEN EXISTS(SELECT TRUE FROM ion_timeline WHERE heap_name = 'all')
-      THEN 'all'
-    ELSE 'system'
-  END AS name
+    CASE
+      WHEN EXISTS(SELECT TRUE FROM ion_timeline WHERE heap_name = 'all')
+        THEN 'all'
+      ELSE 'system'
+    END AS name
 ),
 oom_score_at_lmk_time AS (
   SELECT
@@ -44,7 +44,7 @@
 ion_at_lmk_time AS (
   SELECT
     lmk_events.ts,
-    CAST(ion_timeline.value AS INT) ion_size
+    CAST(ion_timeline.value AS INT) AS ion_size
   FROM lmk_events
   JOIN ion_timeline ON (
     lmk_events.ts
@@ -76,7 +76,7 @@
     'anon_rss_bytes', anon_rss_val,
     'shmem_rss_bytes', shmem_rss_val,
     'swap_bytes', swap_val
-  )) processes
+    )) AS processes
   FROM lmk_process_sizes
   JOIN process_metadata USING (upid)
   LEFT JOIN oom_score_at_lmk_time USING (ts, upid)
@@ -89,7 +89,7 @@
       'system_ion_heap_size', ion_size,
       'ion_heaps_bytes', ion_size,
       'processes', processes
-    ))
+      ))
     FROM lmk_events
     LEFT JOIN oom_score_at_lmk_time USING (ts, upid)
     LEFT JOIN ion_at_lmk_time USING (ts)
diff --git a/src/trace_processor/metrics/sql/android/android_mem.sql b/src/trace_processor/metrics/sql/android/android_mem.sql
index d50b85a..0873bd5 100644
--- a/src/trace_processor/metrics/sql/android/android_mem.sql
+++ b/src/trace_processor/metrics/sql/android/android_mem.sql
@@ -41,33 +41,33 @@
 CREATE VIEW mem_all_processes AS
 SELECT DISTINCT process_name
 FROM
-(
-  SELECT process_name FROM anon_rss_stats_proto
-  UNION
-  SELECT process_name FROM file_rss_stats_proto
-  UNION
-  SELECT process_name FROM swap_stats_proto
-  UNION
-  SELECT process_name FROM anon_and_swap_stats_proto
-  UNION
-  SELECT process_name FROM java_heap_stats_proto
-);
+  (
+    SELECT process_name FROM anon_rss_stats_proto
+    UNION
+    SELECT process_name FROM file_rss_stats_proto
+    UNION
+    SELECT process_name FROM swap_stats_proto
+    UNION
+    SELECT process_name FROM anon_and_swap_stats_proto
+    UNION
+    SELECT process_name FROM java_heap_stats_proto
+  );
 
 DROP VIEW IF EXISTS mem_all_process_priorities;
 CREATE VIEW mem_all_process_priorities AS
 SELECT DISTINCT process_name, priority
 FROM
-(
-  SELECT process_name, priority FROM anon_rss_by_priority_stats_proto
-  UNION
-  SELECT process_name, priority FROM file_rss_by_priority_stats_proto
-  UNION
-  SELECT process_name, priority FROM swap_by_priority_stats_proto
-  UNION
-  SELECT process_name, priority FROM anon_and_swap_by_priority_stats_proto
-  UNION
-  SELECT process_name, priority FROM java_heap_by_priority_stats_proto
-);
+  (
+    SELECT process_name, priority FROM anon_rss_by_priority_stats_proto
+    UNION
+    SELECT process_name, priority FROM file_rss_by_priority_stats_proto
+    UNION
+    SELECT process_name, priority FROM swap_by_priority_stats_proto
+    UNION
+    SELECT process_name, priority FROM anon_and_swap_by_priority_stats_proto
+    UNION
+    SELECT process_name, priority FROM java_heap_by_priority_stats_proto
+  );
 
 DROP VIEW IF EXISTS process_priority_view;
 CREATE VIEW process_priority_view AS
@@ -110,11 +110,11 @@
   ) AS metric
 FROM
   mem_all_processes
-  LEFT JOIN anon_rss_stats_proto USING (process_name)
-  LEFT JOIN file_rss_stats_proto USING (process_name)
-  LEFT JOIN swap_stats_proto USING (process_name)
-  LEFT JOIN anon_and_swap_stats_proto USING (process_name)
-  LEFT JOIN java_heap_stats_proto USING (process_name);
+LEFT JOIN anon_rss_stats_proto USING (process_name)
+LEFT JOIN file_rss_stats_proto USING (process_name)
+LEFT JOIN swap_stats_proto USING (process_name)
+LEFT JOIN anon_and_swap_stats_proto USING (process_name)
+LEFT JOIN java_heap_stats_proto USING (process_name);
 
 DROP VIEW IF EXISTS android_mem_output;
 CREATE VIEW android_mem_output AS
diff --git a/src/trace_processor/metrics/sql/android/android_multiuser_populator.sql b/src/trace_processor/metrics/sql/android/android_multiuser_populator.sql
index c1726ff..fc86b7c 100644
--- a/src/trace_processor/metrics/sql/android/android_multiuser_populator.sql
+++ b/src/trace_processor/metrics/sql/android/android_multiuser_populator.sql
@@ -15,37 +15,37 @@
 --
 
 -- Create the base tables and views containing the launch spans.
-SELECT RUN_METRIC('android/startup/launches.sql');
+SELECT IMPORT ('android.startup.startups');
 
 -- Collect the important timestamps for Multiuser events.
 DROP VIEW IF EXISTS multiuser_events;
 CREATE VIEW multiuser_events AS
 SELECT
-      {{start_event}}_time_ns AS event_start_time_ns,
-      {{end_event}}_time_ns AS event_end_time_ns
+  {{start_event}}_time_ns AS event_start_time_ns,
+  {{end_event}}_time_ns AS event_end_time_ns
 FROM
   (
     SELECT MIN(slice.ts) AS user_start_time_ns
     FROM slice
     WHERE (
-        slice.name = "UserDetailView.Adapter#onClick" OR -- QuickSettings
-        slice.name = "UserDetailSettings.switchUser" OR -- Settings
-        slice.name = "shell_runSwitchUser" -- adb shell
+        slice.name = "UserDetailView.Adapter#onClick" -- QuickSettings
+        OR slice.name = "UserDetailSettings.switchUser" -- Settings
+        OR slice.name = "shell_runSwitchUser" -- adb shell
     )
   ),
   (
     SELECT ts_end AS launcher_end_time_ns
-    FROM launches
+    FROM android_startups
     WHERE (package = 'com.android.launcher3' OR package = 'com.google.android.apps.nexuslauncher')
   ),
   (
     SELECT MIN(slice.ts) AS user_create_time_ns
     FROM slice
     WHERE (
-        slice.name = "UserDetailView.Adapter#onClick" OR -- QuickSettings
-        slice.name = "UserSettings.addUserNow" OR -- Settings
-        slice.name = "UserSettings.addGuest" OR -- Settings
-        slice.name = "shell_runCreateUser" -- adb shell
+        slice.name = "UserDetailView.Adapter#onClick" -- QuickSettings
+        OR slice.name = "UserSettings.addUserNow" -- Settings
+        OR slice.name = "UserSettings.addGuest" -- Settings
+        OR slice.name = "shell_runCreateUser" -- adb shell
     )
   );
 
@@ -53,7 +53,7 @@
 DROP VIEW IF EXISTS multiuser_timing;
 CREATE VIEW multiuser_timing AS
 SELECT
-      CAST((event_end_time_ns - event_start_time_ns) / 1e6 + 0.5 AS INT) AS duration_ms
+  CAST((event_end_time_ns - event_start_time_ns) / 1e6 + 0.5 AS INT) AS duration_ms
 FROM
   multiuser_events;
 
@@ -70,9 +70,9 @@
 CREATE VIEW sp_frequency AS
 SELECT
   ts,
-  lead(ts) OVER (PARTITION BY track_id ORDER BY ts) - ts as dur,
+  lead(ts) OVER (PARTITION BY track_id ORDER BY ts) - ts AS dur,
   cpu,
-  value as freq_khz
+  value AS freq_khz
 FROM counter
 JOIN cpu_counter_track ON counter.track_id = cpu_counter_track.id;
 -- Create the span joined table which combines cpu frequency with scheduling slices.
@@ -84,17 +84,17 @@
 DROP VIEW IF EXISTS cpu_usage_all;
 CREATE VIEW cpu_usage_all AS
 SELECT
-    process.uid / 100000 AS user_id,
-    process.name AS process_name,
-    SUM(dur * freq_khz) / 1e9 AS cpu_kcycles
+  process.uid / 100000 AS user_id,
+  process.name AS process_name,
+  SUM(dur * freq_khz) / 1e9 AS cpu_kcycles
 FROM
-    sched_with_frequency
-    JOIN thread USING (utid)
-    JOIN process USING (upid)
+  sched_with_frequency
+JOIN thread USING (utid)
+JOIN process USING (upid)
 WHERE
-    ts >= (SELECT event_start_time_ns FROM multiuser_events)
-    AND
-    ts <= (SELECT event_end_time_ns FROM multiuser_events)
+  ts >= (SELECT event_start_time_ns FROM multiuser_events)
+  AND
+  ts <= (SELECT event_end_time_ns FROM multiuser_events)
 GROUP BY upid, process.name
 ORDER BY cpu_kcycles DESC;
 
@@ -102,13 +102,13 @@
 DROP VIEW IF EXISTS cpu_usage;
 CREATE VIEW cpu_usage AS
 SELECT
-    user_id,
-    process_name,
-    process_name || ":" || (CASE WHEN user_id = 0 THEN "system" ELSE "secondary" END) AS identifier,
-    CAST(cpu_kcycles / 1e3 AS INT) AS cpu_mcycles,
-    cpu_kcycles / (SELECT SUM(cpu_kcycles) FROM cpu_usage_all) * 100 AS cpu_percentage
+  user_id,
+  process_name,
+  process_name || ":" || (CASE WHEN user_id = 0 THEN "system" ELSE "secondary" END) AS identifier,
+  CAST(cpu_kcycles / 1e3 AS INT) AS cpu_mcycles,
+  cpu_kcycles / (SELECT SUM(cpu_kcycles) FROM cpu_usage_all) * 100 AS cpu_percentage
 FROM
-    cpu_usage_all
+  cpu_usage_all
 ORDER BY cpu_mcycles DESC LIMIT 25;
 
 
diff --git a/src/trace_processor/metrics/sql/android/android_netperf.sql b/src/trace_processor/metrics/sql/android/android_netperf.sql
index 2f67d67..0100927 100644
--- a/src/trace_processor/metrics/sql/android/android_netperf.sql
+++ b/src/trace_processor/metrics/sql/android/android_netperf.sql
@@ -15,232 +15,232 @@
 
 DROP VIEW IF EXISTS rx_packets;
 CREATE VIEW rx_packets AS
-  SELECT
-    ts,
-    REPLACE(name, " Received KB", "") AS dev,
-    EXTRACT_ARG(arg_set_id, 'cpu') AS cpu,
-    EXTRACT_ARG(arg_set_id, 'len') AS len
-  FROM counter c
-  LEFT JOIN counter_track t
-    ON c.track_id = t.id
-  WHERE name GLOB "* Received KB"
-  ORDER BY ts DESC;
+SELECT
+  ts,
+  REPLACE(name, " Received KB", "") AS dev,
+  EXTRACT_ARG(arg_set_id, 'cpu') AS cpu,
+  EXTRACT_ARG(arg_set_id, 'len') AS len
+FROM counter c
+LEFT JOIN counter_track t
+  ON c.track_id = t.id
+WHERE name GLOB "* Received KB"
+ORDER BY ts DESC;
 
 DROP VIEW IF EXISTS gro_rx_packet_count;
 CREATE VIEW gro_rx_packet_count AS
-  SELECT
-    s.name AS dev,
-    COUNT(1) AS cnt
-  FROM slice s
-  LEFT JOIN track t
-    ON s.track_id = t.id
-  WHERE t.name GLOB "Napi Gro Cpu *"
-  GROUP BY s.name;
+SELECT
+  s.name AS dev,
+  COUNT(1) AS cnt
+FROM slice s
+LEFT JOIN track t
+  ON s.track_id = t.id
+WHERE t.name GLOB "Napi Gro Cpu *"
+GROUP BY s.name;
 
 DROP VIEW IF EXISTS tx_packets;
 CREATE VIEW tx_packets AS
-  SELECT
-    ts,
-    REPLACE(name, " Transmitted KB", "") AS dev,
-    EXTRACT_ARG(arg_set_id, 'cpu') AS cpu,
-    EXTRACT_ARG(arg_set_id, 'len') AS len
-  FROM counter c
-  LEFT JOIN counter_track t
-    ON c.track_id = t.id
-  WHERE name GLOB "* Transmitted KB"
-  ORDER BY ts DESC;
+SELECT
+  ts,
+  REPLACE(name, " Transmitted KB", "") AS dev,
+  EXTRACT_ARG(arg_set_id, 'cpu') AS cpu,
+  EXTRACT_ARG(arg_set_id, 'len') AS len
+FROM counter c
+LEFT JOIN counter_track t
+  ON c.track_id = t.id
+WHERE name GLOB "* Transmitted KB"
+ORDER BY ts DESC;
 
 DROP VIEW IF EXISTS net_devices;
 CREATE VIEW net_devices AS
- SELECT DISTINCT dev
- FROM tx_packets
- UNION
- SELECT DISTINCT dev
- FROM rx_packets;
+SELECT DISTINCT dev
+FROM tx_packets
+UNION
+SELECT DISTINCT dev
+FROM rx_packets;
 
 DROP VIEW IF EXISTS tcp_retransmitted_count;
 CREATE VIEW tcp_retransmitted_count AS
-  SELECT
-     COUNT(1) AS cnt
-  FROM slice s
-  LEFT JOIN track t
-    ON s.track_id = t.id
-  WHERE
-    t.name = "TCP Retransmit Skb";
+SELECT
+  COUNT(1) AS cnt
+FROM slice s
+LEFT JOIN track t
+  ON s.track_id = t.id
+WHERE
+  t.name = "TCP Retransmit Skb";
 
 DROP VIEW IF EXISTS kfree_skb_count;
 CREATE VIEW kfree_skb_count AS
-  SELECT
-     MAX(value) AS cnt
-  FROM counter c
-  LEFT JOIN track t
-    ON c.track_id = t.id
-  WHERE
-    t.name = "Kfree Skb IP Prot";
+SELECT
+  MAX(value) AS cnt
+FROM counter c
+LEFT JOIN track t
+  ON c.track_id = t.id
+WHERE
+  t.name = "Kfree Skb IP Prot";
 
 DROP VIEW IF EXISTS device_per_core_ingress_traffic;
 CREATE VIEW device_per_core_ingress_traffic AS
-  SELECT
-    dev,
-    AndroidNetworkMetric_CorePacketStatistic(
-      'id', cpu,
-      'packet_statistic', AndroidNetworkMetric_PacketStatistic(
-        'packets', COUNT(1),
-        'bytes', SUM(len),
-        'first_packet_timestamp_ns', MIN(ts),
-        'last_packet_timestamp_ns', MAX(ts),
-        'interval_ns', IIF((MAX(ts)-MIN(ts))>10000000, MAX(ts)-MIN(ts), 10000000),
-        'data_rate_kbps', (SUM(len)*8)/(IIF((MAX(ts)-MIN(ts))>10000000, MAX(ts)-MIN(ts), 10000000)/1e9)/1024
-      )
-    ) AS proto
-  FROM rx_packets
-  GROUP BY dev, cpu;
+SELECT
+  dev,
+  AndroidNetworkMetric_CorePacketStatistic(
+    'id', cpu,
+    'packet_statistic', AndroidNetworkMetric_PacketStatistic(
+      'packets', COUNT(1),
+      'bytes', SUM(len),
+      'first_packet_timestamp_ns', MIN(ts),
+      'last_packet_timestamp_ns', MAX(ts),
+      'interval_ns', IIF((MAX(ts) - MIN(ts)) > 10000000, MAX(ts) - MIN(ts), 10000000),
+      'data_rate_kbps', (SUM(len) * 8) / (IIF((MAX(ts) - MIN(ts)) > 10000000, MAX(ts) - MIN(ts), 10000000) / 1e9) / 1024
+    )
+  ) AS proto
+FROM rx_packets
+GROUP BY dev, cpu;
 
 DROP VIEW IF EXISTS device_per_core_egress_traffic;
 CREATE VIEW device_per_core_egress_traffic AS
-  SELECT
-    dev,
-    AndroidNetworkMetric_CorePacketStatistic(
-      'id', cpu,
-      'packet_statistic', AndroidNetworkMetric_PacketStatistic(
-        'packets', COUNT(1),
-        'bytes', SUM(len),
-        'first_packet_timestamp_ns', MIN(ts),
-        'last_packet_timestamp_ns', MAX(ts),
-        'interval_ns', IIF((MAX(ts)-MIN(ts))>10000000, MAX(ts)-MIN(ts), 10000000),
-        'data_rate_kbps', (SUM(len)*8)/(IIF((MAX(ts)-MIN(ts))>10000000, MAX(ts)-MIN(ts), 10000000)/1e9)/1024
-      )
-    ) AS proto
-  FROM tx_packets
-  GROUP BY dev, cpu;
+SELECT
+  dev,
+  AndroidNetworkMetric_CorePacketStatistic(
+    'id', cpu,
+    'packet_statistic', AndroidNetworkMetric_PacketStatistic(
+      'packets', COUNT(1),
+      'bytes', SUM(len),
+      'first_packet_timestamp_ns', MIN(ts),
+      'last_packet_timestamp_ns', MAX(ts),
+      'interval_ns', IIF((MAX(ts) - MIN(ts)) > 10000000, MAX(ts) - MIN(ts), 10000000),
+      'data_rate_kbps', (SUM(len) * 8) / (IIF((MAX(ts) - MIN(ts)) > 10000000, MAX(ts) - MIN(ts), 10000000) / 1e9) / 1024
+    )
+  ) AS proto
+FROM tx_packets
+GROUP BY dev, cpu;
 
 DROP VIEW IF EXISTS device_total_ingress_traffic;
 CREATE VIEW device_total_ingress_traffic AS
-  SELECT
-    dev,
-    MIN(ts) AS start_ts,
-    MAX(ts) AS end_ts,
-    IIF((MAX(ts) - MIN(ts)) > 10000000, MAX(ts)-MIN(ts), 10000000) AS interval,
-    COUNT(1) AS packets,
-    SUM(len) AS bytes
-  FROM rx_packets
-  GROUP BY dev;
+SELECT
+  dev,
+  MIN(ts) AS start_ts,
+  MAX(ts) AS end_ts,
+  IIF((MAX(ts) - MIN(ts)) > 10000000, MAX(ts) - MIN(ts), 10000000) AS interval,
+  COUNT(1) AS packets,
+  SUM(len) AS bytes
+FROM rx_packets
+GROUP BY dev;
 
 DROP VIEW IF EXISTS device_total_egress_traffic;
 CREATE VIEW device_total_egress_traffic AS
-  SELECT
-    dev,
-    MIN(ts) AS start_ts,
-    MAX(ts) AS end_ts,
-    IIF((MAX(ts) - MIN(ts)) > 10000000, MAX(ts)-MIN(ts), 10000000) AS interval,
-    COUNT(1) AS packets,
-    SUM(len) AS bytes
-  FROM tx_packets
-  GROUP BY dev;
+SELECT
+  dev,
+  MIN(ts) AS start_ts,
+  MAX(ts) AS end_ts,
+  IIF((MAX(ts) - MIN(ts)) > 10000000, MAX(ts) - MIN(ts), 10000000) AS interval,
+  COUNT(1) AS packets,
+  SUM(len) AS bytes
+FROM tx_packets
+GROUP BY dev;
 
 DROP VIEW IF EXISTS device_traffic_statistic;
 CREATE VIEW device_traffic_statistic AS
-  SELECT
-    AndroidNetworkMetric_NetDevice(
-      'name', net_devices.dev,
-      'rx', (
-        SELECT
-          AndroidNetworkMetric_Rx(
-            'total', AndroidNetworkMetric_PacketStatistic(
-              'packets', packets,
-              'bytes', bytes,
-              'first_packet_timestamp_ns', start_ts,
-              'last_packet_timestamp_ns', end_ts,
-              'interval_ns', interval,
-              'data_rate_kbps', (bytes*8)/(interval/1e9)/1024
-            ),
-            'core', (
-              SELECT
-                RepeatedField(proto)
-              FROM device_per_core_ingress_traffic
-              WHERE device_per_core_ingress_traffic.dev = device_total_ingress_traffic.dev
-            ),
-            'gro_aggregation_ratio', (
-              SELECT
-                CASE
-                  WHEN packets > 0 THEN '1:' || CAST( (cnt*1.0/packets) AS text)
-                  ELSE '0:' || cnt
-               END
-              FROM gro_rx_packet_count
-              WHERE gro_rx_packet_count.dev = net_devices.dev
-	    )
+SELECT
+  AndroidNetworkMetric_NetDevice(
+    'name', net_devices.dev,
+    'rx', (
+      SELECT
+        AndroidNetworkMetric_Rx(
+          'total', AndroidNetworkMetric_PacketStatistic(
+            'packets', packets,
+            'bytes', bytes,
+            'first_packet_timestamp_ns', start_ts,
+            'last_packet_timestamp_ns', end_ts,
+            'interval_ns', interval,
+            'data_rate_kbps', (bytes * 8) / (interval / 1e9) / 1024
+          ),
+          'core', (
+            SELECT
+              RepeatedField(proto)
+            FROM device_per_core_ingress_traffic
+            WHERE device_per_core_ingress_traffic.dev = device_total_ingress_traffic.dev
+          ),
+          'gro_aggregation_ratio', (
+            SELECT
+              CASE
+                WHEN packets > 0 THEN '1:' || CAST((cnt * 1.0 / packets) AS text)
+                ELSE '0:' || cnt
+              END
+            FROM gro_rx_packet_count
+            WHERE gro_rx_packet_count.dev = net_devices.dev
           )
-        FROM device_total_ingress_traffic
-        WHERE device_total_ingress_traffic.dev = net_devices.dev
-      ),
-      'tx', (
-        SELECT
-          AndroidNetworkMetric_Tx(
-            'total', AndroidNetworkMetric_PacketStatistic(
-              'packets', packets,
-              'bytes', bytes,
-              'first_packet_timestamp_ns', start_ts,
-              'last_packet_timestamp_ns', end_ts,
-              'interval_ns', interval,
-              'data_rate_kbps', (bytes*8)/(interval/1e9)/1024
-            ),
-           'core', (
-              SELECT
-                RepeatedField(proto)
-              FROM device_per_core_egress_traffic
-              WHERE device_per_core_egress_traffic.dev = device_total_egress_traffic.dev
-            )
+        )
+      FROM device_total_ingress_traffic
+      WHERE device_total_ingress_traffic.dev = net_devices.dev
+    ),
+    'tx', (
+      SELECT
+        AndroidNetworkMetric_Tx(
+          'total', AndroidNetworkMetric_PacketStatistic(
+            'packets', packets,
+            'bytes', bytes,
+            'first_packet_timestamp_ns', start_ts,
+            'last_packet_timestamp_ns', end_ts,
+            'interval_ns', interval,
+            'data_rate_kbps', (bytes * 8) / (interval / 1e9) / 1024
+          ),
+          'core', (
+            SELECT
+              RepeatedField(proto)
+            FROM device_per_core_egress_traffic
+            WHERE device_per_core_egress_traffic.dev = device_total_egress_traffic.dev
           )
-        FROM device_total_egress_traffic
-        WHERE device_total_egress_traffic.dev = net_devices.dev
-      )
-    ) AS proto
-  FROM net_devices
-  ORDER BY dev;
+        )
+      FROM device_total_egress_traffic
+      WHERE device_total_egress_traffic.dev = net_devices.dev
+    )
+  ) AS proto
+FROM net_devices
+ORDER BY dev;
 
 DROP VIEW IF EXISTS net_rx_actions;
 CREATE VIEW net_rx_actions AS
-  SELECT
-    s.ts,
-    s.dur,
-    CAST(SUBSTR(t.name, 13, 1) AS int) AS cpu
-  FROM slice s
-  LEFT JOIN track t
-    ON s.track_id = t.id
-  WHERE s.name = "NET_RX";
+SELECT
+  s.ts,
+  s.dur,
+  CAST(SUBSTR(t.name, 13, 1) AS int) AS cpu
+FROM slice s
+LEFT JOIN track t
+  ON s.track_id = t.id
+WHERE s.name = "NET_RX";
 
 DROP VIEW IF EXISTS net_tx_actions;
 CREATE VIEW net_tx_actions AS
-  SELECT
-    s.ts,
-    s.dur,
-    CAST(SUBSTR(t.name, 13, 1) AS int) AS cpu
-  FROM slice s
-  LEFT JOIN track t
-    ON s.track_id = t.id
-  WHERE s.name = "NET_TX";
+SELECT
+  s.ts,
+  s.dur,
+  CAST(SUBSTR(t.name, 13, 1) AS int) AS cpu
+FROM slice s
+LEFT JOIN track t
+  ON s.track_id = t.id
+WHERE s.name = "NET_TX";
 
 DROP VIEW IF EXISTS ipi_actions;
 CREATE VIEW ipi_actions AS
-  SELECT
-    s.ts,
-    s.dur,
-    CAST(SUBSTR(t.name, 13, 1) AS int) AS cpu
-  FROM slice s
-  LEFT JOIN track t
-    ON s.track_id = t.id
-  WHERE s.name = "IRQ (IPI)";
+SELECT
+  s.ts,
+  s.dur,
+  CAST(SUBSTR(t.name, 13, 1) AS int) AS cpu
+FROM slice s
+LEFT JOIN track t
+  ON s.track_id = t.id
+WHERE s.name = "IRQ (IPI)";
 
 DROP VIEW IF EXISTS cpu_freq_view;
 CREATE VIEW cpu_freq_view AS
 SELECT
   cpu,
   ts,
-  LEAD(ts, 1, (SELECT end_ts from trace_bounds))
-    OVER (PARTITION by cpu ORDER BY ts) - ts AS dur,
-  CAST(value AS INT) as freq_khz
+  LEAD(ts, 1, (SELECT end_ts FROM trace_bounds))
+  OVER (PARTITION BY cpu ORDER BY ts) - ts AS dur,
+  CAST(value AS INT) AS freq_khz
 FROM counter
-JOIN cpu_counter_track on counter.track_id = cpu_counter_track.id
+JOIN cpu_counter_track ON counter.track_id = cpu_counter_track.id
 WHERE name = 'cpufreq';
 
 DROP TABLE IF EXISTS cpu_freq_net_rx_action_per_core;
@@ -253,97 +253,97 @@
 
 DROP VIEW IF EXISTS total_net_rx_action_statistic;
 CREATE VIEW total_net_rx_action_statistic AS
-  SELECT
-    COUNT(1) AS times,
-    SUM(dur) AS runtime,
-    AVG(dur) AS avg_runtime,
-    (SELECT COUNT(1) FROM rx_packets) AS total_packet
-  FROM net_rx_actions;
+SELECT
+  COUNT(1) AS times,
+  SUM(dur) AS runtime,
+  AVG(dur) AS avg_runtime,
+  (SELECT COUNT(1) FROM rx_packets) AS total_packet
+FROM net_rx_actions;
 
 DROP VIEW IF EXISTS total_net_tx_action_statistic;
 CREATE VIEW total_net_tx_action_statistic AS
-  SELECT
-    COUNT(1) AS times,
-    SUM(dur) AS runtime,
-    AVG(dur) AS avg_runtime
-  FROM net_tx_actions;
+SELECT
+  COUNT(1) AS times,
+  SUM(dur) AS runtime,
+  AVG(dur) AS avg_runtime
+FROM net_tx_actions;
 
 DROP VIEW IF EXISTS total_ipi_action_statistic;
 CREATE VIEW total_ipi_action_statistic AS
-  SELECT
-    COUNT(1) AS times,
-    SUM(dur) AS runtime,
-    AVG(dur) AS avg_runtime
-  FROM ipi_actions;
+SELECT
+  COUNT(1) AS times,
+  SUM(dur) AS runtime,
+  AVG(dur) AS avg_runtime
+FROM ipi_actions;
 
 DROP VIEW IF EXISTS activated_cores_net_rx;
 CREATE VIEW activated_cores_net_rx AS
- SELECT
-   DISTINCT cpu
- FROM net_rx_actions;
+SELECT DISTINCT
+  cpu
+FROM net_rx_actions;
 
 DROP VIEW IF EXISTS activated_cores_net_tx;
 CREATE VIEW activated_cores_net_tx AS
- SELECT
-   DISTINCT cpu
- FROM net_tx_actions;
+SELECT DISTINCT
+  cpu
+FROM net_tx_actions;
 
 DROP VIEW IF EXISTS per_core_net_rx_action_statistic;
 CREATE VIEW per_core_net_rx_action_statistic AS
-  SELECT
-    AndroidNetworkMetric_CoreNetRxActionStatistic(
-      'id', cpu,
-      'net_rx_action_statistic', AndroidNetworkMetric_NetRxActionStatistic(
-        'count', (SELECT COUNT(1) FROM net_rx_actions AS na WHERE na.cpu = ac.cpu),
-        'runtime_ms',  (SELECT SUM(dur)/1e6 FROM net_rx_actions AS na WHERE na.cpu = ac.cpu),
-        'avg_runtime_ms', (SELECT AVG(dur)/1e6 FROM net_rx_actions AS na WHERE na.cpu = ac.cpu),
-        'avg_freq_khz', (SELECT SUM(dur * freq_khz) / SUM(dur) FROM cpu_freq_net_rx_action_per_core AS cc WHERE cc.cpu = ac.cpu),
-        'mcycles', (SELECT CAST(SUM(dur * freq_khz / 1000) / 1e9 AS INT) FROM cpu_freq_net_rx_action_per_core AS cc WHERE cc.cpu = ac.cpu)
-      )
-    ) AS proto
-  FROM activated_cores_net_rx AS ac;
+SELECT
+  AndroidNetworkMetric_CoreNetRxActionStatistic(
+    'id', cpu,
+    'net_rx_action_statistic', AndroidNetworkMetric_NetRxActionStatistic(
+      'count', (SELECT COUNT(1) FROM net_rx_actions AS na WHERE na.cpu = ac.cpu),
+      'runtime_ms', (SELECT SUM(dur) / 1e6 FROM net_rx_actions AS na WHERE na.cpu = ac.cpu),
+      'avg_runtime_ms', (SELECT AVG(dur) / 1e6 FROM net_rx_actions AS na WHERE na.cpu = ac.cpu),
+      'avg_freq_khz', (SELECT SUM(dur * freq_khz) / SUM(dur) FROM cpu_freq_net_rx_action_per_core AS cc WHERE cc.cpu = ac.cpu),
+      'mcycles', (SELECT CAST(SUM(dur * freq_khz / 1000) / 1e9 AS INT) FROM cpu_freq_net_rx_action_per_core AS cc WHERE cc.cpu = ac.cpu)
+    )
+  ) AS proto
+FROM activated_cores_net_rx AS ac;
 
 DROP VIEW IF EXISTS per_core_net_tx_action_statistic;
 CREATE VIEW per_core_net_tx_action_statistic AS
-  SELECT
-    AndroidNetworkMetric_CoreNetTxActionStatistic(
-      'id', cpu,
-      'net_tx_action_statistic', AndroidNetworkMetric_NetTxActionStatistic(
-        'count', (SELECT COUNT(1) FROM net_tx_actions AS na WHERE na.cpu = ac.cpu),
-        'runtime_ms',  (SELECT SUM(dur)/1e6 FROM net_tx_actions AS na WHERE na.cpu = ac.cpu),
-        'avg_runtime_ms', (SELECT AVG(dur)/1e6 FROM net_tx_actions AS na WHERE na.cpu = ac.cpu),
-        'avg_freq_khz', (SELECT SUM(dur * freq_khz) / SUM(dur) FROM cpu_freq_net_tx_action_per_core AS cc WHERE cc.cpu = ac.cpu),
-        'mcycles', (SELECT CAST(SUM(dur * freq_khz / 1000) / 1e9 AS INT) FROM cpu_freq_net_tx_action_per_core AS cc WHERE cc.cpu = ac.cpu)
-      )
-    ) AS proto
-  FROM activated_cores_net_tx AS ac;
+SELECT
+  AndroidNetworkMetric_CoreNetTxActionStatistic(
+    'id', cpu,
+    'net_tx_action_statistic', AndroidNetworkMetric_NetTxActionStatistic(
+      'count', (SELECT COUNT(1) FROM net_tx_actions AS na WHERE na.cpu = ac.cpu),
+      'runtime_ms', (SELECT SUM(dur) / 1e6 FROM net_tx_actions AS na WHERE na.cpu = ac.cpu),
+      'avg_runtime_ms', (SELECT AVG(dur) / 1e6 FROM net_tx_actions AS na WHERE na.cpu = ac.cpu),
+      'avg_freq_khz', (SELECT SUM(dur * freq_khz) / SUM(dur) FROM cpu_freq_net_tx_action_per_core AS cc WHERE cc.cpu = ac.cpu),
+      'mcycles', (SELECT CAST(SUM(dur * freq_khz / 1000) / 1e9 AS INT) FROM cpu_freq_net_tx_action_per_core AS cc WHERE cc.cpu = ac.cpu)
+    )
+  ) AS proto
+FROM activated_cores_net_tx AS ac;
 
 DROP VIEW IF EXISTS android_netperf_output;
 CREATE VIEW android_netperf_output AS
-  SELECT AndroidNetworkMetric(
+SELECT AndroidNetworkMetric(
     'net_devices', (
       SELECT
         RepeatedField(proto)
       FROM device_traffic_statistic
     ),
     'net_rx_action', AndroidNetworkMetric_NetRxAction(
-       'total', AndroidNetworkMetric_NetRxActionStatistic(
-         'count', (SELECT times FROM total_net_rx_action_statistic),
-         'runtime_ms', (SELECT runtime/1e6 FROM total_net_rx_action_statistic),
-         'avg_runtime_ms', (SELECT avg_runtime/1e6 FROM total_net_rx_action_statistic),
-         'avg_freq_khz', (SELECT SUM(dur * freq_khz) / SUM(dur) FROM cpu_freq_net_rx_action_per_core),
-         'mcycles', (SELECT CAST(SUM(dur * freq_khz / 1000) / 1e9 AS INT) FROM cpu_freq_net_rx_action_per_core)
-       ),
-       'core', (
-         SELECT
-           RepeatedField(proto)
-         FROM per_core_net_rx_action_statistic
-       ),
-       'avg_interstack_latency_ms', (
-         SELECT
-           runtime/total_packet/1e6
-         FROM total_net_rx_action_statistic
-       )
+      'total', AndroidNetworkMetric_NetRxActionStatistic(
+        'count', (SELECT times FROM total_net_rx_action_statistic),
+        'runtime_ms', (SELECT runtime / 1e6 FROM total_net_rx_action_statistic),
+        'avg_runtime_ms', (SELECT avg_runtime / 1e6 FROM total_net_rx_action_statistic),
+        'avg_freq_khz', (SELECT SUM(dur * freq_khz) / SUM(dur) FROM cpu_freq_net_rx_action_per_core),
+        'mcycles', (SELECT CAST(SUM(dur * freq_khz / 1000) / 1e9 AS INT) FROM cpu_freq_net_rx_action_per_core)
+      ),
+      'core', (
+        SELECT
+          RepeatedField(proto)
+        FROM per_core_net_rx_action_statistic
+      ),
+      'avg_interstack_latency_ms', (
+        SELECT
+          runtime / total_packet / 1e6
+        FROM total_net_rx_action_statistic
+      )
     ),
     'retransmission_rate', (
       SELECT
@@ -356,25 +356,24 @@
       FROM kfree_skb_count
     ),
     'net_tx_action', AndroidNetworkMetric_NetTxAction(
-       'total', AndroidNetworkMetric_NetTxActionStatistic(
-         'count', (SELECT times FROM total_net_tx_action_statistic),
-         'runtime_ms', (SELECT runtime/1e6 FROM total_net_tx_action_statistic),
-         'avg_runtime_ms', (SELECT avg_runtime/1e6 FROM total_net_tx_action_statistic),
-         'avg_freq_khz', (SELECT SUM(dur * freq_khz) / SUM(dur) FROM cpu_freq_net_tx_action_per_core),
-         'mcycles', (SELECT CAST(SUM(dur * freq_khz / 1000) / 1e9 AS INT) FROM cpu_freq_net_tx_action_per_core)
-       ),
-       'core', (
-         SELECT
-           RepeatedField(proto)
-         FROM per_core_net_tx_action_statistic
-       )
+      'total', AndroidNetworkMetric_NetTxActionStatistic(
+        'count', (SELECT times FROM total_net_tx_action_statistic),
+        'runtime_ms', (SELECT runtime / 1e6 FROM total_net_tx_action_statistic),
+        'avg_runtime_ms', (SELECT avg_runtime / 1e6 FROM total_net_tx_action_statistic),
+        'avg_freq_khz', (SELECT SUM(dur * freq_khz) / SUM(dur) FROM cpu_freq_net_tx_action_per_core),
+        'mcycles', (SELECT CAST(SUM(dur * freq_khz / 1000) / 1e9 AS INT) FROM cpu_freq_net_tx_action_per_core)
+      ),
+      'core', (
+        SELECT
+          RepeatedField(proto)
+        FROM per_core_net_tx_action_statistic
+      )
     ),
     'ipi_action', AndroidNetworkMetric_IpiAction(
-       'total', AndroidNetworkMetric_IpiActionStatistic(
-         'count', (SELECT times FROM total_ipi_action_statistic),
-         'runtime_ms', (SELECT runtime/1e6 FROM total_ipi_action_statistic),
-         'avg_runtime_ms', (SELECT avg_runtime/1e6 FROM total_ipi_action_statistic)
-       )
+      'total', AndroidNetworkMetric_IpiActionStatistic(
+        'count', (SELECT times FROM total_ipi_action_statistic),
+        'runtime_ms', (SELECT runtime / 1e6 FROM total_ipi_action_statistic),
+        'avg_runtime_ms', (SELECT avg_runtime / 1e6 FROM total_ipi_action_statistic)
+      )
     )
   );
-
diff --git a/src/trace_processor/metrics/sql/android/android_other_traces.sql b/src/trace_processor/metrics/sql/android/android_other_traces.sql
index c1ec9fe..5963bef 100644
--- a/src/trace_processor/metrics/sql/android/android_other_traces.sql
+++ b/src/trace_processor/metrics/sql/android/android_other_traces.sql
@@ -41,7 +41,7 @@
 CREATE VIEW android_other_traces_output AS
 SELECT AndroidOtherTracesMetric(
     'finalized_traces_uuid', (
-    SELECT RepeatedField(uuid)
-    FROM android_other_traces_view
-    WHERE event_type = 'Finalize')
+      SELECT RepeatedField(uuid)
+      FROM android_other_traces_view
+      WHERE event_type = 'Finalize')
   );
diff --git a/src/trace_processor/metrics/sql/android/android_package_list.sql b/src/trace_processor/metrics/sql/android/android_package_list.sql
index 1c21678..c55dc51 100644
--- a/src/trace_processor/metrics/sql/android/android_package_list.sql
+++ b/src/trace_processor/metrics/sql/android/android_package_list.sql
@@ -22,6 +22,6 @@
       'package_name', package_name,
       'uid', uid,
       'version_code', version_code
-    )) FROM package_list
+      )) FROM package_list
   )
 );
diff --git a/src/trace_processor/metrics/sql/android/android_powrails.sql b/src/trace_processor/metrics/sql/android/android_powrails.sql
index 65ed83e..3604f3f 100644
--- a/src/trace_processor/metrics/sql/android/android_powrails.sql
+++ b/src/trace_processor/metrics/sql/android/android_powrails.sql
@@ -17,9 +17,9 @@
 -- View of Power Rail counters with ts converted from ns to ms.
 DROP VIEW IF EXISTS power_rails_counters;
 CREATE VIEW power_rails_counters AS
-SELECT value, ts/1000000 AS ts, name
+SELECT value, ts / 1000000 AS ts, name
 FROM counter c
-JOIN counter_track t on c.track_id = t.id
+JOIN counter_track t ON c.track_id = t.id
 WHERE name GLOB 'power.*';
 
 DROP VIEW IF EXISTS avg_used_powers;
@@ -33,11 +33,11 @@
 FROM (
   SELECT
     name,
-    (LEAD(value) OVER (PARTITION BY name ORDER BY ts) - value) /
-      (LEAD(ts) OVER (PARTITION BY name ORDER BY ts) - ts) AS avg_used_power,
+    (LEAD(value) OVER (PARTITION BY name ORDER BY ts) - value)
+    / (LEAD(ts) OVER (PARTITION BY name ORDER BY ts) - ts) AS avg_used_power,
     (LEAD(value) OVER (PARTITION BY name ORDER BY ts) - value) AS tot_used_power,
     ts AS powrail_start_ts,
-    (LEAD(ts) OVER (PARTITION BY name ORDER BY ts)) as powrail_end_ts
+    (LEAD(ts) OVER (PARTITION BY name ORDER BY ts)) AS powrail_end_ts
   FROM (
     SELECT name, MIN(ts) AS ts, value
     FROM power_rails_counters
@@ -65,7 +65,7 @@
       )
     ),
     'avg_used_power_mw', (SELECT avg_used_power FROM avg_used_powers
-        WHERE avg_used_powers.name = power_rails_counters.name)
+      WHERE avg_used_powers.name = power_rails_counters.name)
   ) AS power_rails_proto
 FROM power_rails_counters
 GROUP BY name
diff --git a/src/trace_processor/metrics/sql/android/android_proxy_power.sql b/src/trace_processor/metrics/sql/android/android_proxy_power.sql
index c356edd..c61c91d 100644
--- a/src/trace_processor/metrics/sql/android/android_proxy_power.sql
+++ b/src/trace_processor/metrics/sql/android/android_proxy_power.sql
@@ -29,7 +29,7 @@
 --     CREATE VIEW my_slice_utid AS
 --     SELECT ts, dur, utid
 --     FROM my_slice
---     INNER JOIN thread_track ON track_id = thread_track.id;
+--     JOIN thread_track ON track_id = thread_track.id;
 --
 --     CREATE VIRTUAL TABLE my_slice_power
 --     USING SPAN_JOIN(power_per_thread PARTITIONED utid,
@@ -45,15 +45,15 @@
 DROP VIEW IF EXISTS device;
 CREATE VIEW device AS
 WITH
-  after_first_slash(str) AS (
-      SELECT SUBSTR(str_value, INSTR(str_value, '/') + 1)
-      FROM metadata
-      WHERE name = 'android_build_fingerprint'
-  ),
-  before_second_slash(str) AS (
-      SELECT SUBSTR(str, 0, INSTR(str, '/'))
-      FROM after_first_slash
-  )
+after_first_slash(str) AS (
+  SELECT SUBSTR(str_value, INSTR(str_value, '/') + 1)
+  FROM metadata
+  WHERE name = 'android_build_fingerprint'
+),
+before_second_slash(str) AS (
+  SELECT SUBSTR(str, 0, INSTR(str, '/'))
+  FROM after_first_slash
+)
 SELECT str AS name FROM before_second_slash;
 
 DROP VIEW IF EXISTS power_view;
@@ -68,7 +68,7 @@
   power_profile.device = (SELECT name FROM device)
   AND power_profile.cpu = cpu_freq_view.cpu
   AND power_profile.freq = cpu_freq_view.freq_khz
-);
+  );
 
 -- utid = 0 is a reserved value used to mark sched slices where CPU was idle.
 -- It doesn't correspond to any real thread.
diff --git a/src/trace_processor/metrics/sql/android/android_simpleperf.sql b/src/trace_processor/metrics/sql/android/android_simpleperf.sql
index 20d2bc9..46a27ba 100644
--- a/src/trace_processor/metrics/sql/android/android_simpleperf.sql
+++ b/src/trace_processor/metrics/sql/android/android_simpleperf.sql
@@ -39,8 +39,8 @@
 CREATE VIEW simpleperf_event_raw AS
 SELECT
   SUBSTR(name, 0, tid_pos) AS name,
-  CAST(SUBSTR(name, tid_pos+4, cpu_pos-tid_pos-4) AS INT)  AS tid,
-  CAST(SUBSTR(name, cpu_pos+4) AS INT) AS cpu,
+  CAST(SUBSTR(name, tid_pos + 4, cpu_pos - tid_pos - 4) AS INT) AS tid,
+  CAST(SUBSTR(name, cpu_pos + 4) AS INT) AS cpu,
   total
 FROM (
   SELECT
diff --git a/src/trace_processor/metrics/sql/android/android_startup.sql b/src/trace_processor/metrics/sql/android/android_startup.sql
index f60547f..bd80e8a 100644
--- a/src/trace_processor/metrics/sql/android/android_startup.sql
+++ b/src/trace_processor/metrics/sql/android/android_startup.sql
@@ -15,11 +15,13 @@
 --
 
 -- Create the base tables and views containing the launch spans.
-SELECT RUN_METRIC('android/startup/launches.sql');
+SELECT IMPORT('android.startup.startups');
+SELECT RUN_METRIC('android/process_metadata.sql');
 
 -- Define the helper functions which will be used throught the remainder
 -- of the metric.
 SELECT RUN_METRIC('android/startup/slice_functions.sql');
+SELECT IMPORT('common.timestamps');
 
 -- Run all the HSC metrics.
 SELECT RUN_METRIC('android/startup/hsc.sql');
@@ -42,23 +44,23 @@
 -- Prefer this over process start_ts, since the process might have
 -- been preforked.
 SELECT CREATE_VIEW_FUNCTION(
-  'ZYGOTE_FORK_FOR_LAUNCH(launch_id INT)',
+  'ZYGOTE_FORK_FOR_LAUNCH(startup_id INT)',
   'ts INT, dur INT',
   '
     SELECT slice.ts, slice.dur
-    FROM launches
+    FROM android_startups l
     JOIN slice ON (
-      launches.ts < slice.ts AND
-      slice.ts + slice.dur < launches.ts_end AND
-      STR_SPLIT(slice.name, ": ", 1) = launches.package
+      l.ts < slice.ts AND
+      slice.ts + slice.dur < l.ts_end AND
+      STR_SPLIT(slice.name, ": ", 1) = l.package
     )
-    WHERE launches.id = $launch_id AND slice.name GLOB "Start proc: *"
+    WHERE l.startup_id = $startup_id AND slice.name GLOB "Start proc: *"
   '
 );
 
 -- Returns the fully drawn slice proto given a launch id.
 SELECT CREATE_FUNCTION(
-  'REPORT_FULLY_DRAWN_FOR_LAUNCH(launch_id INT)',
+  'REPORT_FULLY_DRAWN_FOR_LAUNCH(startup_id INT)',
   'PROTO',
   '
     SELECT
@@ -67,69 +69,44 @@
       SELECT
         launches.ts AS launch_ts,
         min(slice.ts) AS report_fully_drawn_ts
-      FROM launches
-      JOIN launch_processes ON (launches.id = launch_processes.launch_id)
+      FROM android_startups launches
+      JOIN android_startup_processes ON (launches.startup_id = android_startup_processes.startup_id)
       JOIN thread USING (upid)
       JOIN thread_track USING (utid)
       JOIN slice ON (slice.track_id = thread_track.id)
       WHERE
         slice.name GLOB "reportFullyDrawn*" AND
         slice.ts >= launches.ts AND
-        launches.id = $launch_id
+        launches.startup_id = $startup_id
     )
   '
 );
 
--- Returns a repeated field of all long binder transaction protos for
--- a given launch id.
-SELECT CREATE_FUNCTION(
-  'BINDER_TRANSACTION_PROTO_FOR_LAUNCH(launch_id INT)',
-  'PROTO',
-  '
-    SELECT RepeatedField(
-      AndroidStartupMetric_BinderTransaction(
-        "duration", STARTUP_SLICE_PROTO(s.slice_dur),
-        "thread", s.thread_name,
-        "destination_thread", EXTRACT_ARG(s.arg_set_id, "destination name"),
-        "destination_process", process.name,
-        "flags", EXTRACT_ARG(s.arg_set_id, "flags"),
-        "code", EXTRACT_ARG(s.arg_set_id, "code"),
-        "data_size", EXTRACT_ARG(s.arg_set_id, "data_size")
-      )
-    )
-    FROM SLICES_FOR_LAUNCH_AND_SLICE_NAME($launch_id, "binder transaction") s
-    JOIN process ON (
-      EXTRACT_ARG(s.arg_set_id, "destination process") = process.pid
-    )
-    WHERE s.slice_dur > 5e7
-  '
-);
-
 -- Define the view
 DROP VIEW IF EXISTS startup_view;
 CREATE VIEW startup_view AS
 SELECT
   AndroidStartupMetric_Startup(
-    'startup_id', launches.id,
+    'startup_id',launches.startup_id,
     'startup_type', (
-      SELECT lp.launch_type
-      FROM launch_processes lp
-      WHERE lp.launch_id = launches.id
+      SELECT lp.startup_type
+      FROM android_startup_processes lp
+      WHERE lp.startup_id =launches.startup_id
       LIMIT 1
     ),
     'package_name', launches.package,
     'process_name', (
       SELECT p.name
-      FROM launch_processes lp
+      FROM android_startup_processes lp
       JOIN process p USING (upid)
-      WHERE lp.launch_id = launches.id
+      WHERE lp.startup_id =launches.startup_id
       LIMIT 1
     ),
     'process', (
       SELECT m.metadata
       FROM process_metadata m
-      JOIN launch_processes p USING (upid)
-      WHERE p.launch_id = launches.id
+      JOIN android_startup_processes p USING (upid)
+      WHERE p.startup_id =launches.startup_id
       LIMIT 1
     ),
     'activities', (
@@ -137,17 +114,30 @@
         'name', (SELECT STR_SPLIT(s.slice_name, ':', 1)),
         'method', (SELECT STR_SPLIT(s.slice_name, ':', 0)),
         'ts_method_start', s.slice_ts
-      ))
+        ))
       FROM thread_slices_for_all_launches s
       WHERE
-        s.launch_id = launches.id AND
-        (s.slice_name GLOB 'performResume:*' OR s.slice_name GLOB 'performCreate:*')
+        s.startup_id =launches.startup_id
+        AND (s.slice_name GLOB 'performResume:*' OR s.slice_name GLOB 'performCreate:*')
     ),
-    'long_binder_transactions', BINDER_TRANSACTION_PROTO_FOR_LAUNCH(launches.id),
-    'zygote_new_process', EXISTS(SELECT TRUE FROM ZYGOTE_FORK_FOR_LAUNCH(launches.id)),
+    'long_binder_transactions', (
+      SELECT RepeatedField(
+        AndroidStartupMetric_BinderTransaction(
+          "duration", STARTUP_SLICE_PROTO(s.slice_dur),
+          "thread", s.thread_name,
+          "destination_thread", EXTRACT_ARG(s.arg_set_id, "destination name"),
+          "destination_process", s.process,
+          "flags", EXTRACT_ARG(s.arg_set_id, "flags"),
+          "code", EXTRACT_ARG(s.arg_set_id, "code"),
+          "data_size", EXTRACT_ARG(s.arg_set_id, "data_size")
+        )
+      )
+      FROM ANDROID_BINDER_TRANSACTION_SLICES_FOR_STARTUP(launches.startup_id, 5e7) s
+    ),
+    'zygote_new_process', EXISTS(SELECT TRUE FROM ZYGOTE_FORK_FOR_LAUNCH(launches.startup_id)),
     'activity_hosting_process_count', (
-      SELECT COUNT(1) FROM launch_processes p
-      WHERE p.launch_id = launches.id
+      SELECT COUNT(1) FROM android_startup_processes p
+      WHERE p.startup_id =launches.startup_id
     ),
     'event_timestamps', AndroidStartupMetric_EventTimestamps(
       'intent_received', launches.ts,
@@ -158,85 +148,84 @@
       'dur_ms', launches.dur / 1e6,
       'main_thread_by_task_state', AndroidStartupMetric_TaskStateBreakdown(
         'running_dur_ns', IFNULL(
-          MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launches.id, 'Running'), 0
+          MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launches.startup_id, 'Running'), 0
         ),
         'runnable_dur_ns', IFNULL(
-          MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launches.id, 'R'), 0
+          MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launches.startup_id, 'R*'), 0
         ),
         'uninterruptible_sleep_dur_ns', IFNULL(
-          MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launches.id, 'D*'), 0
+          MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launches.startup_id, 'D*'), 0
         ),
         'interruptible_sleep_dur_ns', IFNULL(
-          MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launches.id, 'S'), 0
+          MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launches.startup_id, 'S'), 0
+        ),
+        'uninterruptible_io_sleep_dur_ns', IFNULL(
+          MAIN_THREAD_TIME_FOR_LAUNCH_STATE_AND_IO_WAIT(launches.startup_id, 'D*', TRUE), 0
+        ),
+        'uninterruptible_non_io_sleep_dur_ns', IFNULL(
+          MAIN_THREAD_TIME_FOR_LAUNCH_STATE_AND_IO_WAIT(launches.startup_id, 'D*', FALSE), 0
         )
+
       ),
       'mcycles_by_core_type', NULL_IF_EMPTY(AndroidStartupMetric_McyclesByCoreType(
-        'little', MCYCLES_FOR_LAUNCH_AND_CORE_TYPE(launches.id, 'little'),
-        'big', MCYCLES_FOR_LAUNCH_AND_CORE_TYPE(launches.id, 'big'),
-        'bigger', MCYCLES_FOR_LAUNCH_AND_CORE_TYPE(launches.id, 'bigger'),
-        'unknown', MCYCLES_FOR_LAUNCH_AND_CORE_TYPE(launches.id, 'unknown')
+        'little', MCYCLES_FOR_LAUNCH_AND_CORE_TYPE(launches.startup_id, 'little'),
+        'big', MCYCLES_FOR_LAUNCH_AND_CORE_TYPE(launches.startup_id, 'big'),
+        'bigger', MCYCLES_FOR_LAUNCH_AND_CORE_TYPE(launches.startup_id, 'bigger'),
+        'unknown', MCYCLES_FOR_LAUNCH_AND_CORE_TYPE(launches.startup_id, 'unknown')
       )),
       'to_post_fork',
-        LAUNCH_TO_MAIN_THREAD_SLICE_PROTO(launches.id, 'PostFork'),
+      LAUNCH_TO_MAIN_THREAD_SLICE_PROTO(launches.startup_id, 'PostFork'),
       'to_activity_thread_main',
-        LAUNCH_TO_MAIN_THREAD_SLICE_PROTO(launches.id, 'ActivityThreadMain'),
+      LAUNCH_TO_MAIN_THREAD_SLICE_PROTO(launches.startup_id, 'ActivityThreadMain'),
       'to_bind_application',
-        LAUNCH_TO_MAIN_THREAD_SLICE_PROTO(launches.id, 'bindApplication'),
+      LAUNCH_TO_MAIN_THREAD_SLICE_PROTO(launches.startup_id, 'bindApplication'),
       'time_activity_manager', (
         SELECT STARTUP_SLICE_PROTO(l.ts - launches.ts)
-        FROM launching_events l
+        FROM internal_startup_events l
         WHERE l.ts BETWEEN launches.ts AND launches.ts + launches.dur
       ),
       'time_post_fork',
-        DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.id, 'PostFork'),
+      DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.startup_id, 'PostFork'),
       'time_activity_thread_main',
-        DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.id, 'ActivityThreadMain'),
+      DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.startup_id, 'ActivityThreadMain'),
       'time_bind_application',
-        DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.id, 'bindApplication'),
+      DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.startup_id, 'bindApplication'),
       'time_activity_start',
-        DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.id, 'activityStart'),
+      DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.startup_id, 'activityStart'),
       'time_activity_resume',
-        DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.id, 'activityResume'),
+      DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.startup_id, 'activityResume'),
       'time_activity_restart',
-        DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.id, 'activityRestart'),
+      DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.startup_id, 'activityRestart'),
       'time_choreographer',
-        DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.id, 'Choreographer#doFrame*'),
+      DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.startup_id, 'Choreographer#doFrame*'),
       'time_inflate',
-        DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.id, 'inflate'),
+      DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.startup_id, 'inflate'),
       'time_get_resources',
-        DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.id, 'ResourcesManager#getResources'),
+      DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.startup_id, 'ResourcesManager#getResources'),
       'time_dex_open',
-        DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.id, 'OpenDexFilesFromOat*'),
+      DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.startup_id, 'OpenDexFilesFromOat*'),
       'time_verify_class',
-        DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.id, 'VerifyClass*'),
+      DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launches.startup_id, 'VerifyClass*'),
       'time_gc_total', (
-        SELECT NULL_IF_EMPTY(STARTUP_SLICE_PROTO(SUM(slice_dur)))
-        FROM thread_slices_for_all_launches slice
-        WHERE
-          launch_id = launches.id AND
-          (
-            slice_name GLOB "*semispace GC" OR
-            slice_name GLOB "*mark sweep GC" OR
-            slice_name GLOB "*concurrent copying GC"
-          )
+        SELECT NULL_IF_EMPTY(STARTUP_SLICE_PROTO(TOTAL_GC_TIME_BY_LAUNCH(launches.startup_id)))
       ),
       'time_lock_contention_thread_main',
-        DUR_SUM_MAIN_THREAD_SLICE_PROTO_FOR_LAUNCH(
-          launches.id,
-          'Lock contention on*'
-        ),
+      DUR_SUM_MAIN_THREAD_SLICE_PROTO_FOR_LAUNCH(
+       launches.startup_id,
+        'Lock contention on*'
+      ),
       'time_monitor_contention_thread_main',
-        DUR_SUM_MAIN_THREAD_SLICE_PROTO_FOR_LAUNCH(
-          launches.id,
-          'Lock contention on a monitor*'
-        ),
+      DUR_SUM_MAIN_THREAD_SLICE_PROTO_FOR_LAUNCH(
+       launches.startup_id,
+        'Lock contention on a monitor*'
+      ),
       'time_before_start_process', (
         SELECT STARTUP_SLICE_PROTO(ts - launches.ts)
-        FROM ZYGOTE_FORK_FOR_LAUNCH(launches.id)
+        FROM ZYGOTE_FORK_FOR_LAUNCH(launches.startup_id)
       ),
       'time_jit_thread_pool_on_cpu', NULL_IF_EMPTY(STARTUP_SLICE_PROTO(
         THREAD_TIME_FOR_LAUNCH_STATE_AND_THREAD(
-          launches.id,
+         launches.startup_id,
           'Running',
           'Jit thread pool'
         )
@@ -244,25 +233,25 @@
       'time_gc_on_cpu', (
         SELECT STARTUP_SLICE_PROTO(sum_dur)
         FROM running_gc_slices_materialized
-        WHERE launches.id = launch_id
+        WHERE launches.startup_id = startup_id
       ),
       'time_during_start_process', (
         SELECT STARTUP_SLICE_PROTO(dur)
-        FROM ZYGOTE_FORK_FOR_LAUNCH(launches.id)
+        FROM ZYGOTE_FORK_FOR_LAUNCH(launches.startup_id)
       ),
       'jit_compiled_methods', (
         SELECT IIF(COUNT(1) = 0, NULL, COUNT(1))
-        FROM SLICES_FOR_LAUNCH_AND_SLICE_NAME(launches.id, 'JIT compiling*')
+        FROM ANDROID_SLICES_FOR_STARTUP_AND_SLICE_NAME(launches.startup_id, 'JIT compiling*')
         WHERE thread_name = 'Jit thread pool'
       ),
       'other_processes_spawned_count', (
         SELECT COUNT(1)
         FROM process
         WHERE
-          process.start_ts BETWEEN launches.ts AND launches.ts + launches.dur AND
-          process.upid NOT IN (
-            SELECT upid FROM launch_processes
-            WHERE launch_processes.launch_id = launches.id
+          process.start_ts BETWEEN launches.ts AND launches.ts + launches.dur
+          AND process.upid NOT IN (
+            SELECT upid FROM android_startup_processes
+            WHERE android_startup_processes.startup_id =launches.startup_id
           )
       )
     ),
@@ -270,60 +259,204 @@
       'full_startup', (
         SELECT STARTUP_SLICE_PROTO(h.ts_total)
         FROM hsc_based_startup_times h
-        WHERE h.id = launches.id
+        WHERE h.id =launches.startup_id
       )
     )),
-    'report_fully_drawn', NULL_IF_EMPTY(REPORT_FULLY_DRAWN_FOR_LAUNCH(launches.id)),
+    'report_fully_drawn', NULL_IF_EMPTY(REPORT_FULLY_DRAWN_FOR_LAUNCH(launches.startup_id)),
     'optimization_status', (
       SELECT RepeatedField(AndroidStartupMetric_OptimizationStatus(
         'location', SUBSTR(STR_SPLIT(slice_name, ' status=', 0), LENGTH('location=') + 1),
         'odex_status', STR_SPLIT(STR_SPLIT(slice_name, ' status=', 1), ' filter=', 0),
         'compilation_filter', STR_SPLIT(STR_SPLIT(slice_name, ' filter=', 1), ' reason=', 0),
         'compilation_reason', STR_SPLIT(slice_name, ' reason=', 1)
-      ))
+        ))
       FROM (
         SELECT *
-        FROM SLICES_FOR_LAUNCH_AND_SLICE_NAME(
-          launches.id,
+        FROM ANDROID_SLICES_FOR_STARTUP_AND_SLICE_NAME(
+         launches.startup_id,
           'location=* status=* filter=* reason=*'
         )
         ORDER BY slice_name
       )
     ),
+    'startup_concurrent_to_launch', (
+      SELECT RepeatedField(package)
+      FROM android_startups l
+      WHERE l.startup_id != launches.startup_id
+        AND IS_SPANS_OVERLAPPING(l.ts, l.ts_end, launches.ts, launches.ts_end)
+    ),
     'system_state', AndroidStartupMetric_SystemState(
       'dex2oat_running',
-        IS_PROCESS_RUNNING_CONCURRENT_TO_LAUNCH(launches.id, '*dex2oat64'),
+      DUR_OF_PROCESS_RUNNING_CONCURRENT_TO_LAUNCH(launches.startup_id, '*dex2oat64') > 0,
       'installd_running',
-        IS_PROCESS_RUNNING_CONCURRENT_TO_LAUNCH(launches.id, '*installd'),
+      DUR_OF_PROCESS_RUNNING_CONCURRENT_TO_LAUNCH(launches.startup_id, '*installd') > 0,
       'broadcast_dispatched_count',
-        COUNT_SLICES_CONCURRENT_TO_LAUNCH(launches.id, 'Broadcast dispatched*'),
+      COUNT_SLICES_CONCURRENT_TO_LAUNCH(launches.startup_id, 'Broadcast dispatched*'),
       'broadcast_received_count',
-        COUNT_SLICES_CONCURRENT_TO_LAUNCH(launches.id, 'broadcastReceiveReg*'),
+      COUNT_SLICES_CONCURRENT_TO_LAUNCH(launches.startup_id, 'broadcastReceiveReg*'),
       'most_active_non_launch_processes',
-        N_MOST_ACTIVE_PROCESS_NAMES_FOR_LAUNCH(launches.id)
+      N_MOST_ACTIVE_PROCESS_NAMES_FOR_LAUNCH(launches.startup_id),
+      'installd_dur_ns',
+      DUR_OF_PROCESS_RUNNING_CONCURRENT_TO_LAUNCH(launches.startup_id, '*installd'),
+      'dex2oat_dur_ns',
+      DUR_OF_PROCESS_RUNNING_CONCURRENT_TO_LAUNCH(launches.startup_id, '*dex2oat64')
     ),
     'slow_start_reason', (SELECT RepeatedField(slow_cause)
       FROM (
         SELECT 'dex2oat running during launch' AS slow_cause
-        WHERE IS_PROCESS_RUNNING_CONCURRENT_TO_LAUNCH(launches.id, '*dex2oat64')
+        WHERE
+          DUR_OF_PROCESS_RUNNING_CONCURRENT_TO_LAUNCH(launches.startup_id, '*dex2oat64') > 20e6
 
         UNION ALL
         SELECT 'installd running during launch' AS slow_cause
-        WHERE IS_PROCESS_RUNNING_CONCURRENT_TO_LAUNCH(launches.id, '*installd')
+        WHERE
+          DUR_OF_PROCESS_RUNNING_CONCURRENT_TO_LAUNCH(launches.startup_id, '*installd') > 150e6
+
+        UNION ALL
+        SELECT 'Main Thread - Time spent in Running state'
+          AS slow_cause
+        WHERE MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launches.startup_id, 'Running') > 150e6
+
+        UNION ALL
+        SELECT 'Main Thread - Time spent in Runnable state'
+          AS slow_cause
+        WHERE MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launches.startup_id, 'R*') > 100e6
+
+        UNION ALL
+        SELECT 'Main Thread - Time spent in interruptible sleep state'
+          AS slow_cause
+        WHERE MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launches.startup_id, 'S') > 250e6
+
+        UNION ALL
+        SELECT 'Main Thread - Time spent in Blocking I/O'
+        WHERE MAIN_THREAD_TIME_FOR_LAUNCH_STATE_AND_IO_WAIT(launches.startup_id, 'D*', TRUE) > 300e6
+
+        UNION ALL
+        SELECT 'Time spent in OpenDexFilesFromOat*'
+          AS slow_cause
+        WHERE ANDROID_SUM_DUR_FOR_STARTUP_AND_SLICE(launches.startup_id, 'OpenDexFilesFromOat*') > 1e6
+
+        UNION ALL
+        SELECT 'Time spent in bindApplication'
+          AS slow_cause
+        WHERE ANDROID_SUM_DUR_FOR_STARTUP_AND_SLICE(launches.startup_id, 'bindApplication') > 10e6
+
+        UNION ALL
+        SELECT 'Time spent in view inflation'
+          AS slow_cause
+        WHERE ANDROID_SUM_DUR_FOR_STARTUP_AND_SLICE(launches.startup_id, 'inflate') > 600e6
+
+        UNION ALL
+        SELECT 'Time spent in ResourcesManager#getResources'
+          AS slow_cause
+        WHERE ANDROID_SUM_DUR_FOR_STARTUP_AND_SLICE(
+          launches.startup_id, 'ResourcesManager#getResources') > 10e6
+
+        UNION ALL
+        SELECT 'Time spent verifying classes'
+          AS slow_cause
+        WHERE ANDROID_SUM_DUR_FOR_STARTUP_AND_SLICE(launches.startup_id, 'VerifyClass*') > 10e6
+
+        UNION ALL
+        SELECT 'Potential CPU contention with '
+          || MOST_ACTIVE_PROCESS_FOR_LAUNCH(launches.startup_id)
+          AS slow_cause
+        WHERE MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launches.startup_id, 'R*') > 100e6
+          AND MOST_ACTIVE_PROCESS_FOR_LAUNCH(launches.startup_id) IS NOT NULL
+
+        UNION ALL
+        SELECT 'JIT Activity'
+          AS slow_cause
+        WHERE THREAD_TIME_FOR_LAUNCH_STATE_AND_THREAD(
+          launches.startup_id,
+          'Running',
+          'Jit thread pool'
+        ) > 120e6
+
+        UNION ALL
+        SELECT 'Main Thread - Lock contention'
+          AS slow_cause
+        WHERE ANDROID_SUM_DUR_ON_MAIN_THREAD_FOR_STARTUP_AND_SLICE(
+          launches.startup_id,
+          'Lock contention on*'
+        ) > 25e6
+
+        UNION ALL
+        SELECT 'Main Thread - Monitor contention'
+          AS slow_cause
+        WHERE ANDROID_SUM_DUR_ON_MAIN_THREAD_FOR_STARTUP_AND_SLICE(
+          launches.startup_id,
+          'Lock contention on a monitor*'
+        ) > 40e6
+
+        UNION ALL
+        SELECT 'GC Activity'
+        WHERE TOTAL_GC_TIME_BY_LAUNCH(launches.startup_id) > 0
+
+        UNION ALL
+        SELECT 'JIT compiled methods'
+        WHERE (
+          SELECT COUNT(1)
+          FROM ANDROID_SLICES_FOR_STARTUP_AND_SLICE_NAME(launches.startup_id, 'JIT compiling*')
+          WHERE thread_name = 'Jit thread pool'
+        ) > 40
+
+        UNION ALL
+        SELECT 'Broadcast dispatched count'
+        WHERE COUNT_SLICES_CONCURRENT_TO_LAUNCH(
+          launches.startup_id,
+          'Broadcast dispatched*'
+        ) > 10
+
+        UNION ALL
+        SELECT 'Broadcast received count'
+        WHERE COUNT_SLICES_CONCURRENT_TO_LAUNCH(
+          launches.startup_id,
+          'broadcastReceiveReg*'
+        ) > 10
+
+        UNION ALL
+        SELECT 'No baseline or cloud profiles'
+        WHERE MISSING_BASELINE_PROFILE_FOR_LAUNCH(launches.startup_id, launches.package)
+
+        UNION ALL
+        SELECT 'Optimized artifacts missing, run from apk'
+        WHERE  RUN_FROM_APK_FOR_LAUNCH(launches.startup_id)
+
+        UNION ALL
+        SELECT 'Startup running concurrent to launch'
+        WHERE EXISTS(
+          SELECT package
+          FROM android_startups l
+          WHERE l.startup_id != launches.startup_id
+            AND IS_SPANS_OVERLAPPING(l.ts, l.ts_end, launches.ts, launches.ts_end)
+        )
+
+        UNION ALL
+        SELECT 'Main Thread - Binder transactions blocked'
+        WHERE (
+          SELECT COUNT(1)
+          FROM BINDER_TRANSACTION_REPLY_SLICES_FOR_LAUNCH(launches.startup_id, 2e7)
+        ) > 0
+
+        UNION ALL
+        SELECT 'Unlock running during launch'
+        WHERE IS_UNLOCK_RUNNING_DURING_LAUNCH(launches.startup_id)
+
       )
     )
-  ) as startup
-FROM launches;
+  ) AS startup
+FROM android_startups launches;
 
 DROP VIEW IF EXISTS android_startup_event;
 CREATE VIEW android_startup_event 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;
+  'slice' AS track_type,
+  'Android App Startups' AS track_name,
+  l.ts AS ts,
+  l.dur AS dur,
+  l.package AS slice_name
+FROM android_startups l;
 
 DROP VIEW IF EXISTS android_startup_output;
 CREATE VIEW android_startup_output AS
diff --git a/src/trace_processor/metrics/sql/android/android_surfaceflinger.sql b/src/trace_processor/metrics/sql/android/android_surfaceflinger.sql
index b00e491..b14ce84 100644
--- a/src/trace_processor/metrics/sql/android/android_surfaceflinger.sql
+++ b/src/trace_processor/metrics/sql/android/android_surfaceflinger.sql
@@ -44,8 +44,8 @@
 CREATE VIEW surfaceflinger_track AS
 SELECT tr.id AS track_id, t.utid, t.tid
 FROM process p JOIN thread t ON p.upid = t.upid
-     JOIN thread_track tr ON tr.utid = t.utid
-WHERE p.cmdline='/system/bin/surfaceflinger';
+JOIN thread_track tr ON tr.utid = t.utid
+WHERE p.cmdline = '/system/bin/surfaceflinger';
 
 DROP VIEW IF EXISTS gpu_waiting_start;
 CREATE VIEW gpu_waiting_start AS
@@ -60,7 +60,7 @@
 SELECT
   CAST(SUBSTR(s.name, 28) AS UINT32) AS fence_id,
   dur,
-  ts+dur AS end_ts
+  ts + dur AS end_ts
 FROM slices s JOIN surfaceflinger_track t ON s.track_id = t.track_id
 WHERE s.name GLOB 'waiting for GPU completion *';
 
@@ -90,14 +90,14 @@
 CREATE VIEW android_surfaceflinger_output AS
 SELECT
   AndroidSurfaceflingerMetric(
-    'missed_frames', (SELECT COUNT(1) FROM frame_missed WHERE value=1),
-    'missed_hwc_frames', (SELECT COUNT(1) FROM hwc_frame_missed WHERE value=1),
-    'missed_gpu_frames', (SELECT COUNT(1) FROM gpu_frame_missed WHERE value=1),
+    'missed_frames', (SELECT COUNT(1) FROM frame_missed WHERE value = 1),
+    'missed_hwc_frames', (SELECT COUNT(1) FROM hwc_frame_missed WHERE value = 1),
+    'missed_gpu_frames', (SELECT COUNT(1) FROM gpu_frame_missed WHERE value = 1),
     'missed_frame_rate', (SELECT AVG(value) FROM frame_missed),
     'missed_hwc_frame_rate', (SELECT AVG(value) FROM hwc_frame_missed),
     'missed_gpu_frame_rate', (SELECT AVG(value) FROM gpu_frame_missed),
     'gpu_invocations', (SELECT COUNT(1) FROM gpu_waiting_end),
-    'avg_gpu_waiting_dur_ms', (SELECT AVG(dur)/1e6 FROM gpu_waiting_span),
+    'avg_gpu_waiting_dur_ms', (SELECT AVG(dur) / 1e6 FROM gpu_waiting_span),
     'total_non_empty_gpu_waiting_dur_ms',
-        (SELECT SUM(dur)/1e6 FROM gpu_waiting_end)
+    (SELECT SUM(dur) / 1e6 FROM gpu_waiting_end)
   );
diff --git a/src/trace_processor/metrics/sql/android/android_sysui_cuj.sql b/src/trace_processor/metrics/sql/android/android_sysui_cuj.sql
deleted file mode 100644
index 60a2caf..0000000
--- a/src/trace_processor/metrics/sql/android/android_sysui_cuj.sql
+++ /dev/null
@@ -1,549 +0,0 @@
---
--- 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');
-
--- Stores information about the last CUJ (important UI transition) in the trace.
--- There might be more than 1 CUJ in the trace and in that case we pick the one
--- that finished last.
--- This limiting to 1 CUJ is done to simplify the rest of the script.
-DROP TABLE IF EXISTS android_sysui_cuj_last_cuj;
-CREATE TABLE android_sysui_cuj_last_cuj AS
--- Finds slices like J<SHADE_EXPAND_COLLAPSE> which mark which frames were
--- rendered during a specific CUJ.
-  WITH cujs AS (
-  SELECT
-    ROW_NUMBER() OVER (ORDER BY ts) AS cuj_id,
-    process.upid AS upid,
-    process.name AS process_name,
-    process_metadata.metadata AS process_metadata,
-    slice.name AS cuj_slice_name,
-    -- Extracts "CUJ_NAME" from "J<CUJ_NAME>"
-    SUBSTR(slice.name, 3, LENGTH(slice.name) - 3) AS cuj_name,
-    ts AS ts_start,
-    dur,
-    ts + dur AS ts_end
-  FROM slice
-  JOIN process_track
-    ON slice.track_id = process_track.id
-  JOIN process USING (upid)
-  JOIN process_metadata USING (upid)
-  WHERE
-    slice.name GLOB 'J<*>'
-    AND (
-      process.name GLOB 'com.google.android*'
-      OR process.name GLOB 'com.android.*')
-),
--- Slices logged from FrameTracker#markEvent that describe when
--- the instrumentation was started and the reason the CUJ ended.
-cuj_state_markers AS (
-SELECT
-  cujs.cuj_id,
-  cuj_state_marker.ts,
-  cuj_state_marker.dur,
-  cuj_state_marker.name,
-  CASE
-    WHEN cuj_state_marker.name GLOB '*#FT#begin*' THEN 'begin'
-    WHEN cuj_state_marker.name GLOB '*#FT#deferMonitoring*' THEN 'deferMonitoring'
-    WHEN cuj_state_marker.name GLOB '*#FT#end*' THEN 'end'
-    WHEN cuj_state_marker.name GLOB '*#FT#cancel*' THEN 'cancel'
-    ELSE 'other'
-  END AS marker_type
-FROM cujs
-LEFT JOIN slice cuj_state_marker
-  ON cuj_state_marker.ts >= cujs.ts_start
-  AND cuj_state_marker.ts < cujs.ts_end
-  -- e.g. J<CUJ_NAME>#FT#end#0
-  AND cuj_state_marker.name GLOB (cujs.cuj_slice_name || "#FT#*")
-)
-SELECT
-  cujs.*,
-  CASE
-    WHEN EXISTS (
-      SELECT 1
-      FROM cuj_state_markers csm
-      WHERE csm.cuj_id = cujs.cuj_id
-      AND csm.marker_type = 'cancel')
-    THEN 'canceled'
-    WHEN EXISTS (
-      SELECT 1
-      FROM cuj_state_markers csm
-      WHERE csm.cuj_id = cujs.cuj_id
-      AND csm.marker_type = 'end')
-    THEN 'completed'
-  ELSE NULL
-  END AS state
-FROM cujs
-WHERE
-  state <> 'canceled'
-  -- Older builds don't have the state markers so we allow NULL but filter out
-  -- CUJs that are <4ms long - assuming CUJ was canceled in that case.
-  OR (state IS NULL AND cujs.dur > 4e6)
-ORDER BY ts_end DESC LIMIT 1;
-
-SELECT RUN_METRIC(
-  'android/android_hwui_threads.sql',
-  'table_name_prefix', 'android_sysui_cuj',
-  'process_allowlist_table', 'android_sysui_cuj_last_cuj');
-
-DROP TABLE IF EXISTS android_sysui_cuj_do_frame_slices_in_cuj;
-CREATE TABLE android_sysui_cuj_do_frame_slices_in_cuj AS
-SELECT
-  slices.*,
-  lag(slices.ts_end) OVER (ORDER BY vsync ASC) as ts_prev_frame_end
-FROM android_sysui_cuj_do_frame_slices slices
-JOIN android_sysui_cuj_last_cuj last_cuj
-ON ts + slices.dur >= last_cuj.ts_start AND ts <= last_cuj.ts_end;
-
-DROP TABLE IF EXISTS android_sysui_cuj_vsync_boundaries;
-CREATE TABLE android_sysui_cuj_vsync_boundaries AS
-SELECT MIN(vsync) as vsync_min, MAX(vsync) as vsync_max
-FROM android_sysui_cuj_do_frame_slices_in_cuj;
-
-DROP TABLE IF EXISTS android_sysui_cuj_frame_expected_timeline_events;
-CREATE TABLE android_sysui_cuj_frame_expected_timeline_events AS
-  SELECT
-    CAST(e.name as INTEGER) as vsync,
-    e.ts as ts_expected,
-    e.dur as dur_expected,
-    MIN(a.ts) as ts_actual_min,
-    MAX(a.ts + a.dur) as ts_end_actual_max
-  FROM android_sysui_cuj_last_cuj cuj
-  JOIN expected_frame_timeline_slice e USING (upid)
-  JOIN android_sysui_cuj_vsync_boundaries vsync
-    ON CAST(e.name as INTEGER) >= vsync.vsync_min
-    AND CAST(e.name as INTEGER) <= vsync.vsync_max
-  JOIN actual_frame_timeline_slice a ON e.upid = a.upid AND e.name = a.name
-  GROUP BY e.name, e.ts, e.dur;
-
-DROP TABLE IF EXISTS android_sysui_cuj_frame_timeline_events;
-CREATE TABLE android_sysui_cuj_frame_timeline_events AS
-  SELECT
-    actual.layer_name as layer_name,
-    CAST(actual.name as INTEGER) as vsync,
-    actual.ts as ts_actual,
-    actual.dur as dur_actual,
-    actual.jank_type GLOB '*App Deadline Missed*' as app_missed,
-    actual.jank_type,
-    actual.on_time_finish
-  FROM android_sysui_cuj_last_cuj cuj
-  JOIN actual_frame_timeline_slice actual USING (upid)
-  JOIN android_sysui_cuj_vsync_boundaries vsync
-    ON CAST(actual.name as INTEGER) >= vsync.vsync_min
-    AND CAST(actual.name as INTEGER) <= vsync.vsync_max;
-
--- Adjust the timestamp when we consider the work on a given frame started,
--- by looking at the time the previous frame finished on the main thread
--- and the timing from the actual timeline.
--- This is to detect cases where we started doFrame late due to some other work
--- occupying the main thread.
-DROP TABLE IF EXISTS android_sysui_cuj_do_frame_slices_in_cuj_adjusted;
-CREATE TABLE android_sysui_cuj_do_frame_slices_in_cuj_adjusted AS
-SELECT
-  slices.*,
-  CASE
-    WHEN fte.ts_expected IS NULL
-    THEN ts
-    ELSE MAX(COALESCE(slices.ts_prev_frame_end, 0), fte.ts_expected)
-  END as ts_adjusted
-FROM android_sysui_cuj_do_frame_slices_in_cuj slices
-LEFT JOIN android_sysui_cuj_frame_expected_timeline_events fte
-  ON slices.vsync = fte.vsync
--- In rare cases there is a clock drift after device suspends
--- This may cause the actual/expected timeline to be misaligned with the rest
--- of the trace for a short period.
--- Do not use the timelines if it seems that this happened.
-AND slices.ts >= fte.ts_actual_min - 1e6 AND slices.ts <= fte.ts_end_actual_max;
-
-DROP TABLE IF EXISTS android_sysui_cuj_ts_boundaries;
-CREATE TABLE android_sysui_cuj_ts_boundaries AS
-SELECT ts, ts_end - ts as dur, ts_end FROM (
-SELECT
-(SELECT ts_adjusted FROM android_sysui_cuj_do_frame_slices_in_cuj_adjusted ORDER BY ts ASC LIMIT 1) as ts,
-(SELECT ts FROM android_sysui_cuj_do_frame_slices_in_cuj ORDER BY ts DESC LIMIT 1) +
-(SELECT dur_actual FROM android_sysui_cuj_frame_timeline_events ORDER BY vsync DESC LIMIT 1) as ts_end);
-
-DROP VIEW IF EXISTS android_sysui_cuj_thread;
-CREATE VIEW android_sysui_cuj_thread AS
-SELECT
-  process_name,
-  thread.utid,
-  thread.name
-FROM thread
-JOIN android_sysui_cuj_last_cuj process USING (upid);
-
-DROP VIEW IF EXISTS android_sysui_cuj_slices_in_cuj;
-CREATE VIEW android_sysui_cuj_slices_in_cuj AS
-SELECT
-  process_name,
-  thread.utid,
-  thread.name as thread_name,
-  slices.*,
-  slices.ts + slices.dur AS ts_end
-FROM slices
-JOIN thread_track ON slices.track_id = thread_track.id
-JOIN android_sysui_cuj_thread thread USING (utid)
-JOIN android_sysui_cuj_ts_boundaries cuj_boundaries
-ON slices.ts + slices.dur >= cuj_boundaries.ts AND slices.ts <= cuj_boundaries.ts_end
-WHERE slices.dur > 0;
-
-DROP TABLE IF EXISTS android_sysui_cuj_main_thread_slices_in_cuj;
-CREATE TABLE android_sysui_cuj_main_thread_slices_in_cuj AS
-SELECT slices.* FROM android_sysui_cuj_main_thread_slices slices
-JOIN android_sysui_cuj_ts_boundaries cuj_boundaries
-ON slices.ts + slices.dur >= cuj_boundaries.ts AND slices.ts <= cuj_boundaries.ts_end;
-
-DROP TABLE IF EXISTS android_sysui_cuj_render_thread_slices_in_cuj;
-CREATE TABLE android_sysui_cuj_render_thread_slices_in_cuj AS
-SELECT slices.* FROM android_sysui_cuj_render_thread_slices slices
-JOIN android_sysui_cuj_ts_boundaries cuj_boundaries
-ON slices.ts >= cuj_boundaries.ts AND slices.ts <= cuj_boundaries.ts_end;
-
-DROP TABLE IF EXISTS android_sysui_cuj_draw_frame_slices_in_cuj;
-CREATE TABLE android_sysui_cuj_draw_frame_slices_in_cuj AS
-SELECT slices.* FROM android_sysui_cuj_draw_frame_slices slices
-JOIN android_sysui_cuj_ts_boundaries cuj_boundaries
-ON slices.ts >= cuj_boundaries.ts AND slices.ts <= cuj_boundaries.ts_end;
-
-DROP TABLE IF EXISTS android_sysui_cuj_hwc_release_slices_in_cuj;
-CREATE TABLE android_sysui_cuj_hwc_release_slices_in_cuj AS
-SELECT slices.* FROM android_sysui_cuj_hwc_release_slices slices
-JOIN android_sysui_cuj_ts_boundaries cuj_boundaries
-ON slices.ts >= cuj_boundaries.ts AND slices.ts <= cuj_boundaries.ts_end;
-
-DROP TABLE IF EXISTS android_sysui_cuj_gpu_completion_slices_in_cuj;
-CREATE TABLE android_sysui_cuj_gpu_completion_slices_in_cuj AS
-SELECT slices.* FROM android_sysui_cuj_gpu_completion_slices slices
-JOIN android_sysui_cuj_ts_boundaries cuj_boundaries
-ON slices.ts >= cuj_boundaries.ts AND slices.ts <= cuj_boundaries.ts_end;
-
-DROP TABLE IF EXISTS android_sysui_cuj_jit_slices;
-CREATE TABLE android_sysui_cuj_jit_slices AS
-SELECT *
-FROM android_sysui_cuj_slices_in_cuj
-WHERE thread_name GLOB 'Jit thread pool*'
-AND name GLOB 'JIT compiling*'
-AND parent_id IS NULL;
-
-DROP TABLE IF EXISTS android_sysui_cuj_frames;
-CREATE TABLE android_sysui_cuj_frames AS
-  WITH gcs_to_rt_match AS (
-    SELECT
-      rts.ts,
-      CASE
-        WHEN rtfence.name GLOB 'GPU completion fence *'
-          THEN CAST(STR_SPLIT(rtfence.name, ' ', 3) AS INTEGER)
-        WHEN rtfence.name GLOB 'Trace GPU completion fence *'
-          THEN CAST(STR_SPLIT(rtfence.name, ' ', 4) AS INTEGER)
-        ELSE NULL
-      END AS idx
-    FROM android_sysui_cuj_render_thread_slices_in_cuj rts
-    JOIN descendant_slice(rts.id) rtfence ON rtfence.name GLOB '*GPU completion fence*'
-    -- dispatchFrameCallbacks might be seen in case of
-    -- drawing that happens on RT only (e.g. ripple effect)
-    WHERE (rts.name GLOB 'DrawFrame*' OR rts.name = 'dispatchFrameCallbacks')
-  )
-  SELECT
-    ROW_NUMBER() OVER (ORDER BY mts.ts) AS frame_number,
-    mts.vsync as vsync,
-    -- Main thread timings
-    mts.ts_adjusted as ts_main_thread_start,
-    mts.ts_end as ts_main_thread_end,
-    mts.ts_end - mts.ts_adjusted AS dur_main_thread,
-    -- RenderThread timings
-    MIN(rts.ts) AS ts_render_thread_start,
-    MAX(rts.ts_end) AS ts_render_thread_end,
-    SUM(rts.dur) AS dur_render_thread,
-    -- HWC and GPU
-    SUM(gcs.ts_end - MAX(COALESCE(hwc.ts_end, 0), gcs.ts)) as dur_gcs,
-    -- Overall frame timings
-    COALESCE(MAX(gcs.ts_end), MAX(rts.ts_end)) AS ts_frame_end,
-    COALESCE(MAX(gcs.ts_end), MAX(rts.ts_end)) - mts.ts_adjusted AS dur_frame,
-    MAX(gcs_rt.idx) IS NOT NULL as drew_anything
-    -- Match main thread doFrame with RT DrawFrame and optional GPU Completion
-    FROM android_sysui_cuj_do_frame_slices_in_cuj_adjusted mts
-    JOIN android_sysui_cuj_draw_frame_slices_in_cuj rts
-      ON mts.vsync = rts.vsync
-    LEFT JOIN gcs_to_rt_match gcs_rt ON gcs_rt.ts = rts.ts
-    LEFT JOIN android_sysui_cuj_gpu_completion_slices_in_cuj gcs USING(idx)
-    LEFT JOIN android_sysui_cuj_hwc_release_slices_in_cuj hwc USING (idx)
-    GROUP BY mts.vsync, mts.ts_adjusted, mts.ts_end
-    HAVING drew_anything;
-
-DROP TABLE IF EXISTS android_sysui_cuj_missed_frames;
-CREATE TABLE android_sysui_cuj_missed_frames AS
-  SELECT
-    f.*,
-    (SELECT MAX(fte.app_missed)
-     FROM android_sysui_cuj_frame_timeline_events fte
-     WHERE f.vsync = fte.vsync
-     AND fte.on_time_finish = 0) as app_missed
-  FROM android_sysui_cuj_frames f;
-
-DROP VIEW IF EXISTS android_sysui_cuj_frame_main_thread_bounds;
-CREATE VIEW android_sysui_cuj_frame_main_thread_bounds AS
-SELECT frame_number, ts_main_thread_start as ts, dur_main_thread as dur
-FROM android_sysui_cuj_missed_frames
-WHERE app_missed;
-
-DROP VIEW IF EXISTS android_sysui_cuj_main_thread_state_data;
-CREATE VIEW android_sysui_cuj_main_thread_state_data AS
-SELECT * FROM thread_state
-WHERE utid = (SELECT utid FROM android_sysui_cuj_main_thread);
-
-DROP TABLE IF EXISTS android_sysui_cuj_main_thread_state_vt;
-CREATE VIRTUAL TABLE android_sysui_cuj_main_thread_state_vt
-USING span_left_join(android_sysui_cuj_frame_main_thread_bounds, android_sysui_cuj_main_thread_state_data PARTITIONED utid);
-
-DROP TABLE IF EXISTS android_sysui_cuj_main_thread_state;
-CREATE TABLE android_sysui_cuj_main_thread_state AS
-  SELECT
-    frame_number,
-    state,
-    io_wait AS io_wait,
-    SUM(dur) AS dur
-  FROM android_sysui_cuj_main_thread_state_vt
-  GROUP BY frame_number, state, io_wait
-  HAVING dur > 0;
-
-DROP VIEW IF EXISTS android_sysui_cuj_frame_render_thread_bounds;
-CREATE VIEW android_sysui_cuj_frame_render_thread_bounds AS
-SELECT frame_number, ts_render_thread_start as ts, dur_render_thread as dur
-FROM android_sysui_cuj_missed_frames
-WHERE app_missed;
-
-DROP VIEW IF EXISTS android_sysui_cuj_render_thread_state_data;
-CREATE VIEW android_sysui_cuj_render_thread_state_data AS
-SELECT * FROM thread_state
-WHERE utid in (SELECT utid FROM android_sysui_cuj_render_thread);
-
-DROP TABLE IF EXISTS android_sysui_cuj_render_thread_state_vt;
-CREATE VIRTUAL TABLE android_sysui_cuj_render_thread_state_vt
-USING span_left_join(android_sysui_cuj_frame_render_thread_bounds, android_sysui_cuj_render_thread_state_data PARTITIONED utid);
-
-DROP TABLE IF EXISTS android_sysui_cuj_render_thread_state;
-CREATE TABLE android_sysui_cuj_render_thread_state AS
-  SELECT
-    frame_number,
-    state,
-    io_wait AS io_wait,
-    SUM(dur) AS dur
-  FROM android_sysui_cuj_render_thread_state_vt
-  GROUP BY frame_number, state, io_wait
-  HAVING dur > 0;
-
-DROP TABLE IF EXISTS android_sysui_cuj_main_thread_binder;
-CREATE TABLE android_sysui_cuj_main_thread_binder AS
-  SELECT
-    f.frame_number,
-    SUM(mts.dur) AS dur,
-    COUNT(*) AS call_count
-  FROM android_sysui_cuj_missed_frames f
-  JOIN android_sysui_cuj_main_thread_slices_in_cuj mts
-    ON mts.ts >= f.ts_main_thread_start AND mts.ts < f.ts_main_thread_end
-  WHERE mts.name = 'binder transaction'
-  AND f.app_missed
-  GROUP BY f.frame_number;
-
-DROP TABLE IF EXISTS android_sysui_cuj_sf_jank_causes;
-CREATE TABLE android_sysui_cuj_sf_jank_causes AS
-  WITH RECURSIVE split_jank_type(frame_number, jank_cause, remainder) AS (
-    SELECT f.frame_number, "", fte.jank_type || ","
-    FROM android_sysui_cuj_frames f
-    JOIN android_sysui_cuj_frame_timeline_events fte ON f.vsync = fte.vsync
-    UNION ALL SELECT
-    frame_number,
-    STR_SPLIT(remainder, ",", 0) AS jank_cause,
-    TRIM(SUBSTR(remainder, INSTR(remainder, ",") + 1)) AS remainder
-    FROM split_jank_type
-    WHERE remainder <> "")
-  SELECT frame_number, jank_cause
-  FROM split_jank_type
-  WHERE jank_cause NOT IN ('', 'App Deadline Missed', 'None', 'Buffer Stuffing')
-  ORDER BY frame_number ASC;
-
-DROP TABLE IF EXISTS android_sysui_cuj_missed_frames_hwui_times;
-CREATE TABLE android_sysui_cuj_missed_frames_hwui_times AS
-SELECT
-  *,
-  ts_main_thread_start AS ts,
-  ts_render_thread_end - ts_main_thread_start AS dur
-FROM android_sysui_cuj_missed_frames;
-
-DROP TABLE IF EXISTS android_sysui_cuj_missed_frames_render_thread_times;
-CREATE TABLE android_sysui_cuj_missed_frames_render_thread_times AS
-SELECT
-  *,
-  ts_render_thread_start AS ts,
-  dur_render_thread AS dur
-FROM android_sysui_cuj_missed_frames;
-
-DROP TABLE IF EXISTS android_sysui_cuj_jit_slices_join_table;
-CREATE VIRTUAL TABLE android_sysui_cuj_jit_slices_join_table
-USING span_join(android_sysui_cuj_missed_frames_hwui_times partitioned frame_number, android_sysui_cuj_jit_slices);
-
-DROP TABLE IF EXISTS android_sysui_cuj_jank_causes;
-CREATE TABLE android_sysui_cuj_jank_causes AS
-  SELECT
-  frame_number,
-  'RenderThread - long shader_compile' AS jank_cause
-  FROM android_sysui_cuj_missed_frames f
-  JOIN android_sysui_cuj_render_thread_slices_in_cuj rts
-    ON rts.ts >= f.ts_render_thread_start AND rts.ts < f.ts_render_thread_end
-  WHERE rts.name = 'shader_compile'
-  AND f.app_missed
-  AND rts.dur > 8e6
-
-  UNION ALL
-  SELECT
-  frame_number,
-  'RenderThread - long flush layers' AS jank_cause
-  FROM android_sysui_cuj_missed_frames f
-  JOIN android_sysui_cuj_render_thread_slices_in_cuj rts
-    ON rts.ts >= f.ts_render_thread_start AND rts.ts < f.ts_render_thread_end
-  WHERE rts.name = 'flush layers'
-  AND rts.dur > 8e6
-  AND f.app_missed
-
-  UNION ALL
-  SELECT
-  frame_number,
-  'MainThread - IO wait time' AS jank_cause
-  FROM android_sysui_cuj_main_thread_state
-  WHERE
-    ((state = 'D' OR state = 'DK') AND io_wait)
-    OR (state = 'DK' AND io_wait IS NULL)
-  GROUP BY frame_number
-  HAVING SUM(dur) > 8e6
-
-  UNION ALL
-  SELECT
-  frame_number,
-  'MainThread - scheduler' AS jank_cause
-  FROM android_sysui_cuj_main_thread_state
-  WHERE (state = 'R' OR state = 'R+')
-  GROUP BY frame_number
-  HAVING SUM(dur) > 8e6
-  AND SUM(dur) > (
-    SELECT 0.4 * dur_main_thread
-    FROM android_sysui_cuj_frames fs
-    WHERE fs.frame_number = android_sysui_cuj_main_thread_state.frame_number)
-
-  UNION ALL
-  SELECT
-  frame_number,
-  'RenderThread - IO wait time' AS jank_cause
-  FROM android_sysui_cuj_render_thread_state
-  WHERE
-    ((state = 'D' OR state = 'DK') AND io_wait)
-    OR (state = 'DK' AND io_wait IS NULL)
-  GROUP BY frame_number
-  HAVING SUM(dur) > 8e6
-
-  UNION ALL
-  SELECT
-  frame_number,
-  'RenderThread - scheduler' AS jank_cause
-  FROM android_sysui_cuj_render_thread_state
-  WHERE (state = 'R' OR state = 'R+')
-  GROUP BY frame_number
-  HAVING SUM(dur) > 8e6
-  AND SUM(dur) > (
-    SELECT 0.4 * dur_render_thread
-    FROM android_sysui_cuj_frames fs
-    WHERE fs.frame_number = android_sysui_cuj_render_thread_state.frame_number)
-
-  UNION ALL
-  SELECT
-  frame_number,
-  'MainThread - binder transaction time' AS jank_cause
-  FROM android_sysui_cuj_main_thread_binder
-  WHERE dur > 8e6
-
-  UNION ALL
-  SELECT
-  frame_number,
-  'MainThread - binder calls count' AS jank_cause
-  FROM android_sysui_cuj_main_thread_binder
-  WHERE call_count > 8
-
-  UNION ALL
-  SELECT
-  frame_number,
-  'GPU completion - long completion time' AS jank_cause
-  FROM android_sysui_cuj_missed_frames f
-  WHERE dur_gcs > 8e6
-  AND app_missed
-
-  UNION ALL
-  SELECT
-  frame_number,
-  'Long running time' as jank_cause
-  FROM android_sysui_cuj_main_thread_state mts
-  JOIN android_sysui_cuj_render_thread_state rts USING(frame_number)
-  WHERE
-    mts.state = 'Running'
-    AND rts.state = 'Running'
-    AND mts.dur + rts.dur > 15e6
-
-  UNION ALL
-  SELECT
-  f.frame_number,
-  'JIT compiling' as jank_cause
-  FROM android_sysui_cuj_missed_frames f
-  JOIN android_sysui_cuj_jit_slices_join_table jit USING (frame_number)
-  WHERE f.app_missed
-  GROUP BY f.frame_number
-  HAVING SUM(jit.dur) > 8e6
-
-  UNION ALL
-  SELECT frame_number, jank_cause FROM android_sysui_cuj_sf_jank_causes
-  GROUP BY frame_number, jank_cause;
-
--- TODO(b/175098682): Switch to use async slices
-DROP VIEW IF EXISTS android_sysui_cuj_event;
-CREATE VIEW android_sysui_cuj_event AS
- SELECT
-    'slice' as track_type,
-    (SELECT cuj_name FROM android_sysui_cuj_last_cuj)
-        || ' - jank cause' as track_name,
-    f.ts_main_thread_start as ts,
-    f.dur_main_thread as dur,
-    group_concat(jc.jank_cause) as slice_name
-FROM android_sysui_cuj_frames f
-JOIN android_sysui_cuj_jank_causes jc USING (frame_number)
-GROUP BY track_type, track_name, ts, dur;
-
-DROP VIEW IF EXISTS android_sysui_cuj_output;
-CREATE VIEW android_sysui_cuj_output AS
-SELECT
-  AndroidSysUiCujMetrics(
-      'cuj_name', cuj_name,
-      'cuj_start', ts_start,
-      'cuj_dur', dur,
-      'process', process_metadata,
-      'frames',
-       (SELECT RepeatedField(
-         AndroidSysUiCujMetrics_Frame(
-           'number', f.frame_number,
-           'vsync', f.vsync,
-           'ts', f.ts_main_thread_start,
-           'dur', f.dur_frame,
-           'jank_cause',
-              (SELECT RepeatedField(jc.jank_cause)
-              FROM android_sysui_cuj_jank_causes jc WHERE jc.frame_number = f.frame_number)))
-       FROM android_sysui_cuj_frames f
-       ORDER BY frame_number ASC))
-  FROM android_sysui_cuj_last_cuj;
diff --git a/src/trace_processor/metrics/sql/android/android_sysui_cuj_jank_query.sql b/src/trace_processor/metrics/sql/android/android_sysui_cuj_jank_query.sql
deleted file mode 100644
index 1a4e691..0000000
--- a/src/trace_processor/metrics/sql/android/android_sysui_cuj_jank_query.sql
+++ /dev/null
@@ -1,50 +0,0 @@
---
--- Copyright 2021 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.
-
-DROP VIEW IF EXISTS {{table_name_prefix}}_relevant_slices_in_cuj;
-CREATE VIEW {{table_name_prefix}}_relevant_slices_in_cuj AS
-SELECT slice.ts, slice.dur FROM {{relevant_slices_table}} slice
-JOIN android_sysui_cuj_ts_boundaries boundaries
-ON slice.ts + slice.dur >= boundaries.ts AND slice.ts <= boundaries.ts_end;
-
-DROP TABLE IF EXISTS {{table_name_prefix}}_cuj_join_table;
-CREATE VIRTUAL TABLE {{table_name_prefix}}_cuj_join_table
-USING span_join(
-  android_sysui_cuj_ts_boundaries,
-  {{table_name_prefix}}_relevant_slices_in_cuj);
-
-DROP TABLE IF EXISTS {{table_name_prefix}}_frame_join_table;
-CREATE VIRTUAL TABLE {{table_name_prefix}}_frame_join_table
-USING span_join(
-  android_sysui_cuj_missed_frames_hwui_times partitioned frame_number,
-  {{table_name_prefix}}_relevant_slices_in_cuj);
-
-DROP VIEW IF EXISTS {{table_name_prefix}}_per_cuj_output_data;
-CREATE VIEW {{table_name_prefix}}_per_cuj_output_data AS
-SELECT SUM(dur) as dur_sum, MAX(dur) as dur_max
-FROM {{table_name_prefix}}_cuj_join_table;
-
-DROP VIEW IF EXISTS {{table_name_prefix}}_per_frame_output_data;
-CREATE VIEW {{table_name_prefix}}_per_frame_output_data AS
-SELECT
-f.frame_number,
-f.vsync,
-f.dur_frame,
-f.app_missed,
-SUM(jt.dur) as dur_sum,
-MAX(jt.dur) as dur_max
-FROM android_sysui_cuj_missed_frames f
-JOIN {{table_name_prefix}}_frame_join_table jt USING (frame_number)
-GROUP BY f.frame_number, f.vsync, f.dur_frame, f.app_missed;
diff --git a/src/trace_processor/metrics/sql/android/android_sysui_cuj_surfaceflinger.sql b/src/trace_processor/metrics/sql/android/android_sysui_cuj_surfaceflinger.sql
deleted file mode 100644
index bd72bf1..0000000
--- a/src/trace_processor/metrics/sql/android/android_sysui_cuj_surfaceflinger.sql
+++ /dev/null
@@ -1,300 +0,0 @@
--- Isolate the SurfaceFlinger process Id
-DROP TABLE IF EXISTS android_sysui_cuj_sf_process;
-CREATE TABLE android_sysui_cuj_sf_process AS
-SELECT name, upid FROM process
-WHERE process.name='/system/bin/surfaceflinger'
-LIMIT 1;
-
-DROP VIEW IF EXISTS android_sysui_cuj_sf_actual_frame_timeline_slice;
-CREATE VIEW android_sysui_cuj_sf_actual_frame_timeline_slice AS
-SELECT
-  actual.*,
-  actual.ts + actual.dur AS ts_end,
-  CAST(actual.name AS integer) AS vsync
-FROM actual_frame_timeline_slice actual JOIN android_sysui_cuj_sf_process USING (upid);
-
-DROP TABLE IF EXISTS android_sysui_cuj_surfaceflinger_main_thread;
-CREATE TABLE android_sysui_cuj_surfaceflinger_main_thread AS
-  SELECT android_sysui_cuj_sf_process.name AS process_name, thread.utid
-  FROM thread JOIN android_sysui_cuj_sf_process USING (upid)
-  WHERE thread.is_main_thread;
-
-DROP TABLE IF EXISTS android_sysui_cuj_sf_main_thread_track;
-CREATE TABLE android_sysui_cuj_sf_main_thread_track AS
-SELECT thread_track.id
-FROM thread_track
-JOIN android_sysui_cuj_surfaceflinger_main_thread thread USING (utid);
-
-DROP VIEW IF EXISTS android_sysui_cuj_surfaceflinger_gpu_completion_thread;
-CREATE VIEW android_sysui_cuj_surfaceflinger_gpu_completion_thread AS
-  SELECT android_sysui_cuj_sf_process.name AS process_name, thread.utid
-  FROM thread JOIN android_sysui_cuj_sf_process USING (upid)
-  WHERE thread.name = 'GPU completion';
-
-DROP VIEW IF EXISTS android_sysui_cuj_surfaceflinger_renderengine_thread;
-CREATE VIEW android_sysui_cuj_surfaceflinger_renderengine_thread AS
-  SELECT android_sysui_cuj_sf_process.name AS process_name, thread.utid
-  FROM thread JOIN android_sysui_cuj_sf_process USING (upid)
-  WHERE thread.name = 'RenderEngine';
-
-DROP VIEW IF EXISTS android_sysui_cuj_surfaceflinger_gpu_completion_slices;
-CREATE VIEW android_sysui_cuj_surfaceflinger_gpu_completion_slices AS
-  SELECT
-    process_name,
-    thread.utid,
-    slice.*,
-    slice.ts + slice.dur AS ts_end,
-    -- Extracts 1234 from 'waiting for GPU completion 1234'
-    CAST(STR_SPLIT(slice.name, ' ', 4) AS INTEGER) AS idx
-  FROM slice
-  JOIN thread_track ON slice.track_id = thread_track.id
-  JOIN android_sysui_cuj_surfaceflinger_gpu_completion_thread thread USING (utid)
-  WHERE slice.name GLOB 'waiting for GPU completion *'
-  AND dur > 0;
-
--- Find flows between actual frame slices from app process to surfaceflinger, allowing us to
--- correlate vsyncs.
-DROP VIEW IF EXISTS android_sysui_cuj_surfaceflinger_app_flow_vsyncs;
-CREATE VIEW android_sysui_cuj_surfaceflinger_app_flow_vsyncs AS
-SELECT
-  app_slice.name AS app_vsync,
-  app_slice.id AS app_slice_id,
-  cuj_process.process_name AS app_process,
-  sf_slice.name AS sf_vsync,
-  sf_slice.id AS sf_slice_id
-FROM android_sysui_cuj_sf_actual_frame_timeline_slice sf_slice
-JOIN directly_connected_flow(sf_slice.id) flow
-JOIN actual_frame_timeline_slice app_slice ON slice_in = app_slice.id
-JOIN android_sysui_cuj_last_cuj cuj_process ON app_slice.upid = cuj_process.upid
-GROUP BY app_vsync, sf_vsync;
-
--- Filter to those SF frames which flow from app frames that are within the app vsync boundaries of
--- the CUJ
-DROP TABLE IF EXISTS android_sysui_cuj_sf_frames_in_cuj;
-CREATE TABLE android_sysui_cuj_sf_frames_in_cuj AS
-SELECT
-  sf_frame.ts,
-  sf_frame.dur,
-  sf_frame.jank_type,
-  sf_frame.ts + sf_frame.dur AS ts_end,
-  flows.sf_vsync,
-  flows.app_vsync
--- This table contains only the frame timeline slices within the CUJ app vsync boundaries
-FROM android_sysui_cuj_frame_timeline_events app_frames
--- Find the matching SF frame via flow
-JOIN android_sysui_cuj_surfaceflinger_app_flow_vsyncs flows ON app_frames.vsync = flows.app_vsync
-JOIN android_sysui_cuj_sf_actual_frame_timeline_slice sf_frame ON sf_frame.id = flows.sf_slice_id
-GROUP BY flows.sf_vsync;
-
--- Take the min and max vsync to define the SurfaceFlinger boundaries
-DROP TABLE IF EXISTS android_sysui_cuj_sf_vsync_boundaries;
-CREATE TABLE android_sysui_cuj_sf_vsync_boundaries AS
-SELECT MIN(sf_vsync) AS vsync_min, MAX(sf_vsync) AS vsync_max
-FROM android_sysui_cuj_sf_frames_in_cuj;
-
--- Find just the commit slices, within the CUJ (by vsync)
-DROP TABLE IF EXISTS android_sysui_cuj_surfaceflinger_commit_slices_in_cuj;
-CREATE TABLE android_sysui_cuj_surfaceflinger_commit_slices_in_cuj AS
-SELECT * FROM
-  (SELECT
-    -- Extract the vsync number from name like 'commit 235991 vsyncIn 15.992ms'
-    CAST(STR_SPLIT(slice.name, ' ', 1) AS INTEGER) AS vsync,
-    CAST(CAST(STR_SPLIT(slice.name, ' ', 3) AS NUMBER) * 1e6 + slice.ts AS INTEGER) AS expected_vsync_ts,
-    slice.name,
-    slice.ts,
-    slice.dur,
-    slice.ts + slice.dur AS ts_end
-  FROM slice
-  JOIN android_sysui_cuj_sf_main_thread_track main_track ON slice.track_id = main_track.id
-  WHERE slice.dur > 0 AND slice.name GLOB 'commit *')
-JOIN android_sysui_cuj_sf_vsync_boundaries cuj_boundaries
-WHERE vsync >= cuj_boundaries.vsync_min AND vsync <= cuj_boundaries.vsync_max;
-
--- Find matching GPU completion slice idx for top-level mainthread slices
-DROP TABLE IF EXISTS android_sysui_cuj_sf_mts_to_gcs;
-CREATE TABLE android_sysui_cuj_sf_mts_to_gcs AS
-  SELECT
-    slice.ts,
-    CASE
-      WHEN fence.name GLOB 'GPU completion fence *'
-        THEN CAST(STR_SPLIT(fence.name, ' ', 3) AS INTEGER)
-      WHEN fence.name GLOB 'Trace GPU completion fence *'
-        THEN CAST(STR_SPLIT(fence.name, ' ', 4) AS INTEGER)
-      ELSE NULL
-    END AS gcs_idx
-  FROM slice
-  JOIN android_sysui_cuj_sf_main_thread_track main_track ON slice.track_id = main_track.id
-  JOIN descendant_slice(slice.id) fence ON fence.name GLOB '*GPU completion fence*'
-  WHERE (slice.name GLOB 'composite*' OR slice.name GLOB 'onMessageInvalidate*');
-
--- Find just the onMessageInvalidate slices, within the CUJ (by vsync)
-DROP VIEW IF EXISTS android_sysui_cuj_surfaceflinger_on_message_invalidate_slices_in_cuj;
-CREATE VIEW android_sysui_cuj_surfaceflinger_on_message_invalidate_slices_in_cuj AS
-  WITH on_msg AS (
-  SELECT
-    -- Extract the vsync number from name like 'onMessageInvalidate 235991 vsyncIn 15.992ms'
-    CAST(STR_SPLIT(slice.name, ' ', 1) AS INTEGER) AS vsync,
-    slice.ts,
-    slice.ts + slice.dur AS ts_end,
-    slice.dur,
-    CAST(CAST(STR_SPLIT(slice.name, ' ', 3) AS NUMBER) * 1e6 + slice.ts AS INTEGER) AS expected_vsync_ts,
-    gcs.gcs_idx
-  FROM slice
-  JOIN android_sysui_cuj_sf_main_thread_track main_track ON slice.track_id = main_track.id
-  LEFT JOIN android_sysui_cuj_sf_mts_to_gcs gcs ON slice.ts = gcs.ts
-  WHERE slice.name GLOB 'onMessageInvalidate *'
-  )
-SELECT
-    on_msg.*,
-    lag(on_msg.ts_end) OVER (ORDER BY on_msg.ts_end ASC) AS ts_prev_frame_end,
-    lead(on_msg.ts) OVER (ORDER BY on_msg.ts ASC) AS ts_next_frame_start
-FROM on_msg
-JOIN android_sysui_cuj_sf_vsync_boundaries cuj_boundaries
-ON on_msg.vsync >= cuj_boundaries.vsync_min AND on_msg.vsync <= cuj_boundaries.vsync_max;
-
--- Find just the composite slices
-DROP TABLE IF EXISTS android_sysui_cuj_surfaceflinger_composite_slices;
-CREATE TABLE android_sysui_cuj_surfaceflinger_composite_slices AS
-  SELECT
-    slice.name,
-    slice.ts,
-    slice.dur,
-    slice.ts + slice.dur AS ts_end,
-    gcs.gcs_idx
-  FROM slice
-  JOIN android_sysui_cuj_sf_main_thread_track main_track ON slice.track_id = main_track.id
-  LEFT JOIN android_sysui_cuj_sf_mts_to_gcs gcs ON slice.ts = gcs.ts
-  WHERE slice.dur > 0 AND slice.name GLOB 'composite*';
-
-DROP VIEW IF EXISTS android_sysui_cuj_surfaceflinger_commit_composite_frames_in_cuj;
-CREATE VIEW android_sysui_cuj_surfaceflinger_commit_composite_frames_in_cuj AS
-  WITH composite_to_commit AS (
-    SELECT
-      commits.vsync,
-      max(commits.ts) AS commit_ts,
-      composite.ts AS composite_ts,
-      composite.ts_end AS composite_ts_end,
-      composite.gcs_idx
-      FROM android_sysui_cuj_surfaceflinger_composite_slices composite
-      JOIN android_sysui_cuj_surfaceflinger_commit_slices_in_cuj commits ON composite.ts > commits.ts_end
-      GROUP BY composite.ts
-  ),
-  frames AS (
-    SELECT
-      commits.vsync,
-      commits.ts AS ts,
-      max(commits.ts_end, COALESCE(min(composite_ts_end), 0)) AS ts_end,
-      max(commits.ts_end, COALESCE(min(composite_ts_end), 0)) - commits.ts AS dur,
-      commits.expected_vsync_ts,
-      min(composite.gcs_idx) as gcs_idx
-    FROM android_sysui_cuj_surfaceflinger_commit_slices_in_cuj commits
-    LEFT JOIN composite_to_commit composite
-    USING(vsync)
-    GROUP BY (commits.vsync)
-  )
-  SELECT
-    *,
-    lag(ts_end) OVER (ORDER BY ts_end ASC) AS ts_prev_frame_end,
-    lead(ts) OVER (ORDER BY ts ASC) AS ts_next_frame_start
-  FROM frames;
-
--- All SF frames in the CUJ
-DROP TABLE IF EXISTS android_sysui_cuj_surfaceflinger_main_thread_frames;
-CREATE TABLE android_sysui_cuj_surfaceflinger_main_thread_frames AS
-SELECT
-  vsync,
-  ts,
-  ts_end,
-  dur,
-  expected_vsync_ts,
-  gcs_idx,
-  ts_prev_frame_end,
-  ts_next_frame_start
-FROM android_sysui_cuj_surfaceflinger_on_message_invalidate_slices_in_cuj
-UNION ALL
-SELECT
-  vsync,
-  ts,
-  ts_end,
-  dur,
-  expected_vsync_ts,
-  gcs_idx,
-  ts_prev_frame_end,
-  ts_next_frame_start
-FROM android_sysui_cuj_surfaceflinger_commit_composite_frames_in_cuj;
-
--- Our timestamp boundaries are the earliest ts and latest ts_end of the main thread frames (e.g.
--- onMessageInvalidate or commit/composite pair) which flow from app frames within the CUJ
--- Since main-thread 'frames' should be ordered and non-overlapping, it is sufficient to take first
--- and last, and this filters out cases where we have abnormal inner frames that have invalid
--- durations.
-DROP TABLE IF EXISTS android_sysui_cuj_sf_ts_boundaries;
-CREATE TABLE android_sysui_cuj_sf_ts_boundaries AS
-SELECT ts, ts_end - ts AS dur, ts_end
-FROM (
-  SELECT MIN(ts) AS ts, MAX(ts_end) AS ts_end
-  FROM android_sysui_cuj_surfaceflinger_main_thread_frames
-  JOIN android_sysui_cuj_sf_vsync_boundaries ON vsync == vsync_min OR vsync == vsync_max
-  );
-
-DROP TABLE IF EXISTS android_sysui_cuj_surfaceflinger_main_thread_slices_in_cuj;
-CREATE TABLE android_sysui_cuj_surfaceflinger_main_thread_slices_in_cuj AS
-  SELECT
-    slice.*,
-    slice.ts + slice.dur AS ts_end
-  FROM slice
-  JOIN android_sysui_cuj_sf_main_thread_track main_track ON slice.track_id = main_track.id
-  JOIN android_sysui_cuj_sf_ts_boundaries cuj_boundaries
-  ON slice.ts >= cuj_boundaries.ts AND slice.ts <= cuj_boundaries.ts_end
-  WHERE slice.dur > 0;
-
--- Find SurfaceFlinger GPU completions that are within the CUJ
-DROP TABLE IF EXISTS android_sysui_cuj_surfaceflinger_gpu_completion_slices_in_cuj;
-CREATE TABLE android_sysui_cuj_surfaceflinger_gpu_completion_slices_in_cuj AS
-SELECT slice.* FROM android_sysui_cuj_surfaceflinger_gpu_completion_slices slice
-JOIN android_sysui_cuj_sf_ts_boundaries cuj_boundaries
-ON slice.ts >= cuj_boundaries.ts AND slice.ts <= cuj_boundaries.ts_end;
-
-DROP TABLE IF EXISTS android_sysui_cuj_surfaceflinger_renderengine_slices_in_cuj;
-CREATE TABLE android_sysui_cuj_surfaceflinger_renderengine_slices_in_cuj AS
-  SELECT
-    process_name,
-    thread.utid,
-    slice.*,
-    slice.ts + slice.dur AS ts_end
-  FROM slice
-  JOIN thread_track ON slice.track_id = thread_track.id
-  JOIN android_sysui_cuj_surfaceflinger_renderengine_thread thread USING (utid)
-  JOIN android_sysui_cuj_sf_ts_boundaries cuj_boundaries
-  ON slice.ts >= cuj_boundaries.ts AND slice.ts <= cuj_boundaries.ts_end
-  WHERE slice.dur > 0;
-
--- Those SurfaceFlinger Frames where we missed the deadline
--- To avoid overlap - which could result in counting janky slices more than once - we limit the
--- definition of each frame to:
---  * beginning when the shared timeline actual frame starts, or - if later -
---    when the previous main thread computation ended
---  * ending when the next main thread computation begins, but no later than the
---    shared timeline actual frame ends
-DROP TABLE IF EXISTS android_sysui_cuj_sf_missed_frames;
-CREATE TABLE android_sysui_cuj_sf_missed_frames AS
-SELECT
-  CAST(frame.name AS integer) AS frame_number,
-  CAST(frame.name AS integer) AS vsync,
-  MAX(COALESCE(mtf.ts_prev_frame_end, 0), frame.ts) AS ts,
-  MIN(COALESCE(mtf.ts_next_frame_start, frame.ts_end), frame.ts_end) AS ts_end,
-  MIN(COALESCE(mtf.ts_next_frame_start, frame.ts_end), frame.ts_end)
-    - MAX(COALESCE(mtf.ts_prev_frame_end, 0), frame.ts) AS dur,
-  -- Same as `dur` but INTEGER - needed for compatibility with downstream scripts
-  CAST((
-    MIN(COALESCE(mtf.ts_next_frame_start, frame.ts_end), frame.ts_end)
-      - MAX(COALESCE(mtf.ts_prev_frame_end, 0), frame.ts))
-    AS INTEGER) AS dur_frame,
-  gcs.ts AS gcs_ts,
-  gcs.ts_end AS gcs_ts_end,
-  gcs.dur AS gcs_dur,
-  CAST(1 AS INTEGER) AS app_missed
-FROM android_sysui_cuj_sf_actual_frame_timeline_slice frame
-JOIN android_sysui_cuj_surfaceflinger_main_thread_frames mtf ON frame.name = mtf.vsync
-LEFT JOIN android_sysui_cuj_surfaceflinger_gpu_completion_slices gcs ON gcs.idx = gcs_idx
-WHERE frame.jank_type != 'None';
diff --git a/src/trace_processor/metrics/sql/android/android_trace_quality.sql b/src/trace_processor/metrics/sql/android/android_trace_quality.sql
index a68c053..7c45510 100644
--- a/src/trace_processor/metrics/sql/android/android_trace_quality.sql
+++ b/src/trace_processor/metrics/sql/android/android_trace_quality.sql
@@ -18,8 +18,8 @@
 CREATE VIEW android_trace_quality_failures AS
 -- Check that all the sched slices are less than 1 week long.
 SELECT
-  'sched_slice_too_long' as name,
-  MAX(dur) > 1 * 7 * 24 * 60 * 60 * 1000 * 1000 * 1000 as failed
+  'sched_slice_too_long' AS name,
+  MAX(dur) > 1 * 7 * 24 * 60 * 60 * 1000 * 1000 * 1000 AS failed
 FROM sched;
 
 DROP VIEW IF EXISTS android_trace_quality_output;
@@ -28,7 +28,7 @@
   'failures', (
     SELECT RepeatedField(AndroidTraceQualityMetric_Failure(
       'name', name
-    ))
+      ))
     FROM android_trace_quality_failures
     WHERE failed
   )
diff --git a/src/trace_processor/metrics/sql/android/android_trusty_workqueues.sql b/src/trace_processor/metrics/sql/android/android_trusty_workqueues.sql
index a036c9d..254489b 100644
--- a/src/trace_processor/metrics/sql/android/android_trusty_workqueues.sql
+++ b/src/trace_processor/metrics/sql/android/android_trusty_workqueues.sql
@@ -4,12 +4,12 @@
 DROP VIEW IF EXISTS android_trusty_workqueues_event;
 CREATE VIEW android_trusty_workqueues_event AS
 SELECT
-  'slice' as track_type,
-  name as slice_name,
+  'slice' AS track_type,
+  name AS slice_name,
   ts,
   dur,
-  'Cpu ' || EXTRACT_ARG(arg_set_id, 'cpu') as track_name,
-  'Trusty Workqueues' as group_name
+  'Cpu ' || EXTRACT_ARG(arg_set_id, 'cpu') AS track_name,
+  'Trusty Workqueues' AS group_name
 FROM slice
 WHERE slice.name GLOB 'nop_work_func*';
 
diff --git a/src/trace_processor/metrics/sql/android/composer_execution.sql b/src/trace_processor/metrics/sql/android/composer_execution.sql
index 448b0f8..dd3579f 100644
--- a/src/trace_processor/metrics/sql/android/composer_execution.sql
+++ b/src/trace_processor/metrics/sql/android/composer_execution.sql
@@ -28,7 +28,7 @@
   id,
   name,
   ts AS begin_ts,
-  ts+dur AS end_ts,
+  ts + dur AS end_ts,
   dur,
   LEAD(name, 1, '') OVER (PARTITION BY track_id ORDER BY ts) AS next_name,
   LEAD(ts, 1, 0) OVER (PARTITION BY track_id ORDER BY ts) AS next_ts,
diff --git a/src/trace_processor/metrics/sql/android/cpu_info.sql b/src/trace_processor/metrics/sql/android/cpu_info.sql
index 0f2c0f0..45b2eec 100644
--- a/src/trace_processor/metrics/sql/android/cpu_info.sql
+++ b/src/trace_processor/metrics/sql/android/cpu_info.sql
@@ -18,11 +18,11 @@
 
 DROP TABLE IF EXISTS cluster_core_type;
 CREATE TABLE cluster_core_type AS
-    SELECT 0 as cluster, 'little' as core_type
-    UNION ALL
-    SELECT 1, 'big'
-    UNION ALL
-    SELECT 2, 'bigger';
+SELECT 0 AS cluster, 'little' AS core_type
+UNION ALL
+SELECT 1, 'big'
+UNION ALL
+SELECT 2, 'bigger';
 
 DROP VIEW IF EXISTS device_power_profile;
 CREATE VIEW device_power_profile AS
diff --git a/src/trace_processor/metrics/sql/android/display_metrics.sql b/src/trace_processor/metrics/sql/android/display_metrics.sql
index daec213..f4bc187 100644
--- a/src/trace_processor/metrics/sql/android/display_metrics.sql
+++ b/src/trace_processor/metrics/sql/android/display_metrics.sql
@@ -17,21 +17,21 @@
 CREATE VIEW same_frame AS
 SELECT COUNT(name) AS total_duplicate_frames
 FROM counters
-WHERE name='SAME_FRAME'
-AND value=1;
+WHERE name = 'SAME_FRAME'
+  AND value = 1;
 
 DROP VIEW IF EXISTS duplicate_frames_logged;
 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;
+WHERE name = 'SAME_FRAME' AND value = 0;
 
 DROP VIEW IF EXISTS dpu_underrun;
 CREATE VIEW dpu_underrun AS
 SELECT COUNT(name) AS total_dpu_underrun_count
 FROM counters
-WHERE name='DPU_UNDERRUN'
-AND value=1;
+WHERE name = 'DPU_UNDERRUN'
+  AND value = 1;
 
 DROP VIEW IF EXISTS non_repeated_panel_fps;
 CREATE VIEW non_repeated_panel_fps AS
@@ -61,15 +61,22 @@
 )
 WHERE dur > 0;
 
+DROP VIEW IF EXISTS update_power_state_stats;
+CREATE VIEW update_power_state_stats AS
+SELECT
+  CAST(AVG(dur) / 1e3 AS INT64) AS avg_runtime_micro_secs
+FROM slice
+WHERE slice.name = 'DisplayPowerController#updatePowerState' AND slice.dur >= 0;
+
 DROP VIEW IF EXISTS display_metrics_output;
 CREATE VIEW display_metrics_output AS
 SELECT AndroidDisplayMetrics(
   'total_duplicate_frames', (SELECT total_duplicate_frames
-                            FROM same_frame),
+    FROM same_frame),
   'duplicate_frames_logged', (SELECT logs_found
-                            FROM duplicate_frames_logged),
+    FROM duplicate_frames_logged),
   'total_dpu_underrun_count', (SELECT total_dpu_underrun_count
-                              FROM dpu_underrun),
+    FROM dpu_underrun),
   'refresh_rate_switches', (SELECT COUNT(*) FROM panel_fps_spans),
   'refresh_rate_stats', (
     SELECT RepeatedField(metric)
@@ -84,5 +91,11 @@
       GROUP BY value
       ORDER BY value
     )
+  ),
+  'update_power_state', (
+    SELECT AndroidDisplayMetrics_UpdatePowerState(
+      'avg_runtime_micro_secs', avg_runtime_micro_secs
+    )
+    FROM update_power_state_stats
   )
 );
diff --git a/src/trace_processor/metrics/sql/android/frame_missed.sql b/src/trace_processor/metrics/sql/android/frame_missed.sql
index 45499fa..e6546ba 100644
--- a/src/trace_processor/metrics/sql/android/frame_missed.sql
+++ b/src/trace_processor/metrics/sql/android/frame_missed.sql
@@ -31,4 +31,4 @@
   ts,
   dur,
   value
-FROM frame_missed_counters;
\ No newline at end of file
+FROM frame_missed_counters;
diff --git a/src/trace_processor/metrics/sql/android/global_counter_span_view.sql b/src/trace_processor/metrics/sql/android/global_counter_span_view.sql
index 0b531a6..d19d571 100644
--- a/src/trace_processor/metrics/sql/android/global_counter_span_view.sql
+++ b/src/trace_processor/metrics/sql/android/global_counter_span_view.sql
@@ -19,7 +19,7 @@
 SELECT
   ts,
   LEAD(ts, 1, (SELECT end_ts + 1 FROM trace_bounds))
-      OVER(PARTITION BY track_id ORDER BY ts) - ts AS dur,
+  OVER(PARTITION BY track_id ORDER BY ts) - ts AS dur,
   value AS {{table_name}}_val
 FROM counter c JOIN counter_track t
   ON t.id = c.track_id
diff --git a/src/trace_processor/metrics/sql/android/global_counter_span_view_merged.sql b/src/trace_processor/metrics/sql/android/global_counter_span_view_merged.sql
new file mode 100644
index 0000000..c3da378
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/global_counter_span_view_merged.sql
@@ -0,0 +1,31 @@
+--
+-- 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.
+--
+
+DROP VIEW IF EXISTS {{table_name}}_span;
+CREATE VIEW {{table_name}}_span AS
+SELECT
+  ts,
+  LEAD(ts, 1, (SELECT end_ts + 1 FROM trace_bounds))
+  OVER(ORDER BY ts) - ts AS dur,
+  CAST(value AS INT) AS {{table_name}}_val
+FROM (
+    SELECT ts, value, LAG(value) OVER (ORDER BY ts) AS lag_value
+    FROM counter c JOIN counter_track t
+      ON t.id = c.track_id
+    WHERE t.type = 'counter_track'
+      AND name = '{{counter_name}}'
+)
+WHERE value != lag_value OR lag_value IS NULL;
diff --git a/src/trace_processor/metrics/sql/android/gpu_counter_span_view.sql b/src/trace_processor/metrics/sql/android/gpu_counter_span_view.sql
index 7a40fd0..120e7d0 100644
--- a/src/trace_processor/metrics/sql/android/gpu_counter_span_view.sql
+++ b/src/trace_processor/metrics/sql/android/gpu_counter_span_view.sql
@@ -18,8 +18,8 @@
 CREATE VIEW {{table_name}}_span AS
 SELECT
   ts,
-  LEAD(ts, 1, (SELECT end_ts+1 FROM trace_bounds))
-    OVER (PARTITION BY track_id ORDER BY ts) - ts AS dur,
+  LEAD(ts, 1, (SELECT end_ts + 1 FROM trace_bounds))
+  OVER (PARTITION BY track_id ORDER BY ts) - ts AS dur,
   gpu_id,
   value AS {{table_name}}_val
 FROM counter c JOIN gpu_counter_track t
diff --git a/src/trace_processor/metrics/sql/android/jank/cujs.sql b/src/trace_processor/metrics/sql/android/jank/cujs.sql
index 42ed1f2..9c1d8cf 100644
--- a/src/trace_processor/metrics/sql/android/jank/cujs.sql
+++ b/src/trace_processor/metrics/sql/android/jank/cujs.sql
@@ -59,9 +59,9 @@
   FROM cujs
   LEFT JOIN slice cuj_state_marker
     ON cuj_state_marker.ts >= cujs.ts
-    AND cuj_state_marker.ts < cujs.ts_end
-    -- e.g. J<CUJ_NAME>#FT#end#0
-    AND cuj_state_marker.name GLOB (cujs.cuj_slice_name || "#FT#*")
+      AND cuj_state_marker.ts < cujs.ts_end
+      -- e.g. J<CUJ_NAME>#FT#end#0
+      AND cuj_state_marker.name GLOB (cujs.cuj_slice_name || "#FT#*")
 )
 SELECT
   cujs.*,
@@ -70,19 +70,19 @@
       SELECT 1
       FROM cuj_state_markers csm
       WHERE csm.cuj_id = cujs.cuj_id
-      AND csm.marker_type = 'cancel')
-    THEN 'canceled'
+        AND csm.marker_type = 'cancel')
+      THEN 'canceled'
     WHEN EXISTS (
       SELECT 1
       FROM cuj_state_markers csm
       WHERE csm.cuj_id = cujs.cuj_id
-      AND csm.marker_type = 'end')
-    THEN 'completed'
-  ELSE NULL
+        AND csm.marker_type = 'end')
+      THEN 'completed'
+    ELSE NULL
   END AS state
 FROM cujs
 WHERE
-  state <> 'canceled'
+  state != 'canceled'
   -- Older builds don't have the state markers so we allow NULL but filter out
   -- CUJs that are <4ms long - assuming CUJ was canceled in that case.
   OR (state IS NULL AND cujs.dur > 4e6)
diff --git a/src/trace_processor/metrics/sql/android/jank/cujs_boundaries.sql b/src/trace_processor/metrics/sql/android/jank/cujs_boundaries.sql
index 51734d5..01ca38e 100644
--- a/src/trace_processor/metrics/sql/android/jank/cujs_boundaries.sql
+++ b/src/trace_processor/metrics/sql/android/jank/cujs_boundaries.sql
@@ -63,22 +63,23 @@
   SELECT
     cuj_id,
     vsync,
-    e.ts as ts_expected,
+    e.ts AS ts_expected,
     -- In cases where we are drawing multiple layers, there will be  one
     -- expected frame timeline slice, but multiple actual frame timeline slices.
     -- As a simplification we just take here the min(ts) and max(ts_end) of
     -- the actual frame timeline slices.
-    MIN(a.ts) as ts_actual_min,
-    MAX(a.ts + a.dur) as ts_end_actual_max
+    MIN(a.ts) AS ts_actual_min,
+    MAX(a.ts + a.dur) AS ts_end_actual_max
   FROM android_jank_cuj_vsync_boundary vsync_boundary
   JOIN expected_timeline e
     ON e.upid = vsync_boundary.upid
-    AND e.vsync >= vsync_min
-    AND e.vsync <= vsync_max
+      AND e.vsync >= vsync_min
+      AND e.vsync <= vsync_max
   JOIN actual_frame_timeline_slice a
     ON e.upid = a.upid
-    AND e.name = a.name
-  GROUP BY cuj_id, e.vsync, e.ts),
+      AND e.name = a.name
+  GROUP BY cuj_id, e.vsync, e.ts
+),
 -- Orders do_frame slices by vsync to calculate the ts_end of the previous frame
 -- android_jank_cuj_do_frame_slice only contains frames within the CUJ so
 -- the ts_prev_do_frame_end is always missing for the very first frame
@@ -102,26 +103,26 @@
     timeline.ts_expected,
     CASE
       WHEN timeline.ts_expected IS NULL
-      THEN do_frame.ts
+        THEN do_frame.ts
       ELSE MAX(do_frame.ts_prev_do_frame_end, timeline.ts_expected)
     END AS ts
   FROM do_frame_ordered do_frame
   LEFT JOIN cuj_frame_timeline timeline
     ON timeline.cuj_id = do_frame.cuj_id
-    AND do_frame.vsync = timeline.vsync
-  -- There are a few special cases we have to handle:
-  -- *) In rare cases there is a clock drift after device suspends
-  -- This may cause the actual/expected timeline to be misaligned with the rest
-  -- of the trace for a short period.
-  -- Do not use the timelines if it seems that this happened.
-  -- *) Actual timeline start time might also be reported slightly after doFrame
-  -- starts. We allow it to start up to 1ms later.
-  -- *) If the frame is significantly (~100s of ms) over the deadline,
-  -- expected timeline data will be dropped in SF and never recorded. In that case
-  -- the actual timeline will only report the end ts correctly. If this happens
-  -- fall back to calculating the boundaries based on doFrame slices. Ideally we
-  -- would prefer to infer the intended start time of the frame instead.
-  AND do_frame.ts >= timeline.ts_actual_min - 1e6 AND do_frame.ts <= timeline.ts_end_actual_max
+      AND do_frame.vsync = timeline.vsync
+      -- There are a few special cases we have to handle:
+      -- *) In rare cases there is a clock drift after device suspends
+      -- This may cause the actual/expected timeline to be misaligned with the rest
+      -- of the trace for a short period.
+      -- Do not use the timelines if it seems that this happened.
+      -- *) Actual timeline start time might also be reported slightly after doFrame
+      -- starts. We allow it to start up to 1ms later.
+      -- *) If the frame is significantly (~100s of ms) over the deadline,
+      -- expected timeline data will be dropped in SF and never recorded. In that case
+      -- the actual timeline will only report the end ts correctly. If this happens
+      -- fall back to calculating the boundaries based on doFrame slices. Ideally we
+      -- would prefer to infer the intended start time of the frame instead.
+      AND do_frame.ts >= timeline.ts_actual_min - 1e6 AND do_frame.ts <= timeline.ts_end_actual_max
 )
 SELECT
   *,
@@ -179,7 +180,8 @@
   JOIN android_jank_cuj_do_frame_slice do_frame USING (cuj_id, vsync)
   JOIN descendant_slice(do_frame.id) post_and_wait
   WHERE post_and_wait.name = 'postAndWait'
-  GROUP BY draw_frame.cuj_id, draw_frame.utid, draw_frame.vsync)
+  GROUP BY draw_frame.cuj_id, draw_frame.utid, draw_frame.vsync
+)
 SELECT
   *,
   ts_end - ts AS dur
@@ -209,7 +211,7 @@
     main_thread_boundary.ts,
     CASE
       WHEN timeline_slice.ts IS NOT NULL
-      THEN MAX(timeline_slice.ts + timeline_slice.dur)
+        THEN MAX(timeline_slice.ts + timeline_slice.dur)
       ELSE (
         SELECT MAX(MAX(ts_end), cuj.ts_end)
         FROM android_jank_cuj_do_frame_slice do_frame
@@ -220,12 +222,13 @@
   JOIN android_jank_cuj_vsync_boundary USING (cuj_id)
   LEFT JOIN actual_frame_timeline_slice timeline_slice
     ON cuj.upid = timeline_slice.upid
-    -- Timeline slices for this exact VSYNC might be missing (e.g. if the last
-    -- doFrame did not actually produce anything to draw).
-    -- In that case we compute the boundary based on the last doFrame and the
-    -- CUJ markers.
-    AND vsync_max = CAST(timeline_slice.name AS INTEGER)
-  GROUP BY cuj_id, cuj.upid, main_thread_boundary.ts)
+      -- Timeline slices for this exact VSYNC might be missing (e.g. if the last
+      -- doFrame did not actually produce anything to draw).
+      -- In that case we compute the boundary based on the last doFrame and the
+      -- CUJ markers.
+      AND vsync_max = CAST(timeline_slice.name AS INTEGER)
+  GROUP BY cuj_id, cuj.upid, main_thread_boundary.ts
+)
 SELECT
   *,
   ts_end - ts AS dur
diff --git a/src/trace_processor/metrics/sql/android/jank/frames.sql b/src/trace_processor/metrics/sql/android/jank/frames.sql
index 98ded0c..86a9a46 100644
--- a/src/trace_processor/metrics/sql/android/jank/frames.sql
+++ b/src/trace_processor/metrics/sql/android/jank/frames.sql
@@ -45,8 +45,8 @@
 FROM android_jank_cuj_vsync_boundary boundary
 JOIN actual_timeline_with_vsync timeline
   ON boundary.upid = timeline.upid
-  AND vsync >= vsync_min
-  AND vsync <= vsync_max
+    AND vsync >= vsync_min
+    AND vsync <= vsync_max
 LEFT JOIN expected_frame_timeline_slice expected
   ON expected.upid = timeline.upid AND expected.name = timeline.name
 GROUP BY cuj_id, vsync;
@@ -114,7 +114,7 @@
 JOIN android_jank_cuj_sf_process sf_process
 JOIN actual_frame_timeline_slice actual_timeline
   ON actual_timeline.upid = sf_process.upid
-  AND boundary.vsync = CAST(actual_timeline.name AS INTEGER)
+    AND boundary.vsync = CAST(actual_timeline.name AS INTEGER)
 LEFT JOIN expected_frame_timeline_slice expected_timeline
   ON expected_timeline.upid = actual_timeline.upid
-  AND expected_timeline.name = actual_timeline.name;
+    AND expected_timeline.name = actual_timeline.name;
diff --git a/src/trace_processor/metrics/sql/android/jank/internal/counters.sql b/src/trace_processor/metrics/sql/android/jank/internal/counters.sql
index 84d6615..fdcc04c 100644
--- a/src/trace_processor/metrics/sql/android/jank/internal/counters.sql
+++ b/src/trace_processor/metrics/sql/android/jank/internal/counters.sql
@@ -22,7 +22,7 @@
     -- extract the CUJ name inside <>
     STR_SPLIT(STR_SPLIT(track.name, '>#', 0), '<', 1) AS cuj_name,
     -- take the name of the counter after #
-    STR_SPLIT(track.name, '#', 1) as counter_name
+    STR_SPLIT(track.name, '#', 1) AS counter_name
   FROM process_counter_track track
   JOIN android_jank_cuj USING (upid)
   WHERE track.name GLOB 'J<*>#*'
@@ -32,7 +32,7 @@
   upid,
   cuj_name,
   counter_name,
-  CAST(value AS INTEGER) as value
+  CAST(value AS INTEGER) AS value
 FROM counter
 JOIN cuj_counter_track ON counter.track_id = cuj_counter_track.track_id;
 
@@ -58,7 +58,17 @@
 -- CUJs happened in a short succession.
 WITH cujs_ordered AS (
   SELECT
-    *,
+    cuj_id,
+    cuj_name,
+    upid,
+    state,
+    ts_end,
+    CASE
+      WHEN process_name GLOB 'com.android.*' THEN ts_end
+      WHEN process_name = 'com.google.android.apps.nexuslauncher' THEN ts_end
+      -- Some processes publish counters just before logging the CUJ end
+      ELSE MAX(ts, ts_end - 4000000)
+    END AS ts_earliest_allowed_counter,
     LEAD(ts_end) OVER (PARTITION BY cuj_name ORDER BY ts_end ASC) AS ts_end_next_cuj
   FROM android_jank_cuj
 )
@@ -67,11 +77,11 @@
   cuj_name,
   upid,
   state,
-  ANDROID_JANK_CUJ_COUNTER_VALUE(cuj_name, 'totalFrames', ts_end, ts_end_next_cuj) AS total_frames,
-  ANDROID_JANK_CUJ_COUNTER_VALUE(cuj_name, 'missedFrames', ts_end, ts_end_next_cuj) AS missed_frames,
-  ANDROID_JANK_CUJ_COUNTER_VALUE(cuj_name, 'missedAppFrames', ts_end, ts_end_next_cuj) AS missed_app_frames,
-  ANDROID_JANK_CUJ_COUNTER_VALUE(cuj_name, 'missedSfFrames', ts_end, ts_end_next_cuj) AS missed_sf_frames,
-  ANDROID_JANK_CUJ_COUNTER_VALUE(cuj_name, 'maxSuccessiveMissedFrames', ts_end, ts_end_next_cuj) AS missed_frames_max_successive,
+  ANDROID_JANK_CUJ_COUNTER_VALUE(cuj_name, 'totalFrames', ts_earliest_allowed_counter, ts_end_next_cuj) AS total_frames,
+  ANDROID_JANK_CUJ_COUNTER_VALUE(cuj_name, 'missedFrames', ts_earliest_allowed_counter, ts_end_next_cuj) AS missed_frames,
+  ANDROID_JANK_CUJ_COUNTER_VALUE(cuj_name, 'missedAppFrames', ts_earliest_allowed_counter, ts_end_next_cuj) AS missed_app_frames,
+  ANDROID_JANK_CUJ_COUNTER_VALUE(cuj_name, 'missedSfFrames', ts_earliest_allowed_counter, ts_end_next_cuj) AS missed_sf_frames,
+  ANDROID_JANK_CUJ_COUNTER_VALUE(cuj_name, 'maxSuccessiveMissedFrames', ts_earliest_allowed_counter, ts_end_next_cuj) AS missed_frames_max_successive,
   -- convert ms to nanos to align with the unit for `dur` in the other tables
-  ANDROID_JANK_CUJ_COUNTER_VALUE(cuj_name, 'maxFrameTimeMillis', ts_end, ts_end_next_cuj) * 1000000 AS frame_dur_max
+  ANDROID_JANK_CUJ_COUNTER_VALUE(cuj_name, 'maxFrameTimeMillis', ts_earliest_allowed_counter, ts_end_next_cuj) * 1000000 AS frame_dur_max
 FROM cujs_ordered cuj;
diff --git a/src/trace_processor/metrics/sql/android/jank/internal/derived_events.sql b/src/trace_processor/metrics/sql/android/jank/internal/derived_events.sql
index 8b2e8a7..822bbc6 100644
--- a/src/trace_processor/metrics/sql/android/jank/internal/derived_events.sql
+++ b/src/trace_processor/metrics/sql/android/jank/internal/derived_events.sql
@@ -22,7 +22,7 @@
   cuj.cuj_name AS track_name,
   boundary.ts,
   boundary.dur,
-  cuj.cuj_name || ' (adjusted, id=' || cuj_id || ') '  AS slice_name,
+  cuj.cuj_name || ' (adjusted, id=' || cuj_id || ') ' AS slice_name,
   'CUJ Boundaries' AS group_name
 FROM android_jank_cuj cuj
 JOIN android_jank_cuj_boundary boundary USING (cuj_id)
diff --git a/src/trace_processor/metrics/sql/android/jank/internal/query_base.sql b/src/trace_processor/metrics/sql/android/jank/internal/query_base.sql
index 8779acc..b205f79 100644
--- a/src/trace_processor/metrics/sql/android/jank/internal/query_base.sql
+++ b/src/trace_processor/metrics/sql/android/jank/internal/query_base.sql
@@ -18,18 +18,18 @@
 -- Used to simplify passing arguments to other functions / metrics.
 DROP TABLE IF EXISTS android_jank_cuj_table_set;
 CREATE TABLE android_jank_cuj_table_set(
-    name TEXT,
-    slice_table_name TEXT,
-    frame_boundary_table_name TEXT,
-    cuj_boundary_table_name TEXT,
-    frame_table_name TEXT);
+  name TEXT,
+  slice_table_name TEXT,
+  frame_boundary_table_name TEXT,
+  cuj_boundary_table_name TEXT,
+  frame_table_name TEXT);
 
 INSERT INTO android_jank_cuj_table_set(
-    name,
-    slice_table_name,
-    frame_boundary_table_name,
-    cuj_boundary_table_name,
-    frame_table_name)
+  name,
+  slice_table_name,
+  frame_boundary_table_name,
+  cuj_boundary_table_name,
+  frame_table_name)
 VALUES
 ('App threads',
   'android_jank_cuj_slice',
@@ -65,27 +65,27 @@
 -- Functions below retrieve specific columns for a given table set.
 
 SELECT CREATE_FUNCTION(
-   'ANDROID_JANK_CUJ_TABLE_SET_SLICE(table_set STRING)',
-   'STRING',
-   'SELECT slice_table_name FROM android_jank_cuj_table_set ts WHERE ts.name = $table_set'
+  'ANDROID_JANK_CUJ_TABLE_SET_SLICE(table_set STRING)',
+  'STRING',
+  'SELECT slice_table_name FROM android_jank_cuj_table_set ts WHERE ts.name = $table_set'
 );
 
 SELECT CREATE_FUNCTION(
-   'ANDROID_JANK_CUJ_TABLE_SET_FRAME_BOUNDARY(table_set STRING)',
-   'STRING',
-   'SELECT frame_boundary_table_name FROM android_jank_cuj_table_set ts WHERE ts.name = $table_set'
+  'ANDROID_JANK_CUJ_TABLE_SET_FRAME_BOUNDARY(table_set STRING)',
+  'STRING',
+  'SELECT frame_boundary_table_name FROM android_jank_cuj_table_set ts WHERE ts.name = $table_set'
 );
 
 SELECT CREATE_FUNCTION(
-   'ANDROID_JANK_CUJ_TABLE_SET_CUJ_BOUNDARY(table_set STRING)',
-   'STRING',
-   'SELECT cuj_boundary_table_name FROM android_jank_cuj_table_set ts WHERE ts.name = $table_set'
+  'ANDROID_JANK_CUJ_TABLE_SET_CUJ_BOUNDARY(table_set STRING)',
+  'STRING',
+  'SELECT cuj_boundary_table_name FROM android_jank_cuj_table_set ts WHERE ts.name = $table_set'
 );
 
 SELECT CREATE_FUNCTION(
-   'ANDROID_JANK_CUJ_TABLE_SET_FRAME(table_set STRING)',
-   'STRING',
-   'SELECT frame_table_name FROM android_jank_cuj_table_set ts WHERE ts.name = $table_set'
+  'ANDROID_JANK_CUJ_TABLE_SET_FRAME(table_set STRING)',
+  'STRING',
+  'SELECT frame_table_name FROM android_jank_cuj_table_set ts WHERE ts.name = $table_set'
 );
 
 -- Checks if two slices, described by ts and dur, ts_second and dur_second, overlap.
diff --git a/src/trace_processor/metrics/sql/android/jank/internal/query_frame_slice.sql b/src/trace_processor/metrics/sql/android/jank/internal/query_frame_slice.sql
index 6ba8270..0b50e4a 100644
--- a/src/trace_processor/metrics/sql/android/jank/internal/query_frame_slice.sql
+++ b/src/trace_processor/metrics/sql/android/jank/internal/query_frame_slice.sql
@@ -63,6 +63,7 @@
 CREATE TABLE {{table_name_prefix}}_query_slice AS
 SELECT
   slice.cuj_id,
+  slice.utid,
   slice.id,
   slice.name,
   slice.ts,
@@ -78,6 +79,7 @@
 SELECT
   frame.*,
   query_slice.id AS slice_id,
+  query_slice.utid AS slice_utid,
   query_slice.name AS slice_name,
   MAX(query_slice.ts, frame_boundary.ts) AS slice_ts,
   MIN(query_slice.ts_end, frame_boundary.ts_end) AS slice_ts_end,
@@ -88,7 +90,7 @@
 JOIN {{frame_boundary_table_name}} frame_boundary USING (cuj_id, vsync)
 JOIN {{table_name_prefix}}_query_slice query_slice
   ON frame_boundary.cuj_id = query_slice.cuj_id
-  AND ANDROID_JANK_CUJ_SLICE_OVERLAPS(frame_boundary.ts, frame_boundary.dur, query_slice.ts, query_slice.dur);
+    AND ANDROID_JANK_CUJ_SLICE_OVERLAPS(frame_boundary.ts, frame_boundary.dur, query_slice.ts, query_slice.dur);
 
 -- Aggregated view of frames and slices overall durations within each frame boundaries.
 DROP VIEW IF EXISTS {{table_name_prefix}}_slice_in_frame_agg;
diff --git a/src/trace_processor/metrics/sql/android/jank/params.sql b/src/trace_processor/metrics/sql/android/jank/params.sql
new file mode 100644
index 0000000..c1a295c
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/jank/params.sql
@@ -0,0 +1,18 @@
+-- Table to store parameters that will be matched with CUJs using the CUJ name.
+DROP TABLE IF EXISTS android_jank_cuj_param_set;
+CREATE TABLE android_jank_cuj_param_set (cuj_name_glob STRING, main_thread_override STRING);
+INSERT INTO android_jank_cuj_param_set (cuj_name_glob, main_thread_override)
+VALUES
+('SPLASHSCREEN_EXIT_ANIM', 'll.splashscreen'),
+('SPLASHSCREEN_AVD', 'll.splashscreen'),
+('ONE_HANDED_ENTER_TRANSITION::*', 'wmshell.main'),
+('ONE_HANDED_EXIT_TRANSITION::*', 'wmshell.main'),
+('PIP_TRANSITION::*', 'wmshell.main');
+
+
+-- Matches each CUJ with the right set of parameters.
+DROP TABLE IF EXISTS android_jank_cuj_param;
+CREATE TABLE android_jank_cuj_param AS
+SELECT cuj_id, main_thread_override
+FROM android_jank_cuj
+LEFT JOIN android_jank_cuj_param_set ON cuj_name GLOB cuj_name_glob;
diff --git a/src/trace_processor/metrics/sql/android/jank/query_functions.sql b/src/trace_processor/metrics/sql/android/jank/query_functions.sql
index e2fe019..e96b587 100644
--- a/src/trace_processor/metrics/sql/android/jank/query_functions.sql
+++ b/src/trace_processor/metrics/sql/android/jank/query_functions.sql
@@ -42,9 +42,9 @@
 -- table_name_prefix - Running the function will create multiple tables. This value will be used
 --                     as a prefx for their names to avoid name collisions with other tables.
 SELECT CREATE_FUNCTION(
-   'ANDROID_JANK_CORRELATE_FRAME_SLICE_IMPL(table_set STRING, relevant_slice_table_name STRING, table_name_prefix STRING)',
-   'STRING',
-   '
+  'ANDROID_JANK_CORRELATE_FRAME_SLICE_IMPL(table_set STRING, relevant_slice_table_name STRING, table_name_prefix STRING)',
+  'STRING',
+  '
       SELECT COALESCE( -- COALESCE to return the text with table names to the caller instead of NULL
           RUN_METRIC(
             "android/jank/internal/query_frame_slice.sql",
@@ -61,7 +61,7 @@
 -- Provides a default value for table_name_prefix in ANDROID_JANK_CORRELATE_FRAME_SLICE_IMPL.
 -- See documentation for ANDROID_JANK_CORRELATE_FRAME_SLICE_IMPL.
 SELECT CREATE_FUNCTION(
-   'ANDROID_JANK_CORRELATE_FRAME_SLICE(table_set STRING, relevant_slice_table_name STRING)',
-   'STRING',
-   'SELECT ANDROID_JANK_CORRELATE_FRAME_SLICE_IMPL($table_set, $relevant_slice_table_name, "jank_query")'
-);
\ No newline at end of file
+  'ANDROID_JANK_CORRELATE_FRAME_SLICE(table_set STRING, relevant_slice_table_name STRING)',
+  'STRING',
+  'SELECT ANDROID_JANK_CORRELATE_FRAME_SLICE_IMPL($table_set, $relevant_slice_table_name, "jank_query")'
+);
diff --git a/src/trace_processor/metrics/sql/android/jank/relevant_slices.sql b/src/trace_processor/metrics/sql/android/jank/relevant_slices.sql
index 9a33b45..b779d3c 100644
--- a/src/trace_processor/metrics/sql/android/jank/relevant_slices.sql
+++ b/src/trace_processor/metrics/sql/android/jank/relevant_slices.sql
@@ -37,6 +37,10 @@
       THEN
         CAST(STR_SPLIT($slice_name, " ", 4) AS INTEGER)
       WHEN
+        $slice_name GLOB "Trace HWC release fence *"
+      THEN
+        CAST(STR_SPLIT($slice_name, " ", 4) AS INTEGER)
+      WHEN
         $slice_name GLOB "waiting for HWC release *"
       THEN
         CAST(STR_SPLIT($slice_name, " ", 4) AS INTEGER)
@@ -62,7 +66,7 @@
   ON slice.ts + slice.dur >= cuj.ts AND slice.ts <= cuj.ts_end
 JOIN android_jank_cuj_main_thread main_thread
   ON cuj.cuj_id = main_thread.cuj_id
-  AND main_thread.track_id = slice.track_id
+    AND main_thread.track_id = slice.track_id
 WHERE
   slice.name GLOB 'Choreographer#doFrame*'
   AND slice.dur > 0;
@@ -85,8 +89,8 @@
 JOIN slice
   ON slice.track_id = render_thread.track_id
 WHERE slice.name GLOB 'DrawFrame*'
-AND VSYNC_FROM_NAME(slice.name) = do_frame.vsync
-AND slice.dur > 0;
+  AND VSYNC_FROM_NAME(slice.name) = do_frame.vsync
+  AND slice.dur > 0;
 
 -- Find descendants of DrawFrames which contain the GPU completion fence ID that
 -- is used for signaling that the GPU finished drawing.
@@ -101,23 +105,17 @@
 JOIN descendant_slice(draw_frame.id) fence
   ON fence.name GLOB '*GPU completion fence*';
 
--- Find GPU completion slices which indicate when the GPU finished drawing.
-DROP TABLE IF EXISTS android_jank_cuj_gpu_completion_slice;
-CREATE TABLE android_jank_cuj_gpu_completion_slice AS
+-- Similarly find descendants of DrawFrames which have the HWC release fence ID
+DROP TABLE IF EXISTS android_jank_cuj_hwc_release_fence;
+CREATE TABLE android_jank_cuj_hwc_release_fence AS
 SELECT
-  fence.cuj_id,
+  cuj_id,
   vsync,
-  slice.*,
-  slice.ts + slice.dur AS ts_end,
-  fence.fence_idx
-FROM android_jank_cuj_gpu_completion_thread gpu_completion_thread
-JOIN slice USING (track_id)
-JOIN android_jank_cuj_gpu_completion_fence fence
-  ON fence.cuj_id = gpu_completion_thread.cuj_id
-  AND fence.fence_idx = GPU_COMPLETION_FENCE_ID_FROM_NAME(slice.name)
-WHERE
-  slice.name GLOB 'waiting for GPU completion *'
-  AND slice.dur > 0;
+  draw_frame.id AS draw_frame_slice_id,
+  GPU_COMPLETION_FENCE_ID_FROM_NAME(fence.name) AS fence_idx
+FROM android_jank_cuj_draw_frame_slice draw_frame
+JOIN descendant_slice(draw_frame.id) fence
+  ON fence.name GLOB '*HWC release fence *';
 
 -- Find HWC release slices which indicate when the HWC released the buffer.
 DROP TABLE IF EXISTS android_jank_cuj_hwc_release_slice;
@@ -127,16 +125,38 @@
   vsync,
   slice.*,
   slice.ts + slice.dur AS ts_end,
-  fence.fence_idx
+  fence.fence_idx,
+  draw_frame_slice_id
 FROM android_jank_cuj_hwc_release_thread hwc_release_thread
 JOIN slice USING (track_id)
-JOIN android_jank_cuj_gpu_completion_fence fence
+JOIN android_jank_cuj_hwc_release_fence fence
   ON fence.cuj_id = hwc_release_thread.cuj_id
-  AND fence.fence_idx = GPU_COMPLETION_FENCE_ID_FROM_NAME(slice.name)
+    AND fence.fence_idx = GPU_COMPLETION_FENCE_ID_FROM_NAME(slice.name)
 WHERE
   slice.name GLOB 'waiting for HWC release *'
   AND slice.dur > 0;
 
+-- Find GPU completion slices which indicate when the GPU finished drawing.
+DROP TABLE IF EXISTS android_jank_cuj_gpu_completion_slice;
+CREATE TABLE android_jank_cuj_gpu_completion_slice AS
+SELECT
+  fence.cuj_id,
+  vsync,
+  slice.*,
+  slice.ts + slice.dur AS ts_end,
+  hwc_release.ts_end AS hwc_release_ts_end,
+  fence.fence_idx
+FROM android_jank_cuj_gpu_completion_thread gpu_completion_thread
+JOIN slice USING (track_id)
+JOIN android_jank_cuj_gpu_completion_fence fence
+  ON fence.cuj_id = gpu_completion_thread.cuj_id
+  AND fence.fence_idx = GPU_COMPLETION_FENCE_ID_FROM_NAME(slice.name)
+LEFT JOIN android_jank_cuj_hwc_release_slice hwc_release
+  USING (cuj_id, vsync, draw_frame_slice_id)
+WHERE
+  slice.name GLOB 'waiting for GPU completion *'
+  AND slice.dur > 0;
+
 -- Match the frame timeline on the app side with the frame timeline on the SF side.
 -- This way we get the vsyncs IDs of SF frames within the CUJ.
 -- Note that there might be multiple SF vsync IDs that match a single App vsync ID, e.g.
@@ -152,7 +172,7 @@
 FROM android_jank_cuj_do_frame_slice do_frame
 JOIN actual_frame_timeline_slice app_timeline
   ON do_frame.upid = app_timeline.upid
-  AND do_frame.vsync = CAST(app_timeline.name AS INTEGER)
+    AND do_frame.vsync = CAST(app_timeline.name AS INTEGER)
 JOIN directly_connected_flow(app_timeline.id) flow
   ON flow.slice_in = app_timeline.id
 JOIN actual_frame_timeline_slice sf_timeline
@@ -235,7 +255,7 @@
 JOIN android_jank_cuj_sf_gpu_completion_thread gpu_completion_thread
 JOIN slice
   ON slice.track_id = gpu_completion_thread.track_id
-  AND fence.fence_idx = GPU_COMPLETION_FENCE_ID_FROM_NAME(slice.name)
+    AND fence.fence_idx = GPU_COMPLETION_FENCE_ID_FROM_NAME(slice.name)
 WHERE
   slice.name GLOB 'waiting for GPU completion *'
   AND slice.dur > 0;
@@ -248,15 +268,15 @@
 DROP TABLE IF EXISTS android_jank_cuj_sf_draw_layers_slice;
 CREATE TABLE android_jank_cuj_sf_draw_layers_slice AS
 WITH compose_surfaces AS (
-SELECT
-  cuj_id,
-  vsync,
-  sf_root_slice.id AS sf_root_slice_id,
-  compose_surfaces.ts,
-  compose_surfaces.ts + compose_surfaces.dur AS ts_end
-FROM android_jank_cuj_sf_root_slice sf_root_slice
-JOIN descendant_slice(sf_root_slice.id) compose_surfaces
-  ON compose_surfaces.name = 'composeSurfaces'
+  SELECT
+    cuj_id,
+    vsync,
+    sf_root_slice.id AS sf_root_slice_id,
+    compose_surfaces.ts,
+    compose_surfaces.ts + compose_surfaces.dur AS ts_end
+  FROM android_jank_cuj_sf_root_slice sf_root_slice
+  JOIN descendant_slice(sf_root_slice.id) compose_surfaces
+    ON compose_surfaces.name = 'composeSurfaces'
 )
 SELECT
   cuj_id,
@@ -270,8 +290,8 @@
 JOIN android_jank_cuj_sf_render_engine_thread re_thread
 JOIN slice draw_layers
   ON draw_layers.track_id = re_thread.track_id
-  AND draw_layers.ts >= compose_surfaces.ts
-  AND draw_layers.ts + draw_layers.dur <= compose_surfaces.ts_end
+    AND draw_layers.ts >= compose_surfaces.ts
+    AND draw_layers.ts + draw_layers.dur <= compose_surfaces.ts_end
 WHERE
   draw_layers.name = 'REThreaded::drawLayers'
   AND draw_layers.dur > 0;
diff --git a/src/trace_processor/metrics/sql/android/jank/relevant_threads.sql b/src/trace_processor/metrics/sql/android/jank/relevant_threads.sql
index 949e4a5..7d1761f 100644
--- a/src/trace_processor/metrics/sql/android/jank/relevant_threads.sql
+++ b/src/trace_processor/metrics/sql/android/jank/relevant_threads.sql
@@ -19,7 +19,11 @@
 FROM thread
 JOIN android_jank_cuj cuj USING (upid)
 JOIN thread_track USING (utid)
-WHERE thread.is_main_thread;
+JOIN android_jank_cuj_param p USING (cuj_id)
+WHERE
+  (p.main_thread_override IS NULL AND thread.is_main_thread)
+  -- Some CUJs use a dedicated thread for Choreographer callbacks
+  OR (p.main_thread_override = thread.name);
 
 SELECT CREATE_VIEW_FUNCTION(
   'ANDROID_JANK_CUJ_APP_THREAD(thread_name STRING)',
@@ -53,7 +57,7 @@
 DROP TABLE IF EXISTS android_jank_cuj_sf_process;
 CREATE TABLE android_jank_cuj_sf_process AS
 SELECT * FROM process
-WHERE process.name='/system/bin/surfaceflinger'
+WHERE process.name = '/system/bin/surfaceflinger'
 LIMIT 1;
 
 DROP TABLE IF EXISTS android_jank_cuj_sf_main_thread;
diff --git a/src/trace_processor/metrics/sql/android/jank/slices.sql b/src/trace_processor/metrics/sql/android/jank/slices.sql
index 8c69272..e7c1e2e 100644
--- a/src/trace_processor/metrics/sql/android/jank/slices.sql
+++ b/src/trace_processor/metrics/sql/android/jank/slices.sql
@@ -29,9 +29,9 @@
 JOIN thread_track USING (utid)
 JOIN slice
   ON slice.track_id = thread_track.id
-  -- Take slices which overlap even they started before the boundaries
-  -- This is to be able to query slices that delayed start of a frame
-  AND slice.ts + slice.dur >= boundary.ts AND slice.ts <= boundary.ts_end
+    -- Take slices which overlap even they started before the boundaries
+    -- This is to be able to query slices that delayed start of a frame
+    AND slice.ts + slice.dur >= boundary.ts AND slice.ts <= boundary.ts_end
 WHERE slice.dur > 0;
 
 DROP TABLE IF EXISTS android_jank_cuj_main_thread_slice;
@@ -47,10 +47,10 @@
 JOIN thread USING (utid)
 JOIN slice
   ON slice.track_id = thread_track.id
-  -- Take slices which overlap even they started before the boundaries
-  -- This is to be able to query slices that delayed start of a frame
-  AND slice.ts + slice.dur >= boundary.ts
-  AND slice.ts <= boundary.ts_end
+    -- Take slices which overlap even they started before the boundaries
+    -- This is to be able to query slices that delayed start of a frame
+    AND slice.ts + slice.dur >= boundary.ts
+    AND slice.ts <= boundary.ts_end
 WHERE slice.dur > 0;
 
 DROP TABLE IF EXISTS android_jank_cuj_render_thread_slice;
@@ -66,10 +66,10 @@
 JOIN thread USING (utid)
 JOIN slice
   ON slice.track_id = thread_track.id
-  -- Take slices which overlap even they started before the boundaries
-  -- This is to be able to query slices that delayed start of a frame
-  AND slice.ts + slice.dur >= boundary.ts
-  AND slice.ts <= boundary.ts_end
+    -- Take slices which overlap even they started before the boundaries
+    -- This is to be able to query slices that delayed start of a frame
+    AND slice.ts + slice.dur >= boundary.ts
+    AND slice.ts <= boundary.ts_end
 WHERE slice.dur > 0;
 
 DROP VIEW IF EXISTS android_jank_cuj_sf_slice;
@@ -88,9 +88,9 @@
 JOIN thread_track USING (utid)
 JOIN slice
   ON slice.track_id = thread_track.id
-  -- Take slices which overlap even they started before the boundaries
-  -- This is to be able to query slices that delayed start of a frame
-  AND slice.ts + slice.dur >= sf_boundary.ts AND slice.ts <= sf_boundary.ts_end
+    -- Take slices which overlap even they started before the boundaries
+    -- This is to be able to query slices that delayed start of a frame
+    AND slice.ts + slice.dur >= sf_boundary.ts AND slice.ts <= sf_boundary.ts_end
 WHERE slice.dur > 0;
 
 DROP TABLE IF EXISTS android_jank_cuj_sf_main_thread_slice;
@@ -106,10 +106,10 @@
 JOIN thread USING (utid)
 JOIN slice
   ON slice.track_id = thread_track.id
-  -- Take slices which overlap even they started before the boundaries
-  -- This is to be able to query slices that delayed start of a frame
-  AND slice.ts + slice.dur >= boundary.ts
-  AND slice.ts <= boundary.ts_end
+    -- Take slices which overlap even they started before the boundaries
+    -- This is to be able to query slices that delayed start of a frame
+    AND slice.ts + slice.dur >= boundary.ts
+    AND slice.ts <= boundary.ts_end
 WHERE slice.dur > 0;
 
 -- For RenderEngine thread we use a different approach as it's only used when SF falls back to
@@ -128,8 +128,8 @@
 JOIN thread USING (utid)
 JOIN slice
   ON slice.track_id = thread_track.id
-  -- Take slices which overlap even they started before the boundaries
-  -- This is to be able to query slices that delayed start of a frame
-  AND slice.ts + slice.dur >= boundary.ts
-  AND slice.ts <= boundary.ts_end
+    -- Take slices which overlap even they started before the boundaries
+    -- This is to be able to query slices that delayed start of a frame
+    AND slice.ts + slice.dur >= boundary.ts
+    AND slice.ts <= boundary.ts_end
 WHERE slice.dur > 0;
diff --git a/src/trace_processor/metrics/sql/android/java_heap_histogram.sql b/src/trace_processor/metrics/sql/android/java_heap_histogram.sql
index 2b4546a..e0367d7 100644
--- a/src/trace_processor/metrics/sql/android/java_heap_histogram.sql
+++ b/src/trace_processor/metrics/sql/android/java_heap_histogram.sql
@@ -23,12 +23,7 @@
     'android.view.View',
     'android.app.Activity',
     'android.app.Fragment',
-    'android.app.Service',
-    'android.content.ContentProvider',
-    'android.content.BroadcastReceiver',
     'android.content.ContentProviderClient',
-    'android.content.Context',
-    'android.content.Intent',
     'android.os.Binder',
     'android.os.BinderProxy',
     'android.os.Parcel',
@@ -42,19 +37,19 @@
 
 DROP TABLE IF EXISTS heap_obj_histograms;
 CREATE TABLE heap_obj_histograms AS
-  SELECT
-    o.upid,
-    o.graph_sample_ts,
-    o.type_id cls_id,
-    COUNT(1) obj_count,
-    SUM(IIF(o.reachable, 1, 0)) reachable_obj_count,
-    SUM(self_size) / 1024 size_kb,
-    SUM(IIF(o.reachable, self_size, 0)) / 1024 reachable_size_kb,
-    SUM(native_size) / 1024 native_size_kb,
-    SUM(IIF(o.reachable, native_size, 0)) / 1024 reachable_native_size_kb
-  FROM heap_graph_object o
-  GROUP BY 1, 2, 3
-  ORDER BY 1, 2, 3;
+SELECT
+  o.upid,
+  o.graph_sample_ts,
+  o.type_id AS cls_id,
+  COUNT(1) AS obj_count,
+  SUM(IIF(o.reachable, 1, 0)) AS reachable_obj_count,
+  SUM(self_size) / 1024 AS size_kb,
+  SUM(IIF(o.reachable, self_size, 0)) / 1024 AS reachable_size_kb,
+  SUM(native_size) / 1024 AS native_size_kb,
+  SUM(IIF(o.reachable, native_size, 0)) / 1024 AS reachable_native_size_kb
+FROM heap_graph_object o
+GROUP BY 1, 2, 3
+ORDER BY 1, 2, 3;
 
 DROP VIEW IF EXISTS java_heap_histogram_output;
 CREATE VIEW java_heap_histogram_output AS
diff --git a/src/trace_processor/metrics/sql/android/java_heap_stats.sql b/src/trace_processor/metrics/sql/android/java_heap_stats.sql
index 9f22a8a..15627b4 100644
--- a/src/trace_processor/metrics/sql/android/java_heap_stats.sql
+++ b/src/trace_processor/metrics/sql/android/java_heap_stats.sql
@@ -39,8 +39,8 @@
     upid,
     graph_sample_ts,
     root_type,
-    IFNULL(t.deobfuscated_name, t.name) type_name,
-    COUNT(1) obj_count
+    IFNULL(t.deobfuscated_name, t.name) AS type_name,
+    COUNT(1) AS obj_count
   FROM heap_graph_object o
   JOIN heap_graph_class t ON o.type_id = t.id
   -- Classes are going to be particularly spammy and uninteresting
@@ -57,7 +57,7 @@
       'root_type', root_type,
       'type_name', type_name,
       'obj_count', obj_count
-    )) roots
+    )) AS roots
   FROM heap_roots
   GROUP BY 1, 2
 ),
@@ -74,17 +74,17 @@
       FROM (
         SELECT
           ts, dur,
-          CAST(anon_and_swap_val AS INTEGER) anon_swap_val,
-          ABS(ts - base_stats.graph_sample_ts) diff
+          CAST(anon_and_swap_val AS INTEGER) AS anon_swap_val,
+          ABS(ts - base_stats.graph_sample_ts) AS diff
         FROM anon_and_swap_span
         WHERE upid = base_stats.upid)
       WHERE
         (graph_sample_ts >= ts AND graph_sample_ts < ts + dur)
-         -- If the first memory sample for the UPID comes *after* the heap profile
-         -- accept it if close (500ms)
+        -- If the first memory sample for the UPID comes *after* the heap profile
+        -- accept it if close (500ms)
         OR (graph_sample_ts < ts AND diff <= 500 * 1e6)
       ORDER BY diff LIMIT 1
-    ) val
+    ) AS val
   FROM base_stats
 ),
 -- Group by upid
@@ -101,7 +101,7 @@
       'reachable_obj_count', reachable_obj_count,
       'roots', roots,
       'anon_rss_and_swap_size', closest_anon_swap.val
-    )) sample_protos
+    )) AS sample_protos
   FROM base_stats
   LEFT JOIN closest_anon_swap USING (upid, graph_sample_ts)
   GROUP BY 1
diff --git a/src/trace_processor/metrics/sql/android/mem_stats_priority_breakdown.sql b/src/trace_processor/metrics/sql/android/mem_stats_priority_breakdown.sql
index 645b8bb..0cc7221 100644
--- a/src/trace_processor/metrics/sql/android/mem_stats_priority_breakdown.sql
+++ b/src/trace_processor/metrics/sql/android/mem_stats_priority_breakdown.sql
@@ -31,16 +31,16 @@
     WHEN oom_score_val < -900 THEN 'native'
     WHEN oom_score_val < -800 THEN 'system'
     WHEN oom_score_val < -700 THEN 'persistent'
-    WHEN oom_score_val <  0   THEN 'persistent_service'
-    WHEN oom_score_val <  100 THEN 'foreground'
-    WHEN oom_score_val <  200 THEN 'visible'
-    WHEN oom_score_val <  300 THEN 'perceptible'
-    WHEN oom_score_val <  400 THEN 'backup'
-    WHEN oom_score_val <  500 THEN 'heavy_weight'
-    WHEN oom_score_val <  600 THEN 'service_a'
-    WHEN oom_score_val <  700 THEN 'home'
-    WHEN oom_score_val <  800 THEN 'prev'
-    WHEN oom_score_val <  900 THEN 'service_b'
+    WHEN oom_score_val < 0 THEN 'persistent_service'
+    WHEN oom_score_val < 100 THEN 'foreground'
+    WHEN oom_score_val < 200 THEN 'visible'
+    WHEN oom_score_val < 300 THEN 'perceptible'
+    WHEN oom_score_val < 400 THEN 'backup'
+    WHEN oom_score_val < 500 THEN 'heavy_weight'
+    WHEN oom_score_val < 600 THEN 'service_a'
+    WHEN oom_score_val < 700 THEN 'home'
+    WHEN oom_score_val < 800 THEN 'prev'
+    WHEN oom_score_val < 900 THEN 'service_b'
     ELSE 'cached'
   END AS priority,
   MIN(span.{{table_name}}_val) AS min_value,
diff --git a/src/trace_processor/metrics/sql/android/p_state.sql b/src/trace_processor/metrics/sql/android/p_state.sql
new file mode 100644
index 0000000..79d2155
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/p_state.sql
@@ -0,0 +1,72 @@
+--
+-- Copyright 2022 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/android_cpu_agg.sql");
+
+DROP VIEW IF EXISTS p_state_cpu_idle_counter;
+CREATE VIEW p_state_cpu_idle_counter AS
+SELECT
+  ts,
+  ts - LAG(ts) OVER (
+    PARTITION BY track_id
+    ORDER BY
+      ts
+  ) AS dur,
+  cpu,
+  iif(value = 4294967295, -1, cast(value AS int)) AS idle_value
+FROM
+  counter c
+JOIN cpu_counter_track t ON c.track_id = t.id
+WHERE
+  t.name = "cpuidle";
+
+DROP TABLE IF EXISTS p_state_sched_freq_idle;
+CREATE VIRTUAL TABLE p_state_sched_freq_idle USING span_join(
+  cpu_freq_sched_per_thread PARTITIONED cpu,
+  p_state_cpu_idle_counter PARTITIONED cpu
+);
+
+SELECT
+  CREATE_VIEW_FUNCTION(
+    'P_STATE_OVER_INTERVAL(start_ns LONG, end_ns LONG)',
+    'cpu INT, freq_khz INT, idle_value INT, dur_ns INT',
+    '
+    WITH sched_freq_idle_windowed AS (
+      SELECT
+        freq_khz,
+        idle_value,
+        cpu,
+        IIF(ts + dur <= $end_ns, ts + dur, $end_ns) - IIF(ts >= $start_ns, ts, $start_ns) AS dur
+      FROM
+        p_state_sched_freq_idle
+      WHERE
+        ts + dur > $start_ns
+        AND ts < $end_ns
+    )
+    SELECT
+      cast(cpu AS int) AS cpu,
+      cast(freq_khz AS int) AS freq_khz,
+      cast(idle_value AS int) AS idle_value,
+      cast(sum(dur) AS int) AS dur_ns
+    FROM
+      sched_freq_idle_windowed
+    WHERE
+      freq_khz > 0
+    GROUP BY
+      cpu,
+      freq_khz,
+      idle_value
+  '
+  );
diff --git a/src/trace_processor/metrics/sql/android/power_drain_in_watts.sql b/src/trace_processor/metrics/sql/android/power_drain_in_watts.sql
index 464da59..6902a9b 100644
--- a/src/trace_processor/metrics/sql/android/power_drain_in_watts.sql
+++ b/src/trace_processor/metrics/sql/android/power_drain_in_watts.sql
@@ -21,33 +21,33 @@
 
 INSERT INTO power_counters
 VALUES ('power.VPH_PWR_S5C_S6C_uws', 'cpu_big'),
-  ('power.VPH_PWR_S4C_uws', 'cpu_little'),
-  ('power.VPH_PWR_S2C_S3C_uws', 'soc'),
-  ('power.VPH_PWR_OLED_uws', 'display'),
-  ('power.PPVAR_VPH_PWR_S1A_S9A_S10A_uws', 'soc'),
-  ('power.PPVAR_VPH_PWR_S2A_S3A_uws', 'cpu_big'),
-  ('power.PPVAR_VPH_PWR_S1C_uws', 'cpu_little'),
-  ('power.WCN3998_VDD13 [from PP1304_L2C]_uws', 'wifi'),
-  ('power.PPVAR_VPH_PWR_WLAN_uws', 'wifi'),
-  ('power.PPVAR_VPH_PWR_OLED_uws', 'display'),
-  ('power.PPVAR_VPH_PWR_QTM525_uws', 'cellular'),
-  ('power.PPVAR_VPH_PWR_RF_uws', 'cellular'),
-  ('power.rails.aoc.logic', 'aoc'),
-  ('power.rails.aoc.memory', 'aoc'),
-  ('power.rails.cpu.big', 'cpu_big'),
-  ('power.rails.cpu.little', 'cpu_little'),
-  ('power.rails.cpu.mid', 'cpu_mid'),
-  ('power.rails.ddr.a', 'mem'),
-  ('power.rails.ddr.b', 'mem'),
-  ('power.rails.ddr.c', 'mem'),
-  ('power.rails.gpu', 'gpu'),
-  ('power.rails.display', 'display'),
-  ('power.rails.gps', 'gps'),
-  ('power.rails.memory.interface', 'mem'),
-  ('power.rails.modem', 'cellular'),
-  ('power.rails.radio.frontend', 'cellular'),
-  ('power.rails.system.fabric', 'soc'),
-  ('power.rails.wifi.bt', 'wifi');
+('power.VPH_PWR_S4C_uws', 'cpu_little'),
+('power.VPH_PWR_S2C_S3C_uws', 'soc'),
+('power.VPH_PWR_OLED_uws', 'display'),
+('power.PPVAR_VPH_PWR_S1A_S9A_S10A_uws', 'soc'),
+('power.PPVAR_VPH_PWR_S2A_S3A_uws', 'cpu_big'),
+('power.PPVAR_VPH_PWR_S1C_uws', 'cpu_little'),
+('power.WCN3998_VDD13 [from PP1304_L2C]_uws', 'wifi'),
+('power.PPVAR_VPH_PWR_WLAN_uws', 'wifi'),
+('power.PPVAR_VPH_PWR_OLED_uws', 'display'),
+('power.PPVAR_VPH_PWR_QTM525_uws', 'cellular'),
+('power.PPVAR_VPH_PWR_RF_uws', 'cellular'),
+('power.rails.aoc.logic', 'aoc'),
+('power.rails.aoc.memory', 'aoc'),
+('power.rails.cpu.big', 'cpu_big'),
+('power.rails.cpu.little', 'cpu_little'),
+('power.rails.cpu.mid', 'cpu_mid'),
+('power.rails.ddr.a', 'mem'),
+('power.rails.ddr.b', 'mem'),
+('power.rails.ddr.c', 'mem'),
+('power.rails.gpu', 'gpu'),
+('power.rails.display', 'display'),
+('power.rails.gps', 'gps'),
+('power.rails.memory.interface', 'mem'),
+('power.rails.modem', 'cellular'),
+('power.rails.radio.frontend', 'cellular'),
+('power.rails.system.fabric', 'soc'),
+('power.rails.wifi.bt', 'wifi');
 
 -- Convert power counter data into table of events, where each event has
 -- start timestamp, duration and the average power drain during its duration
@@ -85,6 +85,6 @@
     ) - ts
   ) * 1e3 AS drain_w
 FROM counter
-  JOIN counter_track ON (counter.track_id = counter_track.id)
+JOIN counter_track ON (counter.track_id = counter_track.id)
 WHERE counter_track.type = 'counter_track'
   AND name GLOB "power.*";
diff --git a/src/trace_processor/metrics/sql/android/power_profile_data.sql b/src/trace_processor/metrics/sql/android/power_profile_data.sql
index c124c8a..707c301 100644
--- a/src/trace_processor/metrics/sql/android/power_profile_data.sql
+++ b/src/trace_processor/metrics/sql/android/power_profile_data.sql
@@ -13,1580 +13,19 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 
-INSERT OR REPLACE INTO power_profile VALUES
-("marlin", 0, 0, 307200, 11.272),
-("marlin", 0, 0, 384000, 14.842),
-("marlin", 0, 0, 460800, 18.497),
-("marlin", 0, 0, 537600, 22.518),
-("marlin", 0, 0, 614400, 25.967),
-("marlin", 0, 0, 691200, 31.694),
-("marlin", 0, 0, 768000, 37.673),
-("marlin", 0, 0, 844800, 42.859),
-("marlin", 0, 0, 902600, 46.872),
-("marlin", 0, 0, 979200, 57.92),
-("marlin", 0, 0, 1056000, 67.561),
-("marlin", 0, 0, 1132800, 76.303),
-("marlin", 0, 0, 1209600, 87.613),
-("marlin", 0, 0, 1286400, 97.045),
-("marlin", 0, 0, 1363200, 109.544),
-("marlin", 0, 0, 1440000, 122.054),
-("marlin", 0, 0, 1516800, 136.345),
-("marlin", 0, 0, 1593600, 154.435),
-("marlin", 1, 0, 307200, 11.272),
-("marlin", 1, 0, 384000, 14.842),
-("marlin", 1, 0, 460800, 18.497),
-("marlin", 1, 0, 537600, 22.518),
-("marlin", 1, 0, 614400, 25.967),
-("marlin", 1, 0, 691200, 31.694),
-("marlin", 1, 0, 768000, 37.673),
-("marlin", 1, 0, 844800, 42.859),
-("marlin", 1, 0, 902600, 46.872),
-("marlin", 1, 0, 979200, 57.92),
-("marlin", 1, 0, 1056000, 67.561),
-("marlin", 1, 0, 1132800, 76.303),
-("marlin", 1, 0, 1209600, 87.613),
-("marlin", 1, 0, 1286400, 97.045),
-("marlin", 1, 0, 1363200, 109.544),
-("marlin", 1, 0, 1440000, 122.054),
-("marlin", 1, 0, 1516800, 136.345),
-("marlin", 1, 0, 1593600, 154.435),
-("marlin", 2, 1, 307200, 7.055),
-("marlin", 2, 1, 384000, 11.483),
-("marlin", 2, 1, 460800, 14.979),
-("marlin", 2, 1, 537600, 19.642),
-("marlin", 2, 1, 614400, 23.167),
-("marlin", 2, 1, 691200, 27.479),
-("marlin", 2, 1, 748800, 31.632),
-("marlin", 2, 1, 825600, 39.192),
-("marlin", 2, 1, 902400, 47.817),
-("marlin", 2, 1, 979200, 55.659),
-("marlin", 2, 1, 1056000, 64.908),
-("marlin", 2, 1, 1132800, 73.824),
-("marlin", 2, 1, 1209600, 85.299),
-("marlin", 2, 1, 1286400, 96.036),
-("marlin", 2, 1, 1363200, 109.233),
-("marlin", 2, 1, 1440000, 118.56),
-("marlin", 2, 1, 1516800, 132.959),
-("marlin", 2, 1, 1593600, 143.692),
-("marlin", 2, 1, 1670400, 161.378),
-("marlin", 2, 1, 1747200, 180.616),
-("marlin", 2, 1, 1824000, 193.897),
-("marlin", 2, 1, 1900800, 214.361),
-("marlin", 2, 1, 1977600, 238.338),
-("marlin", 2, 1, 2054400, 265.759),
-("marlin", 2, 1, 2150400, 297.918),
-("marlin", 3, 1, 307200, 7.055),
-("marlin", 3, 1, 384000, 11.483),
-("marlin", 3, 1, 460800, 14.979),
-("marlin", 3, 1, 537600, 19.642),
-("marlin", 3, 1, 614400, 23.167),
-("marlin", 3, 1, 691200, 27.479),
-("marlin", 3, 1, 748800, 31.632),
-("marlin", 3, 1, 825600, 39.192),
-("marlin", 3, 1, 902400, 47.817),
-("marlin", 3, 1, 979200, 55.659),
-("marlin", 3, 1, 1056000, 64.908),
-("marlin", 3, 1, 1132800, 73.824),
-("marlin", 3, 1, 1209600, 85.299),
-("marlin", 3, 1, 1286400, 96.036),
-("marlin", 3, 1, 1363200, 109.233),
-("marlin", 3, 1, 1440000, 118.56),
-("marlin", 3, 1, 1516800, 132.959),
-("marlin", 3, 1, 1593600, 143.692),
-("marlin", 3, 1, 1670400, 161.378),
-("marlin", 3, 1, 1747200, 180.616),
-("marlin", 3, 1, 1824000, 193.897),
-("marlin", 3, 1, 1900800, 214.361),
-("marlin", 3, 1, 1977600, 238.338),
-("marlin", 3, 1, 2054400, 265.759),
-("marlin", 3, 1, 2150400, 297.918),
-("sailfish", 0, 0, 307200, 11.272),
-("sailfish", 0, 0, 384000, 14.842),
-("sailfish", 0, 0, 460800, 18.497),
-("sailfish", 0, 0, 537600, 22.518),
-("sailfish", 0, 0, 614400, 25.967),
-("sailfish", 0, 0, 691200, 31.694),
-("sailfish", 0, 0, 768000, 37.673),
-("sailfish", 0, 0, 844800, 42.859),
-("sailfish", 0, 0, 902600, 46.872),
-("sailfish", 0, 0, 979200, 57.92),
-("sailfish", 0, 0, 1056000, 67.561),
-("sailfish", 0, 0, 1132800, 76.303),
-("sailfish", 0, 0, 1209600, 87.613),
-("sailfish", 0, 0, 1286400, 97.045),
-("sailfish", 0, 0, 1363200, 109.544),
-("sailfish", 0, 0, 1440000, 122.054),
-("sailfish", 0, 0, 1516800, 136.345),
-("sailfish", 0, 0, 1593600, 154.435),
-("sailfish", 1, 0, 307200, 11.272),
-("sailfish", 1, 0, 384000, 14.842),
-("sailfish", 1, 0, 460800, 18.497),
-("sailfish", 1, 0, 537600, 22.518),
-("sailfish", 1, 0, 614400, 25.967),
-("sailfish", 1, 0, 691200, 31.694),
-("sailfish", 1, 0, 768000, 37.673),
-("sailfish", 1, 0, 844800, 42.859),
-("sailfish", 1, 0, 902600, 46.872),
-("sailfish", 1, 0, 979200, 57.92),
-("sailfish", 1, 0, 1056000, 67.561),
-("sailfish", 1, 0, 1132800, 76.303),
-("sailfish", 1, 0, 1209600, 87.613),
-("sailfish", 1, 0, 1286400, 97.045),
-("sailfish", 1, 0, 1363200, 109.544),
-("sailfish", 1, 0, 1440000, 122.054),
-("sailfish", 1, 0, 1516800, 136.345),
-("sailfish", 1, 0, 1593600, 154.435),
-("sailfish", 2, 1, 307200, 7.055),
-("sailfish", 2, 1, 384000, 11.483),
-("sailfish", 2, 1, 460800, 14.979),
-("sailfish", 2, 1, 537600, 19.642),
-("sailfish", 2, 1, 614400, 23.167),
-("sailfish", 2, 1, 691200, 27.479),
-("sailfish", 2, 1, 748800, 31.632),
-("sailfish", 2, 1, 825600, 39.192),
-("sailfish", 2, 1, 902400, 47.817),
-("sailfish", 2, 1, 979200, 55.659),
-("sailfish", 2, 1, 1056000, 64.908),
-("sailfish", 2, 1, 1132800, 73.824),
-("sailfish", 2, 1, 1209600, 85.299),
-("sailfish", 2, 1, 1286400, 96.036),
-("sailfish", 2, 1, 1363200, 109.233),
-("sailfish", 2, 1, 1440000, 118.56),
-("sailfish", 2, 1, 1516800, 132.959),
-("sailfish", 2, 1, 1593600, 143.692),
-("sailfish", 2, 1, 1670400, 161.378),
-("sailfish", 2, 1, 1747200, 180.616),
-("sailfish", 2, 1, 1824000, 193.897),
-("sailfish", 2, 1, 1900800, 214.361),
-("sailfish", 2, 1, 1977600, 238.338),
-("sailfish", 2, 1, 2054400, 265.759),
-("sailfish", 2, 1, 2150400, 297.918),
-("sailfish", 3, 1, 307200, 7.055),
-("sailfish", 3, 1, 384000, 11.483),
-("sailfish", 3, 1, 460800, 14.979),
-("sailfish", 3, 1, 537600, 19.642),
-("sailfish", 3, 1, 614400, 23.167),
-("sailfish", 3, 1, 691200, 27.479),
-("sailfish", 3, 1, 748800, 31.632),
-("sailfish", 3, 1, 825600, 39.192),
-("sailfish", 3, 1, 902400, 47.817),
-("sailfish", 3, 1, 979200, 55.659),
-("sailfish", 3, 1, 1056000, 64.908),
-("sailfish", 3, 1, 1132800, 73.824),
-("sailfish", 3, 1, 1209600, 85.299),
-("sailfish", 3, 1, 1286400, 96.036),
-("sailfish", 3, 1, 1363200, 109.233),
-("sailfish", 3, 1, 1440000, 118.56),
-("sailfish", 3, 1, 1516800, 132.959),
-("sailfish", 3, 1, 1593600, 143.692),
-("sailfish", 3, 1, 1670400, 161.378),
-("sailfish", 3, 1, 1747200, 180.616),
-("sailfish", 3, 1, 1824000, 193.897),
-("sailfish", 3, 1, 1900800, 214.361),
-("sailfish", 3, 1, 1977600, 238.338),
-("sailfish", 3, 1, 2054400, 265.759),
-("sailfish", 3, 1, 2150400, 297.918),
-("walleye", 0, 0, 300000, 3.685),
-("walleye", 0, 0, 364800, 3.598),
-("walleye", 0, 0, 441600, 3.621),
-("walleye", 0, 0, 518400, 4.202),
-("walleye", 0, 0, 595200, 4.935),
-("walleye", 0, 0, 672000, 5.633),
-("walleye", 0, 0, 748800, 6.216),
-("walleye", 0, 0, 825600, 6.71),
-("walleye", 0, 0, 883200, 7.557),
-("walleye", 0, 0, 960000, 8.687),
-("walleye", 0, 0, 1036800, 9.882),
-("walleye", 0, 0, 1094400, 10.95),
-("walleye", 0, 0, 1171200, 12.075),
-("walleye", 0, 0, 1248000, 12.875),
-("walleye", 0, 0, 1324800, 14.424),
-("walleye", 0, 0, 1401600, 15.653),
-("walleye", 0, 0, 1478400, 17.345),
-("walleye", 0, 0, 1555200, 18.71),
-("walleye", 0, 0, 1670400, 21.587),
-("walleye", 0, 0, 1747200, 25.43),
-("walleye", 0, 0, 1824000, 27.165),
-("walleye", 0, 0, 1900800, 31.671),
-("walleye", 1, 0, 300000, 3.685),
-("walleye", 1, 0, 364800, 3.598),
-("walleye", 1, 0, 441600, 3.621),
-("walleye", 1, 0, 518400, 4.202),
-("walleye", 1, 0, 595200, 4.935),
-("walleye", 1, 0, 672000, 5.633),
-("walleye", 1, 0, 748800, 6.216),
-("walleye", 1, 0, 825600, 6.71),
-("walleye", 1, 0, 883200, 7.557),
-("walleye", 1, 0, 960000, 8.687),
-("walleye", 1, 0, 1036800, 9.882),
-("walleye", 1, 0, 1094400, 10.95),
-("walleye", 1, 0, 1171200, 12.075),
-("walleye", 1, 0, 1248000, 12.875),
-("walleye", 1, 0, 1324800, 14.424),
-("walleye", 1, 0, 1401600, 15.653),
-("walleye", 1, 0, 1478400, 17.345),
-("walleye", 1, 0, 1555200, 18.71),
-("walleye", 1, 0, 1670400, 21.587),
-("walleye", 1, 0, 1747200, 25.43),
-("walleye", 1, 0, 1824000, 27.165),
-("walleye", 1, 0, 1900800, 31.671),
-("walleye", 2, 0, 300000, 3.685),
-("walleye", 2, 0, 364800, 3.598),
-("walleye", 2, 0, 441600, 3.621),
-("walleye", 2, 0, 518400, 4.202),
-("walleye", 2, 0, 595200, 4.935),
-("walleye", 2, 0, 672000, 5.633),
-("walleye", 2, 0, 748800, 6.216),
-("walleye", 2, 0, 825600, 6.71),
-("walleye", 2, 0, 883200, 7.557),
-("walleye", 2, 0, 960000, 8.687),
-("walleye", 2, 0, 1036800, 9.882),
-("walleye", 2, 0, 1094400, 10.95),
-("walleye", 2, 0, 1171200, 12.075),
-("walleye", 2, 0, 1248000, 12.875),
-("walleye", 2, 0, 1324800, 14.424),
-("walleye", 2, 0, 1401600, 15.653),
-("walleye", 2, 0, 1478400, 17.345),
-("walleye", 2, 0, 1555200, 18.71),
-("walleye", 2, 0, 1670400, 21.587),
-("walleye", 2, 0, 1747200, 25.43),
-("walleye", 2, 0, 1824000, 27.165),
-("walleye", 2, 0, 1900800, 31.671),
-("walleye", 3, 0, 300000, 3.685),
-("walleye", 3, 0, 364800, 3.598),
-("walleye", 3, 0, 441600, 3.621),
-("walleye", 3, 0, 518400, 4.202),
-("walleye", 3, 0, 595200, 4.935),
-("walleye", 3, 0, 672000, 5.633),
-("walleye", 3, 0, 748800, 6.216),
-("walleye", 3, 0, 825600, 6.71),
-("walleye", 3, 0, 883200, 7.557),
-("walleye", 3, 0, 960000, 8.687),
-("walleye", 3, 0, 1036800, 9.882),
-("walleye", 3, 0, 1094400, 10.95),
-("walleye", 3, 0, 1171200, 12.075),
-("walleye", 3, 0, 1248000, 12.875),
-("walleye", 3, 0, 1324800, 14.424),
-("walleye", 3, 0, 1401600, 15.653),
-("walleye", 3, 0, 1478400, 17.345),
-("walleye", 3, 0, 1555200, 18.71),
-("walleye", 3, 0, 1670400, 21.587),
-("walleye", 3, 0, 1747200, 25.43),
-("walleye", 3, 0, 1824000, 27.165),
-("walleye", 3, 0, 1900800, 31.671),
-("walleye", 4, 1, 300000, 10.722),
-("walleye", 4, 1, 345600, 11.52),
-("walleye", 4, 1, 422400, 14.105),
-("walleye", 4, 1, 499200, 16.68),
-("walleye", 4, 1, 576000, 18.946),
-("walleye", 4, 1, 652800, 21.265),
-("walleye", 4, 1, 729600, 23.432),
-("walleye", 4, 1, 806400, 26.019),
-("walleye", 4, 1, 902400, 28.856),
-("walleye", 4, 1, 979200, 31.085),
-("walleye", 4, 1, 1056000, 33.615),
-("walleye", 4, 1, 1132800, 35.76),
-("walleye", 4, 1, 1190400, 40.608),
-("walleye", 4, 1, 1267200, 43.284),
-("walleye", 4, 1, 1344000, 47.347),
-("walleye", 4, 1, 1420800, 52.231),
-("walleye", 4, 1, 1497600, 57.225),
-("walleye", 4, 1, 1574400, 63.138),
-("walleye", 4, 1, 1651200, 69.251),
-("walleye", 4, 1, 1728000, 76.449),
-("walleye", 4, 1, 1804800, 84.71),
-("walleye", 4, 1, 1881600, 102.551),
-("walleye", 4, 1, 1958400, 107.115),
-("walleye", 4, 1, 2035200, 129.689),
-("walleye", 4, 1, 2112000, 135.832),
-("walleye", 4, 1, 2208000, 164.674),
-("walleye", 4, 1, 2265600, 180.279),
-("walleye", 4, 1, 2323200, 197.024),
-("walleye", 4, 1, 2342400, 204.511),
-("walleye", 4, 1, 2361600, 211.886),
-("walleye", 4, 1, 2457600, 212.147),
-("walleye", 5, 1, 300000, 10.722),
-("walleye", 5, 1, 345600, 11.52),
-("walleye", 5, 1, 422400, 14.105),
-("walleye", 5, 1, 499200, 16.68),
-("walleye", 5, 1, 576000, 18.946),
-("walleye", 5, 1, 652800, 21.265),
-("walleye", 5, 1, 729600, 23.432),
-("walleye", 5, 1, 806400, 26.019),
-("walleye", 5, 1, 902400, 28.856),
-("walleye", 5, 1, 979200, 31.085),
-("walleye", 5, 1, 1056000, 33.615),
-("walleye", 5, 1, 1132800, 35.76),
-("walleye", 5, 1, 1190400, 40.608),
-("walleye", 5, 1, 1267200, 43.284),
-("walleye", 5, 1, 1344000, 47.347),
-("walleye", 5, 1, 1420800, 52.231),
-("walleye", 5, 1, 1497600, 57.225),
-("walleye", 5, 1, 1574400, 63.138),
-("walleye", 5, 1, 1651200, 69.251),
-("walleye", 5, 1, 1728000, 76.449),
-("walleye", 5, 1, 1804800, 84.71),
-("walleye", 5, 1, 1881600, 102.551),
-("walleye", 5, 1, 1958400, 107.115),
-("walleye", 5, 1, 2035200, 129.689),
-("walleye", 5, 1, 2112000, 135.832),
-("walleye", 5, 1, 2208000, 164.674),
-("walleye", 5, 1, 2265600, 180.279),
-("walleye", 5, 1, 2323200, 197.024),
-("walleye", 5, 1, 2342400, 204.511),
-("walleye", 5, 1, 2361600, 211.886),
-("walleye", 5, 1, 2457600, 212.147),
-("walleye", 6, 1, 300000, 10.722),
-("walleye", 6, 1, 345600, 11.52),
-("walleye", 6, 1, 422400, 14.105),
-("walleye", 6, 1, 499200, 16.68),
-("walleye", 6, 1, 576000, 18.946),
-("walleye", 6, 1, 652800, 21.265),
-("walleye", 6, 1, 729600, 23.432),
-("walleye", 6, 1, 806400, 26.019),
-("walleye", 6, 1, 902400, 28.856),
-("walleye", 6, 1, 979200, 31.085),
-("walleye", 6, 1, 1056000, 33.615),
-("walleye", 6, 1, 1132800, 35.76),
-("walleye", 6, 1, 1190400, 40.608),
-("walleye", 6, 1, 1267200, 43.284),
-("walleye", 6, 1, 1344000, 47.347),
-("walleye", 6, 1, 1420800, 52.231),
-("walleye", 6, 1, 1497600, 57.225),
-("walleye", 6, 1, 1574400, 63.138),
-("walleye", 6, 1, 1651200, 69.251),
-("walleye", 6, 1, 1728000, 76.449),
-("walleye", 6, 1, 1804800, 84.71),
-("walleye", 6, 1, 1881600, 102.551),
-("walleye", 6, 1, 1958400, 107.115),
-("walleye", 6, 1, 2035200, 129.689),
-("walleye", 6, 1, 2112000, 135.832),
-("walleye", 6, 1, 2208000, 164.674),
-("walleye", 6, 1, 2265600, 180.279),
-("walleye", 6, 1, 2323200, 197.024),
-("walleye", 6, 1, 2342400, 204.511),
-("walleye", 6, 1, 2361600, 211.886),
-("walleye", 6, 1, 2457600, 212.147),
-("walleye", 7, 1, 300000, 10.722),
-("walleye", 7, 1, 345600, 11.52),
-("walleye", 7, 1, 422400, 14.105),
-("walleye", 7, 1, 499200, 16.68),
-("walleye", 7, 1, 576000, 18.946),
-("walleye", 7, 1, 652800, 21.265),
-("walleye", 7, 1, 729600, 23.432),
-("walleye", 7, 1, 806400, 26.019),
-("walleye", 7, 1, 902400, 28.856),
-("walleye", 7, 1, 979200, 31.085),
-("walleye", 7, 1, 1056000, 33.615),
-("walleye", 7, 1, 1132800, 35.76),
-("walleye", 7, 1, 1190400, 40.608),
-("walleye", 7, 1, 1267200, 43.284),
-("walleye", 7, 1, 1344000, 47.347),
-("walleye", 7, 1, 1420800, 52.231),
-("walleye", 7, 1, 1497600, 57.225),
-("walleye", 7, 1, 1574400, 63.138),
-("walleye", 7, 1, 1651200, 69.251),
-("walleye", 7, 1, 1728000, 76.449),
-("walleye", 7, 1, 1804800, 84.71),
-("walleye", 7, 1, 1881600, 102.551),
-("walleye", 7, 1, 1958400, 107.115),
-("walleye", 7, 1, 2035200, 129.689),
-("walleye", 7, 1, 2112000, 135.832),
-("walleye", 7, 1, 2208000, 164.674),
-("walleye", 7, 1, 2265600, 180.279),
-("walleye", 7, 1, 2323200, 197.024),
-("walleye", 7, 1, 2342400, 204.511),
-("walleye", 7, 1, 2361600, 211.886),
-("walleye", 7, 1, 2457600, 212.147),
-("taimen", 0, 0, 300000, 3.685),
-("taimen", 0, 0, 364800, 3.598),
-("taimen", 0, 0, 441600, 3.621),
-("taimen", 0, 0, 518400, 4.202),
-("taimen", 0, 0, 595200, 4.935),
-("taimen", 0, 0, 672000, 5.633),
-("taimen", 0, 0, 748800, 6.216),
-("taimen", 0, 0, 825600, 6.71),
-("taimen", 0, 0, 883200, 7.557),
-("taimen", 0, 0, 960000, 8.687),
-("taimen", 0, 0, 1036800, 9.882),
-("taimen", 0, 0, 1094400, 10.95),
-("taimen", 0, 0, 1171200, 12.075),
-("taimen", 0, 0, 1248000, 12.875),
-("taimen", 0, 0, 1324800, 14.424),
-("taimen", 0, 0, 1401600, 15.653),
-("taimen", 0, 0, 1478400, 17.345),
-("taimen", 0, 0, 1555200, 18.71),
-("taimen", 0, 0, 1670400, 21.587),
-("taimen", 0, 0, 1747200, 25.43),
-("taimen", 0, 0, 1824000, 27.165),
-("taimen", 0, 0, 1900800, 31.671),
-("taimen", 1, 0, 300000, 3.685),
-("taimen", 1, 0, 364800, 3.598),
-("taimen", 1, 0, 441600, 3.621),
-("taimen", 1, 0, 518400, 4.202),
-("taimen", 1, 0, 595200, 4.935),
-("taimen", 1, 0, 672000, 5.633),
-("taimen", 1, 0, 748800, 6.216),
-("taimen", 1, 0, 825600, 6.71),
-("taimen", 1, 0, 883200, 7.557),
-("taimen", 1, 0, 960000, 8.687),
-("taimen", 1, 0, 1036800, 9.882),
-("taimen", 1, 0, 1094400, 10.95),
-("taimen", 1, 0, 1171200, 12.075),
-("taimen", 1, 0, 1248000, 12.875),
-("taimen", 1, 0, 1324800, 14.424),
-("taimen", 1, 0, 1401600, 15.653),
-("taimen", 1, 0, 1478400, 17.345),
-("taimen", 1, 0, 1555200, 18.71),
-("taimen", 1, 0, 1670400, 21.587),
-("taimen", 1, 0, 1747200, 25.43),
-("taimen", 1, 0, 1824000, 27.165),
-("taimen", 1, 0, 1900800, 31.671),
-("taimen", 2, 0, 300000, 3.685),
-("taimen", 2, 0, 364800, 3.598),
-("taimen", 2, 0, 441600, 3.621),
-("taimen", 2, 0, 518400, 4.202),
-("taimen", 2, 0, 595200, 4.935),
-("taimen", 2, 0, 672000, 5.633),
-("taimen", 2, 0, 748800, 6.216),
-("taimen", 2, 0, 825600, 6.71),
-("taimen", 2, 0, 883200, 7.557),
-("taimen", 2, 0, 960000, 8.687),
-("taimen", 2, 0, 1036800, 9.882),
-("taimen", 2, 0, 1094400, 10.95),
-("taimen", 2, 0, 1171200, 12.075),
-("taimen", 2, 0, 1248000, 12.875),
-("taimen", 2, 0, 1324800, 14.424),
-("taimen", 2, 0, 1401600, 15.653),
-("taimen", 2, 0, 1478400, 17.345),
-("taimen", 2, 0, 1555200, 18.71),
-("taimen", 2, 0, 1670400, 21.587),
-("taimen", 2, 0, 1747200, 25.43),
-("taimen", 2, 0, 1824000, 27.165),
-("taimen", 2, 0, 1900800, 31.671),
-("taimen", 3, 0, 300000, 3.685),
-("taimen", 3, 0, 364800, 3.598),
-("taimen", 3, 0, 441600, 3.621),
-("taimen", 3, 0, 518400, 4.202),
-("taimen", 3, 0, 595200, 4.935),
-("taimen", 3, 0, 672000, 5.633),
-("taimen", 3, 0, 748800, 6.216),
-("taimen", 3, 0, 825600, 6.71),
-("taimen", 3, 0, 883200, 7.557),
-("taimen", 3, 0, 960000, 8.687),
-("taimen", 3, 0, 1036800, 9.882),
-("taimen", 3, 0, 1094400, 10.95),
-("taimen", 3, 0, 1171200, 12.075),
-("taimen", 3, 0, 1248000, 12.875),
-("taimen", 3, 0, 1324800, 14.424),
-("taimen", 3, 0, 1401600, 15.653),
-("taimen", 3, 0, 1478400, 17.345),
-("taimen", 3, 0, 1555200, 18.71),
-("taimen", 3, 0, 1670400, 21.587),
-("taimen", 3, 0, 1747200, 25.43),
-("taimen", 3, 0, 1824000, 27.165),
-("taimen", 3, 0, 1900800, 31.671),
-("taimen", 4, 1, 300000, 10.722),
-("taimen", 4, 1, 345600, 11.52),
-("taimen", 4, 1, 422400, 14.105),
-("taimen", 4, 1, 499200, 16.68),
-("taimen", 4, 1, 576000, 18.946),
-("taimen", 4, 1, 652800, 21.265),
-("taimen", 4, 1, 729600, 23.432),
-("taimen", 4, 1, 806400, 26.019),
-("taimen", 4, 1, 902400, 28.856),
-("taimen", 4, 1, 979200, 31.085),
-("taimen", 4, 1, 1056000, 33.615),
-("taimen", 4, 1, 1132800, 35.76),
-("taimen", 4, 1, 1190400, 40.608),
-("taimen", 4, 1, 1267200, 43.284),
-("taimen", 4, 1, 1344000, 47.347),
-("taimen", 4, 1, 1420800, 52.231),
-("taimen", 4, 1, 1497600, 57.225),
-("taimen", 4, 1, 1574400, 63.138),
-("taimen", 4, 1, 1651200, 69.251),
-("taimen", 4, 1, 1728000, 76.449),
-("taimen", 4, 1, 1804800, 84.71),
-("taimen", 4, 1, 1881600, 102.551),
-("taimen", 4, 1, 1958400, 107.115),
-("taimen", 4, 1, 2035200, 129.689),
-("taimen", 4, 1, 2112000, 135.832),
-("taimen", 4, 1, 2208000, 164.674),
-("taimen", 4, 1, 2265600, 180.279),
-("taimen", 4, 1, 2323200, 197.024),
-("taimen", 4, 1, 2342400, 204.511),
-("taimen", 4, 1, 2361600, 211.886),
-("taimen", 4, 1, 2457600, 212.147),
-("taimen", 5, 1, 300000, 10.722),
-("taimen", 5, 1, 345600, 11.52),
-("taimen", 5, 1, 422400, 14.105),
-("taimen", 5, 1, 499200, 16.68),
-("taimen", 5, 1, 576000, 18.946),
-("taimen", 5, 1, 652800, 21.265),
-("taimen", 5, 1, 729600, 23.432),
-("taimen", 5, 1, 806400, 26.019),
-("taimen", 5, 1, 902400, 28.856),
-("taimen", 5, 1, 979200, 31.085),
-("taimen", 5, 1, 1056000, 33.615),
-("taimen", 5, 1, 1132800, 35.76),
-("taimen", 5, 1, 1190400, 40.608),
-("taimen", 5, 1, 1267200, 43.284),
-("taimen", 5, 1, 1344000, 47.347),
-("taimen", 5, 1, 1420800, 52.231),
-("taimen", 5, 1, 1497600, 57.225),
-("taimen", 5, 1, 1574400, 63.138),
-("taimen", 5, 1, 1651200, 69.251),
-("taimen", 5, 1, 1728000, 76.449),
-("taimen", 5, 1, 1804800, 84.71),
-("taimen", 5, 1, 1881600, 102.551),
-("taimen", 5, 1, 1958400, 107.115),
-("taimen", 5, 1, 2035200, 129.689),
-("taimen", 5, 1, 2112000, 135.832),
-("taimen", 5, 1, 2208000, 164.674),
-("taimen", 5, 1, 2265600, 180.279),
-("taimen", 5, 1, 2323200, 197.024),
-("taimen", 5, 1, 2342400, 204.511),
-("taimen", 5, 1, 2361600, 211.886),
-("taimen", 5, 1, 2457600, 212.147),
-("taimen", 6, 1, 300000, 10.722),
-("taimen", 6, 1, 345600, 11.52),
-("taimen", 6, 1, 422400, 14.105),
-("taimen", 6, 1, 499200, 16.68),
-("taimen", 6, 1, 576000, 18.946),
-("taimen", 6, 1, 652800, 21.265),
-("taimen", 6, 1, 729600, 23.432),
-("taimen", 6, 1, 806400, 26.019),
-("taimen", 6, 1, 902400, 28.856),
-("taimen", 6, 1, 979200, 31.085),
-("taimen", 6, 1, 1056000, 33.615),
-("taimen", 6, 1, 1132800, 35.76),
-("taimen", 6, 1, 1190400, 40.608),
-("taimen", 6, 1, 1267200, 43.284),
-("taimen", 6, 1, 1344000, 47.347),
-("taimen", 6, 1, 1420800, 52.231),
-("taimen", 6, 1, 1497600, 57.225),
-("taimen", 6, 1, 1574400, 63.138),
-("taimen", 6, 1, 1651200, 69.251),
-("taimen", 6, 1, 1728000, 76.449),
-("taimen", 6, 1, 1804800, 84.71),
-("taimen", 6, 1, 1881600, 102.551),
-("taimen", 6, 1, 1958400, 107.115),
-("taimen", 6, 1, 2035200, 129.689),
-("taimen", 6, 1, 2112000, 135.832),
-("taimen", 6, 1, 2208000, 164.674),
-("taimen", 6, 1, 2265600, 180.279),
-("taimen", 6, 1, 2323200, 197.024),
-("taimen", 6, 1, 2342400, 204.511),
-("taimen", 6, 1, 2361600, 211.886),
-("taimen", 6, 1, 2457600, 212.147),
-("taimen", 7, 1, 300000, 10.722),
-("taimen", 7, 1, 345600, 11.52),
-("taimen", 7, 1, 422400, 14.105),
-("taimen", 7, 1, 499200, 16.68),
-("taimen", 7, 1, 576000, 18.946),
-("taimen", 7, 1, 652800, 21.265),
-("taimen", 7, 1, 729600, 23.432),
-("taimen", 7, 1, 806400, 26.019),
-("taimen", 7, 1, 902400, 28.856),
-("taimen", 7, 1, 979200, 31.085),
-("taimen", 7, 1, 1056000, 33.615),
-("taimen", 7, 1, 1132800, 35.76),
-("taimen", 7, 1, 1190400, 40.608),
-("taimen", 7, 1, 1267200, 43.284),
-("taimen", 7, 1, 1344000, 47.347),
-("taimen", 7, 1, 1420800, 52.231),
-("taimen", 7, 1, 1497600, 57.225),
-("taimen", 7, 1, 1574400, 63.138),
-("taimen", 7, 1, 1651200, 69.251),
-("taimen", 7, 1, 1728000, 76.449),
-("taimen", 7, 1, 1804800, 84.71),
-("taimen", 7, 1, 1881600, 102.551),
-("taimen", 7, 1, 1958400, 107.115),
-("taimen", 7, 1, 2035200, 129.689),
-("taimen", 7, 1, 2112000, 135.832),
-("taimen", 7, 1, 2208000, 164.674),
-("taimen", 7, 1, 2265600, 180.279),
-("taimen", 7, 1, 2323200, 197.024),
-("taimen", 7, 1, 2342400, 204.511),
-("taimen", 7, 1, 2361600, 211.886),
-("taimen", 7, 1, 2457600, 212.147),
-("crosshatch", 0, 0, 300000, 2.27),
-("crosshatch", 0, 0, 403200, 3.63),
-("crosshatch", 0, 0, 480000, 4.36),
-("crosshatch", 0, 0, 576000, 5.21),
-("crosshatch", 0, 0, 652800, 5.47),
-("crosshatch", 0, 0, 748800, 6.74),
-("crosshatch", 0, 0, 825600, 7.69),
-("crosshatch", 0, 0, 902400, 8.57),
-("crosshatch", 0, 0, 979200, 9.42),
-("crosshatch", 0, 0, 1056000, 10.41),
-("crosshatch", 0, 0, 1132800, 11.56),
-("crosshatch", 0, 0, 1228800, 12.87),
-("crosshatch", 0, 0, 1324800, 14.61),
-("crosshatch", 0, 0, 1420800, 16.49),
-("crosshatch", 0, 0, 1516800, 18.9),
-("crosshatch", 0, 0, 1612800, 21.62),
-("crosshatch", 0, 0, 1689600, 24.47),
-("crosshatch", 0, 0, 1766400, 26.45),
-("crosshatch", 1, 0, 300000, 2.27),
-("crosshatch", 1, 0, 403200, 3.63),
-("crosshatch", 1, 0, 480000, 4.36),
-("crosshatch", 1, 0, 576000, 5.21),
-("crosshatch", 1, 0, 652800, 5.47),
-("crosshatch", 1, 0, 748800, 6.74),
-("crosshatch", 1, 0, 825600, 7.69),
-("crosshatch", 1, 0, 902400, 8.57),
-("crosshatch", 1, 0, 979200, 9.42),
-("crosshatch", 1, 0, 1056000, 10.41),
-("crosshatch", 1, 0, 1132800, 11.56),
-("crosshatch", 1, 0, 1228800, 12.87),
-("crosshatch", 1, 0, 1324800, 14.61),
-("crosshatch", 1, 0, 1420800, 16.49),
-("crosshatch", 1, 0, 1516800, 18.9),
-("crosshatch", 1, 0, 1612800, 21.62),
-("crosshatch", 1, 0, 1689600, 24.47),
-("crosshatch", 1, 0, 1766400, 26.45),
-("crosshatch", 2, 0, 300000, 2.27),
-("crosshatch", 2, 0, 403200, 3.63),
-("crosshatch", 2, 0, 480000, 4.36),
-("crosshatch", 2, 0, 576000, 5.21),
-("crosshatch", 2, 0, 652800, 5.47),
-("crosshatch", 2, 0, 748800, 6.74),
-("crosshatch", 2, 0, 825600, 7.69),
-("crosshatch", 2, 0, 902400, 8.57),
-("crosshatch", 2, 0, 979200, 9.42),
-("crosshatch", 2, 0, 1056000, 10.41),
-("crosshatch", 2, 0, 1132800, 11.56),
-("crosshatch", 2, 0, 1228800, 12.87),
-("crosshatch", 2, 0, 1324800, 14.61),
-("crosshatch", 2, 0, 1420800, 16.49),
-("crosshatch", 2, 0, 1516800, 18.9),
-("crosshatch", 2, 0, 1612800, 21.62),
-("crosshatch", 2, 0, 1689600, 24.47),
-("crosshatch", 2, 0, 1766400, 26.45),
-("crosshatch", 3, 0, 300000, 2.27),
-("crosshatch", 3, 0, 403200, 3.63),
-("crosshatch", 3, 0, 480000, 4.36),
-("crosshatch", 3, 0, 576000, 5.21),
-("crosshatch", 3, 0, 652800, 5.47),
-("crosshatch", 3, 0, 748800, 6.74),
-("crosshatch", 3, 0, 825600, 7.69),
-("crosshatch", 3, 0, 902400, 8.57),
-("crosshatch", 3, 0, 979200, 9.42),
-("crosshatch", 3, 0, 1056000, 10.41),
-("crosshatch", 3, 0, 1132800, 11.56),
-("crosshatch", 3, 0, 1228800, 12.87),
-("crosshatch", 3, 0, 1324800, 14.61),
-("crosshatch", 3, 0, 1420800, 16.49),
-("crosshatch", 3, 0, 1516800, 18.9),
-("crosshatch", 3, 0, 1612800, 21.62),
-("crosshatch", 3, 0, 1689600, 24.47),
-("crosshatch", 3, 0, 1766400, 26.45),
-("crosshatch", 4, 1, 825600, 28.88),
-("crosshatch", 4, 1, 902400, 32.4),
-("crosshatch", 4, 1, 979200, 36.46),
-("crosshatch", 4, 1, 1056000, 39.99),
-("crosshatch", 4, 1, 1209600, 47.23),
-("crosshatch", 4, 1, 1286400, 51.39),
-("crosshatch", 4, 1, 1363200, 56.9),
-("crosshatch", 4, 1, 1459200, 64.26),
-("crosshatch", 4, 1, 1536000, 69.65),
-("crosshatch", 4, 1, 1612800, 75.14),
-("crosshatch", 4, 1, 1689600, 83.16),
-("crosshatch", 4, 1, 1766400, 91.75),
-("crosshatch", 4, 1, 1843200, 100.66),
-("crosshatch", 4, 1, 1920000, 111.45),
-("crosshatch", 4, 1, 1996800, 122.23),
-("crosshatch", 4, 1, 2092800, 143.54),
-("crosshatch", 4, 1, 2169600, 147.54),
-("crosshatch", 4, 1, 2246400, 153.09),
-("crosshatch", 4, 1, 2323200, 166.44),
-("crosshatch", 4, 1, 2400000, 184.69),
-("crosshatch", 4, 1, 2476800, 204.14),
-("crosshatch", 4, 1, 2553600, 223.37),
-("crosshatch", 4, 1, 2649600, 253.77),
-("crosshatch", 5, 1, 825600, 28.88),
-("crosshatch", 5, 1, 902400, 32.4),
-("crosshatch", 5, 1, 979200, 36.46),
-("crosshatch", 5, 1, 1056000, 39.99),
-("crosshatch", 5, 1, 1209600, 47.23),
-("crosshatch", 5, 1, 1286400, 51.39),
-("crosshatch", 5, 1, 1363200, 56.9),
-("crosshatch", 5, 1, 1459200, 64.26),
-("crosshatch", 5, 1, 1536000, 69.65),
-("crosshatch", 5, 1, 1612800, 75.14),
-("crosshatch", 5, 1, 1689600, 83.16),
-("crosshatch", 5, 1, 1766400, 91.75),
-("crosshatch", 5, 1, 1843200, 100.66),
-("crosshatch", 5, 1, 1920000, 111.45),
-("crosshatch", 5, 1, 1996800, 122.23),
-("crosshatch", 5, 1, 2092800, 143.54),
-("crosshatch", 5, 1, 2169600, 147.54),
-("crosshatch", 5, 1, 2246400, 153.09),
-("crosshatch", 5, 1, 2323200, 166.44),
-("crosshatch", 5, 1, 2400000, 184.69),
-("crosshatch", 5, 1, 2476800, 204.14),
-("crosshatch", 5, 1, 2553600, 223.37),
-("crosshatch", 5, 1, 2649600, 253.77),
-("crosshatch", 6, 1, 825600, 28.88),
-("crosshatch", 6, 1, 902400, 32.4),
-("crosshatch", 6, 1, 979200, 36.46),
-("crosshatch", 6, 1, 1056000, 39.99),
-("crosshatch", 6, 1, 1209600, 47.23),
-("crosshatch", 6, 1, 1286400, 51.39),
-("crosshatch", 6, 1, 1363200, 56.9),
-("crosshatch", 6, 1, 1459200, 64.26),
-("crosshatch", 6, 1, 1536000, 69.65),
-("crosshatch", 6, 1, 1612800, 75.14),
-("crosshatch", 6, 1, 1689600, 83.16),
-("crosshatch", 6, 1, 1766400, 91.75),
-("crosshatch", 6, 1, 1843200, 100.66),
-("crosshatch", 6, 1, 1920000, 111.45),
-("crosshatch", 6, 1, 1996800, 122.23),
-("crosshatch", 6, 1, 2092800, 143.54),
-("crosshatch", 6, 1, 2169600, 147.54),
-("crosshatch", 6, 1, 2246400, 153.09),
-("crosshatch", 6, 1, 2323200, 166.44),
-("crosshatch", 6, 1, 2400000, 184.69),
-("crosshatch", 6, 1, 2476800, 204.14),
-("crosshatch", 6, 1, 2553600, 223.37),
-("crosshatch", 6, 1, 2649600, 253.77),
-("crosshatch", 7, 1, 825600, 28.88),
-("crosshatch", 7, 1, 902400, 32.4),
-("crosshatch", 7, 1, 979200, 36.46),
-("crosshatch", 7, 1, 1056000, 39.99),
-("crosshatch", 7, 1, 1209600, 47.23),
-("crosshatch", 7, 1, 1286400, 51.39),
-("crosshatch", 7, 1, 1363200, 56.9),
-("crosshatch", 7, 1, 1459200, 64.26),
-("crosshatch", 7, 1, 1536000, 69.65),
-("crosshatch", 7, 1, 1612800, 75.14),
-("crosshatch", 7, 1, 1689600, 83.16),
-("crosshatch", 7, 1, 1766400, 91.75),
-("crosshatch", 7, 1, 1843200, 100.66),
-("crosshatch", 7, 1, 1920000, 111.45),
-("crosshatch", 7, 1, 1996800, 122.23),
-("crosshatch", 7, 1, 2092800, 143.54),
-("crosshatch", 7, 1, 2169600, 147.54),
-("crosshatch", 7, 1, 2246400, 153.09),
-("crosshatch", 7, 1, 2323200, 166.44),
-("crosshatch", 7, 1, 2400000, 184.69),
-("crosshatch", 7, 1, 2476800, 204.14),
-("crosshatch", 7, 1, 2553600, 223.37),
-("crosshatch", 7, 1, 2649600, 253.77),
-("blueline", 0, 0, 300000, 2.27),
-("blueline", 0, 0, 403200, 3.63),
-("blueline", 0, 0, 480000, 4.36),
-("blueline", 0, 0, 576000, 5.21),
-("blueline", 0, 0, 652800, 5.47),
-("blueline", 0, 0, 748800, 6.74),
-("blueline", 0, 0, 825600, 7.69),
-("blueline", 0, 0, 902400, 8.57),
-("blueline", 0, 0, 979200, 9.42),
-("blueline", 0, 0, 1056000, 10.41),
-("blueline", 0, 0, 1132800, 11.56),
-("blueline", 0, 0, 1228800, 12.87),
-("blueline", 0, 0, 1324800, 14.61),
-("blueline", 0, 0, 1420800, 16.49),
-("blueline", 0, 0, 1516800, 18.9),
-("blueline", 0, 0, 1612800, 21.62),
-("blueline", 0, 0, 1689600, 24.47),
-("blueline", 0, 0, 1766400, 26.45),
-("blueline", 1, 0, 300000, 2.27),
-("blueline", 1, 0, 403200, 3.63),
-("blueline", 1, 0, 480000, 4.36),
-("blueline", 1, 0, 576000, 5.21),
-("blueline", 1, 0, 652800, 5.47),
-("blueline", 1, 0, 748800, 6.74),
-("blueline", 1, 0, 825600, 7.69),
-("blueline", 1, 0, 902400, 8.57),
-("blueline", 1, 0, 979200, 9.42),
-("blueline", 1, 0, 1056000, 10.41),
-("blueline", 1, 0, 1132800, 11.56),
-("blueline", 1, 0, 1228800, 12.87),
-("blueline", 1, 0, 1324800, 14.61),
-("blueline", 1, 0, 1420800, 16.49),
-("blueline", 1, 0, 1516800, 18.9),
-("blueline", 1, 0, 1612800, 21.62),
-("blueline", 1, 0, 1689600, 24.47),
-("blueline", 1, 0, 1766400, 26.45),
-("blueline", 2, 0, 300000, 2.27),
-("blueline", 2, 0, 403200, 3.63),
-("blueline", 2, 0, 480000, 4.36),
-("blueline", 2, 0, 576000, 5.21),
-("blueline", 2, 0, 652800, 5.47),
-("blueline", 2, 0, 748800, 6.74),
-("blueline", 2, 0, 825600, 7.69),
-("blueline", 2, 0, 902400, 8.57),
-("blueline", 2, 0, 979200, 9.42),
-("blueline", 2, 0, 1056000, 10.41),
-("blueline", 2, 0, 1132800, 11.56),
-("blueline", 2, 0, 1228800, 12.87),
-("blueline", 2, 0, 1324800, 14.61),
-("blueline", 2, 0, 1420800, 16.49),
-("blueline", 2, 0, 1516800, 18.9),
-("blueline", 2, 0, 1612800, 21.62),
-("blueline", 2, 0, 1689600, 24.47),
-("blueline", 2, 0, 1766400, 26.45),
-("blueline", 3, 0, 300000, 2.27),
-("blueline", 3, 0, 403200, 3.63),
-("blueline", 3, 0, 480000, 4.36),
-("blueline", 3, 0, 576000, 5.21),
-("blueline", 3, 0, 652800, 5.47),
-("blueline", 3, 0, 748800, 6.74),
-("blueline", 3, 0, 825600, 7.69),
-("blueline", 3, 0, 902400, 8.57),
-("blueline", 3, 0, 979200, 9.42),
-("blueline", 3, 0, 1056000, 10.41),
-("blueline", 3, 0, 1132800, 11.56),
-("blueline", 3, 0, 1228800, 12.87),
-("blueline", 3, 0, 1324800, 14.61),
-("blueline", 3, 0, 1420800, 16.49),
-("blueline", 3, 0, 1516800, 18.9),
-("blueline", 3, 0, 1612800, 21.62),
-("blueline", 3, 0, 1689600, 24.47),
-("blueline", 3, 0, 1766400, 26.45),
-("blueline", 4, 1, 825600, 28.88),
-("blueline", 4, 1, 902400, 32.4),
-("blueline", 4, 1, 979200, 36.46),
-("blueline", 4, 1, 1056000, 39.99),
-("blueline", 4, 1, 1209600, 47.23),
-("blueline", 4, 1, 1286400, 51.39),
-("blueline", 4, 1, 1363200, 56.9),
-("blueline", 4, 1, 1459200, 64.26),
-("blueline", 4, 1, 1536000, 69.65),
-("blueline", 4, 1, 1612800, 75.14),
-("blueline", 4, 1, 1689600, 83.16),
-("blueline", 4, 1, 1766400, 91.75),
-("blueline", 4, 1, 1843200, 100.66),
-("blueline", 4, 1, 1920000, 111.45),
-("blueline", 4, 1, 1996800, 122.23),
-("blueline", 4, 1, 2092800, 143.54),
-("blueline", 4, 1, 2169600, 147.54),
-("blueline", 4, 1, 2246400, 153.09),
-("blueline", 4, 1, 2323200, 166.44),
-("blueline", 4, 1, 2400000, 184.69),
-("blueline", 4, 1, 2476800, 204.14),
-("blueline", 4, 1, 2553600, 223.37),
-("blueline", 4, 1, 2649600, 253.77),
-("blueline", 5, 1, 825600, 28.88),
-("blueline", 5, 1, 902400, 32.4),
-("blueline", 5, 1, 979200, 36.46),
-("blueline", 5, 1, 1056000, 39.99),
-("blueline", 5, 1, 1209600, 47.23),
-("blueline", 5, 1, 1286400, 51.39),
-("blueline", 5, 1, 1363200, 56.9),
-("blueline", 5, 1, 1459200, 64.26),
-("blueline", 5, 1, 1536000, 69.65),
-("blueline", 5, 1, 1612800, 75.14),
-("blueline", 5, 1, 1689600, 83.16),
-("blueline", 5, 1, 1766400, 91.75),
-("blueline", 5, 1, 1843200, 100.66),
-("blueline", 5, 1, 1920000, 111.45),
-("blueline", 5, 1, 1996800, 122.23),
-("blueline", 5, 1, 2092800, 143.54),
-("blueline", 5, 1, 2169600, 147.54),
-("blueline", 5, 1, 2246400, 153.09),
-("blueline", 5, 1, 2323200, 166.44),
-("blueline", 5, 1, 2400000, 184.69),
-("blueline", 5, 1, 2476800, 204.14),
-("blueline", 5, 1, 2553600, 223.37),
-("blueline", 5, 1, 2649600, 253.77),
-("blueline", 6, 1, 825600, 28.88),
-("blueline", 6, 1, 902400, 32.4),
-("blueline", 6, 1, 979200, 36.46),
-("blueline", 6, 1, 1056000, 39.99),
-("blueline", 6, 1, 1209600, 47.23),
-("blueline", 6, 1, 1286400, 51.39),
-("blueline", 6, 1, 1363200, 56.9),
-("blueline", 6, 1, 1459200, 64.26),
-("blueline", 6, 1, 1536000, 69.65),
-("blueline", 6, 1, 1612800, 75.14),
-("blueline", 6, 1, 1689600, 83.16),
-("blueline", 6, 1, 1766400, 91.75),
-("blueline", 6, 1, 1843200, 100.66),
-("blueline", 6, 1, 1920000, 111.45),
-("blueline", 6, 1, 1996800, 122.23),
-("blueline", 6, 1, 2092800, 143.54),
-("blueline", 6, 1, 2169600, 147.54),
-("blueline", 6, 1, 2246400, 153.09),
-("blueline", 6, 1, 2323200, 166.44),
-("blueline", 6, 1, 2400000, 184.69),
-("blueline", 6, 1, 2476800, 204.14),
-("blueline", 6, 1, 2553600, 223.37),
-("blueline", 6, 1, 2649600, 253.77),
-("blueline", 7, 1, 825600, 28.88),
-("blueline", 7, 1, 902400, 32.4),
-("blueline", 7, 1, 979200, 36.46),
-("blueline", 7, 1, 1056000, 39.99),
-("blueline", 7, 1, 1209600, 47.23),
-("blueline", 7, 1, 1286400, 51.39),
-("blueline", 7, 1, 1363200, 56.9),
-("blueline", 7, 1, 1459200, 64.26),
-("blueline", 7, 1, 1536000, 69.65),
-("blueline", 7, 1, 1612800, 75.14),
-("blueline", 7, 1, 1689600, 83.16),
-("blueline", 7, 1, 1766400, 91.75),
-("blueline", 7, 1, 1843200, 100.66),
-("blueline", 7, 1, 1920000, 111.45),
-("blueline", 7, 1, 1996800, 122.23),
-("blueline", 7, 1, 2092800, 143.54),
-("blueline", 7, 1, 2169600, 147.54),
-("blueline", 7, 1, 2246400, 153.09),
-("blueline", 7, 1, 2323200, 166.44),
-("blueline", 7, 1, 2400000, 184.69),
-("blueline", 7, 1, 2476800, 204.14),
-("blueline", 7, 1, 2553600, 223.37),
-("blueline", 7, 1, 2649600, 253.77),
-("bonito", 0, 0, 300000, 15.2466666667),
-("bonito", 0, 0, 576000, 18.2166666667),
-("bonito", 0, 0, 748800, 20.1866666667),
-("bonito", 0, 0, 998400, 23.29),
-("bonito", 0, 0, 1209600, 25.0116666667),
-("bonito", 0, 0, 1324800, 28.485),
-("bonito", 0, 0, 1516800, 31.6866666667),
-("bonito", 0, 0, 1708800, 35.79),
-("bonito", 1, 0, 300000, 15.2466666667),
-("bonito", 1, 0, 576000, 18.2166666667),
-("bonito", 1, 0, 748800, 20.1866666667),
-("bonito", 1, 0, 998400, 23.29),
-("bonito", 1, 0, 1209600, 25.0116666667),
-("bonito", 1, 0, 1324800, 28.485),
-("bonito", 1, 0, 1516800, 31.6866666667),
-("bonito", 1, 0, 1708800, 35.79),
-("bonito", 2, 0, 300000, 15.2466666667),
-("bonito", 2, 0, 576000, 18.2166666667),
-("bonito", 2, 0, 748800, 20.1866666667),
-("bonito", 2, 0, 998400, 23.29),
-("bonito", 2, 0, 1209600, 25.0116666667),
-("bonito", 2, 0, 1324800, 28.485),
-("bonito", 2, 0, 1516800, 31.6866666667),
-("bonito", 2, 0, 1708800, 35.79),
-("bonito", 3, 0, 300000, 15.2466666667),
-("bonito", 3, 0, 576000, 18.2166666667),
-("bonito", 3, 0, 748800, 20.1866666667),
-("bonito", 3, 0, 998400, 23.29),
-("bonito", 3, 0, 1209600, 25.0116666667),
-("bonito", 3, 0, 1324800, 28.485),
-("bonito", 3, 0, 1516800, 31.6866666667),
-("bonito", 3, 0, 1708800, 35.79),
-("bonito", 4, 0, 300000, 15.2466666667),
-("bonito", 4, 0, 576000, 18.2166666667),
-("bonito", 4, 0, 748800, 20.1866666667),
-("bonito", 4, 0, 998400, 23.29),
-("bonito", 4, 0, 1209600, 25.0116666667),
-("bonito", 4, 0, 1324800, 28.485),
-("bonito", 4, 0, 1516800, 31.6866666667),
-("bonito", 4, 0, 1708800, 35.79),
-("bonito", 5, 0, 300000, 15.2466666667),
-("bonito", 5, 0, 576000, 18.2166666667),
-("bonito", 5, 0, 748800, 20.1866666667),
-("bonito", 5, 0, 998400, 23.29),
-("bonito", 5, 0, 1209600, 25.0116666667),
-("bonito", 5, 0, 1324800, 28.485),
-("bonito", 5, 0, 1516800, 31.6866666667),
-("bonito", 5, 0, 1708800, 35.79),
-("bonito", 6, 1, 300000, 24.06),
-("bonito", 6, 1, 652800, 27.56),
-("bonito", 6, 1, 825600, 29.0),
-("bonito", 6, 1, 979200, 31.675),
-("bonito", 6, 1, 1132800, 34.53),
-("bonito", 6, 1, 1363200, 38.885),
-("bonito", 6, 1, 1536000, 43.075),
-("bonito", 6, 1, 1747200, 48.705),
-("bonito", 6, 1, 1843200, 64.57),
-("bonito", 6, 1, 1996800, 69.805),
-("bonito", 6, 1, 2016000, 76.545),
-("bonito", 7, 1, 300000, 24.06),
-("bonito", 7, 1, 652800, 27.56),
-("bonito", 7, 1, 825600, 29.0),
-("bonito", 7, 1, 979200, 31.675),
-("bonito", 7, 1, 1132800, 34.53),
-("bonito", 7, 1, 1363200, 38.885),
-("bonito", 7, 1, 1536000, 43.075),
-("bonito", 7, 1, 1747200, 48.705),
-("bonito", 7, 1, 1843200, 64.57),
-("bonito", 7, 1, 1996800, 69.805),
-("bonito", 7, 1, 2016000, 76.545),
-("sargo", 0, 0, 300000, 15.2466666667),
-("sargo", 0, 0, 576000, 18.2166666667),
-("sargo", 0, 0, 748800, 20.1866666667),
-("sargo", 0, 0, 998400, 23.29),
-("sargo", 0, 0, 1209600, 25.0116666667),
-("sargo", 0, 0, 1324800, 28.485),
-("sargo", 0, 0, 1516800, 31.6866666667),
-("sargo", 0, 0, 1708800, 35.79),
-("sargo", 1, 0, 300000, 15.2466666667),
-("sargo", 1, 0, 576000, 18.2166666667),
-("sargo", 1, 0, 748800, 20.1866666667),
-("sargo", 1, 0, 998400, 23.29),
-("sargo", 1, 0, 1209600, 25.0116666667),
-("sargo", 1, 0, 1324800, 28.485),
-("sargo", 1, 0, 1516800, 31.6866666667),
-("sargo", 1, 0, 1708800, 35.79),
-("sargo", 2, 0, 300000, 15.2466666667),
-("sargo", 2, 0, 576000, 18.2166666667),
-("sargo", 2, 0, 748800, 20.1866666667),
-("sargo", 2, 0, 998400, 23.29),
-("sargo", 2, 0, 1209600, 25.0116666667),
-("sargo", 2, 0, 1324800, 28.485),
-("sargo", 2, 0, 1516800, 31.6866666667),
-("sargo", 2, 0, 1708800, 35.79),
-("sargo", 3, 0, 300000, 15.2466666667),
-("sargo", 3, 0, 576000, 18.2166666667),
-("sargo", 3, 0, 748800, 20.1866666667),
-("sargo", 3, 0, 998400, 23.29),
-("sargo", 3, 0, 1209600, 25.0116666667),
-("sargo", 3, 0, 1324800, 28.485),
-("sargo", 3, 0, 1516800, 31.6866666667),
-("sargo", 3, 0, 1708800, 35.79),
-("sargo", 4, 0, 300000, 15.2466666667),
-("sargo", 4, 0, 576000, 18.2166666667),
-("sargo", 4, 0, 748800, 20.1866666667),
-("sargo", 4, 0, 998400, 23.29),
-("sargo", 4, 0, 1209600, 25.0116666667),
-("sargo", 4, 0, 1324800, 28.485),
-("sargo", 4, 0, 1516800, 31.6866666667),
-("sargo", 4, 0, 1708800, 35.79),
-("sargo", 5, 0, 300000, 15.2466666667),
-("sargo", 5, 0, 576000, 18.2166666667),
-("sargo", 5, 0, 748800, 20.1866666667),
-("sargo", 5, 0, 998400, 23.29),
-("sargo", 5, 0, 1209600, 25.0116666667),
-("sargo", 5, 0, 1324800, 28.485),
-("sargo", 5, 0, 1516800, 31.6866666667),
-("sargo", 5, 0, 1708800, 35.79),
-("sargo", 6, 1, 300000, 24.06),
-("sargo", 6, 1, 652800, 27.56),
-("sargo", 6, 1, 825600, 29.0),
-("sargo", 6, 1, 979200, 31.675),
-("sargo", 6, 1, 1132800, 34.53),
-("sargo", 6, 1, 1363200, 38.885),
-("sargo", 6, 1, 1536000, 43.075),
-("sargo", 6, 1, 1747200, 48.705),
-("sargo", 6, 1, 1843200, 64.57),
-("sargo", 6, 1, 1996800, 69.805),
-("sargo", 6, 1, 2016000, 76.545),
-("sargo", 7, 1, 300000, 24.06),
-("sargo", 7, 1, 652800, 27.56),
-("sargo", 7, 1, 825600, 29.0),
-("sargo", 7, 1, 979200, 31.675),
-("sargo", 7, 1, 1132800, 34.53),
-("sargo", 7, 1, 1363200, 38.885),
-("sargo", 7, 1, 1536000, 43.075),
-("sargo", 7, 1, 1747200, 48.705),
-("sargo", 7, 1, 1843200, 64.57),
-("sargo", 7, 1, 1996800, 69.805),
-("sargo", 7, 1, 2016000, 76.545),
-("coral", 0, 0, 300000, 9.86),
-("coral", 0, 0, 403200, 10.335),
-("coral", 0, 0, 499200, 10.8925),
-("coral", 0, 0, 576000, 11.37),
-("coral", 0, 0, 672000, 11.8),
-("coral", 0, 0, 768000, 12.41),
-("coral", 0, 0, 844800, 12.97),
-("coral", 0, 0, 940800, 13.335),
-("coral", 0, 0, 1036800, 14.1725),
-("coral", 0, 0, 1113600, 14.695),
-("coral", 0, 0, 1209600, 15.3525),
-("coral", 0, 0, 1305600, 16.2775),
-("coral", 0, 0, 1382400, 16.8725),
-("coral", 0, 0, 1478400, 17.6525),
-("coral", 0, 0, 1555200, 18.0975),
-("coral", 0, 0, 1632000, 18.8575),
-("coral", 0, 0, 1708800, 20.0525),
-("coral", 0, 0, 1785600, 21.2625),
-("coral", 1, 0, 300000, 9.86),
-("coral", 1, 0, 403200, 10.335),
-("coral", 1, 0, 499200, 10.8925),
-("coral", 1, 0, 576000, 11.37),
-("coral", 1, 0, 672000, 11.8),
-("coral", 1, 0, 768000, 12.41),
-("coral", 1, 0, 844800, 12.97),
-("coral", 1, 0, 940800, 13.335),
-("coral", 1, 0, 1036800, 14.1725),
-("coral", 1, 0, 1113600, 14.695),
-("coral", 1, 0, 1209600, 15.3525),
-("coral", 1, 0, 1305600, 16.2775),
-("coral", 1, 0, 1382400, 16.8725),
-("coral", 1, 0, 1478400, 17.6525),
-("coral", 1, 0, 1555200, 18.0975),
-("coral", 1, 0, 1632000, 18.8575),
-("coral", 1, 0, 1708800, 20.0525),
-("coral", 1, 0, 1785600, 21.2625),
-("coral", 2, 0, 300000, 9.86),
-("coral", 2, 0, 403200, 10.335),
-("coral", 2, 0, 499200, 10.8925),
-("coral", 2, 0, 576000, 11.37),
-("coral", 2, 0, 672000, 11.8),
-("coral", 2, 0, 768000, 12.41),
-("coral", 2, 0, 844800, 12.97),
-("coral", 2, 0, 940800, 13.335),
-("coral", 2, 0, 1036800, 14.1725),
-("coral", 2, 0, 1113600, 14.695),
-("coral", 2, 0, 1209600, 15.3525),
-("coral", 2, 0, 1305600, 16.2775),
-("coral", 2, 0, 1382400, 16.8725),
-("coral", 2, 0, 1478400, 17.6525),
-("coral", 2, 0, 1555200, 18.0975),
-("coral", 2, 0, 1632000, 18.8575),
-("coral", 2, 0, 1708800, 20.0525),
-("coral", 2, 0, 1785600, 21.2625),
-("coral", 3, 0, 300000, 9.86),
-("coral", 3, 0, 403200, 10.335),
-("coral", 3, 0, 499200, 10.8925),
-("coral", 3, 0, 576000, 11.37),
-("coral", 3, 0, 672000, 11.8),
-("coral", 3, 0, 768000, 12.41),
-("coral", 3, 0, 844800, 12.97),
-("coral", 3, 0, 940800, 13.335),
-("coral", 3, 0, 1036800, 14.1725),
-("coral", 3, 0, 1113600, 14.695),
-("coral", 3, 0, 1209600, 15.3525),
-("coral", 3, 0, 1305600, 16.2775),
-("coral", 3, 0, 1382400, 16.8725),
-("coral", 3, 0, 1478400, 17.6525),
-("coral", 3, 0, 1555200, 18.0975),
-("coral", 3, 0, 1632000, 18.8575),
-("coral", 3, 0, 1708800, 20.0525),
-("coral", 3, 0, 1785600, 21.2625),
-("coral", 4, 1, 710400, 16.7833333333),
-("coral", 4, 1, 825600, 18.3733333333),
-("coral", 4, 1, 940800, 20.4833333333),
-("coral", 4, 1, 1056000, 23.3066666667),
-("coral", 4, 1, 1171200, 25.8266666667),
-("coral", 4, 1, 1286400, 28.45),
-("coral", 4, 1, 1401600, 31.7233333333),
-("coral", 4, 1, 1497600, 34.42),
-("coral", 4, 1, 1612800, 39.3966666667),
-("coral", 4, 1, 1708800, 44.24),
-("coral", 4, 1, 1804800, 47.9433333333),
-("coral", 4, 1, 1920000, 51.97),
-("coral", 4, 1, 2016000, 63.3866666667),
-("coral", 4, 1, 2131200, 71.0366666667),
-("coral", 4, 1, 2227200, 79.32),
-("coral", 4, 1, 2323200, 88.99),
-("coral", 4, 1, 2419200, 100.68),
-("coral", 5, 1, 710400, 16.7833333333),
-("coral", 5, 1, 825600, 18.3733333333),
-("coral", 5, 1, 940800, 20.4833333333),
-("coral", 5, 1, 1056000, 23.3066666667),
-("coral", 5, 1, 1171200, 25.8266666667),
-("coral", 5, 1, 1286400, 28.45),
-("coral", 5, 1, 1401600, 31.7233333333),
-("coral", 5, 1, 1497600, 34.42),
-("coral", 5, 1, 1612800, 39.3966666667),
-("coral", 5, 1, 1708800, 44.24),
-("coral", 5, 1, 1804800, 47.9433333333),
-("coral", 5, 1, 1920000, 51.97),
-("coral", 5, 1, 2016000, 63.3866666667),
-("coral", 5, 1, 2131200, 71.0366666667),
-("coral", 5, 1, 2227200, 79.32),
-("coral", 5, 1, 2323200, 88.99),
-("coral", 5, 1, 2419200, 100.68),
-("coral", 6, 1, 710400, 16.7833333333),
-("coral", 6, 1, 825600, 18.3733333333),
-("coral", 6, 1, 940800, 20.4833333333),
-("coral", 6, 1, 1056000, 23.3066666667),
-("coral", 6, 1, 1171200, 25.8266666667),
-("coral", 6, 1, 1286400, 28.45),
-("coral", 6, 1, 1401600, 31.7233333333),
-("coral", 6, 1, 1497600, 34.42),
-("coral", 6, 1, 1612800, 39.3966666667),
-("coral", 6, 1, 1708800, 44.24),
-("coral", 6, 1, 1804800, 47.9433333333),
-("coral", 6, 1, 1920000, 51.97),
-("coral", 6, 1, 2016000, 63.3866666667),
-("coral", 6, 1, 2131200, 71.0366666667),
-("coral", 6, 1, 2227200, 79.32),
-("coral", 6, 1, 2323200, 88.99),
-("coral", 6, 1, 2419200, 100.68),
-("coral", 7, 2, 825600, 52.7),
-("coral", 7, 2, 940800, 55.9),
-("coral", 7, 2, 1056000, 59.73),
-("coral", 7, 2, 1171200, 63.66),
-("coral", 7, 2, 1286400, 67.28),
-("coral", 7, 2, 1401600, 71.66),
-("coral", 7, 2, 1497600, 76.47),
-("coral", 7, 2, 1612800, 80.92),
-("coral", 7, 2, 1708800, 85.81),
-("coral", 7, 2, 1804800, 93.19),
-("coral", 7, 2, 1920000, 98.06),
-("coral", 7, 2, 2016000, 119.08),
-("coral", 7, 2, 2131200, 127.88),
-("coral", 7, 2, 2227200, 129.85),
-("coral", 7, 2, 2323200, 140.37),
-("coral", 7, 2, 2419200, 151.22),
-("coral", 7, 2, 2534400, 160.73),
-("coral", 7, 2, 2649600, 175.5),
-("coral", 7, 2, 2745600, 186.29),
-("coral", 7, 2, 2841600, 223.89),
-("flame", 0, 0, 300000, 9.86),
-("flame", 0, 0, 403200, 10.335),
-("flame", 0, 0, 499200, 10.8925),
-("flame", 0, 0, 576000, 11.37),
-("flame", 0, 0, 672000, 11.8),
-("flame", 0, 0, 768000, 12.41),
-("flame", 0, 0, 844800, 12.97),
-("flame", 0, 0, 940800, 13.335),
-("flame", 0, 0, 1036800, 14.1725),
-("flame", 0, 0, 1113600, 14.695),
-("flame", 0, 0, 1209600, 15.3525),
-("flame", 0, 0, 1305600, 16.2775),
-("flame", 0, 0, 1382400, 16.8725),
-("flame", 0, 0, 1478400, 17.6525),
-("flame", 0, 0, 1555200, 18.0975),
-("flame", 0, 0, 1632000, 18.8575),
-("flame", 0, 0, 1708800, 20.0525),
-("flame", 0, 0, 1785600, 21.2625),
-("flame", 1, 0, 300000, 9.86),
-("flame", 1, 0, 403200, 10.335),
-("flame", 1, 0, 499200, 10.8925),
-("flame", 1, 0, 576000, 11.37),
-("flame", 1, 0, 672000, 11.8),
-("flame", 1, 0, 768000, 12.41),
-("flame", 1, 0, 844800, 12.97),
-("flame", 1, 0, 940800, 13.335),
-("flame", 1, 0, 1036800, 14.1725),
-("flame", 1, 0, 1113600, 14.695),
-("flame", 1, 0, 1209600, 15.3525),
-("flame", 1, 0, 1305600, 16.2775),
-("flame", 1, 0, 1382400, 16.8725),
-("flame", 1, 0, 1478400, 17.6525),
-("flame", 1, 0, 1555200, 18.0975),
-("flame", 1, 0, 1632000, 18.8575),
-("flame", 1, 0, 1708800, 20.0525),
-("flame", 1, 0, 1785600, 21.2625),
-("flame", 2, 0, 300000, 9.86),
-("flame", 2, 0, 403200, 10.335),
-("flame", 2, 0, 499200, 10.8925),
-("flame", 2, 0, 576000, 11.37),
-("flame", 2, 0, 672000, 11.8),
-("flame", 2, 0, 768000, 12.41),
-("flame", 2, 0, 844800, 12.97),
-("flame", 2, 0, 940800, 13.335),
-("flame", 2, 0, 1036800, 14.1725),
-("flame", 2, 0, 1113600, 14.695),
-("flame", 2, 0, 1209600, 15.3525),
-("flame", 2, 0, 1305600, 16.2775),
-("flame", 2, 0, 1382400, 16.8725),
-("flame", 2, 0, 1478400, 17.6525),
-("flame", 2, 0, 1555200, 18.0975),
-("flame", 2, 0, 1632000, 18.8575),
-("flame", 2, 0, 1708800, 20.0525),
-("flame", 2, 0, 1785600, 21.2625),
-("flame", 3, 0, 300000, 9.86),
-("flame", 3, 0, 403200, 10.335),
-("flame", 3, 0, 499200, 10.8925),
-("flame", 3, 0, 576000, 11.37),
-("flame", 3, 0, 672000, 11.8),
-("flame", 3, 0, 768000, 12.41),
-("flame", 3, 0, 844800, 12.97),
-("flame", 3, 0, 940800, 13.335),
-("flame", 3, 0, 1036800, 14.1725),
-("flame", 3, 0, 1113600, 14.695),
-("flame", 3, 0, 1209600, 15.3525),
-("flame", 3, 0, 1305600, 16.2775),
-("flame", 3, 0, 1382400, 16.8725),
-("flame", 3, 0, 1478400, 17.6525),
-("flame", 3, 0, 1555200, 18.0975),
-("flame", 3, 0, 1632000, 18.8575),
-("flame", 3, 0, 1708800, 20.0525),
-("flame", 3, 0, 1785600, 21.2625),
-("flame", 4, 1, 710400, 16.7833333333),
-("flame", 4, 1, 825600, 18.3733333333),
-("flame", 4, 1, 940800, 20.4833333333),
-("flame", 4, 1, 1056000, 23.3066666667),
-("flame", 4, 1, 1171200, 25.8266666667),
-("flame", 4, 1, 1286400, 28.45),
-("flame", 4, 1, 1401600, 31.7233333333),
-("flame", 4, 1, 1497600, 34.42),
-("flame", 4, 1, 1612800, 39.3966666667),
-("flame", 4, 1, 1708800, 44.24),
-("flame", 4, 1, 1804800, 47.9433333333),
-("flame", 4, 1, 1920000, 51.97),
-("flame", 4, 1, 2016000, 63.3866666667),
-("flame", 4, 1, 2131200, 71.0366666667),
-("flame", 4, 1, 2227200, 79.32),
-("flame", 4, 1, 2323200, 88.99),
-("flame", 4, 1, 2419200, 100.68),
-("flame", 5, 1, 710400, 16.7833333333),
-("flame", 5, 1, 825600, 18.3733333333),
-("flame", 5, 1, 940800, 20.4833333333),
-("flame", 5, 1, 1056000, 23.3066666667),
-("flame", 5, 1, 1171200, 25.8266666667),
-("flame", 5, 1, 1286400, 28.45),
-("flame", 5, 1, 1401600, 31.7233333333),
-("flame", 5, 1, 1497600, 34.42),
-("flame", 5, 1, 1612800, 39.3966666667),
-("flame", 5, 1, 1708800, 44.24),
-("flame", 5, 1, 1804800, 47.9433333333),
-("flame", 5, 1, 1920000, 51.97),
-("flame", 5, 1, 2016000, 63.3866666667),
-("flame", 5, 1, 2131200, 71.0366666667),
-("flame", 5, 1, 2227200, 79.32),
-("flame", 5, 1, 2323200, 88.99),
-("flame", 5, 1, 2419200, 100.68),
-("flame", 6, 1, 710400, 16.7833333333),
-("flame", 6, 1, 825600, 18.3733333333),
-("flame", 6, 1, 940800, 20.4833333333),
-("flame", 6, 1, 1056000, 23.3066666667),
-("flame", 6, 1, 1171200, 25.8266666667),
-("flame", 6, 1, 1286400, 28.45),
-("flame", 6, 1, 1401600, 31.7233333333),
-("flame", 6, 1, 1497600, 34.42),
-("flame", 6, 1, 1612800, 39.3966666667),
-("flame", 6, 1, 1708800, 44.24),
-("flame", 6, 1, 1804800, 47.9433333333),
-("flame", 6, 1, 1920000, 51.97),
-("flame", 6, 1, 2016000, 63.3866666667),
-("flame", 6, 1, 2131200, 71.0366666667),
-("flame", 6, 1, 2227200, 79.32),
-("flame", 6, 1, 2323200, 88.99),
-("flame", 6, 1, 2419200, 100.68),
-("flame", 7, 2, 825600, 52.7),
-("flame", 7, 2, 940800, 55.9),
-("flame", 7, 2, 1056000, 59.73),
-("flame", 7, 2, 1171200, 63.66),
-("flame", 7, 2, 1286400, 67.28),
-("flame", 7, 2, 1401600, 71.66),
-("flame", 7, 2, 1497600, 76.47),
-("flame", 7, 2, 1612800, 80.92),
-("flame", 7, 2, 1708800, 85.81),
-("flame", 7, 2, 1804800, 93.19),
-("flame", 7, 2, 1920000, 98.06),
-("flame", 7, 2, 2016000, 119.08),
-("flame", 7, 2, 2131200, 127.88),
-("flame", 7, 2, 2227200, 129.85),
-("flame", 7, 2, 2323200, 140.37),
-("flame", 7, 2, 2419200, 151.22),
-("flame", 7, 2, 2534400, 160.73),
-("flame", 7, 2, 2649600, 175.5),
-("flame", 7, 2, 2745600, 186.29),
-("flame", 7, 2, 2841600, 223.89),
-("sunfish", 0, 0, 300000, 5.75833333333),
-("sunfish", 0, 0, 576000, 7.76166666667),
-("sunfish", 0, 0, 768000, 9.14),
-("sunfish", 0, 0, 1017600, 11.36),
-("sunfish", 0, 0, 1248000, 13.45),
-("sunfish", 0, 0, 1324800, 14.4333333333),
-("sunfish", 0, 0, 1497600, 16.5216666667),
-("sunfish", 0, 0, 1612800, 18.5083333333),
-("sunfish", 0, 0, 1708800, 19.9316666667),
-("sunfish", 0, 0, 1804800, 21.4083333333),
-("sunfish", 1, 0, 300000, 5.75833333333),
-("sunfish", 1, 0, 576000, 7.76166666667),
-("sunfish", 1, 0, 768000, 9.14),
-("sunfish", 1, 0, 1017600, 11.36),
-("sunfish", 1, 0, 1248000, 13.45),
-("sunfish", 1, 0, 1324800, 14.4333333333),
-("sunfish", 1, 0, 1497600, 16.5216666667),
-("sunfish", 1, 0, 1612800, 18.5083333333),
-("sunfish", 1, 0, 1708800, 19.9316666667),
-("sunfish", 1, 0, 1804800, 21.4083333333),
-("sunfish", 2, 0, 300000, 5.75833333333),
-("sunfish", 2, 0, 576000, 7.76166666667),
-("sunfish", 2, 0, 768000, 9.14),
-("sunfish", 2, 0, 1017600, 11.36),
-("sunfish", 2, 0, 1248000, 13.45),
-("sunfish", 2, 0, 1324800, 14.4333333333),
-("sunfish", 2, 0, 1497600, 16.5216666667),
-("sunfish", 2, 0, 1612800, 18.5083333333),
-("sunfish", 2, 0, 1708800, 19.9316666667),
-("sunfish", 2, 0, 1804800, 21.4083333333),
-("sunfish", 3, 0, 300000, 5.75833333333),
-("sunfish", 3, 0, 576000, 7.76166666667),
-("sunfish", 3, 0, 768000, 9.14),
-("sunfish", 3, 0, 1017600, 11.36),
-("sunfish", 3, 0, 1248000, 13.45),
-("sunfish", 3, 0, 1324800, 14.4333333333),
-("sunfish", 3, 0, 1497600, 16.5216666667),
-("sunfish", 3, 0, 1612800, 18.5083333333),
-("sunfish", 3, 0, 1708800, 19.9316666667),
-("sunfish", 3, 0, 1804800, 21.4083333333),
-("sunfish", 4, 0, 300000, 5.75833333333),
-("sunfish", 4, 0, 576000, 7.76166666667),
-("sunfish", 4, 0, 768000, 9.14),
-("sunfish", 4, 0, 1017600, 11.36),
-("sunfish", 4, 0, 1248000, 13.45),
-("sunfish", 4, 0, 1324800, 14.4333333333),
-("sunfish", 4, 0, 1497600, 16.5216666667),
-("sunfish", 4, 0, 1612800, 18.5083333333),
-("sunfish", 4, 0, 1708800, 19.9316666667),
-("sunfish", 4, 0, 1804800, 21.4083333333),
-("sunfish", 5, 0, 300000, 5.75833333333),
-("sunfish", 5, 0, 576000, 7.76166666667),
-("sunfish", 5, 0, 768000, 9.14),
-("sunfish", 5, 0, 1017600, 11.36),
-("sunfish", 5, 0, 1248000, 13.45),
-("sunfish", 5, 0, 1324800, 14.4333333333),
-("sunfish", 5, 0, 1497600, 16.5216666667),
-("sunfish", 5, 0, 1612800, 18.5083333333),
-("sunfish", 5, 0, 1708800, 19.9316666667),
-("sunfish", 5, 0, 1804800, 21.4083333333),
-("sunfish", 6, 1, 300000, 21.115),
-("sunfish", 6, 1, 652800, 28.46),
-("sunfish", 6, 1, 806400, 31.705),
-("sunfish", 6, 1, 979200, 36.515),
-("sunfish", 6, 1, 1094400, 40.19),
-("sunfish", 6, 1, 1209600, 43.585),
-("sunfish", 6, 1, 1324800, 48.275),
-("sunfish", 6, 1, 1555200, 62.805),
-("sunfish", 6, 1, 1708800, 72.755),
-("sunfish", 6, 1, 1843200, 91.47),
-("sunfish", 6, 1, 1939200, 99.46),
-("sunfish", 6, 1, 2169600, 119.27),
-("sunfish", 6, 1, 2208000, 133.105),
-("sunfish", 7, 1, 300000, 21.115),
-("sunfish", 7, 1, 652800, 28.46),
-("sunfish", 7, 1, 806400, 31.705),
-("sunfish", 7, 1, 979200, 36.515),
-("sunfish", 7, 1, 1094400, 40.19),
-("sunfish", 7, 1, 1209600, 43.585),
-("sunfish", 7, 1, 1324800, 48.275),
-("sunfish", 7, 1, 1555200, 62.805),
-("sunfish", 7, 1, 1708800, 72.755),
-("sunfish", 7, 1, 1843200, 91.47),
-("sunfish", 7, 1, 1939200, 99.46),
-("sunfish", 7, 1, 2169600, 119.27),
-("sunfish", 7, 1, 2208000, 133.105),
-("redfin", 0, 0, 300000, 6.98666666667),
-("redfin", 0, 0, 576000, 9.93166666667),
-("redfin", 0, 0, 614400, 10.3216666667),
-("redfin", 0, 0, 864000, 13.31),
-("redfin", 0, 0, 1075200, 15.9866666667),
-("redfin", 0, 0, 1363200, 20.3283333333),
-("redfin", 0, 0, 1516800, 23.4533333333),
-("redfin", 0, 0, 1651200, 26.53),
-("redfin", 0, 0, 1804800, 29.365),
-("redfin", 1, 0, 300000, 6.98666666667),
-("redfin", 1, 0, 576000, 9.93166666667),
-("redfin", 1, 0, 614400, 10.3216666667),
-("redfin", 1, 0, 864000, 13.31),
-("redfin", 1, 0, 1075200, 15.9866666667),
-("redfin", 1, 0, 1363200, 20.3283333333),
-("redfin", 1, 0, 1516800, 23.4533333333),
-("redfin", 1, 0, 1651200, 26.53),
-("redfin", 1, 0, 1804800, 29.365),
-("redfin", 2, 0, 300000, 6.98666666667),
-("redfin", 2, 0, 576000, 9.93166666667),
-("redfin", 2, 0, 614400, 10.3216666667),
-("redfin", 2, 0, 864000, 13.31),
-("redfin", 2, 0, 1075200, 15.9866666667),
-("redfin", 2, 0, 1363200, 20.3283333333),
-("redfin", 2, 0, 1516800, 23.4533333333),
-("redfin", 2, 0, 1651200, 26.53),
-("redfin", 2, 0, 1804800, 29.365),
-("redfin", 3, 0, 300000, 6.98666666667),
-("redfin", 3, 0, 576000, 9.93166666667),
-("redfin", 3, 0, 614400, 10.3216666667),
-("redfin", 3, 0, 864000, 13.31),
-("redfin", 3, 0, 1075200, 15.9866666667),
-("redfin", 3, 0, 1363200, 20.3283333333),
-("redfin", 3, 0, 1516800, 23.4533333333),
-("redfin", 3, 0, 1651200, 26.53),
-("redfin", 3, 0, 1804800, 29.365),
-("redfin", 4, 0, 300000, 6.98666666667),
-("redfin", 4, 0, 576000, 9.93166666667),
-("redfin", 4, 0, 614400, 10.3216666667),
-("redfin", 4, 0, 864000, 13.31),
-("redfin", 4, 0, 1075200, 15.9866666667),
-("redfin", 4, 0, 1363200, 20.3283333333),
-("redfin", 4, 0, 1516800, 23.4533333333),
-("redfin", 4, 0, 1651200, 26.53),
-("redfin", 4, 0, 1804800, 29.365),
-("redfin", 5, 0, 300000, 6.98666666667),
-("redfin", 5, 0, 576000, 9.93166666667),
-("redfin", 5, 0, 614400, 10.3216666667),
-("redfin", 5, 0, 864000, 13.31),
-("redfin", 5, 0, 1075200, 15.9866666667),
-("redfin", 5, 0, 1363200, 20.3283333333),
-("redfin", 5, 0, 1516800, 23.4533333333),
-("redfin", 5, 0, 1651200, 26.53),
-("redfin", 5, 0, 1804800, 29.365),
-("redfin", 6, 1, 652800, 32.13),
-("redfin", 6, 1, 940800, 35.98),
-("redfin", 6, 1, 1152000, 40.03),
-("redfin", 6, 1, 1478400, 51.02),
-("redfin", 6, 1, 1728000, 77.06),
-("redfin", 6, 1, 1900800, 86.25),
-("redfin", 6, 1, 2092800, 97.3),
-("redfin", 6, 1, 2208000, 101.61),
-("redfin", 7, 2, 806400, 56.44),
-("redfin", 7, 2, 1094400, 65.72),
-("redfin", 7, 2, 1401600, 77.01),
-("redfin", 7, 2, 1766400, 104.91),
-("redfin", 7, 2, 1996800, 112.35),
-("redfin", 7, 2, 2188800, 118.53),
-("redfin", 7, 2, 2304000, 122.34),
-("redfin", 7, 2, 2400000, 135.0),
-("bramble", 0, 0, 300000, 6.98666666667),
-("bramble", 0, 0, 576000, 9.93166666667),
-("bramble", 0, 0, 614400, 10.3216666667),
-("bramble", 0, 0, 864000, 13.31),
-("bramble", 0, 0, 1075200, 15.9866666667),
-("bramble", 0, 0, 1363200, 20.3283333333),
-("bramble", 0, 0, 1516800, 23.4533333333),
-("bramble", 0, 0, 1651200, 26.53),
-("bramble", 0, 0, 1804800, 29.365),
-("bramble", 1, 0, 300000, 6.98666666667),
-("bramble", 1, 0, 576000, 9.93166666667),
-("bramble", 1, 0, 614400, 10.3216666667),
-("bramble", 1, 0, 864000, 13.31),
-("bramble", 1, 0, 1075200, 15.9866666667),
-("bramble", 1, 0, 1363200, 20.3283333333),
-("bramble", 1, 0, 1516800, 23.4533333333),
-("bramble", 1, 0, 1651200, 26.53),
-("bramble", 1, 0, 1804800, 29.365),
-("bramble", 2, 0, 300000, 6.98666666667),
-("bramble", 2, 0, 576000, 9.93166666667),
-("bramble", 2, 0, 614400, 10.3216666667),
-("bramble", 2, 0, 864000, 13.31),
-("bramble", 2, 0, 1075200, 15.9866666667),
-("bramble", 2, 0, 1363200, 20.3283333333),
-("bramble", 2, 0, 1516800, 23.4533333333),
-("bramble", 2, 0, 1651200, 26.53),
-("bramble", 2, 0, 1804800, 29.365),
-("bramble", 3, 0, 300000, 6.98666666667),
-("bramble", 3, 0, 576000, 9.93166666667),
-("bramble", 3, 0, 614400, 10.3216666667),
-("bramble", 3, 0, 864000, 13.31),
-("bramble", 3, 0, 1075200, 15.9866666667),
-("bramble", 3, 0, 1363200, 20.3283333333),
-("bramble", 3, 0, 1516800, 23.4533333333),
-("bramble", 3, 0, 1651200, 26.53),
-("bramble", 3, 0, 1804800, 29.365),
-("bramble", 4, 0, 300000, 6.98666666667),
-("bramble", 4, 0, 576000, 9.93166666667),
-("bramble", 4, 0, 614400, 10.3216666667),
-("bramble", 4, 0, 864000, 13.31),
-("bramble", 4, 0, 1075200, 15.9866666667),
-("bramble", 4, 0, 1363200, 20.3283333333),
-("bramble", 4, 0, 1516800, 23.4533333333),
-("bramble", 4, 0, 1651200, 26.53),
-("bramble", 4, 0, 1804800, 29.365),
-("bramble", 5, 0, 300000, 6.98666666667),
-("bramble", 5, 0, 576000, 9.93166666667),
-("bramble", 5, 0, 614400, 10.3216666667),
-("bramble", 5, 0, 864000, 13.31),
-("bramble", 5, 0, 1075200, 15.9866666667),
-("bramble", 5, 0, 1363200, 20.3283333333),
-("bramble", 5, 0, 1516800, 23.4533333333),
-("bramble", 5, 0, 1651200, 26.53),
-("bramble", 5, 0, 1804800, 29.365),
-("bramble", 6, 1, 652800, 32.13),
-("bramble", 6, 1, 940800, 35.98),
-("bramble", 6, 1, 1152000, 40.03),
-("bramble", 6, 1, 1478400, 51.02),
-("bramble", 6, 1, 1728000, 77.06),
-("bramble", 6, 1, 1900800, 86.25),
-("bramble", 6, 1, 2092800, 97.3),
-("bramble", 6, 1, 2208000, 101.61),
-("bramble", 7, 2, 806400, 56.44),
-("bramble", 7, 2, 1094400, 65.72),
-("bramble", 7, 2, 1401600, 77.01),
-("bramble", 7, 2, 1766400, 104.91),
-("bramble", 7, 2, 1996800, 112.35),
-("bramble", 7, 2, 2188800, 118.53),
-("bramble", 7, 2, 2304000, 122.34),
-("bramble", 7, 2, 2400000, 135.0);
\ No newline at end of file
+SELECT RUN_METRIC('android/power_profile_data/marlin.sql');
+SELECT RUN_METRIC('android/power_profile_data/walleye.sql');
+SELECT RUN_METRIC('android/power_profile_data/taimen.sql');
+SELECT RUN_METRIC('android/power_profile_data/blueline.sql');
+SELECT RUN_METRIC('android/power_profile_data/crosshatch.sql');
+SELECT RUN_METRIC('android/power_profile_data/bonito.sql');
+SELECT RUN_METRIC('android/power_profile_data/sargo.sql');
+SELECT RUN_METRIC('android/power_profile_data/flame.sql');
+SELECT RUN_METRIC('android/power_profile_data/coral.sql');
+SELECT RUN_METRIC('android/power_profile_data/sunfish.sql');
+SELECT RUN_METRIC('android/power_profile_data/bramble.sql');
+SELECT RUN_METRIC('android/power_profile_data/redfin.sql');
+SELECT RUN_METRIC('android/power_profile_data/barbet.sql');
+SELECT RUN_METRIC('android/power_profile_data/oriole.sql');
+SELECT RUN_METRIC('android/power_profile_data/raven.sql');
+SELECT RUN_METRIC('android/power_profile_data/bluejay.sql');
diff --git a/src/trace_processor/metrics/sql/android/power_profile_data/barbet.sql b/src/trace_processor/metrics/sql/android/power_profile_data/barbet.sql
new file mode 100644
index 0000000..ea3500f
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/power_profile_data/barbet.sql
@@ -0,0 +1,86 @@
+--
+-- 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.
+
+INSERT OR REPLACE INTO power_profile VALUES
+("barbet", 0, 0, 300000, 41.92),
+("barbet", 0, 0, 576000, 59.59),
+("barbet", 0, 0, 614400, 61.93),
+("barbet", 0, 0, 864000, 79.86),
+("barbet", 0, 0, 1075200, 95.92),
+("barbet", 0, 0, 1363200, 121.97),
+("barbet", 0, 0, 1516800, 140.72),
+("barbet", 0, 0, 1651200, 159.18),
+("barbet", 0, 0, 1804800, 176.19),
+("barbet", 1, 0, 300000, 41.92),
+("barbet", 1, 0, 576000, 59.59),
+("barbet", 1, 0, 614400, 61.93),
+("barbet", 1, 0, 864000, 79.86),
+("barbet", 1, 0, 1075200, 95.92),
+("barbet", 1, 0, 1363200, 121.97),
+("barbet", 1, 0, 1516800, 140.72),
+("barbet", 1, 0, 1651200, 159.18),
+("barbet", 1, 0, 1804800, 176.19),
+("barbet", 2, 0, 300000, 41.92),
+("barbet", 2, 0, 576000, 59.59),
+("barbet", 2, 0, 614400, 61.93),
+("barbet", 2, 0, 864000, 79.86),
+("barbet", 2, 0, 1075200, 95.92),
+("barbet", 2, 0, 1363200, 121.97),
+("barbet", 2, 0, 1516800, 140.72),
+("barbet", 2, 0, 1651200, 159.18),
+("barbet", 2, 0, 1804800, 176.19),
+("barbet", 3, 0, 300000, 41.92),
+("barbet", 3, 0, 576000, 59.59),
+("barbet", 3, 0, 614400, 61.93),
+("barbet", 3, 0, 864000, 79.86),
+("barbet", 3, 0, 1075200, 95.92),
+("barbet", 3, 0, 1363200, 121.97),
+("barbet", 3, 0, 1516800, 140.72),
+("barbet", 3, 0, 1651200, 159.18),
+("barbet", 3, 0, 1804800, 176.19),
+("barbet", 4, 0, 300000, 41.92),
+("barbet", 4, 0, 576000, 59.59),
+("barbet", 4, 0, 614400, 61.93),
+("barbet", 4, 0, 864000, 79.86),
+("barbet", 4, 0, 1075200, 95.92),
+("barbet", 4, 0, 1363200, 121.97),
+("barbet", 4, 0, 1516800, 140.72),
+("barbet", 4, 0, 1651200, 159.18),
+("barbet", 4, 0, 1804800, 176.19),
+("barbet", 5, 0, 300000, 41.92),
+("barbet", 5, 0, 576000, 59.59),
+("barbet", 5, 0, 614400, 61.93),
+("barbet", 5, 0, 864000, 79.86),
+("barbet", 5, 0, 1075200, 95.92),
+("barbet", 5, 0, 1363200, 121.97),
+("barbet", 5, 0, 1516800, 140.72),
+("barbet", 5, 0, 1651200, 159.18),
+("barbet", 5, 0, 1804800, 176.19),
+("barbet", 6, 1, 652800, 32.13),
+("barbet", 6, 1, 940800, 35.98),
+("barbet", 6, 1, 1152000, 40.03),
+("barbet", 6, 1, 1478400, 51.02),
+("barbet", 6, 1, 1728000, 77.06),
+("barbet", 6, 1, 1900800, 86.25),
+("barbet", 6, 1, 2092800, 97.3),
+("barbet", 6, 1, 2208000, 101.61),
+("barbet", 7, 2, 806400, 56.44),
+("barbet", 7, 2, 1094400, 65.72),
+("barbet", 7, 2, 1401600, 77.01),
+("barbet", 7, 2, 1766400, 104.91),
+("barbet", 7, 2, 1996800, 112.35),
+("barbet", 7, 2, 2188800, 118.53),
+("barbet", 7, 2, 2304000, 122.34),
+("barbet", 7, 2, 2400000, 135.0);
diff --git a/src/trace_processor/metrics/sql/android/power_profile_data/bluejay.sql b/src/trace_processor/metrics/sql/android/power_profile_data/bluejay.sql
new file mode 100644
index 0000000..f2c2ba3
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/power_profile_data/bluejay.sql
@@ -0,0 +1,122 @@
+--
+-- Copyright 2022 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.
+
+INSERT OR REPLACE INTO power_profile VALUES
+("bluejay", 0, 0, 300000, 1.89),
+("bluejay", 0, 0, 574000, 6.15),
+("bluejay", 0, 0, 738000, 9.34),
+("bluejay", 0, 0, 930000, 14.22),
+("bluejay", 0, 0, 1098000, 18.94),
+("bluejay", 0, 0, 1197000, 21.98),
+("bluejay", 0, 0, 1328000, 26.83),
+("bluejay", 0, 0, 1401000, 30.17),
+("bluejay", 0, 0, 1598000, 41.55),
+("bluejay", 0, 0, 1704000, 48.36),
+("bluejay", 0, 0, 1803000, 58.45),
+("bluejay", 1, 0, 300000, 1.89),
+("bluejay", 1, 0, 574000, 6.15),
+("bluejay", 1, 0, 738000, 9.34),
+("bluejay", 1, 0, 930000, 14.22),
+("bluejay", 1, 0, 1098000, 18.94),
+("bluejay", 1, 0, 1197000, 21.98),
+("bluejay", 1, 0, 1328000, 26.83),
+("bluejay", 1, 0, 1401000, 30.17),
+("bluejay", 1, 0, 1598000, 41.55),
+("bluejay", 1, 0, 1704000, 48.36),
+("bluejay", 1, 0, 1803000, 58.45),
+("bluejay", 2, 0, 300000, 1.89),
+("bluejay", 2, 0, 574000, 6.15),
+("bluejay", 2, 0, 738000, 9.34),
+("bluejay", 2, 0, 930000, 14.22),
+("bluejay", 2, 0, 1098000, 18.94),
+("bluejay", 2, 0, 1197000, 21.98),
+("bluejay", 2, 0, 1328000, 26.83),
+("bluejay", 2, 0, 1401000, 30.17),
+("bluejay", 2, 0, 1598000, 41.55),
+("bluejay", 2, 0, 1704000, 48.36),
+("bluejay", 2, 0, 1803000, 58.45),
+("bluejay", 3, 0, 300000, 1.89),
+("bluejay", 3, 0, 574000, 6.15),
+("bluejay", 3, 0, 738000, 9.34),
+("bluejay", 3, 0, 930000, 14.22),
+("bluejay", 3, 0, 1098000, 18.94),
+("bluejay", 3, 0, 1197000, 21.98),
+("bluejay", 3, 0, 1328000, 26.83),
+("bluejay", 3, 0, 1401000, 30.17),
+("bluejay", 3, 0, 1598000, 41.55),
+("bluejay", 3, 0, 1704000, 48.36),
+("bluejay", 3, 0, 1803000, 58.45),
+("bluejay", 4, 1, 400000, 3.71),
+("bluejay", 4, 1, 553000, 6.16),
+("bluejay", 4, 1, 696000, 8.0),
+("bluejay", 4, 1, 799000, 10.94),
+("bluejay", 4, 1, 910000, 12.73),
+("bluejay", 4, 1, 1024000, 14.4),
+("bluejay", 4, 1, 1197000, 21.39),
+("bluejay", 4, 1, 1328000, 24.1),
+("bluejay", 4, 1, 1491000, 30.42),
+("bluejay", 4, 1, 1663000, 42.49),
+("bluejay", 4, 1, 1836000, 49.37),
+("bluejay", 4, 1, 1999000, 58.09),
+("bluejay", 4, 1, 2130000, 67.54),
+("bluejay", 4, 1, 2253000, 79.04),
+("bluejay", 5, 1, 400000, 3.71),
+("bluejay", 5, 1, 553000, 6.16),
+("bluejay", 5, 1, 696000, 8.0),
+("bluejay", 5, 1, 799000, 10.94),
+("bluejay", 5, 1, 910000, 12.73),
+("bluejay", 5, 1, 1024000, 14.4),
+("bluejay", 5, 1, 1197000, 21.39),
+("bluejay", 5, 1, 1328000, 24.1),
+("bluejay", 5, 1, 1491000, 30.42),
+("bluejay", 5, 1, 1663000, 42.49),
+("bluejay", 5, 1, 1836000, 49.37),
+("bluejay", 5, 1, 1999000, 58.09),
+("bluejay", 5, 1, 2130000, 67.54),
+("bluejay", 5, 1, 2253000, 79.04),
+("bluejay", 6, 2, 500000, 8.36),
+("bluejay", 6, 2, 851000, 16.33),
+("bluejay", 6, 2, 984000, 19.44),
+("bluejay", 6, 2, 1106000, 36.71),
+("bluejay", 6, 2, 1277000, 41.42),
+("bluejay", 6, 2, 1426000, 48.24),
+("bluejay", 6, 2, 1582000, 54.77),
+("bluejay", 6, 2, 1745000, 65.32),
+("bluejay", 6, 2, 1826000, 69.58),
+("bluejay", 6, 2, 2048000, 128.49),
+("bluejay", 6, 2, 2188000, 142.15),
+("bluejay", 6, 2, 2252000, 149.74),
+("bluejay", 6, 2, 2401000, 164.78),
+("bluejay", 6, 2, 2507000, 188.68),
+("bluejay", 6, 2, 2630000, 193.15),
+("bluejay", 6, 2, 2704000, 227.98),
+("bluejay", 6, 2, 2802000, 254.25),
+("bluejay", 7, 2, 500000, 8.36),
+("bluejay", 7, 2, 851000, 16.33),
+("bluejay", 7, 2, 984000, 19.44),
+("bluejay", 7, 2, 1106000, 36.71),
+("bluejay", 7, 2, 1277000, 41.42),
+("bluejay", 7, 2, 1426000, 48.24),
+("bluejay", 7, 2, 1582000, 54.77),
+("bluejay", 7, 2, 1745000, 65.32),
+("bluejay", 7, 2, 1826000, 69.58),
+("bluejay", 7, 2, 2048000, 128.49),
+("bluejay", 7, 2, 2188000, 142.15),
+("bluejay", 7, 2, 2252000, 149.74),
+("bluejay", 7, 2, 2401000, 164.78),
+("bluejay", 7, 2, 2507000, 188.68),
+("bluejay", 7, 2, 2630000, 193.15),
+("bluejay", 7, 2, 2704000, 227.98),
+("bluejay", 7, 2, 2802000, 254.25);
diff --git a/src/trace_processor/metrics/sql/android/power_profile_data/blueline.sql b/src/trace_processor/metrics/sql/android/power_profile_data/blueline.sql
new file mode 100644
index 0000000..f6b0f08
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/power_profile_data/blueline.sql
@@ -0,0 +1,180 @@
+--
+-- 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.
+
+INSERT OR REPLACE INTO power_profile VALUES
+("blueline", 0, 0, 300000, 2.27),
+("blueline", 0, 0, 403200, 3.63),
+("blueline", 0, 0, 480000, 4.36),
+("blueline", 0, 0, 576000, 5.21),
+("blueline", 0, 0, 652800, 5.47),
+("blueline", 0, 0, 748800, 6.74),
+("blueline", 0, 0, 825600, 7.69),
+("blueline", 0, 0, 902400, 8.57),
+("blueline", 0, 0, 979200, 9.42),
+("blueline", 0, 0, 1056000, 10.41),
+("blueline", 0, 0, 1132800, 11.56),
+("blueline", 0, 0, 1228800, 12.87),
+("blueline", 0, 0, 1324800, 14.61),
+("blueline", 0, 0, 1420800, 16.49),
+("blueline", 0, 0, 1516800, 18.9),
+("blueline", 0, 0, 1612800, 21.62),
+("blueline", 0, 0, 1689600, 24.47),
+("blueline", 0, 0, 1766400, 26.45),
+("blueline", 1, 0, 300000, 2.27),
+("blueline", 1, 0, 403200, 3.63),
+("blueline", 1, 0, 480000, 4.36),
+("blueline", 1, 0, 576000, 5.21),
+("blueline", 1, 0, 652800, 5.47),
+("blueline", 1, 0, 748800, 6.74),
+("blueline", 1, 0, 825600, 7.69),
+("blueline", 1, 0, 902400, 8.57),
+("blueline", 1, 0, 979200, 9.42),
+("blueline", 1, 0, 1056000, 10.41),
+("blueline", 1, 0, 1132800, 11.56),
+("blueline", 1, 0, 1228800, 12.87),
+("blueline", 1, 0, 1324800, 14.61),
+("blueline", 1, 0, 1420800, 16.49),
+("blueline", 1, 0, 1516800, 18.9),
+("blueline", 1, 0, 1612800, 21.62),
+("blueline", 1, 0, 1689600, 24.47),
+("blueline", 1, 0, 1766400, 26.45),
+("blueline", 2, 0, 300000, 2.27),
+("blueline", 2, 0, 403200, 3.63),
+("blueline", 2, 0, 480000, 4.36),
+("blueline", 2, 0, 576000, 5.21),
+("blueline", 2, 0, 652800, 5.47),
+("blueline", 2, 0, 748800, 6.74),
+("blueline", 2, 0, 825600, 7.69),
+("blueline", 2, 0, 902400, 8.57),
+("blueline", 2, 0, 979200, 9.42),
+("blueline", 2, 0, 1056000, 10.41),
+("blueline", 2, 0, 1132800, 11.56),
+("blueline", 2, 0, 1228800, 12.87),
+("blueline", 2, 0, 1324800, 14.61),
+("blueline", 2, 0, 1420800, 16.49),
+("blueline", 2, 0, 1516800, 18.9),
+("blueline", 2, 0, 1612800, 21.62),
+("blueline", 2, 0, 1689600, 24.47),
+("blueline", 2, 0, 1766400, 26.45),
+("blueline", 3, 0, 300000, 2.27),
+("blueline", 3, 0, 403200, 3.63),
+("blueline", 3, 0, 480000, 4.36),
+("blueline", 3, 0, 576000, 5.21),
+("blueline", 3, 0, 652800, 5.47),
+("blueline", 3, 0, 748800, 6.74),
+("blueline", 3, 0, 825600, 7.69),
+("blueline", 3, 0, 902400, 8.57),
+("blueline", 3, 0, 979200, 9.42),
+("blueline", 3, 0, 1056000, 10.41),
+("blueline", 3, 0, 1132800, 11.56),
+("blueline", 3, 0, 1228800, 12.87),
+("blueline", 3, 0, 1324800, 14.61),
+("blueline", 3, 0, 1420800, 16.49),
+("blueline", 3, 0, 1516800, 18.9),
+("blueline", 3, 0, 1612800, 21.62),
+("blueline", 3, 0, 1689600, 24.47),
+("blueline", 3, 0, 1766400, 26.45),
+("blueline", 4, 1, 825600, 28.88),
+("blueline", 4, 1, 902400, 32.4),
+("blueline", 4, 1, 979200, 36.46),
+("blueline", 4, 1, 1056000, 39.99),
+("blueline", 4, 1, 1209600, 47.23),
+("blueline", 4, 1, 1286400, 51.39),
+("blueline", 4, 1, 1363200, 56.9),
+("blueline", 4, 1, 1459200, 64.26),
+("blueline", 4, 1, 1536000, 69.65),
+("blueline", 4, 1, 1612800, 75.14),
+("blueline", 4, 1, 1689600, 83.16),
+("blueline", 4, 1, 1766400, 91.75),
+("blueline", 4, 1, 1843200, 100.66),
+("blueline", 4, 1, 1920000, 111.45),
+("blueline", 4, 1, 1996800, 122.23),
+("blueline", 4, 1, 2092800, 143.54),
+("blueline", 4, 1, 2169600, 147.54),
+("blueline", 4, 1, 2246400, 153.09),
+("blueline", 4, 1, 2323200, 166.44),
+("blueline", 4, 1, 2400000, 184.69),
+("blueline", 4, 1, 2476800, 204.14),
+("blueline", 4, 1, 2553600, 223.37),
+("blueline", 4, 1, 2649600, 253.77),
+("blueline", 5, 1, 825600, 28.88),
+("blueline", 5, 1, 902400, 32.4),
+("blueline", 5, 1, 979200, 36.46),
+("blueline", 5, 1, 1056000, 39.99),
+("blueline", 5, 1, 1209600, 47.23),
+("blueline", 5, 1, 1286400, 51.39),
+("blueline", 5, 1, 1363200, 56.9),
+("blueline", 5, 1, 1459200, 64.26),
+("blueline", 5, 1, 1536000, 69.65),
+("blueline", 5, 1, 1612800, 75.14),
+("blueline", 5, 1, 1689600, 83.16),
+("blueline", 5, 1, 1766400, 91.75),
+("blueline", 5, 1, 1843200, 100.66),
+("blueline", 5, 1, 1920000, 111.45),
+("blueline", 5, 1, 1996800, 122.23),
+("blueline", 5, 1, 2092800, 143.54),
+("blueline", 5, 1, 2169600, 147.54),
+("blueline", 5, 1, 2246400, 153.09),
+("blueline", 5, 1, 2323200, 166.44),
+("blueline", 5, 1, 2400000, 184.69),
+("blueline", 5, 1, 2476800, 204.14),
+("blueline", 5, 1, 2553600, 223.37),
+("blueline", 5, 1, 2649600, 253.77),
+("blueline", 6, 1, 825600, 28.88),
+("blueline", 6, 1, 902400, 32.4),
+("blueline", 6, 1, 979200, 36.46),
+("blueline", 6, 1, 1056000, 39.99),
+("blueline", 6, 1, 1209600, 47.23),
+("blueline", 6, 1, 1286400, 51.39),
+("blueline", 6, 1, 1363200, 56.9),
+("blueline", 6, 1, 1459200, 64.26),
+("blueline", 6, 1, 1536000, 69.65),
+("blueline", 6, 1, 1612800, 75.14),
+("blueline", 6, 1, 1689600, 83.16),
+("blueline", 6, 1, 1766400, 91.75),
+("blueline", 6, 1, 1843200, 100.66),
+("blueline", 6, 1, 1920000, 111.45),
+("blueline", 6, 1, 1996800, 122.23),
+("blueline", 6, 1, 2092800, 143.54),
+("blueline", 6, 1, 2169600, 147.54),
+("blueline", 6, 1, 2246400, 153.09),
+("blueline", 6, 1, 2323200, 166.44),
+("blueline", 6, 1, 2400000, 184.69),
+("blueline", 6, 1, 2476800, 204.14),
+("blueline", 6, 1, 2553600, 223.37),
+("blueline", 6, 1, 2649600, 253.77),
+("blueline", 7, 1, 825600, 28.88),
+("blueline", 7, 1, 902400, 32.4),
+("blueline", 7, 1, 979200, 36.46),
+("blueline", 7, 1, 1056000, 39.99),
+("blueline", 7, 1, 1209600, 47.23),
+("blueline", 7, 1, 1286400, 51.39),
+("blueline", 7, 1, 1363200, 56.9),
+("blueline", 7, 1, 1459200, 64.26),
+("blueline", 7, 1, 1536000, 69.65),
+("blueline", 7, 1, 1612800, 75.14),
+("blueline", 7, 1, 1689600, 83.16),
+("blueline", 7, 1, 1766400, 91.75),
+("blueline", 7, 1, 1843200, 100.66),
+("blueline", 7, 1, 1920000, 111.45),
+("blueline", 7, 1, 1996800, 122.23),
+("blueline", 7, 1, 2092800, 143.54),
+("blueline", 7, 1, 2169600, 147.54),
+("blueline", 7, 1, 2246400, 153.09),
+("blueline", 7, 1, 2323200, 166.44),
+("blueline", 7, 1, 2400000, 184.69),
+("blueline", 7, 1, 2476800, 204.14),
+("blueline", 7, 1, 2553600, 223.37),
+("blueline", 7, 1, 2649600, 253.77);
diff --git a/src/trace_processor/metrics/sql/android/power_profile_data/bonito.sql b/src/trace_processor/metrics/sql/android/power_profile_data/bonito.sql
new file mode 100644
index 0000000..cc8ffba
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/power_profile_data/bonito.sql
@@ -0,0 +1,86 @@
+--
+-- 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.
+
+INSERT OR REPLACE INTO power_profile VALUES
+("bonito", 0, 0, 300000, 15.2466666667),
+("bonito", 0, 0, 576000, 18.2166666667),
+("bonito", 0, 0, 748800, 20.1866666667),
+("bonito", 0, 0, 998400, 23.29),
+("bonito", 0, 0, 1209600, 25.0116666667),
+("bonito", 0, 0, 1324800, 28.485),
+("bonito", 0, 0, 1516800, 31.6866666667),
+("bonito", 0, 0, 1708800, 35.79),
+("bonito", 1, 0, 300000, 15.2466666667),
+("bonito", 1, 0, 576000, 18.2166666667),
+("bonito", 1, 0, 748800, 20.1866666667),
+("bonito", 1, 0, 998400, 23.29),
+("bonito", 1, 0, 1209600, 25.0116666667),
+("bonito", 1, 0, 1324800, 28.485),
+("bonito", 1, 0, 1516800, 31.6866666667),
+("bonito", 1, 0, 1708800, 35.79),
+("bonito", 2, 0, 300000, 15.2466666667),
+("bonito", 2, 0, 576000, 18.2166666667),
+("bonito", 2, 0, 748800, 20.1866666667),
+("bonito", 2, 0, 998400, 23.29),
+("bonito", 2, 0, 1209600, 25.0116666667),
+("bonito", 2, 0, 1324800, 28.485),
+("bonito", 2, 0, 1516800, 31.6866666667),
+("bonito", 2, 0, 1708800, 35.79),
+("bonito", 3, 0, 300000, 15.2466666667),
+("bonito", 3, 0, 576000, 18.2166666667),
+("bonito", 3, 0, 748800, 20.1866666667),
+("bonito", 3, 0, 998400, 23.29),
+("bonito", 3, 0, 1209600, 25.0116666667),
+("bonito", 3, 0, 1324800, 28.485),
+("bonito", 3, 0, 1516800, 31.6866666667),
+("bonito", 3, 0, 1708800, 35.79),
+("bonito", 4, 0, 300000, 15.2466666667),
+("bonito", 4, 0, 576000, 18.2166666667),
+("bonito", 4, 0, 748800, 20.1866666667),
+("bonito", 4, 0, 998400, 23.29),
+("bonito", 4, 0, 1209600, 25.0116666667),
+("bonito", 4, 0, 1324800, 28.485),
+("bonito", 4, 0, 1516800, 31.6866666667),
+("bonito", 4, 0, 1708800, 35.79),
+("bonito", 5, 0, 300000, 15.2466666667),
+("bonito", 5, 0, 576000, 18.2166666667),
+("bonito", 5, 0, 748800, 20.1866666667),
+("bonito", 5, 0, 998400, 23.29),
+("bonito", 5, 0, 1209600, 25.0116666667),
+("bonito", 5, 0, 1324800, 28.485),
+("bonito", 5, 0, 1516800, 31.6866666667),
+("bonito", 5, 0, 1708800, 35.79),
+("bonito", 6, 1, 300000, 24.06),
+("bonito", 6, 1, 652800, 27.56),
+("bonito", 6, 1, 825600, 29.0),
+("bonito", 6, 1, 979200, 31.675),
+("bonito", 6, 1, 1132800, 34.53),
+("bonito", 6, 1, 1363200, 38.885),
+("bonito", 6, 1, 1536000, 43.075),
+("bonito", 6, 1, 1747200, 48.705),
+("bonito", 6, 1, 1843200, 64.57),
+("bonito", 6, 1, 1996800, 69.805),
+("bonito", 6, 1, 2016000, 76.545),
+("bonito", 7, 1, 300000, 24.06),
+("bonito", 7, 1, 652800, 27.56),
+("bonito", 7, 1, 825600, 29.0),
+("bonito", 7, 1, 979200, 31.675),
+("bonito", 7, 1, 1132800, 34.53),
+("bonito", 7, 1, 1363200, 38.885),
+("bonito", 7, 1, 1536000, 43.075),
+("bonito", 7, 1, 1747200, 48.705),
+("bonito", 7, 1, 1843200, 64.57),
+("bonito", 7, 1, 1996800, 69.805),
+("bonito", 7, 1, 2016000, 76.545);
diff --git a/src/trace_processor/metrics/sql/android/power_profile_data/bramble.sql b/src/trace_processor/metrics/sql/android/power_profile_data/bramble.sql
new file mode 100644
index 0000000..bb67303
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/power_profile_data/bramble.sql
@@ -0,0 +1,86 @@
+--
+-- 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.
+
+INSERT OR REPLACE INTO power_profile VALUES
+("bramble", 0, 0, 300000, 6.98666666667),
+("bramble", 0, 0, 576000, 9.93166666667),
+("bramble", 0, 0, 614400, 10.3216666667),
+("bramble", 0, 0, 864000, 13.31),
+("bramble", 0, 0, 1075200, 15.9866666667),
+("bramble", 0, 0, 1363200, 20.3283333333),
+("bramble", 0, 0, 1516800, 23.4533333333),
+("bramble", 0, 0, 1651200, 26.53),
+("bramble", 0, 0, 1804800, 29.365),
+("bramble", 1, 0, 300000, 6.98666666667),
+("bramble", 1, 0, 576000, 9.93166666667),
+("bramble", 1, 0, 614400, 10.3216666667),
+("bramble", 1, 0, 864000, 13.31),
+("bramble", 1, 0, 1075200, 15.9866666667),
+("bramble", 1, 0, 1363200, 20.3283333333),
+("bramble", 1, 0, 1516800, 23.4533333333),
+("bramble", 1, 0, 1651200, 26.53),
+("bramble", 1, 0, 1804800, 29.365),
+("bramble", 2, 0, 300000, 6.98666666667),
+("bramble", 2, 0, 576000, 9.93166666667),
+("bramble", 2, 0, 614400, 10.3216666667),
+("bramble", 2, 0, 864000, 13.31),
+("bramble", 2, 0, 1075200, 15.9866666667),
+("bramble", 2, 0, 1363200, 20.3283333333),
+("bramble", 2, 0, 1516800, 23.4533333333),
+("bramble", 2, 0, 1651200, 26.53),
+("bramble", 2, 0, 1804800, 29.365),
+("bramble", 3, 0, 300000, 6.98666666667),
+("bramble", 3, 0, 576000, 9.93166666667),
+("bramble", 3, 0, 614400, 10.3216666667),
+("bramble", 3, 0, 864000, 13.31),
+("bramble", 3, 0, 1075200, 15.9866666667),
+("bramble", 3, 0, 1363200, 20.3283333333),
+("bramble", 3, 0, 1516800, 23.4533333333),
+("bramble", 3, 0, 1651200, 26.53),
+("bramble", 3, 0, 1804800, 29.365),
+("bramble", 4, 0, 300000, 6.98666666667),
+("bramble", 4, 0, 576000, 9.93166666667),
+("bramble", 4, 0, 614400, 10.3216666667),
+("bramble", 4, 0, 864000, 13.31),
+("bramble", 4, 0, 1075200, 15.9866666667),
+("bramble", 4, 0, 1363200, 20.3283333333),
+("bramble", 4, 0, 1516800, 23.4533333333),
+("bramble", 4, 0, 1651200, 26.53),
+("bramble", 4, 0, 1804800, 29.365),
+("bramble", 5, 0, 300000, 6.98666666667),
+("bramble", 5, 0, 576000, 9.93166666667),
+("bramble", 5, 0, 614400, 10.3216666667),
+("bramble", 5, 0, 864000, 13.31),
+("bramble", 5, 0, 1075200, 15.9866666667),
+("bramble", 5, 0, 1363200, 20.3283333333),
+("bramble", 5, 0, 1516800, 23.4533333333),
+("bramble", 5, 0, 1651200, 26.53),
+("bramble", 5, 0, 1804800, 29.365),
+("bramble", 6, 1, 652800, 32.13),
+("bramble", 6, 1, 940800, 35.98),
+("bramble", 6, 1, 1152000, 40.03),
+("bramble", 6, 1, 1478400, 51.02),
+("bramble", 6, 1, 1728000, 77.06),
+("bramble", 6, 1, 1900800, 86.25),
+("bramble", 6, 1, 2092800, 97.3),
+("bramble", 6, 1, 2208000, 101.61),
+("bramble", 7, 2, 806400, 56.44),
+("bramble", 7, 2, 1094400, 65.72),
+("bramble", 7, 2, 1401600, 77.01),
+("bramble", 7, 2, 1766400, 104.91),
+("bramble", 7, 2, 1996800, 112.35),
+("bramble", 7, 2, 2188800, 118.53),
+("bramble", 7, 2, 2304000, 122.34),
+("bramble", 7, 2, 2400000, 135.0);
diff --git a/src/trace_processor/metrics/sql/android/power_profile_data/coral.sql b/src/trace_processor/metrics/sql/android/power_profile_data/coral.sql
new file mode 100644
index 0000000..c1c17b4
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/power_profile_data/coral.sql
@@ -0,0 +1,159 @@
+--
+-- 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.
+
+INSERT OR REPLACE INTO power_profile VALUES
+("coral", 0, 0, 300000, 9.86),
+("coral", 0, 0, 403200, 10.335),
+("coral", 0, 0, 499200, 10.8925),
+("coral", 0, 0, 576000, 11.37),
+("coral", 0, 0, 672000, 11.8),
+("coral", 0, 0, 768000, 12.41),
+("coral", 0, 0, 844800, 12.97),
+("coral", 0, 0, 940800, 13.335),
+("coral", 0, 0, 1036800, 14.1725),
+("coral", 0, 0, 1113600, 14.695),
+("coral", 0, 0, 1209600, 15.3525),
+("coral", 0, 0, 1305600, 16.2775),
+("coral", 0, 0, 1382400, 16.8725),
+("coral", 0, 0, 1478400, 17.6525),
+("coral", 0, 0, 1555200, 18.0975),
+("coral", 0, 0, 1632000, 18.8575),
+("coral", 0, 0, 1708800, 20.0525),
+("coral", 0, 0, 1785600, 21.2625),
+("coral", 1, 0, 300000, 9.86),
+("coral", 1, 0, 403200, 10.335),
+("coral", 1, 0, 499200, 10.8925),
+("coral", 1, 0, 576000, 11.37),
+("coral", 1, 0, 672000, 11.8),
+("coral", 1, 0, 768000, 12.41),
+("coral", 1, 0, 844800, 12.97),
+("coral", 1, 0, 940800, 13.335),
+("coral", 1, 0, 1036800, 14.1725),
+("coral", 1, 0, 1113600, 14.695),
+("coral", 1, 0, 1209600, 15.3525),
+("coral", 1, 0, 1305600, 16.2775),
+("coral", 1, 0, 1382400, 16.8725),
+("coral", 1, 0, 1478400, 17.6525),
+("coral", 1, 0, 1555200, 18.0975),
+("coral", 1, 0, 1632000, 18.8575),
+("coral", 1, 0, 1708800, 20.0525),
+("coral", 1, 0, 1785600, 21.2625),
+("coral", 2, 0, 300000, 9.86),
+("coral", 2, 0, 403200, 10.335),
+("coral", 2, 0, 499200, 10.8925),
+("coral", 2, 0, 576000, 11.37),
+("coral", 2, 0, 672000, 11.8),
+("coral", 2, 0, 768000, 12.41),
+("coral", 2, 0, 844800, 12.97),
+("coral", 2, 0, 940800, 13.335),
+("coral", 2, 0, 1036800, 14.1725),
+("coral", 2, 0, 1113600, 14.695),
+("coral", 2, 0, 1209600, 15.3525),
+("coral", 2, 0, 1305600, 16.2775),
+("coral", 2, 0, 1382400, 16.8725),
+("coral", 2, 0, 1478400, 17.6525),
+("coral", 2, 0, 1555200, 18.0975),
+("coral", 2, 0, 1632000, 18.8575),
+("coral", 2, 0, 1708800, 20.0525),
+("coral", 2, 0, 1785600, 21.2625),
+("coral", 3, 0, 300000, 9.86),
+("coral", 3, 0, 403200, 10.335),
+("coral", 3, 0, 499200, 10.8925),
+("coral", 3, 0, 576000, 11.37),
+("coral", 3, 0, 672000, 11.8),
+("coral", 3, 0, 768000, 12.41),
+("coral", 3, 0, 844800, 12.97),
+("coral", 3, 0, 940800, 13.335),
+("coral", 3, 0, 1036800, 14.1725),
+("coral", 3, 0, 1113600, 14.695),
+("coral", 3, 0, 1209600, 15.3525),
+("coral", 3, 0, 1305600, 16.2775),
+("coral", 3, 0, 1382400, 16.8725),
+("coral", 3, 0, 1478400, 17.6525),
+("coral", 3, 0, 1555200, 18.0975),
+("coral", 3, 0, 1632000, 18.8575),
+("coral", 3, 0, 1708800, 20.0525),
+("coral", 3, 0, 1785600, 21.2625),
+("coral", 4, 1, 710400, 16.7833333333),
+("coral", 4, 1, 825600, 18.3733333333),
+("coral", 4, 1, 940800, 20.4833333333),
+("coral", 4, 1, 1056000, 23.3066666667),
+("coral", 4, 1, 1171200, 25.8266666667),
+("coral", 4, 1, 1286400, 28.45),
+("coral", 4, 1, 1401600, 31.7233333333),
+("coral", 4, 1, 1497600, 34.42),
+("coral", 4, 1, 1612800, 39.3966666667),
+("coral", 4, 1, 1708800, 44.24),
+("coral", 4, 1, 1804800, 47.9433333333),
+("coral", 4, 1, 1920000, 51.97),
+("coral", 4, 1, 2016000, 63.3866666667),
+("coral", 4, 1, 2131200, 71.0366666667),
+("coral", 4, 1, 2227200, 79.32),
+("coral", 4, 1, 2323200, 88.99),
+("coral", 4, 1, 2419200, 100.68),
+("coral", 5, 1, 710400, 16.7833333333),
+("coral", 5, 1, 825600, 18.3733333333),
+("coral", 5, 1, 940800, 20.4833333333),
+("coral", 5, 1, 1056000, 23.3066666667),
+("coral", 5, 1, 1171200, 25.8266666667),
+("coral", 5, 1, 1286400, 28.45),
+("coral", 5, 1, 1401600, 31.7233333333),
+("coral", 5, 1, 1497600, 34.42),
+("coral", 5, 1, 1612800, 39.3966666667),
+("coral", 5, 1, 1708800, 44.24),
+("coral", 5, 1, 1804800, 47.9433333333),
+("coral", 5, 1, 1920000, 51.97),
+("coral", 5, 1, 2016000, 63.3866666667),
+("coral", 5, 1, 2131200, 71.0366666667),
+("coral", 5, 1, 2227200, 79.32),
+("coral", 5, 1, 2323200, 88.99),
+("coral", 5, 1, 2419200, 100.68),
+("coral", 6, 1, 710400, 16.7833333333),
+("coral", 6, 1, 825600, 18.3733333333),
+("coral", 6, 1, 940800, 20.4833333333),
+("coral", 6, 1, 1056000, 23.3066666667),
+("coral", 6, 1, 1171200, 25.8266666667),
+("coral", 6, 1, 1286400, 28.45),
+("coral", 6, 1, 1401600, 31.7233333333),
+("coral", 6, 1, 1497600, 34.42),
+("coral", 6, 1, 1612800, 39.3966666667),
+("coral", 6, 1, 1708800, 44.24),
+("coral", 6, 1, 1804800, 47.9433333333),
+("coral", 6, 1, 1920000, 51.97),
+("coral", 6, 1, 2016000, 63.3866666667),
+("coral", 6, 1, 2131200, 71.0366666667),
+("coral", 6, 1, 2227200, 79.32),
+("coral", 6, 1, 2323200, 88.99),
+("coral", 6, 1, 2419200, 100.68),
+("coral", 7, 2, 825600, 52.7),
+("coral", 7, 2, 940800, 55.9),
+("coral", 7, 2, 1056000, 59.73),
+("coral", 7, 2, 1171200, 63.66),
+("coral", 7, 2, 1286400, 67.28),
+("coral", 7, 2, 1401600, 71.66),
+("coral", 7, 2, 1497600, 76.47),
+("coral", 7, 2, 1612800, 80.92),
+("coral", 7, 2, 1708800, 85.81),
+("coral", 7, 2, 1804800, 93.19),
+("coral", 7, 2, 1920000, 98.06),
+("coral", 7, 2, 2016000, 119.08),
+("coral", 7, 2, 2131200, 127.88),
+("coral", 7, 2, 2227200, 129.85),
+("coral", 7, 2, 2323200, 140.37),
+("coral", 7, 2, 2419200, 151.22),
+("coral", 7, 2, 2534400, 160.73),
+("coral", 7, 2, 2649600, 175.5),
+("coral", 7, 2, 2745600, 186.29),
+("coral", 7, 2, 2841600, 223.89);
diff --git a/src/trace_processor/metrics/sql/android/power_profile_data/crosshatch.sql b/src/trace_processor/metrics/sql/android/power_profile_data/crosshatch.sql
new file mode 100644
index 0000000..f6813a0
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/power_profile_data/crosshatch.sql
@@ -0,0 +1,180 @@
+--
+-- 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.
+
+INSERT OR REPLACE INTO power_profile VALUES
+("crosshatch", 0, 0, 300000, 2.27),
+("crosshatch", 0, 0, 403200, 3.63),
+("crosshatch", 0, 0, 480000, 4.36),
+("crosshatch", 0, 0, 576000, 5.21),
+("crosshatch", 0, 0, 652800, 5.47),
+("crosshatch", 0, 0, 748800, 6.74),
+("crosshatch", 0, 0, 825600, 7.69),
+("crosshatch", 0, 0, 902400, 8.57),
+("crosshatch", 0, 0, 979200, 9.42),
+("crosshatch", 0, 0, 1056000, 10.41),
+("crosshatch", 0, 0, 1132800, 11.56),
+("crosshatch", 0, 0, 1228800, 12.87),
+("crosshatch", 0, 0, 1324800, 14.61),
+("crosshatch", 0, 0, 1420800, 16.49),
+("crosshatch", 0, 0, 1516800, 18.9),
+("crosshatch", 0, 0, 1612800, 21.62),
+("crosshatch", 0, 0, 1689600, 24.47),
+("crosshatch", 0, 0, 1766400, 26.45),
+("crosshatch", 1, 0, 300000, 2.27),
+("crosshatch", 1, 0, 403200, 3.63),
+("crosshatch", 1, 0, 480000, 4.36),
+("crosshatch", 1, 0, 576000, 5.21),
+("crosshatch", 1, 0, 652800, 5.47),
+("crosshatch", 1, 0, 748800, 6.74),
+("crosshatch", 1, 0, 825600, 7.69),
+("crosshatch", 1, 0, 902400, 8.57),
+("crosshatch", 1, 0, 979200, 9.42),
+("crosshatch", 1, 0, 1056000, 10.41),
+("crosshatch", 1, 0, 1132800, 11.56),
+("crosshatch", 1, 0, 1228800, 12.87),
+("crosshatch", 1, 0, 1324800, 14.61),
+("crosshatch", 1, 0, 1420800, 16.49),
+("crosshatch", 1, 0, 1516800, 18.9),
+("crosshatch", 1, 0, 1612800, 21.62),
+("crosshatch", 1, 0, 1689600, 24.47),
+("crosshatch", 1, 0, 1766400, 26.45),
+("crosshatch", 2, 0, 300000, 2.27),
+("crosshatch", 2, 0, 403200, 3.63),
+("crosshatch", 2, 0, 480000, 4.36),
+("crosshatch", 2, 0, 576000, 5.21),
+("crosshatch", 2, 0, 652800, 5.47),
+("crosshatch", 2, 0, 748800, 6.74),
+("crosshatch", 2, 0, 825600, 7.69),
+("crosshatch", 2, 0, 902400, 8.57),
+("crosshatch", 2, 0, 979200, 9.42),
+("crosshatch", 2, 0, 1056000, 10.41),
+("crosshatch", 2, 0, 1132800, 11.56),
+("crosshatch", 2, 0, 1228800, 12.87),
+("crosshatch", 2, 0, 1324800, 14.61),
+("crosshatch", 2, 0, 1420800, 16.49),
+("crosshatch", 2, 0, 1516800, 18.9),
+("crosshatch", 2, 0, 1612800, 21.62),
+("crosshatch", 2, 0, 1689600, 24.47),
+("crosshatch", 2, 0, 1766400, 26.45),
+("crosshatch", 3, 0, 300000, 2.27),
+("crosshatch", 3, 0, 403200, 3.63),
+("crosshatch", 3, 0, 480000, 4.36),
+("crosshatch", 3, 0, 576000, 5.21),
+("crosshatch", 3, 0, 652800, 5.47),
+("crosshatch", 3, 0, 748800, 6.74),
+("crosshatch", 3, 0, 825600, 7.69),
+("crosshatch", 3, 0, 902400, 8.57),
+("crosshatch", 3, 0, 979200, 9.42),
+("crosshatch", 3, 0, 1056000, 10.41),
+("crosshatch", 3, 0, 1132800, 11.56),
+("crosshatch", 3, 0, 1228800, 12.87),
+("crosshatch", 3, 0, 1324800, 14.61),
+("crosshatch", 3, 0, 1420800, 16.49),
+("crosshatch", 3, 0, 1516800, 18.9),
+("crosshatch", 3, 0, 1612800, 21.62),
+("crosshatch", 3, 0, 1689600, 24.47),
+("crosshatch", 3, 0, 1766400, 26.45),
+("crosshatch", 4, 1, 825600, 28.88),
+("crosshatch", 4, 1, 902400, 32.4),
+("crosshatch", 4, 1, 979200, 36.46),
+("crosshatch", 4, 1, 1056000, 39.99),
+("crosshatch", 4, 1, 1209600, 47.23),
+("crosshatch", 4, 1, 1286400, 51.39),
+("crosshatch", 4, 1, 1363200, 56.9),
+("crosshatch", 4, 1, 1459200, 64.26),
+("crosshatch", 4, 1, 1536000, 69.65),
+("crosshatch", 4, 1, 1612800, 75.14),
+("crosshatch", 4, 1, 1689600, 83.16),
+("crosshatch", 4, 1, 1766400, 91.75),
+("crosshatch", 4, 1, 1843200, 100.66),
+("crosshatch", 4, 1, 1920000, 111.45),
+("crosshatch", 4, 1, 1996800, 122.23),
+("crosshatch", 4, 1, 2092800, 143.54),
+("crosshatch", 4, 1, 2169600, 147.54),
+("crosshatch", 4, 1, 2246400, 153.09),
+("crosshatch", 4, 1, 2323200, 166.44),
+("crosshatch", 4, 1, 2400000, 184.69),
+("crosshatch", 4, 1, 2476800, 204.14),
+("crosshatch", 4, 1, 2553600, 223.37),
+("crosshatch", 4, 1, 2649600, 253.77),
+("crosshatch", 5, 1, 825600, 28.88),
+("crosshatch", 5, 1, 902400, 32.4),
+("crosshatch", 5, 1, 979200, 36.46),
+("crosshatch", 5, 1, 1056000, 39.99),
+("crosshatch", 5, 1, 1209600, 47.23),
+("crosshatch", 5, 1, 1286400, 51.39),
+("crosshatch", 5, 1, 1363200, 56.9),
+("crosshatch", 5, 1, 1459200, 64.26),
+("crosshatch", 5, 1, 1536000, 69.65),
+("crosshatch", 5, 1, 1612800, 75.14),
+("crosshatch", 5, 1, 1689600, 83.16),
+("crosshatch", 5, 1, 1766400, 91.75),
+("crosshatch", 5, 1, 1843200, 100.66),
+("crosshatch", 5, 1, 1920000, 111.45),
+("crosshatch", 5, 1, 1996800, 122.23),
+("crosshatch", 5, 1, 2092800, 143.54),
+("crosshatch", 5, 1, 2169600, 147.54),
+("crosshatch", 5, 1, 2246400, 153.09),
+("crosshatch", 5, 1, 2323200, 166.44),
+("crosshatch", 5, 1, 2400000, 184.69),
+("crosshatch", 5, 1, 2476800, 204.14),
+("crosshatch", 5, 1, 2553600, 223.37),
+("crosshatch", 5, 1, 2649600, 253.77),
+("crosshatch", 6, 1, 825600, 28.88),
+("crosshatch", 6, 1, 902400, 32.4),
+("crosshatch", 6, 1, 979200, 36.46),
+("crosshatch", 6, 1, 1056000, 39.99),
+("crosshatch", 6, 1, 1209600, 47.23),
+("crosshatch", 6, 1, 1286400, 51.39),
+("crosshatch", 6, 1, 1363200, 56.9),
+("crosshatch", 6, 1, 1459200, 64.26),
+("crosshatch", 6, 1, 1536000, 69.65),
+("crosshatch", 6, 1, 1612800, 75.14),
+("crosshatch", 6, 1, 1689600, 83.16),
+("crosshatch", 6, 1, 1766400, 91.75),
+("crosshatch", 6, 1, 1843200, 100.66),
+("crosshatch", 6, 1, 1920000, 111.45),
+("crosshatch", 6, 1, 1996800, 122.23),
+("crosshatch", 6, 1, 2092800, 143.54),
+("crosshatch", 6, 1, 2169600, 147.54),
+("crosshatch", 6, 1, 2246400, 153.09),
+("crosshatch", 6, 1, 2323200, 166.44),
+("crosshatch", 6, 1, 2400000, 184.69),
+("crosshatch", 6, 1, 2476800, 204.14),
+("crosshatch", 6, 1, 2553600, 223.37),
+("crosshatch", 6, 1, 2649600, 253.77),
+("crosshatch", 7, 1, 825600, 28.88),
+("crosshatch", 7, 1, 902400, 32.4),
+("crosshatch", 7, 1, 979200, 36.46),
+("crosshatch", 7, 1, 1056000, 39.99),
+("crosshatch", 7, 1, 1209600, 47.23),
+("crosshatch", 7, 1, 1286400, 51.39),
+("crosshatch", 7, 1, 1363200, 56.9),
+("crosshatch", 7, 1, 1459200, 64.26),
+("crosshatch", 7, 1, 1536000, 69.65),
+("crosshatch", 7, 1, 1612800, 75.14),
+("crosshatch", 7, 1, 1689600, 83.16),
+("crosshatch", 7, 1, 1766400, 91.75),
+("crosshatch", 7, 1, 1843200, 100.66),
+("crosshatch", 7, 1, 1920000, 111.45),
+("crosshatch", 7, 1, 1996800, 122.23),
+("crosshatch", 7, 1, 2092800, 143.54),
+("crosshatch", 7, 1, 2169600, 147.54),
+("crosshatch", 7, 1, 2246400, 153.09),
+("crosshatch", 7, 1, 2323200, 166.44),
+("crosshatch", 7, 1, 2400000, 184.69),
+("crosshatch", 7, 1, 2476800, 204.14),
+("crosshatch", 7, 1, 2553600, 223.37),
+("crosshatch", 7, 1, 2649600, 253.77);
diff --git a/src/trace_processor/metrics/sql/android/power_profile_data/flame.sql b/src/trace_processor/metrics/sql/android/power_profile_data/flame.sql
new file mode 100644
index 0000000..97ea27f
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/power_profile_data/flame.sql
@@ -0,0 +1,159 @@
+--
+-- 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.
+
+INSERT OR REPLACE INTO power_profile VALUES
+("flame", 0, 0, 300000, 9.86),
+("flame", 0, 0, 403200, 10.335),
+("flame", 0, 0, 499200, 10.8925),
+("flame", 0, 0, 576000, 11.37),
+("flame", 0, 0, 672000, 11.8),
+("flame", 0, 0, 768000, 12.41),
+("flame", 0, 0, 844800, 12.97),
+("flame", 0, 0, 940800, 13.335),
+("flame", 0, 0, 1036800, 14.1725),
+("flame", 0, 0, 1113600, 14.695),
+("flame", 0, 0, 1209600, 15.3525),
+("flame", 0, 0, 1305600, 16.2775),
+("flame", 0, 0, 1382400, 16.8725),
+("flame", 0, 0, 1478400, 17.6525),
+("flame", 0, 0, 1555200, 18.0975),
+("flame", 0, 0, 1632000, 18.8575),
+("flame", 0, 0, 1708800, 20.0525),
+("flame", 0, 0, 1785600, 21.2625),
+("flame", 1, 0, 300000, 9.86),
+("flame", 1, 0, 403200, 10.335),
+("flame", 1, 0, 499200, 10.8925),
+("flame", 1, 0, 576000, 11.37),
+("flame", 1, 0, 672000, 11.8),
+("flame", 1, 0, 768000, 12.41),
+("flame", 1, 0, 844800, 12.97),
+("flame", 1, 0, 940800, 13.335),
+("flame", 1, 0, 1036800, 14.1725),
+("flame", 1, 0, 1113600, 14.695),
+("flame", 1, 0, 1209600, 15.3525),
+("flame", 1, 0, 1305600, 16.2775),
+("flame", 1, 0, 1382400, 16.8725),
+("flame", 1, 0, 1478400, 17.6525),
+("flame", 1, 0, 1555200, 18.0975),
+("flame", 1, 0, 1632000, 18.8575),
+("flame", 1, 0, 1708800, 20.0525),
+("flame", 1, 0, 1785600, 21.2625),
+("flame", 2, 0, 300000, 9.86),
+("flame", 2, 0, 403200, 10.335),
+("flame", 2, 0, 499200, 10.8925),
+("flame", 2, 0, 576000, 11.37),
+("flame", 2, 0, 672000, 11.8),
+("flame", 2, 0, 768000, 12.41),
+("flame", 2, 0, 844800, 12.97),
+("flame", 2, 0, 940800, 13.335),
+("flame", 2, 0, 1036800, 14.1725),
+("flame", 2, 0, 1113600, 14.695),
+("flame", 2, 0, 1209600, 15.3525),
+("flame", 2, 0, 1305600, 16.2775),
+("flame", 2, 0, 1382400, 16.8725),
+("flame", 2, 0, 1478400, 17.6525),
+("flame", 2, 0, 1555200, 18.0975),
+("flame", 2, 0, 1632000, 18.8575),
+("flame", 2, 0, 1708800, 20.0525),
+("flame", 2, 0, 1785600, 21.2625),
+("flame", 3, 0, 300000, 9.86),
+("flame", 3, 0, 403200, 10.335),
+("flame", 3, 0, 499200, 10.8925),
+("flame", 3, 0, 576000, 11.37),
+("flame", 3, 0, 672000, 11.8),
+("flame", 3, 0, 768000, 12.41),
+("flame", 3, 0, 844800, 12.97),
+("flame", 3, 0, 940800, 13.335),
+("flame", 3, 0, 1036800, 14.1725),
+("flame", 3, 0, 1113600, 14.695),
+("flame", 3, 0, 1209600, 15.3525),
+("flame", 3, 0, 1305600, 16.2775),
+("flame", 3, 0, 1382400, 16.8725),
+("flame", 3, 0, 1478400, 17.6525),
+("flame", 3, 0, 1555200, 18.0975),
+("flame", 3, 0, 1632000, 18.8575),
+("flame", 3, 0, 1708800, 20.0525),
+("flame", 3, 0, 1785600, 21.2625),
+("flame", 4, 1, 710400, 16.7833333333),
+("flame", 4, 1, 825600, 18.3733333333),
+("flame", 4, 1, 940800, 20.4833333333),
+("flame", 4, 1, 1056000, 23.3066666667),
+("flame", 4, 1, 1171200, 25.8266666667),
+("flame", 4, 1, 1286400, 28.45),
+("flame", 4, 1, 1401600, 31.7233333333),
+("flame", 4, 1, 1497600, 34.42),
+("flame", 4, 1, 1612800, 39.3966666667),
+("flame", 4, 1, 1708800, 44.24),
+("flame", 4, 1, 1804800, 47.9433333333),
+("flame", 4, 1, 1920000, 51.97),
+("flame", 4, 1, 2016000, 63.3866666667),
+("flame", 4, 1, 2131200, 71.0366666667),
+("flame", 4, 1, 2227200, 79.32),
+("flame", 4, 1, 2323200, 88.99),
+("flame", 4, 1, 2419200, 100.68),
+("flame", 5, 1, 710400, 16.7833333333),
+("flame", 5, 1, 825600, 18.3733333333),
+("flame", 5, 1, 940800, 20.4833333333),
+("flame", 5, 1, 1056000, 23.3066666667),
+("flame", 5, 1, 1171200, 25.8266666667),
+("flame", 5, 1, 1286400, 28.45),
+("flame", 5, 1, 1401600, 31.7233333333),
+("flame", 5, 1, 1497600, 34.42),
+("flame", 5, 1, 1612800, 39.3966666667),
+("flame", 5, 1, 1708800, 44.24),
+("flame", 5, 1, 1804800, 47.9433333333),
+("flame", 5, 1, 1920000, 51.97),
+("flame", 5, 1, 2016000, 63.3866666667),
+("flame", 5, 1, 2131200, 71.0366666667),
+("flame", 5, 1, 2227200, 79.32),
+("flame", 5, 1, 2323200, 88.99),
+("flame", 5, 1, 2419200, 100.68),
+("flame", 6, 1, 710400, 16.7833333333),
+("flame", 6, 1, 825600, 18.3733333333),
+("flame", 6, 1, 940800, 20.4833333333),
+("flame", 6, 1, 1056000, 23.3066666667),
+("flame", 6, 1, 1171200, 25.8266666667),
+("flame", 6, 1, 1286400, 28.45),
+("flame", 6, 1, 1401600, 31.7233333333),
+("flame", 6, 1, 1497600, 34.42),
+("flame", 6, 1, 1612800, 39.3966666667),
+("flame", 6, 1, 1708800, 44.24),
+("flame", 6, 1, 1804800, 47.9433333333),
+("flame", 6, 1, 1920000, 51.97),
+("flame", 6, 1, 2016000, 63.3866666667),
+("flame", 6, 1, 2131200, 71.0366666667),
+("flame", 6, 1, 2227200, 79.32),
+("flame", 6, 1, 2323200, 88.99),
+("flame", 6, 1, 2419200, 100.68),
+("flame", 7, 2, 825600, 52.7),
+("flame", 7, 2, 940800, 55.9),
+("flame", 7, 2, 1056000, 59.73),
+("flame", 7, 2, 1171200, 63.66),
+("flame", 7, 2, 1286400, 67.28),
+("flame", 7, 2, 1401600, 71.66),
+("flame", 7, 2, 1497600, 76.47),
+("flame", 7, 2, 1612800, 80.92),
+("flame", 7, 2, 1708800, 85.81),
+("flame", 7, 2, 1804800, 93.19),
+("flame", 7, 2, 1920000, 98.06),
+("flame", 7, 2, 2016000, 119.08),
+("flame", 7, 2, 2131200, 127.88),
+("flame", 7, 2, 2227200, 129.85),
+("flame", 7, 2, 2323200, 140.37),
+("flame", 7, 2, 2419200, 151.22),
+("flame", 7, 2, 2534400, 160.73),
+("flame", 7, 2, 2649600, 175.5),
+("flame", 7, 2, 2745600, 186.29),
+("flame", 7, 2, 2841600, 223.89);
diff --git a/src/trace_processor/metrics/sql/android/power_profile_data/marlin.sql b/src/trace_processor/metrics/sql/android/power_profile_data/marlin.sql
new file mode 100644
index 0000000..8123fd9d
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/power_profile_data/marlin.sql
@@ -0,0 +1,102 @@
+--
+-- 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.
+
+INSERT OR REPLACE INTO power_profile VALUES
+("marlin", 0, 0, 307200, 11.272),
+("marlin", 0, 0, 384000, 14.842),
+("marlin", 0, 0, 460800, 18.497),
+("marlin", 0, 0, 537600, 22.518),
+("marlin", 0, 0, 614400, 25.967),
+("marlin", 0, 0, 691200, 31.694),
+("marlin", 0, 0, 768000, 37.673),
+("marlin", 0, 0, 844800, 42.859),
+("marlin", 0, 0, 902600, 46.872),
+("marlin", 0, 0, 979200, 57.92),
+("marlin", 0, 0, 1056000, 67.561),
+("marlin", 0, 0, 1132800, 76.303),
+("marlin", 0, 0, 1209600, 87.613),
+("marlin", 0, 0, 1286400, 97.045),
+("marlin", 0, 0, 1363200, 109.544),
+("marlin", 0, 0, 1440000, 122.054),
+("marlin", 0, 0, 1516800, 136.345),
+("marlin", 0, 0, 1593600, 154.435),
+("marlin", 1, 0, 307200, 11.272),
+("marlin", 1, 0, 384000, 14.842),
+("marlin", 1, 0, 460800, 18.497),
+("marlin", 1, 0, 537600, 22.518),
+("marlin", 1, 0, 614400, 25.967),
+("marlin", 1, 0, 691200, 31.694),
+("marlin", 1, 0, 768000, 37.673),
+("marlin", 1, 0, 844800, 42.859),
+("marlin", 1, 0, 902600, 46.872),
+("marlin", 1, 0, 979200, 57.92),
+("marlin", 1, 0, 1056000, 67.561),
+("marlin", 1, 0, 1132800, 76.303),
+("marlin", 1, 0, 1209600, 87.613),
+("marlin", 1, 0, 1286400, 97.045),
+("marlin", 1, 0, 1363200, 109.544),
+("marlin", 1, 0, 1440000, 122.054),
+("marlin", 1, 0, 1516800, 136.345),
+("marlin", 1, 0, 1593600, 154.435),
+("marlin", 2, 1, 307200, 7.055),
+("marlin", 2, 1, 384000, 11.483),
+("marlin", 2, 1, 460800, 14.979),
+("marlin", 2, 1, 537600, 19.642),
+("marlin", 2, 1, 614400, 23.167),
+("marlin", 2, 1, 691200, 27.479),
+("marlin", 2, 1, 748800, 31.632),
+("marlin", 2, 1, 825600, 39.192),
+("marlin", 2, 1, 902400, 47.817),
+("marlin", 2, 1, 979200, 55.659),
+("marlin", 2, 1, 1056000, 64.908),
+("marlin", 2, 1, 1132800, 73.824),
+("marlin", 2, 1, 1209600, 85.299),
+("marlin", 2, 1, 1286400, 96.036),
+("marlin", 2, 1, 1363200, 109.233),
+("marlin", 2, 1, 1440000, 118.56),
+("marlin", 2, 1, 1516800, 132.959),
+("marlin", 2, 1, 1593600, 143.692),
+("marlin", 2, 1, 1670400, 161.378),
+("marlin", 2, 1, 1747200, 180.616),
+("marlin", 2, 1, 1824000, 193.897),
+("marlin", 2, 1, 1900800, 214.361),
+("marlin", 2, 1, 1977600, 238.338),
+("marlin", 2, 1, 2054400, 265.759),
+("marlin", 2, 1, 2150400, 297.918),
+("marlin", 3, 1, 307200, 7.055),
+("marlin", 3, 1, 384000, 11.483),
+("marlin", 3, 1, 460800, 14.979),
+("marlin", 3, 1, 537600, 19.642),
+("marlin", 3, 1, 614400, 23.167),
+("marlin", 3, 1, 691200, 27.479),
+("marlin", 3, 1, 748800, 31.632),
+("marlin", 3, 1, 825600, 39.192),
+("marlin", 3, 1, 902400, 47.817),
+("marlin", 3, 1, 979200, 55.659),
+("marlin", 3, 1, 1056000, 64.908),
+("marlin", 3, 1, 1132800, 73.824),
+("marlin", 3, 1, 1209600, 85.299),
+("marlin", 3, 1, 1286400, 96.036),
+("marlin", 3, 1, 1363200, 109.233),
+("marlin", 3, 1, 1440000, 118.56),
+("marlin", 3, 1, 1516800, 132.959),
+("marlin", 3, 1, 1593600, 143.692),
+("marlin", 3, 1, 1670400, 161.378),
+("marlin", 3, 1, 1747200, 180.616),
+("marlin", 3, 1, 1824000, 193.897),
+("marlin", 3, 1, 1900800, 214.361),
+("marlin", 3, 1, 1977600, 238.338),
+("marlin", 3, 1, 2054400, 265.759),
+("marlin", 3, 1, 2150400, 297.918);
diff --git a/src/trace_processor/metrics/sql/android/power_profile_data/oriole.sql b/src/trace_processor/metrics/sql/android/power_profile_data/oriole.sql
new file mode 100644
index 0000000..6f7b31c
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/power_profile_data/oriole.sql
@@ -0,0 +1,122 @@
+--
+-- 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.
+
+INSERT OR REPLACE INTO power_profile VALUES
+("oriole", 0, 0, 300000, 1.89),
+("oriole", 0, 0, 574000, 6.15),
+("oriole", 0, 0, 738000, 9.34),
+("oriole", 0, 0, 930000, 14.22),
+("oriole", 0, 0, 1098000, 18.94),
+("oriole", 0, 0, 1197000, 21.98),
+("oriole", 0, 0, 1328000, 26.83),
+("oriole", 0, 0, 1401000, 30.17),
+("oriole", 0, 0, 1598000, 41.55),
+("oriole", 0, 0, 1704000, 48.36),
+("oriole", 0, 0, 1803000, 58.45),
+("oriole", 1, 0, 300000, 1.89),
+("oriole", 1, 0, 574000, 6.15),
+("oriole", 1, 0, 738000, 9.34),
+("oriole", 1, 0, 930000, 14.22),
+("oriole", 1, 0, 1098000, 18.94),
+("oriole", 1, 0, 1197000, 21.98),
+("oriole", 1, 0, 1328000, 26.83),
+("oriole", 1, 0, 1401000, 30.17),
+("oriole", 1, 0, 1598000, 41.55),
+("oriole", 1, 0, 1704000, 48.36),
+("oriole", 1, 0, 1803000, 58.45),
+("oriole", 2, 0, 300000, 1.89),
+("oriole", 2, 0, 574000, 6.15),
+("oriole", 2, 0, 738000, 9.34),
+("oriole", 2, 0, 930000, 14.22),
+("oriole", 2, 0, 1098000, 18.94),
+("oriole", 2, 0, 1197000, 21.98),
+("oriole", 2, 0, 1328000, 26.83),
+("oriole", 2, 0, 1401000, 30.17),
+("oriole", 2, 0, 1598000, 41.55),
+("oriole", 2, 0, 1704000, 48.36),
+("oriole", 2, 0, 1803000, 58.45),
+("oriole", 3, 0, 300000, 1.89),
+("oriole", 3, 0, 574000, 6.15),
+("oriole", 3, 0, 738000, 9.34),
+("oriole", 3, 0, 930000, 14.22),
+("oriole", 3, 0, 1098000, 18.94),
+("oriole", 3, 0, 1197000, 21.98),
+("oriole", 3, 0, 1328000, 26.83),
+("oriole", 3, 0, 1401000, 30.17),
+("oriole", 3, 0, 1598000, 41.55),
+("oriole", 3, 0, 1704000, 48.36),
+("oriole", 3, 0, 1803000, 58.45),
+("oriole", 4, 1, 400000, 3.71),
+("oriole", 4, 1, 553000, 6.16),
+("oriole", 4, 1, 696000, 8.0),
+("oriole", 4, 1, 799000, 10.94),
+("oriole", 4, 1, 910000, 12.73),
+("oriole", 4, 1, 1024000, 14.4),
+("oriole", 4, 1, 1197000, 21.39),
+("oriole", 4, 1, 1328000, 24.1),
+("oriole", 4, 1, 1491000, 30.42),
+("oriole", 4, 1, 1663000, 42.49),
+("oriole", 4, 1, 1836000, 49.37),
+("oriole", 4, 1, 1999000, 58.09),
+("oriole", 4, 1, 2130000, 67.54),
+("oriole", 4, 1, 2253000, 79.04),
+("oriole", 5, 1, 400000, 3.71),
+("oriole", 5, 1, 553000, 6.16),
+("oriole", 5, 1, 696000, 8.0),
+("oriole", 5, 1, 799000, 10.94),
+("oriole", 5, 1, 910000, 12.73),
+("oriole", 5, 1, 1024000, 14.4),
+("oriole", 5, 1, 1197000, 21.39),
+("oriole", 5, 1, 1328000, 24.1),
+("oriole", 5, 1, 1491000, 30.42),
+("oriole", 5, 1, 1663000, 42.49),
+("oriole", 5, 1, 1836000, 49.37),
+("oriole", 5, 1, 1999000, 58.09),
+("oriole", 5, 1, 2130000, 67.54),
+("oriole", 5, 1, 2253000, 79.04),
+("oriole", 6, 2, 500000, 8.36),
+("oriole", 6, 2, 851000, 16.33),
+("oriole", 6, 2, 984000, 19.44),
+("oriole", 6, 2, 1106000, 36.71),
+("oriole", 6, 2, 1277000, 41.42),
+("oriole", 6, 2, 1426000, 48.24),
+("oriole", 6, 2, 1582000, 54.77),
+("oriole", 6, 2, 1745000, 65.32),
+("oriole", 6, 2, 1826000, 69.58),
+("oriole", 6, 2, 2048000, 128.49),
+("oriole", 6, 2, 2188000, 142.15),
+("oriole", 6, 2, 2252000, 149.74),
+("oriole", 6, 2, 2401000, 164.78),
+("oriole", 6, 2, 2507000, 188.68),
+("oriole", 6, 2, 2630000, 193.15),
+("oriole", 6, 2, 2704000, 227.98),
+("oriole", 6, 2, 2802000, 254.25),
+("oriole", 7, 2, 500000, 8.36),
+("oriole", 7, 2, 851000, 16.33),
+("oriole", 7, 2, 984000, 19.44),
+("oriole", 7, 2, 1106000, 36.71),
+("oriole", 7, 2, 1277000, 41.42),
+("oriole", 7, 2, 1426000, 48.24),
+("oriole", 7, 2, 1582000, 54.77),
+("oriole", 7, 2, 1745000, 65.32),
+("oriole", 7, 2, 1826000, 69.58),
+("oriole", 7, 2, 2048000, 128.49),
+("oriole", 7, 2, 2188000, 142.15),
+("oriole", 7, 2, 2252000, 149.74),
+("oriole", 7, 2, 2401000, 164.78),
+("oriole", 7, 2, 2507000, 188.68),
+("oriole", 7, 2, 2630000, 193.15),
+("oriole", 7, 2, 2704000, 227.98),
+("oriole", 7, 2, 2802000, 254.25);
diff --git a/src/trace_processor/metrics/sql/android/power_profile_data/raven.sql b/src/trace_processor/metrics/sql/android/power_profile_data/raven.sql
new file mode 100644
index 0000000..7055c50
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/power_profile_data/raven.sql
@@ -0,0 +1,122 @@
+--
+-- 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.
+
+INSERT OR REPLACE INTO power_profile VALUES
+("raven", 0, 0, 300000, 1.89),
+("raven", 0, 0, 574000, 6.15),
+("raven", 0, 0, 738000, 9.34),
+("raven", 0, 0, 930000, 14.22),
+("raven", 0, 0, 1098000, 18.94),
+("raven", 0, 0, 1197000, 21.98),
+("raven", 0, 0, 1328000, 26.83),
+("raven", 0, 0, 1401000, 30.17),
+("raven", 0, 0, 1598000, 41.55),
+("raven", 0, 0, 1704000, 48.36),
+("raven", 0, 0, 1803000, 58.45),
+("raven", 1, 0, 300000, 1.89),
+("raven", 1, 0, 574000, 6.15),
+("raven", 1, 0, 738000, 9.34),
+("raven", 1, 0, 930000, 14.22),
+("raven", 1, 0, 1098000, 18.94),
+("raven", 1, 0, 1197000, 21.98),
+("raven", 1, 0, 1328000, 26.83),
+("raven", 1, 0, 1401000, 30.17),
+("raven", 1, 0, 1598000, 41.55),
+("raven", 1, 0, 1704000, 48.36),
+("raven", 1, 0, 1803000, 58.45),
+("raven", 2, 0, 300000, 1.89),
+("raven", 2, 0, 574000, 6.15),
+("raven", 2, 0, 738000, 9.34),
+("raven", 2, 0, 930000, 14.22),
+("raven", 2, 0, 1098000, 18.94),
+("raven", 2, 0, 1197000, 21.98),
+("raven", 2, 0, 1328000, 26.83),
+("raven", 2, 0, 1401000, 30.17),
+("raven", 2, 0, 1598000, 41.55),
+("raven", 2, 0, 1704000, 48.36),
+("raven", 2, 0, 1803000, 58.45),
+("raven", 3, 0, 300000, 1.89),
+("raven", 3, 0, 574000, 6.15),
+("raven", 3, 0, 738000, 9.34),
+("raven", 3, 0, 930000, 14.22),
+("raven", 3, 0, 1098000, 18.94),
+("raven", 3, 0, 1197000, 21.98),
+("raven", 3, 0, 1328000, 26.83),
+("raven", 3, 0, 1401000, 30.17),
+("raven", 3, 0, 1598000, 41.55),
+("raven", 3, 0, 1704000, 48.36),
+("raven", 3, 0, 1803000, 58.45),
+("raven", 4, 1, 400000, 3.71),
+("raven", 4, 1, 553000, 6.16),
+("raven", 4, 1, 696000, 8.0),
+("raven", 4, 1, 799000, 10.94),
+("raven", 4, 1, 910000, 12.73),
+("raven", 4, 1, 1024000, 14.4),
+("raven", 4, 1, 1197000, 21.39),
+("raven", 4, 1, 1328000, 24.1),
+("raven", 4, 1, 1491000, 30.42),
+("raven", 4, 1, 1663000, 42.49),
+("raven", 4, 1, 1836000, 49.37),
+("raven", 4, 1, 1999000, 58.09),
+("raven", 4, 1, 2130000, 67.54),
+("raven", 4, 1, 2253000, 79.04),
+("raven", 5, 1, 400000, 3.71),
+("raven", 5, 1, 553000, 6.16),
+("raven", 5, 1, 696000, 8.0),
+("raven", 5, 1, 799000, 10.94),
+("raven", 5, 1, 910000, 12.73),
+("raven", 5, 1, 1024000, 14.4),
+("raven", 5, 1, 1197000, 21.39),
+("raven", 5, 1, 1328000, 24.1),
+("raven", 5, 1, 1491000, 30.42),
+("raven", 5, 1, 1663000, 42.49),
+("raven", 5, 1, 1836000, 49.37),
+("raven", 5, 1, 1999000, 58.09),
+("raven", 5, 1, 2130000, 67.54),
+("raven", 5, 1, 2253000, 79.04),
+("raven", 6, 2, 500000, 8.36),
+("raven", 6, 2, 851000, 16.33),
+("raven", 6, 2, 984000, 19.44),
+("raven", 6, 2, 1106000, 36.71),
+("raven", 6, 2, 1277000, 41.42),
+("raven", 6, 2, 1426000, 48.24),
+("raven", 6, 2, 1582000, 54.77),
+("raven", 6, 2, 1745000, 65.32),
+("raven", 6, 2, 1826000, 69.58),
+("raven", 6, 2, 2048000, 128.49),
+("raven", 6, 2, 2188000, 142.15),
+("raven", 6, 2, 2252000, 149.74),
+("raven", 6, 2, 2401000, 164.78),
+("raven", 6, 2, 2507000, 188.68),
+("raven", 6, 2, 2630000, 193.15),
+("raven", 6, 2, 2704000, 227.98),
+("raven", 6, 2, 2802000, 254.25),
+("raven", 7, 2, 500000, 8.36),
+("raven", 7, 2, 851000, 16.33),
+("raven", 7, 2, 984000, 19.44),
+("raven", 7, 2, 1106000, 36.71),
+("raven", 7, 2, 1277000, 41.42),
+("raven", 7, 2, 1426000, 48.24),
+("raven", 7, 2, 1582000, 54.77),
+("raven", 7, 2, 1745000, 65.32),
+("raven", 7, 2, 1826000, 69.58),
+("raven", 7, 2, 2048000, 128.49),
+("raven", 7, 2, 2188000, 142.15),
+("raven", 7, 2, 2252000, 149.74),
+("raven", 7, 2, 2401000, 164.78),
+("raven", 7, 2, 2507000, 188.68),
+("raven", 7, 2, 2630000, 193.15),
+("raven", 7, 2, 2704000, 227.98),
+("raven", 7, 2, 2802000, 254.25);
diff --git a/src/trace_processor/metrics/sql/android/power_profile_data/redfin.sql b/src/trace_processor/metrics/sql/android/power_profile_data/redfin.sql
new file mode 100644
index 0000000..18127b6
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/power_profile_data/redfin.sql
@@ -0,0 +1,86 @@
+--
+-- 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.
+
+INSERT OR REPLACE INTO power_profile VALUES
+("redfin", 0, 0, 300000, 41.92),
+("redfin", 0, 0, 576000, 59.59),
+("redfin", 0, 0, 614400, 61.93),
+("redfin", 0, 0, 864000, 79.86),
+("redfin", 0, 0, 1075200, 95.92),
+("redfin", 0, 0, 1363200, 121.97),
+("redfin", 0, 0, 1516800, 140.72),
+("redfin", 0, 0, 1651200, 159.18),
+("redfin", 0, 0, 1804800, 176.19),
+("redfin", 1, 0, 300000, 41.92),
+("redfin", 1, 0, 576000, 59.59),
+("redfin", 1, 0, 614400, 61.93),
+("redfin", 1, 0, 864000, 79.86),
+("redfin", 1, 0, 1075200, 95.92),
+("redfin", 1, 0, 1363200, 121.97),
+("redfin", 1, 0, 1516800, 140.72),
+("redfin", 1, 0, 1651200, 159.18),
+("redfin", 1, 0, 1804800, 176.19),
+("redfin", 2, 0, 300000, 41.92),
+("redfin", 2, 0, 576000, 59.59),
+("redfin", 2, 0, 614400, 61.93),
+("redfin", 2, 0, 864000, 79.86),
+("redfin", 2, 0, 1075200, 95.92),
+("redfin", 2, 0, 1363200, 121.97),
+("redfin", 2, 0, 1516800, 140.72),
+("redfin", 2, 0, 1651200, 159.18),
+("redfin", 2, 0, 1804800, 176.19),
+("redfin", 3, 0, 300000, 41.92),
+("redfin", 3, 0, 576000, 59.59),
+("redfin", 3, 0, 614400, 61.93),
+("redfin", 3, 0, 864000, 79.86),
+("redfin", 3, 0, 1075200, 95.92),
+("redfin", 3, 0, 1363200, 121.97),
+("redfin", 3, 0, 1516800, 140.72),
+("redfin", 3, 0, 1651200, 159.18),
+("redfin", 3, 0, 1804800, 176.19),
+("redfin", 4, 0, 300000, 41.92),
+("redfin", 4, 0, 576000, 59.59),
+("redfin", 4, 0, 614400, 61.93),
+("redfin", 4, 0, 864000, 79.86),
+("redfin", 4, 0, 1075200, 95.92),
+("redfin", 4, 0, 1363200, 121.97),
+("redfin", 4, 0, 1516800, 140.72),
+("redfin", 4, 0, 1651200, 159.18),
+("redfin", 4, 0, 1804800, 176.19),
+("redfin", 5, 0, 300000, 41.92),
+("redfin", 5, 0, 576000, 59.59),
+("redfin", 5, 0, 614400, 61.93),
+("redfin", 5, 0, 864000, 79.86),
+("redfin", 5, 0, 1075200, 95.92),
+("redfin", 5, 0, 1363200, 121.97),
+("redfin", 5, 0, 1516800, 140.72),
+("redfin", 5, 0, 1651200, 159.18),
+("redfin", 5, 0, 1804800, 176.19),
+("redfin", 6, 1, 652800, 32.13),
+("redfin", 6, 1, 940800, 35.98),
+("redfin", 6, 1, 1152000, 40.03),
+("redfin", 6, 1, 1478400, 51.02),
+("redfin", 6, 1, 1728000, 77.06),
+("redfin", 6, 1, 1900800, 86.25),
+("redfin", 6, 1, 2092800, 97.3),
+("redfin", 6, 1, 2208000, 101.61),
+("redfin", 7, 2, 806400, 56.44),
+("redfin", 7, 2, 1094400, 65.72),
+("redfin", 7, 2, 1401600, 77.01),
+("redfin", 7, 2, 1766400, 104.91),
+("redfin", 7, 2, 1996800, 112.35),
+("redfin", 7, 2, 2188800, 118.53),
+("redfin", 7, 2, 2304000, 122.34),
+("redfin", 7, 2, 2400000, 135.0);
diff --git a/src/trace_processor/metrics/sql/android/power_profile_data/sailfish.sql b/src/trace_processor/metrics/sql/android/power_profile_data/sailfish.sql
new file mode 100644
index 0000000..8ef57bb
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/power_profile_data/sailfish.sql
@@ -0,0 +1,102 @@
+--
+-- 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.
+
+INSERT OR REPLACE INTO power_profile VALUES
+("sailfish", 0, 0, 307200, 11.272),
+("sailfish", 0, 0, 384000, 14.842),
+("sailfish", 0, 0, 460800, 18.497),
+("sailfish", 0, 0, 537600, 22.518),
+("sailfish", 0, 0, 614400, 25.967),
+("sailfish", 0, 0, 691200, 31.694),
+("sailfish", 0, 0, 768000, 37.673),
+("sailfish", 0, 0, 844800, 42.859),
+("sailfish", 0, 0, 902600, 46.872),
+("sailfish", 0, 0, 979200, 57.92),
+("sailfish", 0, 0, 1056000, 67.561),
+("sailfish", 0, 0, 1132800, 76.303),
+("sailfish", 0, 0, 1209600, 87.613),
+("sailfish", 0, 0, 1286400, 97.045),
+("sailfish", 0, 0, 1363200, 109.544),
+("sailfish", 0, 0, 1440000, 122.054),
+("sailfish", 0, 0, 1516800, 136.345),
+("sailfish", 0, 0, 1593600, 154.435),
+("sailfish", 1, 0, 307200, 11.272),
+("sailfish", 1, 0, 384000, 14.842),
+("sailfish", 1, 0, 460800, 18.497),
+("sailfish", 1, 0, 537600, 22.518),
+("sailfish", 1, 0, 614400, 25.967),
+("sailfish", 1, 0, 691200, 31.694),
+("sailfish", 1, 0, 768000, 37.673),
+("sailfish", 1, 0, 844800, 42.859),
+("sailfish", 1, 0, 902600, 46.872),
+("sailfish", 1, 0, 979200, 57.92),
+("sailfish", 1, 0, 1056000, 67.561),
+("sailfish", 1, 0, 1132800, 76.303),
+("sailfish", 1, 0, 1209600, 87.613),
+("sailfish", 1, 0, 1286400, 97.045),
+("sailfish", 1, 0, 1363200, 109.544),
+("sailfish", 1, 0, 1440000, 122.054),
+("sailfish", 1, 0, 1516800, 136.345),
+("sailfish", 1, 0, 1593600, 154.435),
+("sailfish", 2, 1, 307200, 7.055),
+("sailfish", 2, 1, 384000, 11.483),
+("sailfish", 2, 1, 460800, 14.979),
+("sailfish", 2, 1, 537600, 19.642),
+("sailfish", 2, 1, 614400, 23.167),
+("sailfish", 2, 1, 691200, 27.479),
+("sailfish", 2, 1, 748800, 31.632),
+("sailfish", 2, 1, 825600, 39.192),
+("sailfish", 2, 1, 902400, 47.817),
+("sailfish", 2, 1, 979200, 55.659),
+("sailfish", 2, 1, 1056000, 64.908),
+("sailfish", 2, 1, 1132800, 73.824),
+("sailfish", 2, 1, 1209600, 85.299),
+("sailfish", 2, 1, 1286400, 96.036),
+("sailfish", 2, 1, 1363200, 109.233),
+("sailfish", 2, 1, 1440000, 118.56),
+("sailfish", 2, 1, 1516800, 132.959),
+("sailfish", 2, 1, 1593600, 143.692),
+("sailfish", 2, 1, 1670400, 161.378),
+("sailfish", 2, 1, 1747200, 180.616),
+("sailfish", 2, 1, 1824000, 193.897),
+("sailfish", 2, 1, 1900800, 214.361),
+("sailfish", 2, 1, 1977600, 238.338),
+("sailfish", 2, 1, 2054400, 265.759),
+("sailfish", 2, 1, 2150400, 297.918),
+("sailfish", 3, 1, 307200, 7.055),
+("sailfish", 3, 1, 384000, 11.483),
+("sailfish", 3, 1, 460800, 14.979),
+("sailfish", 3, 1, 537600, 19.642),
+("sailfish", 3, 1, 614400, 23.167),
+("sailfish", 3, 1, 691200, 27.479),
+("sailfish", 3, 1, 748800, 31.632),
+("sailfish", 3, 1, 825600, 39.192),
+("sailfish", 3, 1, 902400, 47.817),
+("sailfish", 3, 1, 979200, 55.659),
+("sailfish", 3, 1, 1056000, 64.908),
+("sailfish", 3, 1, 1132800, 73.824),
+("sailfish", 3, 1, 1209600, 85.299),
+("sailfish", 3, 1, 1286400, 96.036),
+("sailfish", 3, 1, 1363200, 109.233),
+("sailfish", 3, 1, 1440000, 118.56),
+("sailfish", 3, 1, 1516800, 132.959),
+("sailfish", 3, 1, 1593600, 143.692),
+("sailfish", 3, 1, 1670400, 161.378),
+("sailfish", 3, 1, 1747200, 180.616),
+("sailfish", 3, 1, 1824000, 193.897),
+("sailfish", 3, 1, 1900800, 214.361),
+("sailfish", 3, 1, 1977600, 238.338),
+("sailfish", 3, 1, 2054400, 265.759),
+("sailfish", 3, 1, 2150400, 297.918);
diff --git a/src/trace_processor/metrics/sql/android/power_profile_data/sargo.sql b/src/trace_processor/metrics/sql/android/power_profile_data/sargo.sql
new file mode 100644
index 0000000..c4853c0
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/power_profile_data/sargo.sql
@@ -0,0 +1,86 @@
+--
+-- 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.
+
+INSERT OR REPLACE INTO power_profile VALUES
+("sargo", 0, 0, 300000, 15.2466666667),
+("sargo", 0, 0, 576000, 18.2166666667),
+("sargo", 0, 0, 748800, 20.1866666667),
+("sargo", 0, 0, 998400, 23.29),
+("sargo", 0, 0, 1209600, 25.0116666667),
+("sargo", 0, 0, 1324800, 28.485),
+("sargo", 0, 0, 1516800, 31.6866666667),
+("sargo", 0, 0, 1708800, 35.79),
+("sargo", 1, 0, 300000, 15.2466666667),
+("sargo", 1, 0, 576000, 18.2166666667),
+("sargo", 1, 0, 748800, 20.1866666667),
+("sargo", 1, 0, 998400, 23.29),
+("sargo", 1, 0, 1209600, 25.0116666667),
+("sargo", 1, 0, 1324800, 28.485),
+("sargo", 1, 0, 1516800, 31.6866666667),
+("sargo", 1, 0, 1708800, 35.79),
+("sargo", 2, 0, 300000, 15.2466666667),
+("sargo", 2, 0, 576000, 18.2166666667),
+("sargo", 2, 0, 748800, 20.1866666667),
+("sargo", 2, 0, 998400, 23.29),
+("sargo", 2, 0, 1209600, 25.0116666667),
+("sargo", 2, 0, 1324800, 28.485),
+("sargo", 2, 0, 1516800, 31.6866666667),
+("sargo", 2, 0, 1708800, 35.79),
+("sargo", 3, 0, 300000, 15.2466666667),
+("sargo", 3, 0, 576000, 18.2166666667),
+("sargo", 3, 0, 748800, 20.1866666667),
+("sargo", 3, 0, 998400, 23.29),
+("sargo", 3, 0, 1209600, 25.0116666667),
+("sargo", 3, 0, 1324800, 28.485),
+("sargo", 3, 0, 1516800, 31.6866666667),
+("sargo", 3, 0, 1708800, 35.79),
+("sargo", 4, 0, 300000, 15.2466666667),
+("sargo", 4, 0, 576000, 18.2166666667),
+("sargo", 4, 0, 748800, 20.1866666667),
+("sargo", 4, 0, 998400, 23.29),
+("sargo", 4, 0, 1209600, 25.0116666667),
+("sargo", 4, 0, 1324800, 28.485),
+("sargo", 4, 0, 1516800, 31.6866666667),
+("sargo", 4, 0, 1708800, 35.79),
+("sargo", 5, 0, 300000, 15.2466666667),
+("sargo", 5, 0, 576000, 18.2166666667),
+("sargo", 5, 0, 748800, 20.1866666667),
+("sargo", 5, 0, 998400, 23.29),
+("sargo", 5, 0, 1209600, 25.0116666667),
+("sargo", 5, 0, 1324800, 28.485),
+("sargo", 5, 0, 1516800, 31.6866666667),
+("sargo", 5, 0, 1708800, 35.79),
+("sargo", 6, 1, 300000, 24.06),
+("sargo", 6, 1, 652800, 27.56),
+("sargo", 6, 1, 825600, 29.0),
+("sargo", 6, 1, 979200, 31.675),
+("sargo", 6, 1, 1132800, 34.53),
+("sargo", 6, 1, 1363200, 38.885),
+("sargo", 6, 1, 1536000, 43.075),
+("sargo", 6, 1, 1747200, 48.705),
+("sargo", 6, 1, 1843200, 64.57),
+("sargo", 6, 1, 1996800, 69.805),
+("sargo", 6, 1, 2016000, 76.545),
+("sargo", 7, 1, 300000, 24.06),
+("sargo", 7, 1, 652800, 27.56),
+("sargo", 7, 1, 825600, 29.0),
+("sargo", 7, 1, 979200, 31.675),
+("sargo", 7, 1, 1132800, 34.53),
+("sargo", 7, 1, 1363200, 38.885),
+("sargo", 7, 1, 1536000, 43.075),
+("sargo", 7, 1, 1747200, 48.705),
+("sargo", 7, 1, 1843200, 64.57),
+("sargo", 7, 1, 1996800, 69.805),
+("sargo", 7, 1, 2016000, 76.545);
diff --git a/src/trace_processor/metrics/sql/android/power_profile_data/sunfish.sql b/src/trace_processor/metrics/sql/android/power_profile_data/sunfish.sql
new file mode 100644
index 0000000..c3393e6
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/power_profile_data/sunfish.sql
@@ -0,0 +1,102 @@
+--
+-- 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.
+
+INSERT OR REPLACE INTO power_profile VALUES
+("sunfish", 0, 0, 300000, 5.75833333333),
+("sunfish", 0, 0, 576000, 7.76166666667),
+("sunfish", 0, 0, 768000, 9.14),
+("sunfish", 0, 0, 1017600, 11.36),
+("sunfish", 0, 0, 1248000, 13.45),
+("sunfish", 0, 0, 1324800, 14.4333333333),
+("sunfish", 0, 0, 1497600, 16.5216666667),
+("sunfish", 0, 0, 1612800, 18.5083333333),
+("sunfish", 0, 0, 1708800, 19.9316666667),
+("sunfish", 0, 0, 1804800, 21.4083333333),
+("sunfish", 1, 0, 300000, 5.75833333333),
+("sunfish", 1, 0, 576000, 7.76166666667),
+("sunfish", 1, 0, 768000, 9.14),
+("sunfish", 1, 0, 1017600, 11.36),
+("sunfish", 1, 0, 1248000, 13.45),
+("sunfish", 1, 0, 1324800, 14.4333333333),
+("sunfish", 1, 0, 1497600, 16.5216666667),
+("sunfish", 1, 0, 1612800, 18.5083333333),
+("sunfish", 1, 0, 1708800, 19.9316666667),
+("sunfish", 1, 0, 1804800, 21.4083333333),
+("sunfish", 2, 0, 300000, 5.75833333333),
+("sunfish", 2, 0, 576000, 7.76166666667),
+("sunfish", 2, 0, 768000, 9.14),
+("sunfish", 2, 0, 1017600, 11.36),
+("sunfish", 2, 0, 1248000, 13.45),
+("sunfish", 2, 0, 1324800, 14.4333333333),
+("sunfish", 2, 0, 1497600, 16.5216666667),
+("sunfish", 2, 0, 1612800, 18.5083333333),
+("sunfish", 2, 0, 1708800, 19.9316666667),
+("sunfish", 2, 0, 1804800, 21.4083333333),
+("sunfish", 3, 0, 300000, 5.75833333333),
+("sunfish", 3, 0, 576000, 7.76166666667),
+("sunfish", 3, 0, 768000, 9.14),
+("sunfish", 3, 0, 1017600, 11.36),
+("sunfish", 3, 0, 1248000, 13.45),
+("sunfish", 3, 0, 1324800, 14.4333333333),
+("sunfish", 3, 0, 1497600, 16.5216666667),
+("sunfish", 3, 0, 1612800, 18.5083333333),
+("sunfish", 3, 0, 1708800, 19.9316666667),
+("sunfish", 3, 0, 1804800, 21.4083333333),
+("sunfish", 4, 0, 300000, 5.75833333333),
+("sunfish", 4, 0, 576000, 7.76166666667),
+("sunfish", 4, 0, 768000, 9.14),
+("sunfish", 4, 0, 1017600, 11.36),
+("sunfish", 4, 0, 1248000, 13.45),
+("sunfish", 4, 0, 1324800, 14.4333333333),
+("sunfish", 4, 0, 1497600, 16.5216666667),
+("sunfish", 4, 0, 1612800, 18.5083333333),
+("sunfish", 4, 0, 1708800, 19.9316666667),
+("sunfish", 4, 0, 1804800, 21.4083333333),
+("sunfish", 5, 0, 300000, 5.75833333333),
+("sunfish", 5, 0, 576000, 7.76166666667),
+("sunfish", 5, 0, 768000, 9.14),
+("sunfish", 5, 0, 1017600, 11.36),
+("sunfish", 5, 0, 1248000, 13.45),
+("sunfish", 5, 0, 1324800, 14.4333333333),
+("sunfish", 5, 0, 1497600, 16.5216666667),
+("sunfish", 5, 0, 1612800, 18.5083333333),
+("sunfish", 5, 0, 1708800, 19.9316666667),
+("sunfish", 5, 0, 1804800, 21.4083333333),
+("sunfish", 6, 1, 300000, 21.115),
+("sunfish", 6, 1, 652800, 28.46),
+("sunfish", 6, 1, 806400, 31.705),
+("sunfish", 6, 1, 979200, 36.515),
+("sunfish", 6, 1, 1094400, 40.19),
+("sunfish", 6, 1, 1209600, 43.585),
+("sunfish", 6, 1, 1324800, 48.275),
+("sunfish", 6, 1, 1555200, 62.805),
+("sunfish", 6, 1, 1708800, 72.755),
+("sunfish", 6, 1, 1843200, 91.47),
+("sunfish", 6, 1, 1939200, 99.46),
+("sunfish", 6, 1, 2169600, 119.27),
+("sunfish", 6, 1, 2208000, 133.105),
+("sunfish", 7, 1, 300000, 21.115),
+("sunfish", 7, 1, 652800, 28.46),
+("sunfish", 7, 1, 806400, 31.705),
+("sunfish", 7, 1, 979200, 36.515),
+("sunfish", 7, 1, 1094400, 40.19),
+("sunfish", 7, 1, 1209600, 43.585),
+("sunfish", 7, 1, 1324800, 48.275),
+("sunfish", 7, 1, 1555200, 62.805),
+("sunfish", 7, 1, 1708800, 72.755),
+("sunfish", 7, 1, 1843200, 91.47),
+("sunfish", 7, 1, 1939200, 99.46),
+("sunfish", 7, 1, 2169600, 119.27),
+("sunfish", 7, 1, 2208000, 133.105);
diff --git a/src/trace_processor/metrics/sql/android/power_profile_data/taimen.sql b/src/trace_processor/metrics/sql/android/power_profile_data/taimen.sql
new file mode 100644
index 0000000..14fed34
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/power_profile_data/taimen.sql
@@ -0,0 +1,228 @@
+--
+-- 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.
+
+INSERT OR REPLACE INTO power_profile VALUES
+("taimen", 0, 0, 300000, 3.685),
+("taimen", 0, 0, 364800, 3.598),
+("taimen", 0, 0, 441600, 3.621),
+("taimen", 0, 0, 518400, 4.202),
+("taimen", 0, 0, 595200, 4.935),
+("taimen", 0, 0, 672000, 5.633),
+("taimen", 0, 0, 748800, 6.216),
+("taimen", 0, 0, 825600, 6.71),
+("taimen", 0, 0, 883200, 7.557),
+("taimen", 0, 0, 960000, 8.687),
+("taimen", 0, 0, 1036800, 9.882),
+("taimen", 0, 0, 1094400, 10.95),
+("taimen", 0, 0, 1171200, 12.075),
+("taimen", 0, 0, 1248000, 12.875),
+("taimen", 0, 0, 1324800, 14.424),
+("taimen", 0, 0, 1401600, 15.653),
+("taimen", 0, 0, 1478400, 17.345),
+("taimen", 0, 0, 1555200, 18.71),
+("taimen", 0, 0, 1670400, 21.587),
+("taimen", 0, 0, 1747200, 25.43),
+("taimen", 0, 0, 1824000, 27.165),
+("taimen", 0, 0, 1900800, 31.671),
+("taimen", 1, 0, 300000, 3.685),
+("taimen", 1, 0, 364800, 3.598),
+("taimen", 1, 0, 441600, 3.621),
+("taimen", 1, 0, 518400, 4.202),
+("taimen", 1, 0, 595200, 4.935),
+("taimen", 1, 0, 672000, 5.633),
+("taimen", 1, 0, 748800, 6.216),
+("taimen", 1, 0, 825600, 6.71),
+("taimen", 1, 0, 883200, 7.557),
+("taimen", 1, 0, 960000, 8.687),
+("taimen", 1, 0, 1036800, 9.882),
+("taimen", 1, 0, 1094400, 10.95),
+("taimen", 1, 0, 1171200, 12.075),
+("taimen", 1, 0, 1248000, 12.875),
+("taimen", 1, 0, 1324800, 14.424),
+("taimen", 1, 0, 1401600, 15.653),
+("taimen", 1, 0, 1478400, 17.345),
+("taimen", 1, 0, 1555200, 18.71),
+("taimen", 1, 0, 1670400, 21.587),
+("taimen", 1, 0, 1747200, 25.43),
+("taimen", 1, 0, 1824000, 27.165),
+("taimen", 1, 0, 1900800, 31.671),
+("taimen", 2, 0, 300000, 3.685),
+("taimen", 2, 0, 364800, 3.598),
+("taimen", 2, 0, 441600, 3.621),
+("taimen", 2, 0, 518400, 4.202),
+("taimen", 2, 0, 595200, 4.935),
+("taimen", 2, 0, 672000, 5.633),
+("taimen", 2, 0, 748800, 6.216),
+("taimen", 2, 0, 825600, 6.71),
+("taimen", 2, 0, 883200, 7.557),
+("taimen", 2, 0, 960000, 8.687),
+("taimen", 2, 0, 1036800, 9.882),
+("taimen", 2, 0, 1094400, 10.95),
+("taimen", 2, 0, 1171200, 12.075),
+("taimen", 2, 0, 1248000, 12.875),
+("taimen", 2, 0, 1324800, 14.424),
+("taimen", 2, 0, 1401600, 15.653),
+("taimen", 2, 0, 1478400, 17.345),
+("taimen", 2, 0, 1555200, 18.71),
+("taimen", 2, 0, 1670400, 21.587),
+("taimen", 2, 0, 1747200, 25.43),
+("taimen", 2, 0, 1824000, 27.165),
+("taimen", 2, 0, 1900800, 31.671),
+("taimen", 3, 0, 300000, 3.685),
+("taimen", 3, 0, 364800, 3.598),
+("taimen", 3, 0, 441600, 3.621),
+("taimen", 3, 0, 518400, 4.202),
+("taimen", 3, 0, 595200, 4.935),
+("taimen", 3, 0, 672000, 5.633),
+("taimen", 3, 0, 748800, 6.216),
+("taimen", 3, 0, 825600, 6.71),
+("taimen", 3, 0, 883200, 7.557),
+("taimen", 3, 0, 960000, 8.687),
+("taimen", 3, 0, 1036800, 9.882),
+("taimen", 3, 0, 1094400, 10.95),
+("taimen", 3, 0, 1171200, 12.075),
+("taimen", 3, 0, 1248000, 12.875),
+("taimen", 3, 0, 1324800, 14.424),
+("taimen", 3, 0, 1401600, 15.653),
+("taimen", 3, 0, 1478400, 17.345),
+("taimen", 3, 0, 1555200, 18.71),
+("taimen", 3, 0, 1670400, 21.587),
+("taimen", 3, 0, 1747200, 25.43),
+("taimen", 3, 0, 1824000, 27.165),
+("taimen", 3, 0, 1900800, 31.671),
+("taimen", 4, 1, 300000, 10.722),
+("taimen", 4, 1, 345600, 11.52),
+("taimen", 4, 1, 422400, 14.105),
+("taimen", 4, 1, 499200, 16.68),
+("taimen", 4, 1, 576000, 18.946),
+("taimen", 4, 1, 652800, 21.265),
+("taimen", 4, 1, 729600, 23.432),
+("taimen", 4, 1, 806400, 26.019),
+("taimen", 4, 1, 902400, 28.856),
+("taimen", 4, 1, 979200, 31.085),
+("taimen", 4, 1, 1056000, 33.615),
+("taimen", 4, 1, 1132800, 35.76),
+("taimen", 4, 1, 1190400, 40.608),
+("taimen", 4, 1, 1267200, 43.284),
+("taimen", 4, 1, 1344000, 47.347),
+("taimen", 4, 1, 1420800, 52.231),
+("taimen", 4, 1, 1497600, 57.225),
+("taimen", 4, 1, 1574400, 63.138),
+("taimen", 4, 1, 1651200, 69.251),
+("taimen", 4, 1, 1728000, 76.449),
+("taimen", 4, 1, 1804800, 84.71),
+("taimen", 4, 1, 1881600, 102.551),
+("taimen", 4, 1, 1958400, 107.115),
+("taimen", 4, 1, 2035200, 129.689),
+("taimen", 4, 1, 2112000, 135.832),
+("taimen", 4, 1, 2208000, 164.674),
+("taimen", 4, 1, 2265600, 180.279),
+("taimen", 4, 1, 2323200, 197.024),
+("taimen", 4, 1, 2342400, 204.511),
+("taimen", 4, 1, 2361600, 211.886),
+("taimen", 4, 1, 2457600, 212.147),
+("taimen", 5, 1, 300000, 10.722),
+("taimen", 5, 1, 345600, 11.52),
+("taimen", 5, 1, 422400, 14.105),
+("taimen", 5, 1, 499200, 16.68),
+("taimen", 5, 1, 576000, 18.946),
+("taimen", 5, 1, 652800, 21.265),
+("taimen", 5, 1, 729600, 23.432),
+("taimen", 5, 1, 806400, 26.019),
+("taimen", 5, 1, 902400, 28.856),
+("taimen", 5, 1, 979200, 31.085),
+("taimen", 5, 1, 1056000, 33.615),
+("taimen", 5, 1, 1132800, 35.76),
+("taimen", 5, 1, 1190400, 40.608),
+("taimen", 5, 1, 1267200, 43.284),
+("taimen", 5, 1, 1344000, 47.347),
+("taimen", 5, 1, 1420800, 52.231),
+("taimen", 5, 1, 1497600, 57.225),
+("taimen", 5, 1, 1574400, 63.138),
+("taimen", 5, 1, 1651200, 69.251),
+("taimen", 5, 1, 1728000, 76.449),
+("taimen", 5, 1, 1804800, 84.71),
+("taimen", 5, 1, 1881600, 102.551),
+("taimen", 5, 1, 1958400, 107.115),
+("taimen", 5, 1, 2035200, 129.689),
+("taimen", 5, 1, 2112000, 135.832),
+("taimen", 5, 1, 2208000, 164.674),
+("taimen", 5, 1, 2265600, 180.279),
+("taimen", 5, 1, 2323200, 197.024),
+("taimen", 5, 1, 2342400, 204.511),
+("taimen", 5, 1, 2361600, 211.886),
+("taimen", 5, 1, 2457600, 212.147),
+("taimen", 6, 1, 300000, 10.722),
+("taimen", 6, 1, 345600, 11.52),
+("taimen", 6, 1, 422400, 14.105),
+("taimen", 6, 1, 499200, 16.68),
+("taimen", 6, 1, 576000, 18.946),
+("taimen", 6, 1, 652800, 21.265),
+("taimen", 6, 1, 729600, 23.432),
+("taimen", 6, 1, 806400, 26.019),
+("taimen", 6, 1, 902400, 28.856),
+("taimen", 6, 1, 979200, 31.085),
+("taimen", 6, 1, 1056000, 33.615),
+("taimen", 6, 1, 1132800, 35.76),
+("taimen", 6, 1, 1190400, 40.608),
+("taimen", 6, 1, 1267200, 43.284),
+("taimen", 6, 1, 1344000, 47.347),
+("taimen", 6, 1, 1420800, 52.231),
+("taimen", 6, 1, 1497600, 57.225),
+("taimen", 6, 1, 1574400, 63.138),
+("taimen", 6, 1, 1651200, 69.251),
+("taimen", 6, 1, 1728000, 76.449),
+("taimen", 6, 1, 1804800, 84.71),
+("taimen", 6, 1, 1881600, 102.551),
+("taimen", 6, 1, 1958400, 107.115),
+("taimen", 6, 1, 2035200, 129.689),
+("taimen", 6, 1, 2112000, 135.832),
+("taimen", 6, 1, 2208000, 164.674),
+("taimen", 6, 1, 2265600, 180.279),
+("taimen", 6, 1, 2323200, 197.024),
+("taimen", 6, 1, 2342400, 204.511),
+("taimen", 6, 1, 2361600, 211.886),
+("taimen", 6, 1, 2457600, 212.147),
+("taimen", 7, 1, 300000, 10.722),
+("taimen", 7, 1, 345600, 11.52),
+("taimen", 7, 1, 422400, 14.105),
+("taimen", 7, 1, 499200, 16.68),
+("taimen", 7, 1, 576000, 18.946),
+("taimen", 7, 1, 652800, 21.265),
+("taimen", 7, 1, 729600, 23.432),
+("taimen", 7, 1, 806400, 26.019),
+("taimen", 7, 1, 902400, 28.856),
+("taimen", 7, 1, 979200, 31.085),
+("taimen", 7, 1, 1056000, 33.615),
+("taimen", 7, 1, 1132800, 35.76),
+("taimen", 7, 1, 1190400, 40.608),
+("taimen", 7, 1, 1267200, 43.284),
+("taimen", 7, 1, 1344000, 47.347),
+("taimen", 7, 1, 1420800, 52.231),
+("taimen", 7, 1, 1497600, 57.225),
+("taimen", 7, 1, 1574400, 63.138),
+("taimen", 7, 1, 1651200, 69.251),
+("taimen", 7, 1, 1728000, 76.449),
+("taimen", 7, 1, 1804800, 84.71),
+("taimen", 7, 1, 1881600, 102.551),
+("taimen", 7, 1, 1958400, 107.115),
+("taimen", 7, 1, 2035200, 129.689),
+("taimen", 7, 1, 2112000, 135.832),
+("taimen", 7, 1, 2208000, 164.674),
+("taimen", 7, 1, 2265600, 180.279),
+("taimen", 7, 1, 2323200, 197.024),
+("taimen", 7, 1, 2342400, 204.511),
+("taimen", 7, 1, 2361600, 211.886),
+("taimen", 7, 1, 2457600, 212.147);
diff --git a/src/trace_processor/metrics/sql/android/power_profile_data/walleye.sql b/src/trace_processor/metrics/sql/android/power_profile_data/walleye.sql
new file mode 100644
index 0000000..1405972
--- /dev/null
+++ b/src/trace_processor/metrics/sql/android/power_profile_data/walleye.sql
@@ -0,0 +1,228 @@
+--
+-- 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.
+
+INSERT OR REPLACE INTO power_profile VALUES
+("walleye", 0, 0, 300000, 3.685),
+("walleye", 0, 0, 364800, 3.598),
+("walleye", 0, 0, 441600, 3.621),
+("walleye", 0, 0, 518400, 4.202),
+("walleye", 0, 0, 595200, 4.935),
+("walleye", 0, 0, 672000, 5.633),
+("walleye", 0, 0, 748800, 6.216),
+("walleye", 0, 0, 825600, 6.71),
+("walleye", 0, 0, 883200, 7.557),
+("walleye", 0, 0, 960000, 8.687),
+("walleye", 0, 0, 1036800, 9.882),
+("walleye", 0, 0, 1094400, 10.95),
+("walleye", 0, 0, 1171200, 12.075),
+("walleye", 0, 0, 1248000, 12.875),
+("walleye", 0, 0, 1324800, 14.424),
+("walleye", 0, 0, 1401600, 15.653),
+("walleye", 0, 0, 1478400, 17.345),
+("walleye", 0, 0, 1555200, 18.71),
+("walleye", 0, 0, 1670400, 21.587),
+("walleye", 0, 0, 1747200, 25.43),
+("walleye", 0, 0, 1824000, 27.165),
+("walleye", 0, 0, 1900800, 31.671),
+("walleye", 1, 0, 300000, 3.685),
+("walleye", 1, 0, 364800, 3.598),
+("walleye", 1, 0, 441600, 3.621),
+("walleye", 1, 0, 518400, 4.202),
+("walleye", 1, 0, 595200, 4.935),
+("walleye", 1, 0, 672000, 5.633),
+("walleye", 1, 0, 748800, 6.216),
+("walleye", 1, 0, 825600, 6.71),
+("walleye", 1, 0, 883200, 7.557),
+("walleye", 1, 0, 960000, 8.687),
+("walleye", 1, 0, 1036800, 9.882),
+("walleye", 1, 0, 1094400, 10.95),
+("walleye", 1, 0, 1171200, 12.075),
+("walleye", 1, 0, 1248000, 12.875),
+("walleye", 1, 0, 1324800, 14.424),
+("walleye", 1, 0, 1401600, 15.653),
+("walleye", 1, 0, 1478400, 17.345),
+("walleye", 1, 0, 1555200, 18.71),
+("walleye", 1, 0, 1670400, 21.587),
+("walleye", 1, 0, 1747200, 25.43),
+("walleye", 1, 0, 1824000, 27.165),
+("walleye", 1, 0, 1900800, 31.671),
+("walleye", 2, 0, 300000, 3.685),
+("walleye", 2, 0, 364800, 3.598),
+("walleye", 2, 0, 441600, 3.621),
+("walleye", 2, 0, 518400, 4.202),
+("walleye", 2, 0, 595200, 4.935),
+("walleye", 2, 0, 672000, 5.633),
+("walleye", 2, 0, 748800, 6.216),
+("walleye", 2, 0, 825600, 6.71),
+("walleye", 2, 0, 883200, 7.557),
+("walleye", 2, 0, 960000, 8.687),
+("walleye", 2, 0, 1036800, 9.882),
+("walleye", 2, 0, 1094400, 10.95),
+("walleye", 2, 0, 1171200, 12.075),
+("walleye", 2, 0, 1248000, 12.875),
+("walleye", 2, 0, 1324800, 14.424),
+("walleye", 2, 0, 1401600, 15.653),
+("walleye", 2, 0, 1478400, 17.345),
+("walleye", 2, 0, 1555200, 18.71),
+("walleye", 2, 0, 1670400, 21.587),
+("walleye", 2, 0, 1747200, 25.43),
+("walleye", 2, 0, 1824000, 27.165),
+("walleye", 2, 0, 1900800, 31.671),
+("walleye", 3, 0, 300000, 3.685),
+("walleye", 3, 0, 364800, 3.598),
+("walleye", 3, 0, 441600, 3.621),
+("walleye", 3, 0, 518400, 4.202),
+("walleye", 3, 0, 595200, 4.935),
+("walleye", 3, 0, 672000, 5.633),
+("walleye", 3, 0, 748800, 6.216),
+("walleye", 3, 0, 825600, 6.71),
+("walleye", 3, 0, 883200, 7.557),
+("walleye", 3, 0, 960000, 8.687),
+("walleye", 3, 0, 1036800, 9.882),
+("walleye", 3, 0, 1094400, 10.95),
+("walleye", 3, 0, 1171200, 12.075),
+("walleye", 3, 0, 1248000, 12.875),
+("walleye", 3, 0, 1324800, 14.424),
+("walleye", 3, 0, 1401600, 15.653),
+("walleye", 3, 0, 1478400, 17.345),
+("walleye", 3, 0, 1555200, 18.71),
+("walleye", 3, 0, 1670400, 21.587),
+("walleye", 3, 0, 1747200, 25.43),
+("walleye", 3, 0, 1824000, 27.165),
+("walleye", 3, 0, 1900800, 31.671),
+("walleye", 4, 1, 300000, 10.722),
+("walleye", 4, 1, 345600, 11.52),
+("walleye", 4, 1, 422400, 14.105),
+("walleye", 4, 1, 499200, 16.68),
+("walleye", 4, 1, 576000, 18.946),
+("walleye", 4, 1, 652800, 21.265),
+("walleye", 4, 1, 729600, 23.432),
+("walleye", 4, 1, 806400, 26.019),
+("walleye", 4, 1, 902400, 28.856),
+("walleye", 4, 1, 979200, 31.085),
+("walleye", 4, 1, 1056000, 33.615),
+("walleye", 4, 1, 1132800, 35.76),
+("walleye", 4, 1, 1190400, 40.608),
+("walleye", 4, 1, 1267200, 43.284),
+("walleye", 4, 1, 1344000, 47.347),
+("walleye", 4, 1, 1420800, 52.231),
+("walleye", 4, 1, 1497600, 57.225),
+("walleye", 4, 1, 1574400, 63.138),
+("walleye", 4, 1, 1651200, 69.251),
+("walleye", 4, 1, 1728000, 76.449),
+("walleye", 4, 1, 1804800, 84.71),
+("walleye", 4, 1, 1881600, 102.551),
+("walleye", 4, 1, 1958400, 107.115),
+("walleye", 4, 1, 2035200, 129.689),
+("walleye", 4, 1, 2112000, 135.832),
+("walleye", 4, 1, 2208000, 164.674),
+("walleye", 4, 1, 2265600, 180.279),
+("walleye", 4, 1, 2323200, 197.024),
+("walleye", 4, 1, 2342400, 204.511),
+("walleye", 4, 1, 2361600, 211.886),
+("walleye", 4, 1, 2457600, 212.147),
+("walleye", 5, 1, 300000, 10.722),
+("walleye", 5, 1, 345600, 11.52),
+("walleye", 5, 1, 422400, 14.105),
+("walleye", 5, 1, 499200, 16.68),
+("walleye", 5, 1, 576000, 18.946),
+("walleye", 5, 1, 652800, 21.265),
+("walleye", 5, 1, 729600, 23.432),
+("walleye", 5, 1, 806400, 26.019),
+("walleye", 5, 1, 902400, 28.856),
+("walleye", 5, 1, 979200, 31.085),
+("walleye", 5, 1, 1056000, 33.615),
+("walleye", 5, 1, 1132800, 35.76),
+("walleye", 5, 1, 1190400, 40.608),
+("walleye", 5, 1, 1267200, 43.284),
+("walleye", 5, 1, 1344000, 47.347),
+("walleye", 5, 1, 1420800, 52.231),
+("walleye", 5, 1, 1497600, 57.225),
+("walleye", 5, 1, 1574400, 63.138),
+("walleye", 5, 1, 1651200, 69.251),
+("walleye", 5, 1, 1728000, 76.449),
+("walleye", 5, 1, 1804800, 84.71),
+("walleye", 5, 1, 1881600, 102.551),
+("walleye", 5, 1, 1958400, 107.115),
+("walleye", 5, 1, 2035200, 129.689),
+("walleye", 5, 1, 2112000, 135.832),
+("walleye", 5, 1, 2208000, 164.674),
+("walleye", 5, 1, 2265600, 180.279),
+("walleye", 5, 1, 2323200, 197.024),
+("walleye", 5, 1, 2342400, 204.511),
+("walleye", 5, 1, 2361600, 211.886),
+("walleye", 5, 1, 2457600, 212.147),
+("walleye", 6, 1, 300000, 10.722),
+("walleye", 6, 1, 345600, 11.52),
+("walleye", 6, 1, 422400, 14.105),
+("walleye", 6, 1, 499200, 16.68),
+("walleye", 6, 1, 576000, 18.946),
+("walleye", 6, 1, 652800, 21.265),
+("walleye", 6, 1, 729600, 23.432),
+("walleye", 6, 1, 806400, 26.019),
+("walleye", 6, 1, 902400, 28.856),
+("walleye", 6, 1, 979200, 31.085),
+("walleye", 6, 1, 1056000, 33.615),
+("walleye", 6, 1, 1132800, 35.76),
+("walleye", 6, 1, 1190400, 40.608),
+("walleye", 6, 1, 1267200, 43.284),
+("walleye", 6, 1, 1344000, 47.347),
+("walleye", 6, 1, 1420800, 52.231),
+("walleye", 6, 1, 1497600, 57.225),
+("walleye", 6, 1, 1574400, 63.138),
+("walleye", 6, 1, 1651200, 69.251),
+("walleye", 6, 1, 1728000, 76.449),
+("walleye", 6, 1, 1804800, 84.71),
+("walleye", 6, 1, 1881600, 102.551),
+("walleye", 6, 1, 1958400, 107.115),
+("walleye", 6, 1, 2035200, 129.689),
+("walleye", 6, 1, 2112000, 135.832),
+("walleye", 6, 1, 2208000, 164.674),
+("walleye", 6, 1, 2265600, 180.279),
+("walleye", 6, 1, 2323200, 197.024),
+("walleye", 6, 1, 2342400, 204.511),
+("walleye", 6, 1, 2361600, 211.886),
+("walleye", 6, 1, 2457600, 212.147),
+("walleye", 7, 1, 300000, 10.722),
+("walleye", 7, 1, 345600, 11.52),
+("walleye", 7, 1, 422400, 14.105),
+("walleye", 7, 1, 499200, 16.68),
+("walleye", 7, 1, 576000, 18.946),
+("walleye", 7, 1, 652800, 21.265),
+("walleye", 7, 1, 729600, 23.432),
+("walleye", 7, 1, 806400, 26.019),
+("walleye", 7, 1, 902400, 28.856),
+("walleye", 7, 1, 979200, 31.085),
+("walleye", 7, 1, 1056000, 33.615),
+("walleye", 7, 1, 1132800, 35.76),
+("walleye", 7, 1, 1190400, 40.608),
+("walleye", 7, 1, 1267200, 43.284),
+("walleye", 7, 1, 1344000, 47.347),
+("walleye", 7, 1, 1420800, 52.231),
+("walleye", 7, 1, 1497600, 57.225),
+("walleye", 7, 1, 1574400, 63.138),
+("walleye", 7, 1, 1651200, 69.251),
+("walleye", 7, 1, 1728000, 76.449),
+("walleye", 7, 1, 1804800, 84.71),
+("walleye", 7, 1, 1881600, 102.551),
+("walleye", 7, 1, 1958400, 107.115),
+("walleye", 7, 1, 2035200, 129.689),
+("walleye", 7, 1, 2112000, 135.832),
+("walleye", 7, 1, 2208000, 164.674),
+("walleye", 7, 1, 2265600, 180.279),
+("walleye", 7, 1, 2323200, 197.024),
+("walleye", 7, 1, 2342400, 204.511),
+("walleye", 7, 1, 2361600, 211.886),
+("walleye", 7, 1, 2457600, 212.147);
diff --git a/src/trace_processor/metrics/sql/android/process_mem.sql b/src/trace_processor/metrics/sql/android/process_mem.sql
index 908f11d..d47578d 100644
--- a/src/trace_processor/metrics/sql/android/process_mem.sql
+++ b/src/trace_processor/metrics/sql/android/process_mem.sql
@@ -69,14 +69,14 @@
 CREATE VIEW rss_and_swap_span AS
 SELECT
   ts, dur, upid,
-  CAST(IFNULL(file_rss_val, 0) AS INT) file_rss_val,
-  CAST(IFNULL(anon_rss_val, 0) AS INT) anon_rss_val,
-  CAST(IFNULL(shmem_rss_val, 0) AS INT) shmem_rss_val,
-  CAST(IFNULL(swap_val, 0) AS INT) swap_val,
+  CAST(IFNULL(file_rss_val, 0) AS INT) AS file_rss_val,
+  CAST(IFNULL(anon_rss_val, 0) AS INT) AS anon_rss_val,
+  CAST(IFNULL(shmem_rss_val, 0) AS INT) AS shmem_rss_val,
+  CAST(IFNULL(swap_val, 0) AS INT) AS swap_val,
   CAST(
-    IFNULL(anon_rss_val, 0) +
-    IFNULL(file_rss_val, 0) +
-    IFNULL(shmem_rss_val, 0) AS int) AS rss_val,
+    IFNULL(anon_rss_val, 0)
+    + IFNULL(file_rss_val, 0)
+    + IFNULL(shmem_rss_val, 0) AS int) AS rss_val,
   CAST(
     IFNULL(anon_rss_val, 0)
     + IFNULL(swap_val, 0)
diff --git a/src/trace_processor/metrics/sql/android/process_metadata.sql b/src/trace_processor/metrics/sql/android/process_metadata.sql
index 7167e12..4cf11d4 100644
--- a/src/trace_processor/metrics/sql/android/process_metadata.sql
+++ b/src/trace_processor/metrics/sql/android/process_metadata.sql
@@ -14,58 +14,26 @@
 -- limitations under the License.
 --
 
-DROP TABLE IF EXISTS uid_package_count;
+SELECT IMPORT('android.process_metadata');
 
-CREATE TABLE uid_package_count AS
-SELECT uid, COUNT(1) AS cnt
-FROM package_list
-GROUP BY 1;
+DROP VIEW IF EXISTS process_metadata_table;
+CREATE VIEW process_metadata_table AS
+SELECT * FROM android_process_metadata;
 
-DROP TABLE IF EXISTS process_metadata_table;
-
-CREATE TABLE process_metadata_table AS
-SELECT
-  process.upid,
-  -- workaround for b/169226092: the bug has been fixed it Android T, but
-  -- we support ingesting traces from older Android versions.
-  CASE
-      -- cmdline gets rewritten after fork, if these are still there we must
-      -- have seen a racy capture.
-    WHEN length(process.name) = 15 AND (
-      process.cmdline in ('zygote', 'zygote64', '<pre-initialized>')
-      OR process.cmdline GLOB '*' || process.name)
-    THEN process.cmdline
-    ELSE process.name
-  END 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 process
-LEFT JOIN uid_package_count ON process.android_appid = uid_package_count.uid
-LEFT JOIN package_list plist
-ON (
-  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 process.name GLOB plist.package_name || '*')
-  );
+DROP VIEW IF EXISTS uid_package_count;
+CREATE VIEW uid_package_count AS
+SELECT * FROM internal_uid_package_count;
 
 DROP VIEW IF EXISTS process_metadata;
-
 CREATE VIEW process_metadata AS
 WITH upid_packages AS (
   SELECT
-  upid,
-  RepeatedField(AndroidProcessMetadata_Package(
-    'package_name', package_list.package_name,
-    'apk_version_code', package_list.version_code,
-    'debuggable', package_list.debuggable
-  )) packages_for_uid
+    upid,
+    RepeatedField(AndroidProcessMetadata_Package(
+      'package_name', package_list.package_name,
+      'apk_version_code', package_list.version_code,
+      'debuggable', package_list.debuggable
+    )) AS packages_for_uid
   FROM process
   JOIN package_list ON process.android_appid = package_list.uid
   GROUP BY upid
diff --git a/src/trace_processor/metrics/sql/android/process_oom_score.sql b/src/trace_processor/metrics/sql/android/process_oom_score.sql
index 3a53688..863bff3 100644
--- a/src/trace_processor/metrics/sql/android/process_oom_score.sql
+++ b/src/trace_processor/metrics/sql/android/process_oom_score.sql
@@ -20,7 +20,7 @@
 SELECT
   ts,
   LEAD(ts, 1, (SELECT end_ts + 1 FROM trace_bounds))
-    OVER(PARTITION BY track_id ORDER BY ts) - ts AS dur,
+  OVER(PARTITION BY track_id ORDER BY ts) - ts AS dur,
   upid,
   CAST(value AS INT) AS oom_score_val
 FROM counter c JOIN process_counter_track t
diff --git a/src/trace_processor/metrics/sql/android/process_unagg_mem_view.sql b/src/trace_processor/metrics/sql/android/process_unagg_mem_view.sql
index d19c4e2..0a1aea8 100644
--- a/src/trace_processor/metrics/sql/android/process_unagg_mem_view.sql
+++ b/src/trace_processor/metrics/sql/android/process_unagg_mem_view.sql
@@ -25,6 +25,6 @@
       'oom_score', oom_score_val,
       'value', {{table_name}}_val
     )
-  ) as metric
+  ) AS metric
 FROM {{table_name}}_by_oom_span
 GROUP BY upid;
diff --git a/src/trace_processor/metrics/sql/android/profiler_smaps.sql b/src/trace_processor/metrics/sql/android/profiler_smaps.sql
index 481c74e..9745801 100644
--- a/src/trace_processor/metrics/sql/android/profiler_smaps.sql
+++ b/src/trace_processor/metrics/sql/android/profiler_smaps.sql
@@ -14,40 +14,40 @@
 -- limitations under the License.
 --
 
-SELECT RUN_METRIC('android/process_metadata.sql') as unused;
+SELECT RUN_METRIC('android/process_metadata.sql') AS unused;
 
 DROP VIEW IF EXISTS profiler_smaps_output;
 CREATE VIEW profiler_smaps_output AS
-  WITH base_stat_counts AS (
-    SELECT
-      ts,
-      upid,
-      path,
-      SUM(size_kb) size_kb,
-      SUM(private_dirty_kb) private_dirty_kb,
-      SUM(swap_kb) swap_kb
-    FROM profiler_smaps
-    GROUP BY 1, 2, 3
-    ORDER BY 4 DESC
-  ),
-  mapping_protos AS (
-    SELECT
-      ts,
-      upid,
-      RepeatedField(ProfilerSmaps_Mapping(
+WITH base_stat_counts AS (
+  SELECT
+    ts,
+    upid,
+    path,
+    SUM(size_kb) AS size_kb,
+    SUM(private_dirty_kb) AS private_dirty_kb,
+    SUM(swap_kb) AS swap_kb
+  FROM profiler_smaps
+  GROUP BY 1, 2, 3
+  ORDER BY 4 DESC
+),
+mapping_protos AS (
+  SELECT
+    ts,
+    upid,
+    RepeatedField(ProfilerSmaps_Mapping(
         'path', path,
         'size_kb', size_kb,
         'private_dirty_kb', private_dirty_kb,
         'swap_kb', swap_kb
-      )) mappings
-    FROM base_stat_counts
-    GROUP BY 1, 2
-  )
-  SELECT ProfilerSmaps(
+      )) AS mappings
+  FROM base_stat_counts
+  GROUP BY 1, 2
+)
+SELECT ProfilerSmaps(
     'instance', RepeatedField(
       ProfilerSmaps_Instance(
         'process', process_metadata.metadata,
         'mappings', mappings
       ))
   )
-  FROM mapping_protos JOIN process_metadata USING (upid);
+FROM mapping_protos JOIN process_metadata USING (upid);
diff --git a/src/trace_processor/metrics/sql/android/span_view_stats.sql b/src/trace_processor/metrics/sql/android/span_view_stats.sql
index 80f73f5..824c42a 100644
--- a/src/trace_processor/metrics/sql/android/span_view_stats.sql
+++ b/src/trace_processor/metrics/sql/android/span_view_stats.sql
@@ -40,7 +40,7 @@
 SELECT
   upid,
   -- max over all ts
-  MAX(delta) delta
+  MAX(delta) AS delta
 FROM rolling_delta
 GROUP BY 1;
 
diff --git a/src/trace_processor/metrics/sql/android/startup/gc_slices.sql b/src/trace_processor/metrics/sql/android/startup/gc_slices.sql
index 372c4da..9979b2c 100644
--- a/src/trace_processor/metrics/sql/android/startup/gc_slices.sql
+++ b/src/trace_processor/metrics/sql/android/startup/gc_slices.sql
@@ -16,12 +16,12 @@
 
 DROP VIEW IF EXISTS gc_slices;
 CREATE VIEW gc_slices AS
-SELECT slice_ts AS ts, slice_dur AS dur, utid, launch_id
+SELECT slice_ts AS ts, slice_dur AS dur, utid, startup_id AS launch_id
 FROM thread_slices_for_all_launches
 WHERE
-  slice_name GLOB '*mark sweep GC' OR
-  slice_name GLOB '*concurrent copying GC' OR
-  slice_name GLOB '*semispace GC';
+  slice_name GLOB '*mark sweep GC'
+  OR slice_name GLOB '*concurrent copying GC'
+  OR slice_name GLOB '*semispace GC';
 
 DROP TABLE IF EXISTS gc_slices_by_state;
 CREATE VIRTUAL TABLE gc_slices_by_state
@@ -29,7 +29,7 @@
 
 DROP TABLE IF EXISTS running_gc_slices_materialized;
 CREATE TABLE running_gc_slices_materialized AS
-SELECT launch_id, SUM(dur) as sum_dur
+SELECT launch_id, SUM(dur) AS sum_dur
 FROM gc_slices_by_state
 WHERE state = 'Running'
 GROUP BY launch_id;
diff --git a/src/trace_processor/metrics/sql/android/startup/hsc.sql b/src/trace_processor/metrics/sql/android/startup/hsc.sql
index 09db7d9..c632673 100644
--- a/src/trace_processor/metrics/sql/android/startup/hsc.sql
+++ b/src/trace_processor/metrics/sql/android/startup/hsc.sql
@@ -14,44 +14,50 @@
 -- limitations under the License.
 --
 
+SELECT IMPORT('android.startup.startups');
+
 -- Must be invoked after populating launches table in android_startup.
 DROP VIEW IF EXISTS functions;
 CREATE VIEW functions AS
 SELECT
-    slices.ts as ts,
-    slices.dur as dur,
-    process.name as process_name,
-    thread.name as thread_name,
-    slices.name as function_name
+  slices.ts AS ts,
+  slices.dur AS dur,
+  process.name AS process_name,
+  thread.name AS thread_name,
+  slices.name AS function_name
 FROM slices
-INNER JOIN thread_track on slices.track_id = thread_track.id
-INNER JOIN thread USING(utid)
-INNER JOIN process USING(upid);
+JOIN thread_track ON slices.track_id = thread_track.id
+JOIN thread USING(utid)
+JOIN process USING(upid);
 
 -- Animators don't occur on threads, so add them here.
 DROP VIEW IF EXISTS animators;
 CREATE VIEW animators AS
 SELECT
-    slices.ts AS ts,
-    slices.dur AS dur,
-    thread.name AS process_name,
-    slices.name AS animator_name
+  slices.ts AS ts,
+  slices.dur AS dur,
+  thread.name AS process_name,
+  slices.name AS animator_name
 FROM slices
-INNER JOIN process_track on slices.track_id = process_track.id
-INNER JOIN thread USING(upid)
+JOIN process_track ON slices.track_id = process_track.id
+JOIN thread USING(upid)
 WHERE slices.name GLOB "animator*";
 
+DROP VIEW IF EXISTS android_frame_times;
+CREATE VIEW android_frame_times AS
+SELECT
+  functions.ts AS ts,
+  functions.ts + functions.dur AS ts_end,
+  launches.package AS name,
+  launches.startup_id,
+  ROW_NUMBER() OVER(PARTITION BY launches.startup_id ORDER BY functions.ts ASC) AS number
+FROM functions
+JOIN android_startups launches ON launches.package GLOB '*' || functions.process_name || '*'
+WHERE functions.function_name GLOB "Choreographer#doFrame*" AND functions.ts > launches.ts;
+
 DROP VIEW IF EXISTS frame_times;
 CREATE VIEW frame_times AS
-SELECT
-    functions.ts AS ts,
-    functions.ts + functions.dur AS ts_end,
-    launches.package AS name,
-    launches.id AS launch_id,
-    ROW_NUMBER() OVER(PARTITION BY launches.id ORDER BY functions.ts ASC) as number
-FROM functions
-INNER JOIN launches on launches.package GLOB '*' || functions.process_name || '*'
-WHERE functions.function_name GLOB "Choreographer#doFrame*" AND functions.ts > launches.ts;
+SELECT startup_id AS launch_id, * FROM android_frame_times;
 
 DROP TABLE IF EXISTS hsc_based_startup_times;
 CREATE TABLE hsc_based_startup_times(package STRING, id INT, ts_total INT);
@@ -59,201 +65,201 @@
 -- Calculator
 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 GLOB '*' || frame_times.name || '*'
-WHERE frame_times.number=2 AND frame_times.name GLOB "*roid.calcul*" AND frame_times.launch_id = launches.id;
+  launches.package AS package,
+  launches.startup_id AS id,
+  android_frame_times.ts_end - launches.ts AS ts_total
+FROM android_frame_times
+JOIN android_startups launches ON launches.package GLOB '*' || android_frame_times.name || '*'
+WHERE android_frame_times.number = 2 AND android_frame_times.name GLOB "*roid.calcul*" AND android_frame_times.startup_id = launches.startup_id;
 
 -- Calendar
 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 GLOB '*' || frame_times.name || '*'
-WHERE frame_times.name GLOB "*id.calendar*" AND frame_times.launch_id = launches.id
-ORDER BY ABS(frame_times.ts_end - (SELECT ts + dur FROM functions WHERE function_name GLOB "DrawFrame*" AND process_name GLOB "*id.calendar" ORDER BY ts LIMIT 1)) LIMIT 1;
+  launches.package AS package,
+  launches.startup_id AS id,
+  android_frame_times.ts_end - launches.ts AS ts_total
+FROM android_frame_times
+JOIN android_startups launches ON launches.package GLOB '*' || android_frame_times.name || '*'
+WHERE android_frame_times.name GLOB "*id.calendar*" AND android_frame_times.startup_id = launches.startup_id
+ORDER BY ABS(android_frame_times.ts_end - (SELECT ts + dur FROM functions WHERE function_name GLOB "DrawFrame*" AND process_name GLOB "*id.calendar" ORDER BY ts LIMIT 1)) LIMIT 1;
 
 -- Camera
 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 GLOB '*' || frame_times.name || '*'
-WHERE frame_times.number=2 AND frame_times.name GLOB "*GoogleCamera*" AND frame_times.launch_id = launches.id;
+  launches.package AS package,
+  launches.startup_id AS id,
+  android_frame_times.ts_end - launches.ts AS ts_total
+FROM android_frame_times
+JOIN android_startups launches ON launches.package GLOB '*' || android_frame_times.name || '*'
+WHERE android_frame_times.number = 2 AND android_frame_times.name GLOB "*GoogleCamera*" AND android_frame_times.startup_id = launches.startup_id;
 
 -- Chrome
 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 GLOB '*' || frame_times.name || '*'
-WHERE frame_times.number=1 AND frame_times.name GLOB "*chrome*" AND frame_times.launch_id = launches.id;
+  launches.package AS package,
+  launches.startup_id AS id,
+  android_frame_times.ts_end - launches.ts AS ts_total
+FROM android_frame_times
+JOIN android_startups launches ON launches.package GLOB '*' || android_frame_times.name || '*'
+WHERE android_frame_times.number = 4 AND android_frame_times.name GLOB "*chrome*" AND android_frame_times.startup_id = launches.startup_id;
 
 -- Clock
 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 GLOB '*' || frame_times.name || '*'
-WHERE frame_times.ts > (SELECT ts + dur FROM animators WHERE animator_name="animator:translationZ" AND process_name GLOB "*id.deskclock" ORDER BY (ts+dur) DESC LIMIT 1) AND frame_times.name GLOB "*id.deskclock" AND frame_times.launch_id = launches.id
+  launches.package AS package,
+  launches.startup_id AS id,
+  android_frame_times.ts_end - launches.ts AS ts_total
+FROM android_frame_times
+JOIN android_startups launches ON launches.package GLOB '*' || android_frame_times.name || '*'
+WHERE android_frame_times.ts > (SELECT ts + dur FROM animators WHERE animator_name = "animator:translationZ" AND process_name GLOB "*id.deskclock" ORDER BY (ts + dur) DESC LIMIT 1) AND android_frame_times.name GLOB "*id.deskclock" AND android_frame_times.startup_id = launches.startup_id
 ORDER BY ts_total LIMIT 1;
 
 -- Contacts
 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 GLOB '*' || frame_times.name || '*'
-WHERE frame_times.number=3 AND frame_times.name GLOB "*id.contacts" AND frame_times.launch_id=launches.id;
+  launches.package AS package,
+  launches.startup_id AS id,
+  android_frame_times.ts_end - launches.ts AS ts_total
+FROM android_frame_times
+JOIN android_startups launches ON launches.package GLOB '*' || android_frame_times.name || '*'
+WHERE android_frame_times.number = 3 AND android_frame_times.name GLOB "*id.contacts" AND android_frame_times.startup_id = launches.startup_id;
 
 -- Dialer
 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 GLOB '*' || frame_times.name || '*'
-WHERE frame_times.number=1 AND frame_times.name GLOB "*id.dialer" AND frame_times.launch_id=launches.id;
+  launches.package AS package,
+  launches.startup_id AS id,
+  android_frame_times.ts_end - launches.ts AS ts_total
+FROM android_frame_times
+JOIN android_startups launches ON launches.package GLOB '*' || android_frame_times.name || '*'
+WHERE android_frame_times.number = 1 AND android_frame_times.name GLOB "*id.dialer" AND android_frame_times.startup_id = launches.startup_id;
 
 -- Facebook
 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 GLOB '*' || frame_times.name || '*'
-WHERE frame_times.ts > (SELECT ts+dur FROM slices WHERE slices.name GLOB "fb_startup_complete" ORDER BY ts LIMIT 1) AND frame_times.name GLOB "*ok.katana" AND frame_times.launch_id = launches.id
+  launches.package AS package,
+  launches.startup_id AS id,
+  android_frame_times.ts_end - launches.ts AS ts_total
+FROM android_frame_times
+JOIN android_startups launches ON launches.package GLOB '*' || android_frame_times.name || '*'
+WHERE android_frame_times.ts > (SELECT ts + dur FROM slices WHERE slices.name GLOB "fb_startup_complete" ORDER BY ts LIMIT 1) AND android_frame_times.name GLOB "*ok.katana" AND android_frame_times.startup_id = launches.startup_id
 ORDER BY ts_total LIMIT 1;
 
 -- Facebook Messenger
 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 GLOB '*' || frame_times.name || '*'
-WHERE frame_times.ts > (SELECT ts+dur FROM slices WHERE slices.name GLOB "msgr_cold_start_to_cached_content" ORDER BY ts LIMIT 1) AND frame_times.name GLOB "*book.orca" AND frame_times.launch_id = launches.id
+  launches.package AS package,
+  launches.startup_id AS id,
+  android_frame_times.ts_end - launches.ts AS ts_total
+FROM android_frame_times
+JOIN android_startups launches ON launches.package GLOB '*' || android_frame_times.name || '*'
+WHERE android_frame_times.ts > (SELECT ts + dur FROM slices WHERE slices.name GLOB "msgr_cold_start_to_cached_content" ORDER BY ts LIMIT 1) AND android_frame_times.name GLOB "*book.orca" AND android_frame_times.startup_id = launches.startup_id
 ORDER BY ts_total LIMIT 1;
 
 -- Gmail
 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 GLOB '*' || frame_times.name || '*'
-WHERE frame_times.ts > (SELECT ts + dur FROM animators WHERE animator_name="animator:elevation" AND process_name GLOB "*android.gm" ORDER BY (ts+dur) DESC LIMIT 1) AND frame_times.name GLOB "*android.gm" AND frame_times.launch_id = launches.id
+  launches.package AS package,
+  launches.startup_id AS id,
+  android_frame_times.ts_end - launches.ts AS ts_total
+FROM android_frame_times
+JOIN android_startups launches ON launches.package GLOB '*' || android_frame_times.name || '*'
+WHERE android_frame_times.ts > (SELECT ts + dur FROM animators WHERE animator_name = "animator:elevation" AND process_name GLOB "*android.gm" ORDER BY (ts + dur) DESC LIMIT 1) AND android_frame_times.name GLOB "*android.gm" AND android_frame_times.startup_id = launches.startup_id
 ORDER BY ts_total LIMIT 1;
 
 -- Instagram
 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 GLOB '*' || frame_times.name || '*'
-WHERE frame_times.ts > (SELECT ts+dur FROM slices WHERE slices.name GLOB "ig_cold_start_to_cached_content" ORDER BY ts LIMIT 1) AND frame_times.name GLOB "*gram.android" AND frame_times.launch_id = launches.id
+  launches.package AS package,
+  launches.startup_id AS id,
+  android_frame_times.ts_end - launches.ts AS ts_total
+FROM android_frame_times
+JOIN android_startups launches ON launches.package GLOB '*' || android_frame_times.name || '*'
+WHERE android_frame_times.ts > (SELECT ts + dur FROM slices WHERE slices.name GLOB "ig_cold_start_to_cached_content" ORDER BY ts LIMIT 1) AND android_frame_times.name GLOB "*gram.android" AND android_frame_times.startup_id = launches.startup_id
 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 GLOB '*' || frame_times.name || '*'
-WHERE frame_times.number=1 AND frame_times.name GLOB "*maps*" AND frame_times.launch_id = launches.id;
+  launches.package AS package,
+  launches.startup_id AS id,
+  android_frame_times.ts_end - launches.ts AS ts_total
+FROM android_frame_times
+JOIN android_startups launches ON launches.package GLOB '*' || android_frame_times.name || '*'
+WHERE android_frame_times.number = 1 AND android_frame_times.name GLOB "*maps*" AND android_frame_times.startup_id = launches.startup_id;
 
 -- Messages
 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 GLOB '*' || frame_times.name || '*'
-WHERE frame_times.ts_end > (SELECT ts + dur FROM animators WHERE animator_name="animator:translationZ" AND process_name GLOB "*apps.messaging*" ORDER BY ts LIMIT 1) AND frame_times.name GLOB "*apps.messaging*" AND frame_times.launch_id = launches.id
+  launches.package AS package,
+  launches.startup_id AS id,
+  android_frame_times.ts_end - launches.ts AS ts_total
+FROM android_frame_times
+JOIN android_startups launches ON launches.package GLOB '*' || android_frame_times.name || '*'
+WHERE android_frame_times.ts_end > (SELECT ts + dur FROM animators WHERE animator_name = "animator:translationZ" AND process_name GLOB "*apps.messaging*" ORDER BY ts LIMIT 1) AND android_frame_times.name GLOB "*apps.messaging*" AND android_frame_times.startup_id = launches.startup_id
 ORDER BY ts_total LIMIT 1;
 
 -- 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 GLOB '*' || frame_times.name || '*'
-WHERE frame_times.ts < (SELECT ts FROM animators WHERE animator_name GLOB "animator*" AND process_name GLOB "*lix.mediaclient" ORDER BY ts LIMIT 1) AND frame_times.name GLOB "*lix.mediaclient*" AND frame_times.launch_id = launches.id
+  launches.package AS package,
+  launches.startup_id AS id,
+  android_frame_times.ts_end - launches.ts AS ts_total
+FROM android_frame_times
+JOIN android_startups launches ON launches.package GLOB '*' || android_frame_times.name || '*'
+WHERE android_frame_times.ts < (SELECT ts FROM animators WHERE animator_name GLOB "animator*" AND process_name GLOB "*lix.mediaclient" ORDER BY ts LIMIT 1) AND android_frame_times.name GLOB "*lix.mediaclient*" AND android_frame_times.startup_id = launches.startup_id
 ORDER BY ts_total DESC LIMIT 1;
 
 -- Photos
 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 GLOB '*' || frame_times.name || '*'
-WHERE frame_times.number=1 AND frame_times.name GLOB "*apps.photos*" AND frame_times.launch_id = launches.id;
+  launches.package AS package,
+  launches.startup_id AS id,
+  android_frame_times.ts_end - launches.ts AS ts_total
+FROM android_frame_times
+JOIN android_startups launches ON launches.package GLOB '*' || android_frame_times.name || '*'
+WHERE android_frame_times.number = 1 AND android_frame_times.name GLOB "*apps.photos*" AND android_frame_times.startup_id = launches.startup_id;
 
 -- Settings was deprecated in favor of reportFullyDrawn b/169694037.
 
 -- Snapchat
 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 GLOB '*' || frame_times.name || '*'
-WHERE frame_times.number=1 AND frame_times.name GLOB "*napchat.android" AND frame_times.launch_id = launches.id;
+  launches.package AS package,
+  launches.startup_id AS id,
+  android_frame_times.ts_end - launches.ts AS ts_total
+FROM android_frame_times
+JOIN android_startups launches ON launches.package GLOB '*' || android_frame_times.name || '*'
+WHERE android_frame_times.number = 1 AND android_frame_times.name GLOB "*napchat.android" AND android_frame_times.startup_id = launches.startup_id;
 
 -- Twitter
 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 GLOB '*' || frame_times.name || '*'
-WHERE frame_times.ts_end > (SELECT ts FROM animators WHERE animator_name="animator" AND process_name GLOB "*tter.android" ORDER BY ts LIMIT 1) AND frame_times.name GLOB "*tter.android" AND frame_times.launch_id = launches.id
+  launches.package AS package,
+  launches.startup_id AS id,
+  android_frame_times.ts_end - launches.ts AS ts_total
+FROM android_frame_times
+JOIN android_startups launches ON launches.package GLOB '*' || android_frame_times.name || '*'
+WHERE android_frame_times.ts_end > (SELECT ts FROM animators WHERE animator_name = "animator" AND process_name GLOB "*tter.android" ORDER BY ts LIMIT 1) AND android_frame_times.name GLOB "*tter.android" AND android_frame_times.startup_id = launches.startup_id
 ORDER BY ts_total LIMIT 1;
 
 -- WhatsApp
 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 GLOB '*' || frame_times.name || '*'
-WHERE frame_times.ts > (SELECT ts+dur FROM slices WHERE slices.name GLOB "wa_startup_complete" ORDER BY ts LIMIT 1) AND frame_times.name GLOB "*om.whatsapp" AND frame_times.launch_id = launches.id
+  launches.package AS package,
+  launches.startup_id AS id,
+  android_frame_times.ts_end - launches.ts AS ts_total
+FROM android_frame_times
+JOIN android_startups launches ON launches.package GLOB '*' || android_frame_times.name || '*'
+WHERE android_frame_times.ts > (SELECT ts + dur FROM slices WHERE slices.name GLOB "wa_startup_complete" ORDER BY ts LIMIT 1) AND android_frame_times.name GLOB "*om.whatsapp" AND android_frame_times.startup_id = launches.startup_id
 ORDER BY ts_total LIMIT 1;
 
 -- Youtube
 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 GLOB '*' || frame_times.name || '*'
-WHERE frame_times.number=2 AND frame_times.name GLOB "*id.youtube" AND frame_times.launch_id = launches.id;
+  launches.package AS package,
+  launches.startup_id AS id,
+  android_frame_times.ts_end - launches.ts AS ts_total
+FROM android_frame_times
+JOIN android_startups launches ON launches.package GLOB '*' || android_frame_times.name || '*'
+WHERE android_frame_times.number = 2 AND android_frame_times.name GLOB "*id.youtube" AND android_frame_times.startup_id = launches.startup_id;
diff --git a/src/trace_processor/metrics/sql/android/startup/launches.sql b/src/trace_processor/metrics/sql/android/startup/launches.sql
index 422042b..8387cfe 100644
--- a/src/trace_processor/metrics/sql/android/startup/launches.sql
+++ b/src/trace_processor/metrics/sql/android/startup/launches.sql
@@ -13,116 +13,23 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 
--- Define process metadata functions.
-SELECT RUN_METRIC('android/process_metadata.sql');
+-- This metric will be deprecated soon. All of the tables have been 
+-- migrated to SQL standard library and can be imported from:
+SELECT IMPORT('android.startup.startups');
 
--- 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.
-DROP TABLE IF EXISTS launching_events;
-CREATE TABLE launching_events AS
-SELECT
-  ts,
-  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
-  s.name GLOB 'launching: *' AND
-  (process.name IS NULL OR process.name = 'system_server');
 
-SELECT CREATE_FUNCTION(
-  'SLICE_COUNT(slice_glob STRING)',
-  'INT',
-  'SELECT COUNT(1) FROM slice WHERE name GLOB $slice_glob'
-);
+DROP VIEW IF EXISTS launches;
+CREATE VIEW launches AS
+SELECT startup_id AS launch_id, *, startup_type as launch_type FROM android_startups;
 
--- All activity launches in the trace, keyed by ID.
--- Populated by different scripts depending on the platform version / contents.
--- See android/startup/launches*.sql
-DROP TABLE IF EXISTS launches;
-CREATE TABLE launches(
-  id INTEGER PRIMARY KEY,
-  ts BIG INT,
-  ts_end BIG INT,
-  dur BIG INT,
-  package STRING
-);
+DROP VIEW IF EXISTS launch_processes;
+CREATE VIEW launch_processes AS
+SELECT startup_id AS launch_id, * FROM android_startup_processes;
 
--- Note: on Q, we didn't have Android fingerprints but we *did*
--- have ActivityMetricsLogger events so we will use this approach
--- if we see any such events.
-SELECT CASE
-  WHEN SLICE_COUNT('launchingActivity#*:*') > 0
-    THEN RUN_METRIC('android/startup/launches_minsdk33.sql')
-  WHEN SLICE_COUNT('MetricsLogger:*') > 0
-    THEN RUN_METRIC('android/startup/launches_minsdk29.sql')
-  ELSE RUN_METRIC('android/startup/launches_maxsdk28.sql')
-END;
-
--- Maps a launch to the corresponding set of processes that handled the
--- activity start. The vast majority of cases should be a single process.
--- However it is possible that the process dies during the activity launch
--- and is respawned.
-DROP TABLE IF EXISTS launch_processes;
-CREATE TABLE launch_processes(launch_id INT, upid BIG INT, launch_type STRING);
-
-SELECT CREATE_FUNCTION(
-  'STARTUP_SLICE_COUNT(start_ts LONG, end_ts LONG, utid INT, name STRING)',
-  'INT',
-  '
-    SELECT COUNT(1)
-    FROM thread_track t
-    JOIN slice s ON s.track_id = t.id
-    WHERE
-      t.utid = $utid AND
-      s.ts >= $start_ts AND
-      s.ts < $end_ts AND
-      s.name = $name
-  '
-);
-
-INSERT INTO launch_processes(launch_id, upid, launch_type)
-SELECT *
-FROM (
-  -- This is intentionally a nested subquery. For some reason, if we put
-  -- the `WHERE launch_type IS NOT NULL` constraint inside, we end up with a
-  -- query which is an order of magnitude slower than being outside :(
-  SELECT
-    launch_id,
-    upid,
-    CASE
-      WHEN bind_app > 0 AND a_start > 0 AND a_resume > 0 THEN 'cold'
-      WHEN a_start > 0 AND a_resume > 0 THEN 'warm'
-      WHEN a_resume > 0 THEN 'hot'
-      ELSE NULL
-    END AS launch_type
-  FROM (
-    SELECT
-      l.id AS launch_id,
-      p.upid,
-      STARTUP_SLICE_COUNT(l.ts, l.ts_end, t.utid, 'bindApplication') bind_app,
-      STARTUP_SLICE_COUNT(l.ts, l.ts_end, t.utid, 'activityStart') a_start,
-      STARTUP_SLICE_COUNT(l.ts, l.ts_end, t.utid, 'activityResume') a_resume
-    FROM launches l
-    JOIN process_metadata_table p ON (l.package = p.package_name)
-    JOIN thread t ON (p.upid = t.upid AND t.is_main_thread)
-  )
-)
-WHERE launch_type IS NOT NULL;
-
--- Tracks all main process threads.
 DROP VIEW IF EXISTS launch_threads;
 CREATE VIEW launch_threads AS
-SELECT
-  launches.id AS launch_id,
-  launches.ts AS ts,
-  launches.dur AS dur,
-  thread.utid AS utid,
-  thread.name AS thread_name,
-  thread.is_main_thread AS is_main_thread
-FROM launches
-JOIN launch_processes ON (launches.id = launch_processes.launch_id)
-JOIN thread USING (upid);
+SELECT startup_id AS launch_id, * FROM android_startup_threads;
+
+DROP VIEW IF EXISTS launching_events;
+CREATE VIEW launching_events AS
+SELECT * FROM internal_startup_events;
diff --git a/src/trace_processor/metrics/sql/android/startup/launches_maxsdk28.sql b/src/trace_processor/metrics/sql/android/startup/launches_maxsdk28.sql
index 35c797e..9e67c4a 100644
--- a/src/trace_processor/metrics/sql/android/startup/launches_maxsdk28.sql
+++ b/src/trace_processor/metrics/sql/android/startup/launches_maxsdk28.sql
@@ -15,13 +15,14 @@
 --
 
 -- Cold/warm starts emitted launching slices on API level 28-.
-INSERT INTO launches(id, ts, ts_end, dur, package)
+INSERT INTO launches(id, ts, ts_end, dur, package, launch_type)
 SELECT
   ROW_NUMBER() OVER(ORDER BY ts) AS id,
   launching_events.ts AS ts,
   launching_events.ts_end AS ts_end,
   launching_events.ts_end - launching_events.ts AS dur,
-  package_name AS package
+  package_name AS package,
+  NULL AS launch_type
 FROM launching_events
 ORDER BY ts;
 
diff --git a/src/trace_processor/metrics/sql/android/startup/launches_minsdk29.sql b/src/trace_processor/metrics/sql/android/startup/launches_minsdk29.sql
index e5631bf..1f4cd9a 100644
--- a/src/trace_processor/metrics/sql/android/startup/launches_minsdk29.sql
+++ b/src/trace_processor/metrics/sql/android/startup/launches_minsdk29.sql
@@ -25,12 +25,12 @@
 -- We will refine these progressively in the next steps to only encompass
 -- activity starts.
 DROP TABLE IF EXISTS activity_intent_recv_spans;
-CREATE TABLE activity_intent_recv_spans(id INT, ts BIG INT, dur BIG INT);
+CREATE TABLE activity_intent_recv_spans(id INT, ts BIGINT, dur BIGINT);
 
 INSERT INTO activity_intent_recv_spans
 SELECT
   ROW_NUMBER()
-    OVER(ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS id,
+  OVER(ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS id,
   ts,
   LEAD(ts, 1, (SELECT end_ts FROM trace_bounds)) OVER(ORDER BY ts) - ts AS dur
 FROM activity_intent_received
@@ -55,17 +55,18 @@
 
 -- Use the starting event package name. The finish event package name
 -- is not reliable in the case of failed launches.
-INSERT INTO launches(id, ts, ts_end, dur, package)
+INSERT INTO launches(id, ts, ts_end, dur, package, launch_type)
 SELECT
   lpart.id AS id,
   lpart.ts AS ts,
   launching_events.ts_end AS ts_end,
   launching_events.ts_end - lpart.ts AS dur,
-  package_name AS package
+  package_name AS package,
+  NULL AS launch_type
 FROM launch_partitions AS lpart
 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)
+  (launching_events.ts BETWEEN lpart.ts AND lpart.ts + lpart.dur)
+  AND (launching_events.ts_end BETWEEN lpart.ts AND lpart.ts + lpart.dur)
 WHERE (
   SELECT COUNT(1)
   FROM activity_intent_launch_successful AS successful
diff --git a/src/trace_processor/metrics/sql/android/startup/launches_minsdk33.sql b/src/trace_processor/metrics/sql/android/startup/launches_minsdk33.sql
index 9160bee..4d692a9 100644
--- a/src/trace_processor/metrics/sql/android/startup/launches_minsdk33.sql
+++ b/src/trace_processor/metrics/sql/android/startup/launches_minsdk33.sql
@@ -19,34 +19,43 @@
 SELECT
   ts,
   dur,
-  SUBSTR(name, 19) id
+  SUBSTR(name, 19) AS id
 FROM slice
 WHERE
-  name GLOB 'launchingActivity#*' AND
-  dur != 0 AND
-  INSTR(name, ':') = 0;
+  name GLOB 'launchingActivity#*'
+  AND dur != 0
+  AND INSTR(name, ':') = 0;
 
 DROP VIEW IF EXISTS launch_complete_events;
 CREATE VIEW launch_complete_events AS
 SELECT
-  STR_SPLIT(completed, ':completed:', 0) id,
-  STR_SPLIT(completed, ':completed:', 1) package_name,
+  STR_SPLIT(completed, ':', 0) AS id,
+  STR_SPLIT(completed, ':', 2) AS package_name,
+  CASE
+    WHEN STR_SPLIT(completed, ':', 1) = 'completed-hot' THEN 'hot'
+    WHEN STR_SPLIT(completed, ':', 1) = 'completed-warm' THEN 'warm'
+    WHEN STR_SPLIT(completed, ':', 1) = 'completed-cold' THEN 'cold'
+    ELSE NULL
+  END AS launch_type,
   MIN(ts)
 FROM (
-  SELECT ts, SUBSTR(name, 19) completed
+  SELECT ts, SUBSTR(name, 19) AS completed
   FROM slice
   WHERE
-    dur = 0 AND
-    name GLOB 'launchingActivity#*:completed:*'
+    dur = 0
+    -- Originally completed was unqualified, but at some point we introduced
+    -- the startup type as well
+    AND name GLOB 'launchingActivity#*:completed*:*'
 )
-GROUP BY 1, 2;
+GROUP BY 1, 2, 3;
 
-INSERT INTO launches(id, ts, ts_end, dur, package)
+INSERT INTO launches(id, ts, ts_end, dur, package, launch_type)
 SELECT
   id,
   ts,
-  ts + dur ts_end,
+  ts + dur AS ts_end,
   dur,
-  package_name
+  package_name,
+  launch_type
 FROM launch_async_events
 JOIN launch_complete_events USING (id);
diff --git a/src/trace_processor/metrics/sql/android/startup/mcycles_per_launch.sql b/src/trace_processor/metrics/sql/android/startup/mcycles_per_launch.sql
index 44fb0aa..b47381e 100644
--- a/src/trace_processor/metrics/sql/android/startup/mcycles_per_launch.sql
+++ b/src/trace_processor/metrics/sql/android/startup/mcycles_per_launch.sql
@@ -14,6 +14,8 @@
 -- limitations under the License.
 --
 
+SELECT IMPORT('android.startup.startups');
+
 -- Create the base CPU span join table.
 SELECT RUN_METRIC('android/android_cpu_agg.sql');
 SELECT RUN_METRIC('android/cpu_info.sql');
@@ -21,17 +23,21 @@
 -- Create a span join safe launches view; since both views
 -- being span joined have an "id" column, we need to rename
 -- the id column for launches to disambiguate the two.
+DROP VIEW IF EXISTS android_launches_span_join_safe;
+CREATE VIEW android_launches_span_join_safe AS
+SELECT ts, dur, startup_id
+FROM android_startups;
+
 DROP VIEW IF EXISTS launches_span_join_safe;
 CREATE VIEW launches_span_join_safe AS
-SELECT ts, dur, id AS launch_id
-FROM launches;
+SELECT startup_id AS launch_id, * FROM android_launches_span_join_safe;
 
 -- Span join the CPU table with the launches table to get the
 -- breakdown per-cpu.
 DROP TABLE IF EXISTS cpu_freq_sched_per_thread_per_launch;
 CREATE VIRTUAL TABLE cpu_freq_sched_per_thread_per_launch
 USING SPAN_JOIN(
-  launches_span_join_safe,
+  android_launches_span_join_safe,
   cpu_freq_sched_per_thread PARTITIONED cpu
 );
 
@@ -39,7 +45,7 @@
 DROP TABLE IF EXISTS mcycles_per_core_type_per_launch;
 CREATE TABLE mcycles_per_core_type_per_launch AS
 SELECT
-  launch_id,
+  startup_id,
   IFNULL(core_type_per_cpu.core_type, 'unknown') AS core_type,
   CAST(SUM(dur * freq_khz / 1000) / 1e9 AS INT) AS mcycles
 FROM cpu_freq_sched_per_thread_per_launch
@@ -50,38 +56,12 @@
 -- Given a launch id and core type, returns the number of mcycles consumed
 -- on CPUs of that core type during the launch.
 SELECT CREATE_FUNCTION(
-  'MCYCLES_FOR_LAUNCH_AND_CORE_TYPE(launch_id INT, core_type STRING)',
+  'MCYCLES_FOR_LAUNCH_AND_CORE_TYPE(startup_id INT, core_type STRING)',
   'INT',
   '
     SELECT mcycles
     FROM mcycles_per_core_type_per_launch m
-    WHERE m.launch_id = $launch_id AND m.core_type = $core_type
-  '
-);
-
--- Given a launch id, returns the |n| processes which consume the most mcycles
--- during the launch excluding hte process(es) being launched. 
-SELECT CREATE_VIEW_FUNCTION(
-  'N_MOST_ACTIVE_PROCESSES_FOR_LAUNCH_UNMATERIALIZED(launch_id INT, n INT)',
-  'upid INT, mcycles INT',
-  '
-    SELECT
-      upid,
-      CAST(SUM(dur * freq_khz / 1000) / 1e9 AS INT) AS mcycles
-    FROM cpu_freq_sched_per_thread_per_launch c
-    JOIN thread USING (utid)
-    JOIN process USING (upid)
-    WHERE
-      launch_id = $launch_id AND
-      utid != 0 AND
-      upid NOT IN (
-        SELECT upid
-        FROM launch_processes l
-        WHERE c.launch_id = $launch_id
-      )
-    GROUP BY 1
-    ORDER BY 2 DESC
-    LIMIT $n
+    WHERE m.startup_id = $startup_id AND m.core_type = $core_type
   '
 );
 
@@ -90,15 +70,35 @@
 -- Materialized to avoid span-joining once per launch.
 DROP TABLE IF EXISTS top_mcyles_process_excluding_started_per_launch;
 CREATE TABLE top_mcyles_process_excluding_started_per_launch AS
-SELECT launches.id AS launch_id, upid, mcycles
-FROM
-  launches,
-  N_MOST_ACTIVE_PROCESSES_FOR_LAUNCH_UNMATERIALIZED(launches.id, 5);
+WITH mcycles_per_launch_and_process AS MATERIALIZED (
+  SELECT
+    startup_id,
+    upid,
+    CAST(SUM(dur * freq_khz / 1000) / 1e9 AS INT) AS mcycles
+  FROM cpu_freq_sched_per_thread_per_launch c
+  JOIN thread USING (utid)
+  JOIN process USING (upid)
+  WHERE
+    utid != 0
+    AND upid NOT IN (
+      SELECT upid
+      FROM android_startup_processes l
+    )
+  GROUP BY startup_id, upid
+)
+SELECT *
+FROM (
+  SELECT
+    *,
+    ROW_NUMBER() OVER (PARTITION BY startup_id ORDER BY mcycles DESC) AS mcycles_rank
+  FROM mcycles_per_launch_and_process
+)
+WHERE mcycles_rank <= 5;
 
 -- Given a launch id, returns the name of the processes consuming the most
 -- mcycles during the launch excluding the process being started.
 SELECT CREATE_FUNCTION(
-  'N_MOST_ACTIVE_PROCESS_NAMES_FOR_LAUNCH(launch_id INT)',
+  'N_MOST_ACTIVE_PROCESS_NAMES_FOR_LAUNCH(startup_id INT)',
   'STRING',
   '
     SELECT RepeatedField(process_name)
@@ -106,8 +106,21 @@
       SELECT IFNULL(process.name, "[NULL]") AS process_name
       FROM top_mcyles_process_excluding_started_per_launch
       JOIN process USING (upid)
-      WHERE launch_id = $launch_id
+      WHERE startup_id = $startup_id
       ORDER BY mcycles DESC
     );
   '
 );
+
+-- Given a launch id, returns the most active process name.
+SELECT CREATE_FUNCTION(
+  'MOST_ACTIVE_PROCESS_FOR_LAUNCH(startup_id INT)',
+  'STRING',
+  '
+    SELECT process.name AS process_name
+    FROM top_mcyles_process_excluding_started_per_launch
+    JOIN process USING (upid)
+    WHERE startup_id = $startup_id
+    ORDER BY mcycles DESC LIMIT 1;
+  '
+);
diff --git a/src/trace_processor/metrics/sql/android/startup/slice_functions.sql b/src/trace_processor/metrics/sql/android/startup/slice_functions.sql
index 80194b0..cd5b7b2 100644
--- a/src/trace_processor/metrics/sql/android/startup/slice_functions.sql
+++ b/src/trace_processor/metrics/sql/android/startup/slice_functions.sql
@@ -14,6 +14,8 @@
 -- limitations under the License.
 --
 
+SELECT IMPORT('android.startup.startups');
+
 -- Helper function to build a Slice proto from a duration.
 SELECT CREATE_FUNCTION('STARTUP_SLICE_PROTO(dur INT)', 'PROTO', '
   SELECT AndroidStartupMetric_Slice(
@@ -27,54 +29,46 @@
 -- this view should be used.
 DROP VIEW IF EXISTS thread_slices_for_all_launches;
 CREATE VIEW thread_slices_for_all_launches AS
-SELECT
-  launch_threads.ts AS launch_ts,
-  launch_threads.ts + launch_threads.dur AS launch_ts_end,
-  launch_threads.launch_id AS launch_id,
-  launch_threads.utid AS utid,
-  launch_threads.thread_name AS thread_name,
-  launch_threads.is_main_thread AS is_main_thread,
-  slice.arg_set_id AS arg_set_id,
-  slice.name AS slice_name,
-  slice.ts AS slice_ts,
-  slice.dur AS slice_dur
-FROM launch_threads
-JOIN thread_track USING (utid)
-JOIN slice ON (slice.track_id = thread_track.id)
-WHERE slice.ts BETWEEN launch_threads.ts AND launch_threads.ts + launch_threads.dur;
+SELECT * FROM android_thread_slices_for_all_startups;
 
--- Given a launch id and GLOB for a slice name, returns columns for matching slices.
-SELECT CREATE_VIEW_FUNCTION(
-  'SLICES_FOR_LAUNCH_AND_SLICE_NAME(launch_id INT, slice_name STRING)',
-  'slice_name STRING, slice_ts INT, slice_dur INT, thread_name STRING, arg_set_id INT',
+
+-- Given a launch id and GLOB for a slice name,
+-- summing the slice durations across the whole startup.
+SELECT CREATE_FUNCTION(
+  'ANDROID_SUM_DUR_FOR_STARTUP_AND_SLICE(startup_id LONG, slice_name STRING)',
+  'INT',
   '
-    SELECT slice_name, slice_ts, slice_dur, thread_name, arg_set_id
-    FROM thread_slices_for_all_launches
-    WHERE launch_id = $launch_id AND slice_name GLOB $slice_name
+    SELECT SUM(slice_dur)
+    FROM android_thread_slices_for_all_startups
+    WHERE startup_id = $startup_id AND slice_name GLOB $slice_name
   '
 );
 
 -- Given a launch id and GLOB for a slice name, returns the startup slice proto,
 -- summing the slice durations across the whole startup.
 SELECT CREATE_FUNCTION(
-  'DUR_SUM_SLICE_PROTO_FOR_LAUNCH(launch_id LONG, slice_name STRING)',
+  'DUR_SUM_SLICE_PROTO_FOR_LAUNCH(startup_id LONG, slice_name STRING)',
   'PROTO',
   '
-    SELECT NULL_IF_EMPTY(STARTUP_SLICE_PROTO(SUM(slice_dur)))
-    FROM thread_slices_for_all_launches
-    WHERE launch_id = $launch_id AND slice_name GLOB $slice_name
+    SELECT NULL_IF_EMPTY(
+      STARTUP_SLICE_PROTO(
+        ANDROID_SUM_DUR_FOR_STARTUP_AND_SLICE($startup_id, $slice_name)
+      )
+    )
   '
 );
 
 -- Same as |DUR_SUM_SLICE_PROTO_FOR_LAUNCH| except only counting slices happening
 -- on the main thread.
 SELECT CREATE_FUNCTION(
-  'DUR_SUM_MAIN_THREAD_SLICE_PROTO_FOR_LAUNCH(launch_id LONG, slice_name STRING)',
+  'DUR_SUM_MAIN_THREAD_SLICE_PROTO_FOR_LAUNCH(startup_id LONG, slice_name STRING)',
   'PROTO',
   '
-    SELECT NULL_IF_EMPTY(STARTUP_SLICE_PROTO(SUM(slice_dur)))
-    FROM thread_slices_for_all_launches
-    WHERE launch_id = $launch_id AND slice_name GLOB $slice_name AND is_main_thread
+    SELECT NULL_IF_EMPTY(
+      STARTUP_SLICE_PROTO(
+        ANDROID_SUM_DUR_ON_MAIN_THREAD_FOR_STARTUP_AND_SLICE($startup_id, $slice_name)
+      )
+    )
   '
 );
 
@@ -82,16 +76,110 @@
 -- taking the duration between the start of the launch and start of the slice.
 -- If multiple slices match, picks the latest one which started during the launch.
 SELECT CREATE_FUNCTION(
-  'LAUNCH_TO_MAIN_THREAD_SLICE_PROTO(launch_id INT, slice_name STRING)',
+  'LAUNCH_TO_MAIN_THREAD_SLICE_PROTO(startup_id INT, slice_name STRING)',
   'PROTO',
   '
-    SELECT NULL_IF_EMPTY(STARTUP_SLICE_PROTO(MAX(slice_ts) - launch_ts))
-    FROM thread_slices_for_all_launches s
+    SELECT NULL_IF_EMPTY(STARTUP_SLICE_PROTO(MAX(slice_ts) - startup_ts))
+    FROM android_thread_slices_for_all_startups s
     JOIN thread t USING (utid)
     WHERE
       s.slice_name GLOB $slice_name AND
-      s.launch_id = $launch_id AND
+      s.startup_id = $startup_id AND
       s.is_main_thread AND
-      (t.end_ts IS NULL OR t.end_ts >= s.launch_ts_end)
+      (t.end_ts IS NULL OR t.end_ts >= s.startup_ts_end)
+  '
+);
+
+-- Given a lauch id, returns the total time spent in GC
+SELECT CREATE_FUNCTION(
+  'TOTAL_GC_TIME_BY_LAUNCH(startup_id LONG)',
+  'INT',
+  '
+    SELECT SUM(slice_dur)
+        FROM android_thread_slices_for_all_startups slice
+        WHERE
+          slice.startup_id = $startup_id AND
+          (
+            slice_name GLOB "*semispace GC" OR
+            slice_name GLOB "*mark sweep GC" OR
+            slice_name GLOB "*concurrent copying GC"
+          )
+  '
+);
+
+-- Given a launch id and package name, returns if baseline or cloud profile is missing.
+SELECT CREATE_FUNCTION(
+  'MISSING_BASELINE_PROFILE_FOR_LAUNCH(startup_id LONG, pkg_name STRING)',
+  'BOOL',
+  '
+    SELECT (COUNT(slice_name) > 0)
+    FROM (
+      SELECT *
+      FROM ANDROID_SLICES_FOR_STARTUP_AND_SLICE_NAME(
+        $startup_id,
+        "location=* status=* filter=* reason=*"
+      )
+      ORDER BY slice_name
+    )
+    WHERE
+      -- when location is the package odex file and the reason is "install" or "install-dm",
+      -- if the compilation filter is not "speed-profile", baseline/cloud profile is missing.
+      SUBSTR(STR_SPLIT(slice_name, " status=", 0), LENGTH("location=") + 1)
+        GLOB ("*" || $pkg_name || "*odex")
+      AND (STR_SPLIT(slice_name, " reason=", 1) = "install"
+        OR STR_SPLIT(slice_name, " reason=", 1) = "install-dm")
+      AND STR_SPLIT(STR_SPLIT(slice_name, " filter=", 1), " reason=", 0) != "speed-profile"
+  '
+);
+
+SELECT CREATE_FUNCTION(
+  'RUN_FROM_APK_FOR_LAUNCH(launch_id LONG)',
+  'BOOL',
+  '
+    SELECT EXISTS(
+      SELECT slice_name
+      FROM (
+        SELECT *
+        FROM ANDROID_SLICES_FOR_STARTUP_AND_SLICE_NAME(
+          $launch_id,
+          "location=* status=* filter=* reason=*"
+        )
+      )
+      WHERE
+        STR_SPLIT(STR_SPLIT(slice_name, " filter=", 1), " reason=", 0)
+          GLOB ("*" || "run-from-apk" || "*")
+    )
+  '
+);
+
+SELECT CREATE_VIEW_FUNCTION(
+  'BINDER_TRANSACTION_REPLY_SLICES_FOR_LAUNCH(startup_id INT, threshold DOUBLE)',
+  'name STRING',
+  '
+    SELECT reply.name AS name
+    FROM ANDROID_BINDER_TRANSACTION_SLICES_FOR_STARTUP($startup_id, $threshold) request
+    JOIN following_flow(request.id) arrow
+    JOIN slice reply ON reply.id = arrow.slice_in
+    WHERE reply.dur > $threshold AND request.is_main_thread
+  '
+);
+
+-- Given a launch id, return if unlock is running by systemui during the launch.
+SELECT CREATE_FUNCTION(
+  'IS_UNLOCK_RUNNING_DURING_LAUNCH(startup_id LONG)',
+  'BOOL',
+  '
+    SELECT EXISTS(
+      SELECT slice.name
+      FROM slice, android_startups launches
+      JOIN thread_track ON slice.track_id = thread_track.id
+      JOIN thread USING(utid)
+      JOIN process USING(upid)
+      WHERE launches.startup_id = $startup_id
+      AND slice.name = "KeyguardUpdateMonitor#onAuthenticationSucceeded"
+      AND process.name = "com.android.systemui"
+      AND slice.ts >= launches.ts
+      AND (slice.ts + slice.dur) <= launches.ts_end
+    )
   '
 );
diff --git a/src/trace_processor/metrics/sql/android/startup/system_state.sql b/src/trace_processor/metrics/sql/android/startup/system_state.sql
index 9ed2c9c..5b49ace 100644
--- a/src/trace_processor/metrics/sql/android/startup/system_state.sql
+++ b/src/trace_processor/metrics/sql/android/startup/system_state.sql
@@ -17,45 +17,44 @@
 -- Functions useful for filling the SystemState proto which gives
 -- context to what was happening on the system during a startup.
 
--- Given a launch id and process name glob, returns whether a process with
+SELECT IMPORT('android.startup.startups');
+
+-- Given a launch id and process name glob, returns the sched.dur if a process with
 -- that name was running on a CPU concurrent to that launch.
 SELECT CREATE_FUNCTION(
-  'IS_PROCESS_RUNNING_CONCURRENT_TO_LAUNCH(launch_id INT, process_glob STRING)',
-  'BOOL',
+  'DUR_OF_PROCESS_RUNNING_CONCURRENT_TO_LAUNCH(startup_id INT, process_glob STRING)',
+  'INT',
   '
-    SELECT EXISTS(
-      SELECT sched.dur
+      SELECT IFNULL(SUM(sched.dur), 0)
       FROM sched
       JOIN thread USING (utid)
       JOIN process USING (upid)
       JOIN (
         SELECT ts, ts_end
-        FROM launches
-        WHERE id = $launch_id
+        FROM android_startups
+        WHERE startup_id = $startup_id
       ) launch
       WHERE
         process.name GLOB $process_glob AND
         sched.ts BETWEEN launch.ts AND launch.ts_end
-      LIMIT 1
-    )
   '
 );
 
 -- Given a launch id and slice name glob, returns the number of slices with that
 -- name which start concurrent to that launch.
 SELECT CREATE_FUNCTION(
-  'COUNT_SLICES_CONCURRENT_TO_LAUNCH(launch_id INT, slice_glob STRING)',
+  'COUNT_SLICES_CONCURRENT_TO_LAUNCH(startup_id INT, slice_glob STRING)',
   'INT',
   '
     SELECT COUNT(1)
     FROM slice
     JOIN (
       SELECT ts, ts_end
-      FROM launches
-      WHERE id = $launch_id
+      FROM android_startups
+      WHERE startup_id = $startup_id
     ) launch
     WHERE
       slice.name GLOB $slice_glob AND
       slice.ts BETWEEN launch.ts AND launch.ts_end
   '
-);
\ No newline at end of file
+);
diff --git a/src/trace_processor/metrics/sql/android/startup/thread_state_breakdown.sql b/src/trace_processor/metrics/sql/android/startup/thread_state_breakdown.sql
index e735aa1..b85d40b 100644
--- a/src/trace_processor/metrics/sql/android/startup/thread_state_breakdown.sql
+++ b/src/trace_processor/metrics/sql/android/startup/thread_state_breakdown.sql
@@ -14,59 +14,82 @@
 -- limitations under the License.
 --
 
+SELECT IMPORT('android.startup.startups');
+
 DROP VIEW IF EXISTS thread_state_extended;
 CREATE VIEW thread_state_extended AS
 SELECT
   ts,
   IIF(dur = -1, (SELECT end_ts FROM trace_bounds), dur) AS dur,
   utid,
-  state
+  state,
+  io_wait
 FROM thread_state;
 
 DROP TABLE IF EXISTS launch_threads_by_thread_state;
 CREATE VIRTUAL TABLE launch_threads_by_thread_state
 USING SPAN_JOIN(
-  launch_threads PARTITIONED utid,
+  android_startup_threads PARTITIONED utid,
   thread_state_extended PARTITIONED utid
 );
 
 -- Materialized to avoid repeatedly span joining per each thread state.
-DROP TABLE IF EXISTS launch_thread_state_dur_sum;
-CREATE TABLE launch_thread_state_dur_sum AS
-SELECT launch_id, state, is_main_thread, thread_name, SUM(dur) AS dur
+DROP TABLE IF EXISTS launch_thread_state_io_wait_dur_sum;
+CREATE TABLE launch_thread_state_io_wait_dur_sum AS
+SELECT startup_id, state, is_main_thread, thread_name, io_wait, SUM(dur) AS dur
 FROM launch_threads_by_thread_state l
 WHERE
-  is_main_thread OR
+  is_main_thread
   -- Allowlist specific threads which need this. Do not add to this list
   -- without careful consideration as every thread added here can cause
   -- memory usage to balloon.
-  thread_name IN (
+  OR thread_name IN (
     'Jit thread pool'
   )
+GROUP BY 1, 2, 3, 4, 5;
+
+DROP VIEW IF EXISTS launch_thread_state_dur_sum;
+CREATE VIEW launch_thread_state_dur_sum AS
+SELECT startup_id, state, is_main_thread, thread_name, SUM(dur) AS dur
+FROM launch_thread_state_io_wait_dur_sum
 GROUP BY 1, 2, 3, 4;
 
 -- Given a launch id and thread state value, returns the aggregate sum
 -- of time spent in that state by the main thread of the process being started up.
 SELECT CREATE_FUNCTION(
-  'MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(launch_id INT, state STRING)',
+  'MAIN_THREAD_TIME_FOR_LAUNCH_AND_STATE(startup_id INT, state STRING)',
   'INT',
   '
     SELECT SUM(dur)
     FROM launch_thread_state_dur_sum l
-    WHERE l.launch_id = $launch_id AND state GLOB $state AND is_main_thread;
+    WHERE l.startup_id = $startup_id AND state GLOB $state AND is_main_thread;
   '
 );
 
+-- Given a launch id, thread state  and io_wait value, returns the aggregate sum
+-- of time spent in that state by the main thread of the process being started up.
+SELECT CREATE_FUNCTION(
+  'MAIN_THREAD_TIME_FOR_LAUNCH_STATE_AND_IO_WAIT(startup_id INT, state STRING, io_wait BOOL)',
+  'INT',
+  '
+    SELECT SUM(dur)
+    FROM launch_thread_state_io_wait_dur_sum l
+    WHERE l.startup_id = $startup_id AND state GLOB $state
+      AND is_main_thread AND l.io_wait = $io_wait;
+  '
+);
+
+
 -- Given a launch id, thread state value and name of a thread, returns the aggregate sum
 -- of time spent in that state by that thread. Note: only threads of the processes
 -- being started are considered by this function - if a thread from a different name
 -- happens to match the name passed, it will *not* be included.
 SELECT CREATE_FUNCTION(
-  'THREAD_TIME_FOR_LAUNCH_STATE_AND_THREAD(launch_id INT, state STRING, thread_name STRING)',
+  'THREAD_TIME_FOR_LAUNCH_STATE_AND_THREAD(startup_id INT, state STRING, thread_name STRING)',
   'INT',
   '
     SELECT SUM(dur)
     FROM launch_thread_state_dur_sum l
-    WHERE l.launch_id = $launch_id AND state GLOB $state AND thread_name = $thread_name;
+    WHERE l.startup_id = $startup_id AND state GLOB $state AND thread_name = $thread_name;
   '
 );
diff --git a/src/trace_processor/metrics/sql/android/unsymbolized_frames.sql b/src/trace_processor/metrics/sql/android/unsymbolized_frames.sql
index 9a0ecd6..0915608 100644
--- a/src/trace_processor/metrics/sql/android/unsymbolized_frames.sql
+++ b/src/trace_processor/metrics/sql/android/unsymbolized_frames.sql
@@ -28,30 +28,30 @@
 -- Note that in SQL SUBSTR() indexes are 1-based, not 0 based.
 DROP VIEW IF EXISTS mangled_stack_profile_mapping;
 CREATE VIEW mangled_stack_profile_mapping AS
-  SELECT
-    id,
-    name,
-    build_id,
-    CASE ((name GLOB '*libmonochrome_64.so'
-         OR name GLOB '*libchrome.so'
-         OR name GLOB '*libmonochrome.so'
-         OR name GLOB '*libwebviewchromium.so'
-         OR name GLOB '*libchromium_android_linker.so'
-        ) AND length(build_id) >= 40)
-      WHEN 0 THEN build_id
-      ELSE (
-        SUBSTR(build_id, 7, 2) ||
-        SUBSTR(build_id, 5, 2) ||
-        SUBSTR(build_id, 3, 2) ||
-        SUBSTR(build_id, 1, 2) ||
-        SUBSTR(build_id, 11, 2) ||
-        SUBSTR(build_id, 9, 2) ||
-        SUBSTR(build_id, 15, 2) ||
-        SUBSTR(build_id, 13, 2) ||
-        SUBSTR(build_id, 17, 16) ||
-      '0')
-    END as google_lookup_id
-  FROM stack_profile_mapping;
+SELECT
+  id,
+  name,
+  build_id,
+  CASE ((name GLOB '*libmonochrome_64.so'
+    OR name GLOB '*libchrome.so'
+    OR name GLOB '*libmonochrome.so'
+    OR name GLOB '*libwebviewchromium.so'
+    OR name GLOB '*libchromium_android_linker.so'
+  ) AND length(build_id) >= 40)
+  WHEN 0 THEN build_id
+  ELSE (
+    SUBSTR(build_id, 7, 2)
+    || SUBSTR(build_id, 5, 2)
+    || SUBSTR(build_id, 3, 2)
+    || SUBSTR(build_id, 1, 2)
+    || SUBSTR(build_id, 11, 2)
+    || SUBSTR(build_id, 9, 2)
+    || SUBSTR(build_id, 15, 2)
+    || SUBSTR(build_id, 13, 2)
+    || SUBSTR(build_id, 17, 16)
+    || '0')
+  END AS google_lookup_id
+FROM stack_profile_mapping;
 
 DROP VIEW IF EXISTS unsymbolized_frames_view;
 CREATE VIEW unsymbolized_frames_view AS
@@ -63,9 +63,9 @@
 ) AS frame_proto
 FROM stack_profile_frame spf
 JOIN mangled_stack_profile_mapping spm
-ON spf.mapping = spm.id
+  ON spf.mapping = spm.id
 WHERE spm.build_id != ''
-AND (spf.symbol_set_id == 0 OR spf.symbol_set_id IS NULL);
+  AND spf.symbol_set_id IS NULL;
 
 DROP VIEW IF EXISTS unsymbolized_frames_output;
 CREATE VIEW unsymbolized_frames_output AS
diff --git a/src/trace_processor/metrics/sql/chrome/BUILD.gn b/src/trace_processor/metrics/sql/chrome/BUILD.gn
new file mode 100644
index 0000000..0f74594
--- /dev/null
+++ b/src/trace_processor/metrics/sql/chrome/BUILD.gn
@@ -0,0 +1,77 @@
+# Copyright (C) 2022 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/perfetto_sql.gni")
+
+assert(enable_perfetto_trace_processor_sqlite)
+
+# This target cannot be named "chrome" because it breaks Chrome on iOS from
+# being built.
+perfetto_sql_source_set("chrome_sql") {
+  sources = [
+    "actual_power_by_category.sql",
+    "actual_power_by_rail_mode.sql",
+    "chrome_args_class_names.sql",
+    "chrome_event_metadata.sql",
+    "chrome_histogram_hashes.sql",
+    "chrome_input_to_browser_intervals.sql",
+    "chrome_input_to_browser_intervals_base.sql",
+    "chrome_input_to_browser_intervals_template.sql",
+    "chrome_long_tasks.sql",
+    "chrome_long_tasks_delaying_input_processing.sql",
+    "chrome_performance_mark_hashes.sql",
+    "chrome_processes.sql",
+    "chrome_reliable_range.sql",
+    "chrome_scroll_inputs_per_frame.sql",
+    "chrome_scroll_jank_caused_by_scheduling.sql",
+    "chrome_slice_names.sql",
+    "chrome_stack_samples_for_task.sql",
+    "chrome_tasks.sql",
+    "chrome_tasks_delaying_input_processing.sql",
+    "chrome_tasks_delaying_input_processing_base.sql",
+    "chrome_tasks_delaying_input_processing_template.sql",
+    "chrome_tasks_template.sql",
+    "chrome_thread_slice.sql",
+    "chrome_unsymbolized_args.sql",
+    "chrome_user_event_hashes.sql",
+    "cpu_time_by_category.sql",
+    "cpu_time_by_rail_mode.sql",
+    "estimated_power_by_category.sql",
+    "estimated_power_by_rail_mode.sql",
+    "event_latency_scroll_jank.sql",
+    "event_latency_scroll_jank_cause.sql",
+    "event_latency_to_breakdowns.sql",
+    "experimental_reliable_chrome_tasks_delaying_input_processing.sql",
+    "gesture_flow_event.sql",
+    "gesture_flow_event_queuing_delay.sql",
+    "gesture_jank.sql",
+    "jank_utilities.sql",
+    "rail_modes.sql",
+    "scroll_flow_event.sql",
+    "scroll_flow_event_queuing_delay.sql",
+    "scroll_jank.sql",
+    "scroll_jank_cause.sql",
+    "scroll_jank_cause_blocking_task.sql",
+    "scroll_jank_cause_blocking_touch_move.sql",
+    "scroll_jank_cause_get_bitmap.sql",
+    "scroll_jank_cause_queuing_delay.sql",
+    "sufficient_chrome_processes.sql",
+    "test_chrome_metric.sql",
+    "touch_flow_event.sql",
+    "touch_flow_event_queuing_delay.sql",
+    "touch_jank.sql",
+    "vsync_intervals.sql",
+  ]
+}
diff --git a/src/trace_processor/metrics/sql/chrome/actual_power_by_category.sql b/src/trace_processor/metrics/sql/chrome/actual_power_by_category.sql
index 27b632e..d5d0f4b 100644
--- a/src/trace_processor/metrics/sql/chrome/actual_power_by_category.sql
+++ b/src/trace_processor/metrics/sql/chrome/actual_power_by_category.sql
@@ -36,12 +36,12 @@
 CREATE VIEW mapped_drain_in_watts AS
 SELECT d.name, ts, dur, drain_w, idx
 FROM drain_in_watts d
-  JOIN power_rail_name_mapping p ON d.name = p.name;
+JOIN power_rail_name_mapping p ON d.name = p.name;
 
 DROP TABLE IF EXISTS real_{{input}}_power;
 CREATE VIRTUAL TABLE real_{{input}}_power USING SPAN_JOIN(
-    {{input}},
-    mapped_drain_in_watts PARTITIONED idx
+  {{input}},
+  mapped_drain_in_watts PARTITIONED idx
 );
 
 -- Actual power usage for chrome across the categorised slices contained in the
@@ -60,20 +60,20 @@
       subsystem,
       SUM(drain_w * dur / 1e9) AS joules
     FROM real_{{input}}_power
-      JOIN power_counters
+    JOIN power_counters
     WHERE real_{{input}}_power.name = power_counters.name
     GROUP BY id,
       subsystem
   ) p
-  JOIN {{input}} s
+JOIN {{input}} s
 WHERE s.id = p.id
 ORDER BY s.id;
 
 SELECT id,
-      subsystem,
-      SUM(drain_w * dur / 1e9) AS joules
-    FROM real_{{input}}_power
-      JOIN power_counters
-    WHERE real_{{input}}_power.name = power_counters.name
-    GROUP BY id,
-      subsystem
+  subsystem,
+  SUM(drain_w * dur / 1e9) AS joules
+FROM real_{{input}}_power
+JOIN power_counters
+WHERE real_{{input}}_power.name = power_counters.name
+GROUP BY id,
+  subsystem;
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_args_class_names.sql b/src/trace_processor/metrics/sql/chrome/chrome_args_class_names.sql
new file mode 100644
index 0000000..eedf812
--- /dev/null
+++ b/src/trace_processor/metrics/sql/chrome/chrome_args_class_names.sql
@@ -0,0 +1,56 @@
+--
+-- Copyright 2022 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.
+
+-- Creates a table view with a list of class names with a specific key
+-- from the "args" table per |package_name| & |version_code|.
+-- |package_name| and |version_code| can be NULL.
+
+DROP VIEW IF EXISTS chrome_args_class_names_per_version;
+CREATE VIEW chrome_args_class_names_per_version AS
+WITH class_info AS (
+  SELECT
+    package_list.package_name AS package_name,
+    package_list.version_code AS version_code,
+    RepeatedField(args.string_value) AS class_names
+  FROM args
+  JOIN slice
+    ON args.arg_set_id = slice.arg_set_id
+      AND args.flat_key = 'android_view_dump.activity.view.class_name'
+  JOIN thread_track
+    ON slice.track_id = thread_track.id
+  JOIN thread
+    ON thread_track.utid = thread.utid
+  JOIN process
+    ON thread.upid = process.upid
+  LEFT JOIN package_list
+    ON process.uid = package_list.uid
+  GROUP BY package_name, version_code
+)
+SELECT
+  ChromeArgsClassNames_ChromeArgsClassNamesPerVersion(
+    'package_name', package_name,
+    'version_code', version_code,
+    'class_name', class_names
+  ) AS class_names_per_version
+FROM class_info;
+
+DROP VIEW IF EXISTS chrome_args_class_names_output;
+CREATE VIEW chrome_args_class_names_output
+AS
+SELECT
+  ChromeArgsClassNames(
+    'class_names_per_version', RepeatedField(class_names_per_version)
+  )
+FROM chrome_args_class_names_per_version;
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals.sql b/src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals.sql
index 9f64718..facb845 100644
--- a/src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals.sql
+++ b/src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals.sql
@@ -20,235 +20,8 @@
 -- The final table includes the time between the arrival of gesture update
 -- input timestamp, and the time it started being processed by CrBrowserMain.
 
-SELECT RUN_METRIC('chrome/chrome_tasks.sql');
-
-SELECT CREATE_FUNCTION(
-  -- Given a slice id of an event, get timestamp of the most recent flow
-  -- event on the Chrome IO thread that preceded this slice.
-  -- This helps us identify the last flow event on the IO thread before
-  -- letting the browser main thread be aware of input.
-  -- We need this for flings (generated by the GPU process) and blocked
-  -- touch moves that are forwarded from the renderer.
-  'PRECEDING_IO_THREAD_EVENT_FLOW_ID(id LONG)',
-  -- Returning the slice id for the flow_out on the chrome IO thread.
-  'LONG',
-  'SELECT MAX(slice.id) AS id
-  FROM
-    (SELECT * FROM PRECEDING_FLOW(($id))) flow
-    JOIN slice
-      ON flow.slice_out = slice.id
-    JOIN thread_track
-      ON thread_track.id = slice.track_id
-    JOIN thread
-      ON thread.utid = thread_track.utid
-      AND thread.name = "Chrome_IOThread"
-      AND slice.name = "SequenceManager PostTask"');
-
-SELECT CREATE_FUNCTION(
-  'GET_MOJO_PARENT_INTERFACE_TAG(id LONG)',
-  'STRING',
-  'SELECT
-    interface_name
-    FROM ancestor_slice(($id)) ancestors
-    JOIN chrome_mojo_slices_internal USING(id)'
+SELECT RUN_METRIC(
+  'chrome/chrome_input_to_browser_intervals_template.sql',
+  'slice_table_name', 'slice',
+  'function_prefix', ''
 );
-
-SELECT CREATE_FUNCTION(
-  'GET_SCROLL_TYPE(blocked_gesture BOOL, mojo_interface_tag STRING)',
-  'STRING',
-  'SELECT
-    CASE WHEN ($blocked_gesture)
-    THEN (SELECT
-            CASE WHEN ($mojo_interface_tag) = "viz.mojom.BeginFrameObserver"
-            THEN "fling"
-            WHEN ($mojo_interface_tag) = "blink.mojom.WidgetInputHandler"
-            THEN "blocking_touch_move"
-            ELSE "unkown" END)
-    ELSE "regular" END AS delay_type');
-
--- Get all InputLatency::GestureScrollUpdate events, to use their
--- flows later on to decide how much time we waited from queueing the event
--- until we started processing it.
-DROP VIEW IF EXISTS chrome_valid_gesture_updates;
-CREATE VIEW chrome_valid_gesture_updates
-AS
-SELECT
-  name,
-  EXTRACT_ARG(
-    arg_set_id, 'chrome_latency_info.trace_id') AS trace_id,
-  ts,
-  id,
-  dur
-FROM
-  slice
-WHERE
-  name = 'InputLatency::GestureScrollUpdate'
-  AND EXTRACT_ARG(
-    arg_set_id, "chrome_latency_info.is_coalesced")
-    = 0
-ORDER BY trace_id;
-
--- Get all chrome_latency_info_for_gesture_slices where trace_ids are not -1,
--- as those are faulty, then join with the GestureScrollUpdate table to get
--- only slices associated with update events.
-DROP VIEW IF EXISTS chrome_flow_slices_for_gestures;
-CREATE VIEW chrome_flow_slices_for_gestures
-AS
-SELECT
-  slice.ts,
-  EXTRACT_ARG(
-    slice.arg_set_id, 'chrome_latency_info.trace_id') AS trace_id,
-  slice.arg_set_id,
-  slice.id,
-  slice.track_id,
-  slice.dur
-FROM
-  slice
-JOIN chrome_valid_gesture_updates
-  ON
-    chrome_valid_gesture_updates.trace_id = EXTRACT_ARG(
-    slice.arg_set_id, 'chrome_latency_info.trace_id')
-    AND slice.ts >= chrome_valid_gesture_updates.ts
-    AND slice.ts + slice.dur <=
-      chrome_valid_gesture_updates.ts + chrome_valid_gesture_updates.dur
-WHERE
-  slice.name = 'LatencyInfo.Flow'
-  AND trace_id != -1;
-
--- Tie chrome_latency_info_for_gesture_slices slices to processes to avoid
--- calculating intervals per process as multiple chrome instances can be up
--- on system traces.
-DROP VIEW IF EXISTS chrome_flow_slices_for_gestures_tied_process;
-CREATE VIEW chrome_flow_slices_for_gestures_tied_process
-AS
-SELECT
-  ts,
-  trace_id,
-  arg_set_id,
-  chrome_flow_slices_for_gestures.id,
-  dur,
-  thread.upid
-FROM
-  chrome_flow_slices_for_gestures
-JOIN thread_track ON chrome_flow_slices_for_gestures.track_id = thread_track.id
-JOIN thread ON thread_track.utid = thread.utid
-AND is_main_thread;
-
--- Index all flows per trace_id, to get the first flow event per input
--- GestureScrollUpdate, this will later be used to calculate the time
--- from receiving input to the first flow event appearing.
-DROP TABLE IF EXISTS chrome_indexed_flow_per_gesture;
-CREATE TABLE chrome_indexed_flow_per_gesture
-AS
-SELECT
-  ts,
-  trace_id,
-  arg_set_id,
-  id,
-  ROW_NUMBER()
-    OVER (
-      PARTITION BY trace_id, upid
-      ORDER BY
-        ts ASC
-    ) AS flow_order,
-    upid,
-    dur
-FROM
-  chrome_flow_slices_for_gestures_tied_process;
-
--- TODO(b/235067134) all the previous views including this one are
--- reimplementations of gesture_jank.sql with less restrictions, let's
--- merge both of them into one script or make this script a base for the
--- other.
--- Get the first flow event per gesture.
-DROP VIEW IF EXISTS chrome_first_flow_per_gesture;
-CREATE VIEW chrome_first_flow_per_gesture
-AS
-SELECT
-  *
-FROM
-  chrome_indexed_flow_per_gesture
-WHERE
-  flow_order = 1;
-
-
-
--- The decision for processing on the browser main thread for a frame can be
--- instant, or delayed by the renderer in cases where the renderer needs to
--- decide whether the touch move is an ScrollUpdate or not, and in other cases
--- for flings, the scroll itself will be generated by the viz compositor thread
--- on each vsync interval.
-DROP VIEW IF EXISTS chrome_categorized_first_flow_events;
-CREATE VIEW chrome_categorized_first_flow_events
-AS
-SELECT
-  *,
-  CASE
-    WHEN
-      (
-        SELECT
-          COUNT()
-        FROM
-          ancestor_slice(chrome_first_flow_per_gesture.id) ancestor_slices
-          JOIN chrome_tasks ON ancestor_slices.id=chrome_tasks.id
-        WHERE
-          -- sendTouchEvent means the event wasn't delayed by the renderer
-          -- and is not a fling generated by the viz compositor thread(GPU process).
-          ancestor_slices.name = "sendTouchEvent"
-      )
-      = 1
-      THEN FALSE
-    ELSE TRUE
-    END AS blocked_gesture
-FROM
-  chrome_first_flow_per_gesture;
-
--- For cases where it's not blocked, get the timestamp of input as the
--- beginning of time we theoretically could have started processing
--- the input event, which is the timestamp of the GestureScrollUpdate event
--- otherwise fall back to the top level slice to check the timestamp
--- of it's calling flow
-DROP VIEW IF EXISTS chrome_input_to_browser_interval_slice_ids;
-CREATE VIEW chrome_input_to_browser_interval_slice_ids
-AS
-SELECT
-  chrome_categorized_first_flow_events.id AS window_end_id,
-  chrome_categorized_first_flow_events.ts AS window_end_ts,
-  -- If blocked, get the flow_out slice's timestamp as our beginning
-  -- given that the flow_in is coming to our toplevel parent task
-  CASE
-    WHEN blocked_gesture
-      THEN
-        PRECEDING_IO_THREAD_EVENT_FLOW_ID(chrome_categorized_first_flow_events.id)
-    ELSE
-    -- TODO(b/236590359): is selecting here better or join and ordering by?
-    -- Let's benchmark and modify accordingly.
-      (
-        SELECT
-          chrome_gestures.id
-        FROM
-          chrome_valid_gesture_updates chrome_gestures
-        WHERE
-          chrome_gestures.trace_id = chrome_categorized_first_flow_events.trace_id
-          AND chrome_gestures.ts <= chrome_categorized_first_flow_events.ts
-          AND chrome_gestures.ts + chrome_gestures.dur >= chrome_categorized_first_flow_events.ts +
-            chrome_categorized_first_flow_events.dur
-      )
-    END AS window_start_id,
-  blocked_gesture,
-  upid
-FROM
-  chrome_categorized_first_flow_events;
-
-DROP TABLE IF EXISTS chrome_input_to_browser_intervals;
-CREATE TABLE chrome_input_to_browser_intervals
-AS
-SELECT
-  (SELECT ts FROM slice WHERE id = window_start_id) AS window_start_ts,
-  window_start_id,
-  window_end_ts,
-  window_end_id,
-  blocked_gesture,
-  upid,
-  GET_SCROLL_TYPE(blocked_gesture, GET_MOJO_PARENT_INTERFACE_TAG(window_end_id)) AS scroll_type
-FROM chrome_input_to_browser_interval_slice_ids;
\ No newline at end of file
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals_base.sql b/src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals_base.sql
new file mode 100644
index 0000000..8d5d8a5
--- /dev/null
+++ b/src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals_base.sql
@@ -0,0 +1,231 @@
+--
+-- Copyright 2022 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.
+--
+-- WARNING: This metric should not be used as a source of truth. It is under
+--          active development and the values & meaning might change without
+--          notice.
+
+SELECT CREATE_FUNCTION(
+  -- Given a slice id of an event, get timestamp of the most recent flow
+  -- event on the Chrome IO thread that preceded this slice.
+  -- This helps us identify the last flow event on the IO thread before
+  -- letting the browser main thread be aware of input.
+  -- We need this for flings (generated by the GPU process) and blocked
+  -- touch moves that are forwarded from the renderer.
+  '{{function_prefix}}PRECEDING_IO_THREAD_EVENT_FLOW_ID(id LONG)',
+  -- Returning the slice id for the flow_out on the chrome IO thread.
+  'LONG',
+  'SELECT MAX(flow.slice_out) AS id
+  FROM
+    PRECEDING_FLOW(($id)) flow');
+
+SELECT CREATE_FUNCTION(
+  '{{function_prefix}}GET_MOJO_PARENT_INTERFACE_TAG(id LONG)',
+  'STRING',
+  'SELECT
+    interface_name
+    FROM
+    (SELECT MAX(mojo.id),
+    interface_name
+      FROM ((SELECT id FROM ancestor_slice(($id))
+             UNION
+             SELECT slice.id as id FROM PRECEDING_FLOW(($id)) flow
+        	 JOIN slice ON flow.slice_out = slice.id) candidates
+    JOIN chrome_mojo_slices_tbl mojo
+    ON mojo.id = candidates.id
+      OR (SELECT COUNT() FROM ancestor_slice(candidates.id) parents
+      WHERE parents.id = mojo.id) > 0))'
+);
+
+SELECT CREATE_FUNCTION(
+  '{{function_prefix}}GET_SCROLL_TYPE(blocked_gesture BOOL, mojo_interface_tag STRING)',
+  'STRING',
+  'SELECT
+    CASE WHEN ($blocked_gesture)
+    THEN (SELECT
+            CASE WHEN ($mojo_interface_tag) = "viz.mojom.BeginFrameObserver"
+            THEN "fling"
+            WHEN ($mojo_interface_tag) = "blink.mojom.WidgetInputHandler"
+            THEN "blocking_touch_move"
+            ELSE "unknown" END)
+    ELSE "regular" END AS delay_type');
+
+-- Get all InputLatency::GestureScrollUpdate events, to use their
+-- flows later on to decide how much time we waited from queueing the event
+-- until we started processing it.
+DROP VIEW IF EXISTS chrome_valid_gesture_updates;
+CREATE VIEW chrome_valid_gesture_updates
+AS
+SELECT
+  name,
+  EXTRACT_ARG(
+    arg_set_id, 'chrome_latency_info.trace_id') AS trace_id,
+  ts,
+  id,
+  dur
+FROM
+  {{slice_table_name}}
+WHERE
+  name = 'InputLatency::GestureScrollUpdate'
+  AND EXTRACT_ARG(
+    arg_set_id, "chrome_latency_info.is_coalesced")
+  = 0
+ORDER BY trace_id;
+
+-- Get all chrome_latency_info_for_gesture_slices where trace_ids are not -1,
+-- as those are faulty, then join with the GestureScrollUpdate table to get
+-- only slices associated with update events.
+DROP VIEW IF EXISTS chrome_flow_slices_for_gestures;
+CREATE VIEW chrome_flow_slices_for_gestures
+AS
+SELECT
+  s.ts,
+  EXTRACT_ARG(
+    s.arg_set_id, 'chrome_latency_info.trace_id') AS trace_id,
+  s.arg_set_id,
+  s.id,
+  s.track_id,
+  s.dur
+FROM
+  {{slice_table_name}} s
+JOIN chrome_valid_gesture_updates
+  ON
+    chrome_valid_gesture_updates.trace_id = EXTRACT_ARG(
+      s.arg_set_id, 'chrome_latency_info.trace_id')
+    AND s.ts >= chrome_valid_gesture_updates.ts
+    AND s.ts + s.dur
+    <= chrome_valid_gesture_updates.ts + chrome_valid_gesture_updates.dur
+WHERE
+  s.name = 'LatencyInfo.Flow'
+  AND trace_id != -1;
+
+-- Tie chrome_latency_info_for_gesture_slices slices to processes to avoid
+-- calculating intervals per process as multiple chrome instances can be up
+-- on system traces.
+DROP VIEW IF EXISTS chrome_flow_slices_for_gestures_tied_process;
+CREATE VIEW chrome_flow_slices_for_gestures_tied_process
+AS
+SELECT
+  ts,
+  trace_id,
+  arg_set_id,
+  chrome_flow_slices_for_gestures.id,
+  dur,
+  thread.upid
+FROM
+  chrome_flow_slices_for_gestures
+JOIN thread_track ON chrome_flow_slices_for_gestures.track_id = thread_track.id
+JOIN thread ON thread_track.utid = thread.utid
+  AND is_main_thread;
+
+-- Index all flows per trace_id, to get the first flow event per input
+-- GestureScrollUpdate, this will later be used to calculate the time
+-- from receiving input to the first flow event appearing.
+DROP TABLE IF EXISTS chrome_indexed_flow_per_gesture;
+CREATE TABLE chrome_indexed_flow_per_gesture
+AS
+SELECT
+  ts,
+  trace_id,
+  arg_set_id,
+  id,
+  ROW_NUMBER()
+  OVER (
+    PARTITION BY trace_id, upid
+    ORDER BY
+      ts ASC
+  ) AS flow_order,
+  upid,
+  dur
+FROM
+  chrome_flow_slices_for_gestures_tied_process;
+
+-- TODO(b/235067134) all the previous views including this one are
+-- reimplementations of gesture_jank.sql with less restrictions, let's
+-- merge both of them into one script or make this script a base for the
+-- other.
+-- Get the first flow event per gesture.
+DROP VIEW IF EXISTS chrome_first_flow_per_gesture;
+CREATE VIEW chrome_first_flow_per_gesture
+AS
+SELECT
+  *
+FROM
+  chrome_indexed_flow_per_gesture
+WHERE
+  flow_order = 1;
+
+
+
+-- The decision for processing on the browser main thread for a frame can be
+-- instant, or delayed by the renderer in cases where the renderer needs to
+-- decide whether the touch move is an ScrollUpdate or not, and in other cases
+-- for flings, the scroll itself will be generated by the viz compositor thread
+-- on each vsync interval.
+DROP VIEW IF EXISTS chrome_categorized_first_flow_events;
+CREATE VIEW chrome_categorized_first_flow_events
+AS
+SELECT
+  *,
+  NOT COALESCE((
+    SELECT
+      COUNT()
+    FROM
+      ancestor_slice(chrome_first_flow_per_gesture.id) ancestor_slices
+    WHERE
+      -- sendTouchEvent means the event wasn't delayed by the renderer
+      -- and is not a fling generated by the viz compositor thread(GPU process).
+      ancestor_slices.name = "sendTouchEvent"
+  )
+  = 1, FALSE) AS blocked_gesture
+FROM
+  chrome_first_flow_per_gesture;
+
+-- For cases where it's not blocked, get the timestamp of input as the
+-- beginning of time we theoretically could have started processing
+-- the input event, which is the timestamp of the GestureScrollUpdate event
+-- otherwise fall back to the top level slice to check the timestamp
+-- of it's calling flow
+DROP VIEW IF EXISTS chrome_input_to_browser_interval_slice_ids;
+CREATE VIEW chrome_input_to_browser_interval_slice_ids
+AS
+SELECT
+  chrome_categorized_first_flow_events.id AS window_end_id,
+  chrome_categorized_first_flow_events.ts AS window_end_ts,
+  -- If blocked, get the flow_out slice's timestamp as our beginning
+  -- given that the flow_in is coming to our toplevel parent task
+  CASE
+    WHEN blocked_gesture
+      THEN
+      {{function_prefix}}PRECEDING_IO_THREAD_EVENT_FLOW_ID(chrome_categorized_first_flow_events.id)
+    ELSE
+      -- TODO(b/236590359): is selecting here better or join and ordering by?
+      -- Let's benchmark and modify accordingly.
+      (
+        SELECT
+          chrome_gestures.id
+        FROM
+          chrome_valid_gesture_updates chrome_gestures
+        WHERE
+          chrome_gestures.trace_id = chrome_categorized_first_flow_events.trace_id
+          AND chrome_gestures.ts <= chrome_categorized_first_flow_events.ts
+          AND chrome_gestures.ts + chrome_gestures.dur >= chrome_categorized_first_flow_events.ts
+          + chrome_categorized_first_flow_events.dur
+      )
+  END AS window_start_id,
+  blocked_gesture,
+  upid
+FROM
+  chrome_categorized_first_flow_events;
\ No newline at end of file
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals_template.sql b/src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals_template.sql
new file mode 100644
index 0000000..5fc7f03
--- /dev/null
+++ b/src/trace_processor/metrics/sql/chrome/chrome_input_to_browser_intervals_template.sql
@@ -0,0 +1,46 @@
+--
+-- Copyright 2022 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.
+--
+-- WARNING: This metric should not be used as a source of truth. It is under
+--          active development and the values & meaning might change without
+--          notice.
+
+-- The final table includes the time between the arrival of gesture update
+-- input timestamp, and the time it started being processed by CrBrowserMain.
+
+SELECT RUN_METRIC(
+  'chrome/chrome_tasks_template.sql',
+  'slice_table_name', '{{slice_table_name}}',
+  'function_prefix', '{{function_prefix}}'
+);
+
+SELECT RUN_METRIC(
+  'chrome/chrome_input_to_browser_intervals_base.sql',
+  'slice_table_name', '{{slice_table_name}}',
+  'function_prefix', '{{function_prefix}}'
+);
+
+DROP TABLE IF EXISTS chrome_input_to_browser_intervals;
+CREATE TABLE chrome_input_to_browser_intervals
+AS
+SELECT
+  (SELECT ts FROM {{slice_table_name}} WHERE id = window_start_id) AS window_start_ts,
+  window_start_id,
+  window_end_ts,
+  window_end_id,
+  blocked_gesture,
+  upid,
+  {{function_prefix}}GET_SCROLL_TYPE(blocked_gesture, {{function_prefix}}GET_MOJO_PARENT_INTERFACE_TAG(window_end_id)) AS scroll_type
+FROM chrome_input_to_browser_interval_slice_ids;
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_long_tasks.sql b/src/trace_processor/metrics/sql/chrome/chrome_long_tasks.sql
new file mode 100644
index 0000000..3bd052b
--- /dev/null
+++ b/src/trace_processor/metrics/sql/chrome/chrome_long_tasks.sql
@@ -0,0 +1,164 @@
+--
+-- Copyright 2022 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.
+--
+-- WARNING: This metric should not be used as a source of truth. It is under
+--          active development and the values & meaning might change without
+--          notice.
+
+SELECT RUN_METRIC('chrome/jank_utilities.sql');
+SELECT RUN_METRIC(
+  'chrome/chrome_tasks_template.sql',
+  'slice_table_name', 'slice',
+  'function_prefix', ''
+);
+
+SELECT CREATE_FUNCTION(
+  'IS_LONG_CHOREOGRAPHER_TASK(dur LONG)',
+  'BOOL',
+  'SELECT $dur >= 4 * 1e6'
+);
+
+-- Note that not all slices will be mojo slices; filter on interface_name IS
+-- NOT NULL for mojo slices specifically.
+DROP TABLE IF EXISTS long_tasks_extracted_slices;
+CREATE TABLE long_tasks_extracted_slices AS
+SELECT * FROM SELECT_LONG_TASK_SLICES(/*name*/'LongTaskTracker');
+
+-- Create |long_tasks_internal_tbl| table, which gathers all of the
+-- information needed to produce the full name + metadata required
+-- for LongTaskTracker slices. Unlike toplevel slices, which will
+-- have nested descendants, LongTaskTracker slices will store all of
+-- the relevant information within the single slice.
+DROP TABLE IF EXISTS long_tasks_internal_tbl;
+CREATE TABLE long_tasks_internal_tbl AS
+WITH
+  raw_extracted_values AS (
+    SELECT
+      mojo.id,
+      mojo.interface_name,
+      mojo.ipc_hash,
+      mojo.message_type,
+      EXTRACT_ARG(s.arg_set_id, 'task.posted_from.file_name') AS posted_from_file_name,
+      EXTRACT_ARG(s.arg_set_id, 'task.posted_from.function_name') AS posted_from_function_name
+    FROM long_tasks_extracted_slices mojo
+    JOIN slice s ON mojo.id = s.id
+  )
+SELECT
+  id,
+  CASE
+    WHEN interface_name IS NOT NULL
+      THEN printf('%s %s (hash=%d)', interface_name, message_type, ipc_hash)
+    ELSE
+      FORMAT_SCHEDULER_TASK_NAME(posted_from_file_name || ':' || posted_from_function_name)
+    END AS full_name,
+  interface_name IS NOT NULL AS is_mojo
+FROM raw_extracted_values;
+
+-- Attach java views to its associated LongTaskTracker slice, as they
+-- will be on different tracks. This follows the same logic as creating
+-- chrome_slices_with_java_views_internal, differing only in how a
+-- descendent is calculated.
+DROP VIEW IF EXISTS long_task_slices_with_java_views;
+CREATE VIEW long_task_slices_with_java_views AS
+WITH
+  -- Select UI thread BeginMainFrames frames.
+  root_slices AS (
+    SELECT *
+    FROM SELECT_BEGIN_MAIN_FRAME_JAVA_SLICES('LongTaskTracker')
+    UNION ALL
+    SELECT * FROM chrome_choreographer_tasks WHERE IS_LONG_CHOREOGRAPHER_TASK(dur)
+  ),
+  -- Intermediate step to allow us to sort java view names.
+  root_slice_and_java_view_not_grouped AS (
+    SELECT
+      s1.id, s1.kind, s2.name AS java_view_name
+    FROM root_slices s1
+    JOIN chrome_java_views_internal s2
+      ON (
+        s1.ts < s2.ts AND s1.ts + s1.dur > s2.ts + s2.dur)
+  )
+SELECT
+  s1.id,
+  s1.kind,
+  GROUP_CONCAT(DISTINCT s2.java_view_name) AS java_views
+FROM root_slices s1
+LEFT JOIN root_slice_and_java_view_not_grouped s2
+  USING (id)
+GROUP BY s1.id;
+
+DROP VIEW IF EXISTS chrome_long_tasks_internal;
+CREATE VIEW chrome_long_tasks_internal AS
+WITH -- Generate full names for tasks with java views.
+  java_views_tasks AS (
+    SELECT
+      printf('%s(java_views=%s)', kind, java_views) as full_name,
+      GET_JAVA_VIEWS_TASK_TYPE(kind) AS task_type,
+      id
+    FROM long_task_slices_with_java_views
+    WHERE kind = "SingleThreadProxy::BeginMainFrame"
+  ),
+  scheduler_tasks_with_mojo AS (
+    SELECT
+      full_name,
+      'mojo' as task_type,
+      id
+    FROM long_tasks_internal_tbl
+    WHERE is_mojo
+  ),
+  navigation_tasks AS (
+    SELECT
+      -- NOTE: unless Navigation category is enabled and recorded on the same
+      -- track as the LongTaskTracker slice, frame type will always be unknown.
+      printf('%s (%s)',
+        HUMAN_READABLE_NAVIGATION_TASK_NAME(full_name),
+        IFNULL(EXTRACT_FRAME_TYPE(id), 'unknown frame type')) AS full_name,
+      'navigation_task' AS task_type,
+      id
+    FROM scheduler_tasks_with_mojo
+    WHERE HUMAN_READABLE_NAVIGATION_TASK_NAME(full_name) IS NOT NULL
+  )
+SELECT
+  COALESCE(s4.full_name, s3.full_name, s2.full_name, s1.full_name) AS full_name,
+  COALESCE(s4.task_type, s3.task_type, s2.task_type, 'scheduler') as task_type,
+  s1.id as id
+FROM long_tasks_internal_tbl s1
+LEFT JOIN scheduler_tasks_with_mojo s2 ON s2.id = s1.id
+LEFT JOIN java_views_tasks s3 ON s3.id = s1.id
+LEFT JOIN navigation_tasks s4 ON s4.id = s1.id
+UNION ALL
+-- Choreographer slices won't necessarily be associated with an overlying
+-- LongTaskTracker slice, so join them separately.
+SELECT
+  printf('%s(java_views=%s)', kind, java_views) as full_name,
+  GET_JAVA_VIEWS_TASK_TYPE(kind) AS task_type,
+  id
+FROM long_task_slices_with_java_views
+WHERE kind = "Choreographer";
+
+DROP VIEW IF EXISTS chrome_long_tasks;
+CREATE VIEW chrome_long_tasks AS
+SELECT
+  full_name,
+  task_type,
+  thread.name AS thread_name,
+  thread.utid,
+  process.name AS process_name,
+  thread.upid,
+  ts.*
+FROM chrome_long_tasks_internal cti
+JOIN slice ts USING (id)
+JOIN thread_track tt ON ts.track_id = tt.id
+JOIN thread USING (utid)
+JOIN process USING (upid);
\ No newline at end of file
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_long_tasks_delaying_input_processing.sql b/src/trace_processor/metrics/sql/chrome/chrome_long_tasks_delaying_input_processing.sql
new file mode 100644
index 0000000..5fda93f
--- /dev/null
+++ b/src/trace_processor/metrics/sql/chrome/chrome_long_tasks_delaying_input_processing.sql
@@ -0,0 +1,72 @@
+--
+-- Copyright 2022 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.
+--
+-- WARNING: This metric should not be used as a source of truth. It is under
+--          active development and the values & meaning might change without
+--          notice.
+
+SELECT RUN_METRIC(
+  'chrome/chrome_long_tasks.sql'
+);
+
+-- Input tasks/flows are recorded almost the same way as they are in top-level
+-- slices, however the input task will not necessarily be a parent of the
+-- latencyInfo slice. As such, we can utilize much of the existing input to
+-- browser interval base calculations. Determining whether an input is a fling
+-- or other blocked input will need to be calculated differently, below.
+SELECT RUN_METRIC(
+  'chrome/chrome_input_to_browser_intervals_base.sql',
+  'slice_table_name', 'slice',
+  'function_prefix', ''
+);
+
+-- Needed for calculating chrome input to browser intervals.
+-- Input IPCs are not necessarily always long tasks, hence a new slice name.
+DROP TABLE IF EXISTS chrome_input_to_browser_intervals_long_tasks;
+CREATE TABLE chrome_input_to_browser_intervals_long_tasks
+AS
+SELECT
+  (SELECT ts FROM slice WHERE id = window_start_id) AS window_start_ts,
+  window_start_id,
+  window_end_ts,
+  window_end_id,
+  blocked_gesture,
+  cis.upid,
+  GET_SCROLL_TYPE(blocked_gesture, lts.interface_name) AS scroll_type
+FROM chrome_input_to_browser_interval_slice_ids cis
+LEFT JOIN (
+  SELECT
+    m.interface_name,
+    m.id,
+    upid,
+    s.ts,
+    s.dur
+  FROM
+    SELECT_LONG_TASK_SLICES('InterestingTask_ProcessingTime') m
+    JOIN slice s USING(id)
+    JOIN thread_track tt ON s.track_id = tt.id JOIN thread USING (utid)
+) lts
+ON cis.upid = lts.upid
+  AND (cis.window_end_ts > lts.ts AND cis.window_end_ts <= lts.ts + dur);
+
+-- Calculating chrome tasks delaying input will be the same, just using the
+-- long-task based tables instead of chrome_tasks.
+SELECT RUN_METRIC(
+  'chrome/chrome_tasks_delaying_input_processing_base.sql',
+  'duration_causing_jank_ms', '4',
+  'task_table_name', 'chrome_long_tasks',
+  'input_browser_interval_table_name', 'chrome_input_to_browser_intervals_long_tasks',
+  'function_prefix', ''
+);
\ No newline at end of file
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_performance_mark_hashes.sql b/src/trace_processor/metrics/sql/chrome/chrome_performance_mark_hashes.sql
index b424adb..82f87a6 100644
--- a/src/trace_processor/metrics/sql/chrome/chrome_performance_mark_hashes.sql
+++ b/src/trace_processor/metrics/sql/chrome/chrome_performance_mark_hashes.sql
@@ -30,4 +30,3 @@
     ORDER BY int_value
   )
 );
-
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_processes.sql b/src/trace_processor/metrics/sql/chrome/chrome_processes.sql
index 4903ba5..5c68a47 100644
--- a/src/trace_processor/metrics/sql/chrome/chrome_processes.sql
+++ b/src/trace_processor/metrics/sql/chrome/chrome_processes.sql
@@ -28,15 +28,15 @@
 -- in system traces.
 JOIN
   (SELECT arg_set_id, string_value FROM args WHERE key = 'chrome.process_type')
-    pt
+  pt
   ON process.arg_set_id = pt.arg_set_id;
 
 -- A view of all Chrome threads.
 DROP VIEW IF EXISTS all_chrome_threads;
 CREATE VIEW all_chrome_threads AS
-  SELECT utid, thread.upid, thread.name
-  FROM thread, all_chrome_processes
-  WHERE thread.upid = all_chrome_processes.upid;
+SELECT utid, thread.upid, thread.name
+FROM thread, all_chrome_processes
+WHERE thread.upid = all_chrome_processes.upid;
 
 -- For sandboxed and privileged processes (found in Android system traces), use
 -- the main thread name to type of process.
@@ -47,7 +47,7 @@
 SELECT DISTINCT p.upid,
   SUBSTR(t.name, 3, LENGTH(t.name) - 6) AS sandbox_type
 FROM all_chrome_processes p
-  JOIN all_chrome_threads t ON p.upid = t.upid
+JOIN all_chrome_threads t ON p.upid = t.upid
 WHERE process_type IN ("Sandboxed", "Privileged")
   AND t.name GLOB "Cr*Main";
 
@@ -58,12 +58,12 @@
 SELECT PROCESS.*,
   IIF(sandbox_type IS NULL, process_type, sandbox_type) AS process_type
 FROM PROCESS
-  JOIN (
+JOIN (
     SELECT a.upid,
       sandbox_type,
       process_type
     FROM all_chrome_processes a
-      LEFT JOIN chrome_subprocess_types s ON a.upid = s.upid
+    LEFT JOIN chrome_subprocess_types s ON a.upid = s.upid
   ) c ON PROCESS.upid = c.upid;
 
 -- Contains all the chrome threads from thread with an extra column,
@@ -82,6 +82,6 @@
     SELECT t.utid,
       p.*
     FROM all_chrome_threads t
-      JOIN chrome_process p ON t.upid = p.upid
+    JOIN chrome_process p ON t.upid = p.upid
   ) c
-  JOIN thread ON thread.utid = c.utid;
+JOIN thread ON thread.utid = c.utid;
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_reliable_range.sql b/src/trace_processor/metrics/sql/chrome/chrome_reliable_range.sql
new file mode 100644
index 0000000..20edb1e
--- /dev/null
+++ b/src/trace_processor/metrics/sql/chrome/chrome_reliable_range.sql
@@ -0,0 +1,174 @@
+-- Copyright 2022 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.
+
+-- The "reliable range" is defined as follows:
+-- 1. If a thread_track has a first_packet_on_sequence flag, the thread data is reliable for the
+--    entire duration of the trace.
+-- 2. Otherwise, the thread data is reliable from the first thread event till the end of the trace.
+-- 3. The "reliable range" is an intersection of reliable thread ranges for all threads such that:
+--   a. The number of events on the thread is at or above 25p.
+--   b. The event rate for the thread is at or above 75p.
+-- Note: this metric considers only chrome processes and their threads, i.e. the ones coming
+-- from track_event's.
+SELECT IMPORT('common.metadata');
+
+DROP VIEW IF EXISTS chrome_event_stats_per_thread;
+
+CREATE VIEW chrome_event_stats_per_thread
+AS
+SELECT
+  COUNT(*) AS cnt, CAST(COUNT(*) AS DOUBLE) / (MAX(ts + dur) - MIN(ts)) AS rate, utid
+FROM thread_track
+JOIN slice
+  ON thread_track.id = slice.track_id
+WHERE EXTRACT_ARG(source_arg_set_id, 'source') = 'descriptor'
+GROUP BY utid;
+
+DROP VIEW IF EXISTS chrome_event_cnt_cutoff;
+
+-- Ignore the bottom 25% of threads by event count. 25% is a somewhat arbitrary number. It creates a
+-- cutoff at around 10 events for a typical trace, and threads with fewer events are usually:
+-- 1. Not particularly interesting for the reliable range definition.
+-- 2. Create a lot of noise for other metrics, such as event rate.
+CREATE VIEW chrome_event_cnt_cutoff
+AS
+SELECT cnt
+FROM
+  chrome_event_stats_per_thread
+ORDER BY
+  cnt
+LIMIT
+  1
+  OFFSET(
+    (SELECT COUNT(*) FROM chrome_event_stats_per_thread) / 4);
+
+DROP VIEW IF EXISTS chrome_event_rate_cutoff;
+
+-- Choose the top 25% event rate. 25% is a somewhat arbitrary number. The goal is to strike
+-- balance between not cropping too many events and making sure that the chance of data loss in the
+-- range declared "reliable" is low.
+CREATE VIEW chrome_event_rate_cutoff
+AS
+SELECT rate
+FROM
+  chrome_event_stats_per_thread
+ORDER BY
+  rate
+LIMIT
+  1
+  OFFSET(
+    (SELECT COUNT(*) FROM chrome_event_stats_per_thread) * 3 / 4);
+
+DROP VIEW IF EXISTS chrome_reliable_range_per_thread;
+
+-- This view includes only threads with event count and rate above the cutoff points defined
+-- above.
+-- See b/239830951 for the analysis showing why we don't want to include all threads here
+-- (TL;DR - it makes the "reliable range" too short for a typical trace).
+CREATE VIEW chrome_reliable_range_per_thread
+AS
+SELECT
+  utid,
+  MIN(ts) AS start,
+  MAX(IFNULL(EXTRACT_ARG(source_arg_set_id, 'has_first_packet_on_sequence'), 0))
+  AS has_first_packet_on_sequence
+FROM thread_track
+JOIN slice
+  ON thread_track.id = slice.track_id
+WHERE
+  utid IN (
+    SELECT utid
+    FROM chrome_event_stats_per_thread
+    LEFT JOIN chrome_event_cnt_cutoff
+      ON 1
+    LEFT JOIN chrome_event_rate_cutoff
+      ON 1
+    WHERE
+      chrome_event_stats_per_thread.cnt >= chrome_event_cnt_cutoff.cnt
+      AND chrome_event_stats_per_thread.rate >= chrome_event_rate_cutoff.rate
+  )
+GROUP BY utid;
+
+-- Finds Browser and Renderer processes with a missing main thread. If there
+-- is such a process, the trace definitely has thread data loss, and no part
+-- of the trace is trustworthy/reliable.
+-- As of Jan 2023, all tracing scenarios emit some data from the Browser and
+-- Renderer main thread (assuming that the corresponding process is present).
+DROP VIEW IF EXISTS chrome_processes_with_missing_main;
+
+CREATE VIEW chrome_processes_with_missing_main
+AS
+SELECT
+  upid
+FROM (
+  SELECT upid, utid
+  FROM process
+  LEFT JOIN
+    -- We can't use is_main_thread column for Chrome traces - Renderer
+    -- processes have is_main_thread = 0 for the logical main thread.
+    (SELECT utid, upid FROM thread WHERE thread.name GLOB '*[Mm]ain*')
+  USING (upid)
+  WHERE
+    EXTRACT_ARG(process.arg_set_id, 'chrome.process_type')
+      IN ('Browser', 'Renderer', 'Gpu')
+)
+WHERE utid is NULL;
+
+DROP VIEW IF EXISTS chrome_processes_data_loss_free_period;
+
+CREATE VIEW chrome_processes_data_loss_free_period
+AS
+SELECT
+  upid AS limiting_upid,
+  -- If reliable_from is NULL, the process has data loss until the end of the trace.
+  IFNULL(reliable_from, (SELECT MAX(ts + dur) FROM slice)) AS start
+FROM
+  (
+    SELECT upid, reliable_from
+    FROM experimental_missing_chrome_processes
+    UNION ALL
+    -- A missing main thread means that the process data is unreliable for the
+    -- entire duration of the trace.
+    SELECT upid, NULL AS reliable_from
+    FROM chrome_processes_with_missing_main
+  )
+ORDER BY start DESC
+LIMIT 1;
+
+DROP VIEW IF EXISTS chrome_reliable_range;
+
+CREATE VIEW chrome_reliable_range
+AS
+SELECT
+  -- If the trace has a cropping packet, we don't want to recompute the reliable
+  -- based on cropped track events - the result might be incorrect.
+  IFNULL(EXTRACT_INT_METADATA('range_of_interest_start_us') * 1000,
+         MAX(thread_start, data_loss_free_start)) AS start,
+  IIF(EXTRACT_INT_METADATA('range_of_interest_start_us') IS NOT NULL,
+      'Range of interest packet',
+      IIF(limiting_upid IN (SELECT upid FROM chrome_processes_with_missing_main),
+          'Missing main thread for upid=' || limiting_upid,
+          IIF(thread_start >= data_loss_free_start,
+              'First slice for utid=' || limiting_utid,
+               'Missing process data for upid=' || limiting_upid))) AS reason,
+  limiting_upid AS debug_limiting_upid,
+  limiting_utid AS debug_limiting_utid
+FROM
+  (SELECT
+    COALESCE(MAX(start), 0) AS thread_start,
+    utid AS limiting_utid,
+    COALESCE((SELECT start FROM chrome_processes_data_loss_free_period), 0) AS data_loss_free_start,
+    (SELECT limiting_upid FROM chrome_processes_data_loss_free_period) AS limiting_upid
+    FROM chrome_reliable_range_per_thread
+    WHERE has_first_packet_on_sequence = 0);
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_scroll_inputs_per_frame.sql b/src/trace_processor/metrics/sql/chrome/chrome_scroll_inputs_per_frame.sql
index a7edee2..c92347d 100644
--- a/src/trace_processor/metrics/sql/chrome/chrome_scroll_inputs_per_frame.sql
+++ b/src/trace_processor/metrics/sql/chrome/chrome_scroll_inputs_per_frame.sql
@@ -73,29 +73,29 @@
 DROP VIEW IF EXISTS chrome_frame_main_input_id;
 CREATE VIEW chrome_frame_main_input_id AS
 SELECT
-    id,
-    scroll_id,
-    is_coalesced,
-    ts,
-    dur,
-    track_id,
-    (SELECT
-        MAX(id)
+  id,
+  scroll_id,
+  is_coalesced,
+  ts,
+  dur,
+  track_id,
+  (SELECT
+    MAX(id)
     FROM chrome_all_scroll_updates parent_scrolls
     WHERE NOT is_coalesced
-        AND parent_scrolls.ts <= scrolls.ts) AS presented_scroll_id
+      AND parent_scrolls.ts <= scrolls.ts) AS presented_scroll_id
 FROM chrome_all_scroll_updates scrolls;
 
 -- Count the number of inputs per presented frame.
 DROP VIEW IF EXISTS chrome_scroll_inputs_per_frame;
 CREATE VIEW chrome_scroll_inputs_per_frame AS
 SELECT
-    COUNT() count_for_frame,
-    presented_scroll_id,
-    ts,
-    dur,
-    id AS slice_id,
-    track_id
+  COUNT() AS count_for_frame,
+  presented_scroll_id,
+  ts,
+  dur,
+  id AS slice_id,
+  track_id
 FROM
-    chrome_frame_main_input_id
-GROUP BY presented_scroll_id;
\ No newline at end of file
+  chrome_frame_main_input_id
+GROUP BY presented_scroll_id;
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_scroll_jank_caused_by_scheduling.sql b/src/trace_processor/metrics/sql/chrome/chrome_scroll_jank_caused_by_scheduling.sql
index 9fe4516..ecb9cf0 100644
--- a/src/trace_processor/metrics/sql/chrome/chrome_scroll_jank_caused_by_scheduling.sql
+++ b/src/trace_processor/metrics/sql/chrome/chrome_scroll_jank_caused_by_scheduling.sql
@@ -43,17 +43,18 @@
   SUM(thread_dur / 1e6) AS total_thread_duration_ms,
   MIN(id) AS first_id_per_task_barrage,
   MAX(id) AS last_id_per_task_barrage,
-  COUNT(*) as count,
+  COUNT(*) AS count,
   window_start_id,
   window_start_ts,
   window_end_id,
-  window_end_ts
+  window_end_ts,
+  scroll_type
 FROM
-  (SELECT * FROM (
-    (
+  (
+    SELECT * FROM (
       SELECT
         chrome_tasks.full_name AS full_name,
-        chrome_tasks.dur  AS dur,
+        chrome_tasks.dur AS dur,
         chrome_tasks.thread_dur AS thread_dur,
         chrome_tasks.ts AS ts,
         chrome_tasks.id,
@@ -61,30 +62,29 @@
       FROM
         chrome_tasks
       WHERE
-         chrome_tasks.thread_name = "CrBrowserMain"
-         AND task_type != "java"
-         AND task_type != "choreographer"
+        chrome_tasks.thread_name = "CrBrowserMain"
+        AND task_type != "java"
+        AND task_type != "choreographer"
       ORDER BY chrome_tasks.ts
     ) tasks
     JOIN chrome_input_to_browser_longer_intervals
-      ON (tasks.ts + tasks.dur) >
-      chrome_input_to_browser_longer_intervals.window_start_ts
-      AND (tasks.ts + tasks.dur) <
-      chrome_input_to_browser_longer_intervals.window_end_ts
-      AND tasks.ts > chrome_input_to_browser_longer_intervals.window_start_ts
-      AND tasks.ts < chrome_input_to_browser_longer_intervals.window_end_ts
-      -- For cases when there are multiple chrome instances.
-      and tasks.upid = chrome_input_to_browser_longer_intervals.upid)
-    ORDER BY
-    window_start_ts, window_end_ts
+      ON (tasks.ts + tasks.dur)
+        > chrome_input_to_browser_longer_intervals.window_start_ts
+        AND (tasks.ts + tasks.dur)
+        < chrome_input_to_browser_longer_intervals.window_end_ts
+        AND tasks.ts > chrome_input_to_browser_longer_intervals.window_start_ts
+        AND tasks.ts < chrome_input_to_browser_longer_intervals.window_end_ts
+        -- For cases when there are multiple chrome instances.
+        AND tasks.upid = chrome_input_to_browser_longer_intervals.upid
+    ORDER BY window_start_ts, window_end_ts
   )
-  GROUP BY window_start_ts, window_end_ts;
+GROUP BY window_start_ts, window_end_ts;
 
 -- Filter to task barrages that took more than 8ms, as barrages
 -- that lasted less than that are unlikely to have caused jank.
 DROP VIEW IF EXISTS chrome_scroll_jank_caused_by_scheduling;
 CREATE VIEW chrome_scroll_jank_caused_by_scheduling AS
-  SELECT *
-  FROM chrome_task_barrages_per_interval
-  WHERE total_duration_ms > {{dur_causes_jank_ms}} AND count > 1
-  ORDER BY total_duration_ms DESC;
\ No newline at end of file
+SELECT *
+FROM chrome_task_barrages_per_interval
+WHERE total_duration_ms > {{dur_causes_jank_ms}} AND count > 1
+ORDER BY total_duration_ms DESC;
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_stack_samples_for_task.sql b/src/trace_processor/metrics/sql/chrome/chrome_stack_samples_for_task.sql
index a0523f3..f5a30a3 100644
--- a/src/trace_processor/metrics/sql/chrome/chrome_stack_samples_for_task.sql
+++ b/src/trace_processor/metrics/sql/chrome/chrome_stack_samples_for_task.sql
@@ -62,27 +62,27 @@
 DROP VIEW IF EXISTS chrome_non_symbolized_frames;
 CREATE VIEW chrome_non_symbolized_frames AS
 SELECT
-  frames.name as frame_name,
-  callsite.id as callsite_id,
+  frames.name AS frame_name,
+  callsite.id AS callsite_id,
   *
 FROM
   stack_profile_frame frames
-  JOIN stack_profile_callsite callsite
-    ON callsite.frame_id = frames.id;
+JOIN stack_profile_callsite callsite
+  ON callsite.frame_id = frames.id;
 
 -- Only lowest child frames are join-able with chrome_non_symbolized_frames
 -- which we need for the time at which the callstack was taken.
 DROP VIEW IF EXISTS chrome_symbolized_child_frames;
 CREATE VIEW chrome_symbolized_child_frames AS
 SELECT
-  thread.name as thread_name,
+  thread.name AS thread_name,
   sample.utid AS sample_utid,
   *
 FROM
   chrome_non_symbolized_frames frames
-  JOIN cpu_profile_stack_sample sample USING(callsite_id)
-  JOIN thread USING(utid)
-  JOIN process USING(upid);
+JOIN cpu_profile_stack_sample sample USING(callsite_id)
+JOIN thread USING(utid)
+JOIN process USING(upid);
 
 -- Not all frames are symbolized, in cases where those frames
 -- are not symbolized, use the file name as it is usually descriptive
@@ -90,13 +90,13 @@
 DROP VIEW IF EXISTS chrome_thread_symbolized_child_frames;
 CREATE VIEW chrome_thread_symbolized_child_frames AS
 SELECT
- DescribeSymbol(symbol.name, frame_name) AS description,
- depth,
- ts,
- callsite_id,
- sample_utid
+  DescribeSymbol(symbol.name, frame_name) AS description,
+  depth,
+  ts,
+  callsite_id,
+  sample_utid
 FROM chrome_symbolized_child_frames
-  LEFT JOIN stack_profile_symbol symbol USING(symbol_set_id)
+LEFT JOIN stack_profile_symbol symbol USING(symbol_set_id)
 WHERE thread_name = {{thread_name}} ORDER BY ts DESC;
 
 -- Since only leaf stack frames have a timestamp, let's export this
@@ -104,31 +104,31 @@
 -- filtering frames within specific windows
 DROP VIEW IF EXISTS chrome_non_symbolized_frames_timed;
 CREATE VIEW chrome_non_symbolized_frames_timed AS
-  SELECT
-    chrome_non_symbolized_frames.frame_name,
-    chrome_non_symbolized_frames.depth,
-    chrome_thread_symbolized_child_frames.ts,
-    chrome_thread_symbolized_child_frames.sample_utid,
-    chrome_non_symbolized_frames.callsite_id,
-    symbol_set_id,
-    chrome_non_symbolized_frames.frame_id
+SELECT
+  chrome_non_symbolized_frames.frame_name,
+  chrome_non_symbolized_frames.depth,
+  chrome_thread_symbolized_child_frames.ts,
+  chrome_thread_symbolized_child_frames.sample_utid,
+  chrome_non_symbolized_frames.callsite_id,
+  symbol_set_id,
+  chrome_non_symbolized_frames.frame_id
 FROM chrome_thread_symbolized_child_frames
-  JOIN experimental_ancestor_stack_profile_callsite(
+JOIN experimental_ancestor_stack_profile_callsite(
     chrome_thread_symbolized_child_frames.callsite_id) child
-  JOIN chrome_non_symbolized_frames
+JOIN chrome_non_symbolized_frames
   ON chrome_non_symbolized_frames.callsite_id = child.id;
 
 DROP VIEW IF EXISTS chrome_frames_timed_and_symbolized;
 CREATE VIEW chrome_frames_timed_and_symbolized AS
-  SELECT
-    DescribeSymbol(symbol.name, frame_name) AS description,
-    ts,
-    depth,
-    callsite_id,
-    sample_utid
+SELECT
+  DescribeSymbol(symbol.name, frame_name) AS description,
+  ts,
+  depth,
+  callsite_id,
+  sample_utid
 FROM chrome_non_symbolized_frames_timed
-  LEFT JOIN stack_profile_symbol symbol
-    USING(symbol_set_id)
+LEFT JOIN stack_profile_symbol symbol
+  USING(symbol_set_id)
 ORDER BY DEPTH ASC;
 
 -- Union leaf stack frames with all stack frames after the timestamp
@@ -140,15 +140,15 @@
 FROM
   (SELECT
     * FROM
-      chrome_frames_timed_and_symbolized
-  UNION
-  SELECT
-    description,
+    chrome_frames_timed_and_symbolized
+    UNION
+    SELECT
+      description,
       ts,
       depth,
       callsite_id,
       sample_utid
-  FROM chrome_thread_symbolized_child_frames)
+    FROM chrome_thread_symbolized_child_frames)
 ORDER BY depth ASC;
 
 -- Filter stack samples that happened only during the specified
@@ -156,10 +156,10 @@
 DROP VIEW IF EXISTS chrome_stack_samples_for_task;
 CREATE VIEW chrome_stack_samples_for_task AS
 SELECT
-    all_frames.*
+  all_frames.*
 FROM
-    all_frames JOIN
-    chrome_targeted_task ON
+  all_frames JOIN
+  chrome_targeted_task ON
     all_frames.sample_utid = chrome_targeted_task.utid
     AND all_frames.ts >= chrome_targeted_task.ts
-    AND all_frames.ts <= chrome_targeted_task.ts + chrome_targeted_task.dur;
\ No newline at end of file
+    AND all_frames.ts <= chrome_targeted_task.ts + chrome_targeted_task.dur;
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_tasks.sql b/src/trace_processor/metrics/sql/chrome/chrome_tasks.sql
index d57f8a4..ee14b7e 100644
--- a/src/trace_processor/metrics/sql/chrome/chrome_tasks.sql
+++ b/src/trace_processor/metrics/sql/chrome/chrome_tasks.sql
@@ -14,391 +14,8 @@
 -- limitations under the License.
 --
 
--- Create |chrome_scheduler_tasks| table, which contains a subset of thread_slice
--- table with the slices which correspond to tasks executed by Chrome scheduler.
---
--- |chrome_scheduler_tasks_internal| is the cached table containing scheduler-specific bits.
-DROP TABLE IF EXISTS chrome_scheduler_tasks_internal;
-CREATE TABLE chrome_scheduler_tasks_internal AS
-SELECT
-  EXTRACT_ARG(s.arg_set_id, "task.posted_from.file_name") as posted_from_file_name,
-  EXTRACT_ARG(s.arg_set_id, "task.posted_from.function_name") as posted_from_function_name,
-  (CASE name
-    WHEN "ThreadControllerImpl::RunTask" THEN "SequenceManager"
-    WHEN "ThreadPool_RunTask" THEN "ThreadPool"
-  END) as scheduler_type,
-  s.id
-FROM thread_slice s
-WHERE
-  category="toplevel" AND
-  (name="ThreadControllerImpl::RunTask" or name="ThreadPool_RunTask")
-ORDER BY id;
-
--- |chrome_scheduler_tasks| is a view over |chrome_scheduler_tasks_internal| table, adding
--- columns from |thread_slice| table.
-DROP VIEW IF EXISTS chrome_scheduler_tasks;
-CREATE VIEW chrome_scheduler_tasks AS
-SELECT
-  s1.posted_from_file_name || ":" || s1.posted_from_function_name as posted_from,
-  s1.posted_from_file_name,
-  s1.posted_from_function_name,
-  s1.scheduler_type,
-  s2.*
-FROM chrome_scheduler_tasks_internal s1
-JOIN thread_slice s2 USING (id)
-ORDER BY id;
-
--- Create |chrome_mojo_receive_slices| table, containing a subset of thread_slice
--- table with the slices corresponding to received mojo messages.
---
--- Note: this might include messages received within a sync mojo call.
-DROP TABLE IF EXISTS chrome_mojo_slices_internal;
-CREATE TABLE chrome_mojo_slices_internal AS
-WITH
-  -- Select all new-style (post crrev.com/c/3270337) mojo slices and
-  -- generate |full_name| for them.
-  -- If extended tracing is enabled, the slice name will have the full method
-  -- name (i.e. "Receive content::mojom::FrameHost::DidStopLoading") and we
-  -- should use it as a full name.
-  -- If extended tracing is not enabled, we should include the interface name
-  -- and method hash into the full name.
-  new_mojo_slices AS (
-    SELECT
-      EXTRACT_ARG(s.arg_set_id, "chrome_mojo_event_info.mojo_interface_tag") as interface_name,
-      EXTRACT_ARG(arg_set_id, "chrome_mojo_event_info.ipc_hash") as ipc_hash,
-      (CASE name
-        WHEN "Receive mojo message" THEN "message"
-        WHEN "Receive mojo reply" THEN "reply"
-      END) as message_type,
-      s.id
-    FROM slice s
-    WHERE
-      category="toplevel"
-      AND name GLOB 'Receive *'
-    ORDER BY id
-  ),
-  -- Select old-style slices for channel-associated mojo events.
-  old_associated_mojo_slices AS (
-    SELECT
-      s.name as interface_name,
-      (select
-        EXTRACT_ARG(s2.arg_set_id, "chrome_mojo_event_info.ipc_hash")
-       FROM descendant_slice(s.id) s2
-       WHERE s2.name="ScopedSetIpcHash"
-       ORDER BY s2.id
-       LIMIT 1) as ipc_hash,
-      "message" as message_type,
-      s.id
-    FROM thread_slice s
-    WHERE
-      category="mojom"
-      AND name GLOB '*.mojom.*'
-    ORDER BY id
-  ),
-  -- Select old-style slices for non-(channel-associated) mojo events.
-  old_non_associated_mojo_slices AS (
-    SELECT
-      COALESCE(
-        EXTRACT_ARG(s.arg_set_id, "chrome_mojo_event_info.watcher_notify_interface_tag"),
-        EXTRACT_ARG(s.arg_set_id, "chrome_mojo_event_info.mojo_interface_tag")
-      ) as interface_name,
-      (select
-        EXTRACT_ARG(s2.arg_set_id, "chrome_mojo_event_info.ipc_hash")
-       FROM descendant_slice(s.id) s2
-       WHERE s2.name="ScopedSetIpcHash"
-       ORDER BY s2.id
-       LIMIT 1) as ipc_hash,
-      "message" as message_type,
-      s.id
-    FROM thread_slice s
-    WHERE
-      category="toplevel" and name="Connector::DispatchMessage"
-    ORDER BY id
-  ),
-  -- Merge all mojo slices.
-  all_mojo_slices_non_sorted AS (
-    SELECT * from new_mojo_slices
-    UNION
-    SELECT * from old_associated_mojo_slices
-    UNION
-    SELECT * from old_non_associated_mojo_slices
-  )
-SELECT *
-FROM all_mojo_slices_non_sorted
-ORDER BY id;
-
--- |chrome_mojo_slices| is a view over |chrome_mojo_slices_internal| table, adding
--- columns from |thread_slice| table.
-DROP VIEW IF EXISTS chrome_mojo_slices;
-CREATE VIEW chrome_mojo_slices AS
-SELECT
-  s1.interface_name,
-  s1.ipc_hash,
-  s1.message_type,
-  s2.*
-FROM chrome_mojo_slices_internal s1
-JOIN thread_slice s2 USING (id)
-ORDER BY id;
-
--- This table contains a list of slices corresponding to the _representative_ Chrome Java views.
--- These are the outermost Java view slices after filtering out generic framework views
--- (like FitWindowsLinearLayout) and selecting the outermost slices from the remaining ones.
-DROP TABLE IF EXISTS chrome_java_views_internal;
-CREATE TABLE chrome_java_views_internal AS
-WITH
-  -- .draw, .onLayout and .onMeasure parts of the java view names don't add much, strip them.
-  java_slices_with_trimmed_names AS (
-    SELECT
-      id,
-      REPLACE(
-        REPLACE(
-          REPLACE(
-            REPLACE(
-              REPLACE(
-                s1.name,
-                ".draw", ""),
-              ".onLayout", ""),
-          ".onMeasure", ""),
-        ".Layout", ""),
-      ".Measure", "") as name
-    FROM
-      thread_slice s1
-    where category="Java" and dur > 0
-  ),
-  -- We filter out generic slices from various UI frameworks which don't tell us much about
-  -- what exactly this view is doing.
-  interesting_java_slices AS (
-    SELECT
-      id, name
-    FROM java_slices_with_trimmed_names
-    WHERE not name in (
-      -- AndroidX.
-      "FitWindowsFrameLayout",
-      "FitWindowsLinearLayout",
-      "ContentFrameLayout",
-      "CoordinatorLayout",
-      -- Other non-Chrome UI libraries.
-      "ComponentHost",
-      -- Generic Chrome frameworks.
-      "CompositorView:finalizeLayers",
-      "CompositorViewHolder",
-      "CompositorViewHolder:layout",
-      "CompositorViewHolder:updateContentViewChildrenDimension",
-      "CoordinatorLayoutForPointer",
-      "OptimizedFrameLayout",
-      "ViewResourceAdapter:getBitmap",
-      "ViewResourceFrameLayout",
-      -- Non-specific Chrome slices.
-      "AppCompatImageButton",
-      "ScrollingBottomViewResourceFrameLayout",
-      -- Screenshots get their custom annotations below.
-      "ViewResourceAdapter:captureWithHardwareDraw",
-      "ViewResourceAdapter:captureWithSoftwareDraw",
-      -- Non-bytecode generated slices.
-      "LayoutDriver:onUpdate"
-    )
-  )
-SELECT
-  s1.*,
-  -- While the parent slices are too generic to be used by themselves,
-  -- they can provide some useful metadata.
-  (SELECT count()
-    FROM ancestor_slice(s1.id) s2
-    WHERE s2.name="ViewResourceAdapter:captureWithSoftwareDraw"
-  )>0 as is_software_screenshot,
-  (SELECT count()
-    FROM ancestor_slice(s1.id) s2
-    WHERE s2.name="ViewResourceAdapter:captureWithHardwareDraw"
-  )>0 as is_hardware_screenshot
-FROM interesting_java_slices s1
-WHERE (select count()
-  from ancestor_slice(s1.id) s2
-  join interesting_java_slices s3 on s2.id=s3.id)=0
-ORDER BY s1.id;
-
--- |chrome_java_views| is a view over |chrome_java_views_internal| table, adding the necessary columns
--- from |thread_slice|.
-DROP VIEW IF EXISTS chrome_java_views;
-CREATE VIEW chrome_java_views AS
-SELECT
-  s1.name as filtered_name,
-  s1.is_software_screenshot,
-  s1.is_hardware_screenshot,
-  s2.*
-FROM chrome_java_views_internal s1
-JOIN thread_slice s2 using (id);
-
--- Most of java views will be triggered either by Chrome's BeginMainFrame
--- or by Android's Choreographer.
-DROP VIEW IF EXISTS chrome_slices_with_java_views_internal;
-CREATE VIEW chrome_slices_with_java_views_internal AS
-WITH
-  -- Select UI thread BeginMainFrames and Choreographer frames.
-  root_slices AS (
-    SELECT
-      id,
-      (CASE name
-        WHEN 'ThreadControllerImpl::RunTask' THEN 'SingleThreadProxy::BeginMainFrame'
-        ELSE 'Choreographer'
-       END) as kind
-    FROM thread_slice
-    WHERE
-      (name GLOB 'Looper.dispatch: android.view.Choreographer$FrameHandler*') OR
-      (name='ThreadControllerImpl::RunTask' AND
-        EXTRACT_ARG(arg_set_id, 'task.posted_from.file_name')='cc/trees/single_thread_proxy.cc' AND
-        EXTRACT_ARG(arg_set_id, 'task.posted_from.function_name')='ScheduledActionSendBeginMainFrame')
-    ORDER BY id
-  ),
-  -- Intermediate step to allow us to sort java view names.
-  root_slice_and_java_view_not_grouped AS (
-    SELECT
-      s1.id, s1.kind, s3.name as java_view_name
-    FROM root_slices s1
-    JOIN descendant_slice(s1.id) s2
-    JOIN chrome_java_views_internal s3 ON s2.id=s3.id
-    ORDER BY s1.id, java_view_name
-  )
-SELECT
-  s1.id,
-  s1.kind,
-  GROUP_CONCAT(DISTINCT s2.java_view_name) as java_views
-FROM root_slices s1
-LEFT JOIN root_slice_and_java_view_not_grouped s2 USING (id)
-GROUP BY s1.id
-ORDER BY s1.id;
-
--- Create |chrome_tasks| table, which contains a subset of thread_slice
--- table of the slices which should be considered top-level Chrome tasks with the
--- additional scheduler_type |full_name| column, derived from subevents.
-DROP TABLE IF EXISTS chrome_tasks_internal;
-CREATE TABLE chrome_tasks_internal AS
-WITH
-  -- Select slices from "toplevel" category which do not have another
-  -- "toplevel" slice as ancestor. The possible cases include sync mojo messages
-  -- and tasks in nested runloops.
-  non_embedded_toplevel_slices AS (
-     SELECT * FROM thread_slice s
-     WHERE
-       category IN ("toplevel", "toplevel,viz")
-       AND (SELECT count() FROM ancestor_slice(s.id) s2 
-            WHERE s2.category IN ("toplevel", "toplevel.viz"))=0
-     ORDER BY id
-  ),
-  -- Select slices from "Java" category which do not have another "Java" or
-  -- "toplevel" slice as parent. In the longer term they should probably belong
-  -- to "toplevel" category as well, but for now this will have to do.
-  non_embedded_java_slices AS (
-    SELECT name as full_name, "java" as task_type, id
-    FROM thread_slice s
-    WHERE
-      category="Java"
-      AND (SELECT count()
-           FROM ancestor_slice(s.id) s2
-           WHERE s2.category="toplevel" or s2.category="Java")=0
-    ORDER BY id
-  ),
-  -- Generate full names for scheduler tasks.
-  scheduler_tasks AS (
-    SELECT
-      printf("RunTask(posted_from=%s)", s.posted_from) as full_name,
-      "scheduler" as task_type,
-      s.id
-    FROM chrome_scheduler_tasks s
-    ORDER BY id
-  ),
-  -- Generate full names for mojo slices.
-  mojo_slices AS (
-    SELECT
-      printf('%s %s (hash=%d)',
-        interface_name, message_type, ipc_hash) as full_name,
-      "mojo" as task_type,
-      id
-    FROM chrome_mojo_slices
-    ORDER BY id
-  ),
-  -- Generate full names for tasks with java views.
-  java_views_tasks AS (
-    SELECT
-      printf('%s(java_views=%s)', kind, java_views) as full_name,
-      (CASE kind
-        WHEN 'Choreographer' THEN 'choreographer'
-        WHEN 'SingleThreadProxy::BeginMainFrame' THEN 'ui_thread_begin_main_frame'
-       END) as task_type,
-      id
-    FROM chrome_slices_with_java_views_internal
-    ORDER BY id
-  ),
-  -- Select scheduler tasks which are used to run mojo messages and use the mojo names
-  -- as full names for these slices.
-  -- We restrict this to specific scheduler tasks which are expected to run mojo
-  -- tasks due to sync mojo events, which also emit similar events.
-  scheduler_tasks_with_mojo AS (
-    SELECT
-      (SELECT s3.full_name
-        FROM descendant_slice(s1.id) s2
-        JOIN mojo_slices s3 USING (id)
-        ORDER BY s2.depth LIMIT 1) as full_name,
-      "mojo" as task_type,
-      s1.id
-    FROM
-      chrome_scheduler_tasks s1
-    WHERE
-      s1.posted_from IN (
-        "mojo/public/cpp/system/simple_watcher.cc:Notify",
-        "mojo/public/cpp/bindings/lib/connector.cc:PostDispatchNextMessageFromPipe",
-        "ipc/ipc_mojo_bootstrap.cc:Accept")
-    ORDER BY id
-  ),
-  -- Add scheduler and mojo full names to non-embedded slices from
-  -- the "toplevel" category, with mojo ones taking precedence.
-  non_embedded_toplevel_slices_with_full_name AS (
-    SELECT
-       COALESCE(s4.full_name, s2.full_name, s3.full_name, s1.name) AS full_name,
-       COALESCE(s4.task_type, s2.task_type, s3.task_type, "other") as task_type,
-       s1.id as id
-    FROM non_embedded_toplevel_slices s1
-    LEFT JOIN scheduler_tasks_with_mojo s2 ON s2.id=s1.id
-    LEFT JOIN scheduler_tasks s3 ON s3.id=s1.id
-    LEFT JOIN java_views_tasks s4 ON s4.id=s1.id
-    ORDER BY id
-  ),
-  -- Merge slices from toplevel and Java categories.
-  non_sorted_tasks AS (
-    SELECT * from non_embedded_toplevel_slices_with_full_name
-    UNION ALL
-    SELECT * from non_embedded_java_slices
-  )
-SELECT * FROM non_sorted_tasks
-ORDER BY id;
-
-DROP VIEW IF EXISTS chrome_tasks;
-CREATE VIEW chrome_tasks AS
-SELECT
-  full_name,
-  task_type,
-  thread.name as thread_name,
-  thread.utid,
-  process.name as process_name,
-  thread.upid,
-  ts.*
-FROM chrome_tasks_internal cti
-JOIN thread_slice ts USING (id)
-JOIN thread_track tt ON ts.track_id=tt.id
-JOIN thread USING (utid)
-JOIN process USING (upid)
-ORDER BY id;
-
--- A helper view into Chrome thread slices which don't have a parent task. 
--- TODO(altimin): Use chrome_thread here once it's reliable.
-DROP VIEW IF EXISTS chrome_slices_without_parent_task;
-CREATE VIEW chrome_slices_without_parent_task AS
-SELECT
-  s1.*
-FROM thread_slice s1
-LEFT JOIN chrome_tasks s2 USING (id)
-WHERE
-  (SELECT count()
-   FROM ancestor_slice(s1.id) s3
-   JOIN chrome_tasks s4 ON s3.id=s4.id)=0
-  and s2.id IS NULL
-ORDER BY id;
+SELECT RUN_METRIC(
+  "chrome/chrome_tasks_template.sql",
+  "slice_table_name", "slice",
+  "function_prefix", ""
+);
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_tasks_delaying_input_processing.sql b/src/trace_processor/metrics/sql/chrome/chrome_tasks_delaying_input_processing.sql
index dfab111..80db54b 100644
--- a/src/trace_processor/metrics/sql/chrome/chrome_tasks_delaying_input_processing.sql
+++ b/src/trace_processor/metrics/sql/chrome/chrome_tasks_delaying_input_processing.sql
@@ -17,71 +17,9 @@
 -- {{duration_causing_jank_ms}} : The duration of a single task that would cause
 -- jank, by delaying input from being handled on the main thread.
 
-SELECT RUN_METRIC('chrome/chrome_tasks.sql');
-SELECT RUN_METRIC('chrome/chrome_input_to_browser_intervals.sql');
-
--- Get the tasks that was running for more than 8ms within windows
--- that we could have started processing input but did not on the
--- main thread, because it was blocked by those tasks.
-DROP VIEW IF EXISTS chrome_tasks_delaying_input_processing_unaggregated;
-CREATE VIEW chrome_tasks_delaying_input_processing_unaggregated AS
-SELECT
-  tasks.full_name AS full_name,
-  tasks.dur / 1e6 AS duration_ms,
-  id AS slice_id,
-  thread_dur / 1e6 AS thread_dur_ms,
-  chrome_input_to_browser_intervals.window_start_id,
-  chrome_input_to_browser_intervals.window_end_id
-FROM
-  (
-    (
-      SELECT
-        chrome_tasks.full_name AS full_name,
-        chrome_tasks.dur AS dur,
-        chrome_tasks.ts AS ts,
-        chrome_tasks.id AS id,
-        chrome_tasks.upid AS upid,
-        thread_dur
-      FROM
-        chrome_tasks
-      WHERE
-        chrome_tasks.dur >= {{duration_causing_jank_ms}} * 1e6
-        and chrome_tasks.thread_name = "CrBrowserMain"
-    ) tasks
-    JOIN chrome_input_to_browser_intervals
-      ON tasks.ts + tasks.dur > chrome_input_to_browser_intervals.window_start_ts
-      AND tasks.ts + tasks.dur < chrome_input_to_browser_intervals.window_end_ts
-      AND tasks.upid = chrome_input_to_browser_intervals.upid
-  );
-
--- Same task can delay multiple GestureUpdates, this step dedups
--- multiple occrences of the same slice_id
-DROP VIEW IF EXISTS chrome_tasks_delaying_input_processing;
-CREATE VIEW chrome_tasks_delaying_input_processing AS
-SELECT
-  full_name,
-  duration_ms,
-  slice_id,
-  thread_dur_ms
-FROM chrome_tasks_delaying_input_processing_unaggregated
-GROUP BY slice_id;
-
--- Get the tasks that were running for more than 8ms within windows
--- that we could have started processing input but did not on the
--- main thread, because it was blocked by those tasks.
-DROP VIEW IF EXISTS chrome_tasks_delaying_input_processing_summary;
-CREATE VIEW chrome_tasks_delaying_input_processing_summary AS
-SELECT
-  full_name AS full_name,
-  AVG(duration_ms) AS avg_duration_ms,
-  AVG(thread_dur_ms) AS avg_thread_duration_ms,
-  MIN(duration_ms) AS min_task_duration,
-  MAX(duration_ms) as max_task_duration,
-  SUM(duration_ms) AS total_duration_ms,
-  SUM(thread_dur_ms) AS total_thread_duration_ms,
-  GROUP_CONCAT(slice_id, '-') AS slice_ids,
-  COUNT(*) AS count
-FROM
-  chrome_tasks_delaying_input_processing
-GROUP BY
-  full_name;
\ No newline at end of file
+SELECT RUN_METRIC(
+  'chrome/chrome_tasks_delaying_input_processing_template.sql',
+  'duration_causing_jank_ms', '{{duration_causing_jank_ms}}',
+  'slice_table_name', 'slice',
+  'function_prefix', ''
+);
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_tasks_delaying_input_processing_base.sql b/src/trace_processor/metrics/sql/chrome/chrome_tasks_delaying_input_processing_base.sql
new file mode 100644
index 0000000..ba96dc3
--- /dev/null
+++ b/src/trace_processor/metrics/sql/chrome/chrome_tasks_delaying_input_processing_base.sql
@@ -0,0 +1,92 @@
+--
+-- Copyright 2022 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.
+
+-- Script params:
+-- {{duration_causing_jank_ms}} : The duration of a single task that would cause
+-- jank, by delaying input from being handled on the main thread.
+-- {{task_table_name}} : The table tracking chrome tasks which will be used to
+-- determine which chrome tasks are causing the delay. One of chrome_tasks or
+-- chrome_long_tasks.
+-- {{input_browser_interval_table_name}} : The table tracking chrome input to
+-- browser interval. This may differ based on whether the scenario is for
+-- topLevel events or LongTask events.
+
+SELECT CREATE_VIEW_FUNCTION(
+  '{{function_prefix}}SELECT_SLOW_BROWSER_TASKS()',
+  'full_name STRING, dur INT, ts INT, id INT, upid INT, thread_dur INT',
+  'SELECT
+    task_table.full_name AS full_name,
+    task_table.dur AS dur,
+    task_table.ts AS ts,
+    task_table.id AS id,
+    task_table.upid AS upid,
+    thread_dur
+  FROM
+    {{task_table_name}} task_table
+  WHERE
+    task_table.dur >= {{duration_causing_jank_ms}} * 1e6
+    AND task_table.thread_name = "CrBrowserMain"
+  '
+);
+
+-- Get the tasks that was running for more than 8ms within windows
+-- that we could have started processing input but did not on the
+-- main thread, because it was blocked by those tasks.
+DROP VIEW IF EXISTS chrome_tasks_delaying_input_processing_unaggregated;
+CREATE VIEW chrome_tasks_delaying_input_processing_unaggregated AS
+SELECT
+  tasks.full_name AS full_name,
+  tasks.dur / 1e6 AS duration_ms,
+  id AS slice_id,
+  thread_dur / 1e6 AS thread_dur_ms,
+  input_tbl.window_start_id,
+  input_tbl.window_end_id
+FROM ({{function_prefix}}SELECT_SLOW_BROWSER_TASKS()) tasks
+JOIN {{input_browser_interval_table_name}} input_tbl
+  ON tasks.ts + tasks.dur > input_tbl.window_start_ts
+    AND tasks.ts + tasks.dur < input_tbl.window_end_ts
+    AND tasks.upid = input_tbl.upid;
+
+-- Same task can delay multiple GestureUpdates, this step dedups
+-- multiple occrences of the same slice_id
+DROP VIEW IF EXISTS chrome_tasks_delaying_input_processing;
+CREATE VIEW chrome_tasks_delaying_input_processing AS
+SELECT
+  full_name,
+  duration_ms,
+  slice_id,
+  thread_dur_ms
+FROM chrome_tasks_delaying_input_processing_unaggregated
+GROUP BY slice_id;
+
+-- Get the tasks that were running for more than 8ms within windows
+-- that we could have started processing input but did not on the
+-- main thread, because it was blocked by those tasks.
+DROP VIEW IF EXISTS chrome_tasks_delaying_input_processing_summary;
+CREATE VIEW chrome_tasks_delaying_input_processing_summary AS
+SELECT
+  full_name AS full_name,
+  AVG(duration_ms) AS avg_duration_ms,
+  AVG(thread_dur_ms) AS avg_thread_duration_ms,
+  MIN(duration_ms) AS min_task_duration,
+  MAX(duration_ms) AS max_task_duration,
+  SUM(duration_ms) AS total_duration_ms,
+  SUM(thread_dur_ms) AS total_thread_duration_ms,
+  GROUP_CONCAT(slice_id, '-') AS slice_ids,
+  COUNT(*) AS count
+FROM
+  chrome_tasks_delaying_input_processing
+GROUP BY
+  full_name;
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_tasks_delaying_input_processing_template.sql b/src/trace_processor/metrics/sql/chrome/chrome_tasks_delaying_input_processing_template.sql
new file mode 100644
index 0000000..816487d
--- /dev/null
+++ b/src/trace_processor/metrics/sql/chrome/chrome_tasks_delaying_input_processing_template.sql
@@ -0,0 +1,32 @@
+--
+-- Copyright 2022 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.
+
+-- Script params:
+-- {{duration_causing_jank_ms}} : The duration of a single task that would cause
+-- jank, by delaying input from being handled on the main thread.
+
+SELECT RUN_METRIC(
+  'chrome/chrome_input_to_browser_intervals_template.sql',
+  'slice_table_name', '{{slice_table_name}}',
+  'function_prefix', '{{function_prefix}}'
+);
+
+SELECT RUN_METRIC(
+  'chrome/chrome_tasks_delaying_input_processing_base.sql',
+  'duration_causing_jank_ms', '{{duration_causing_jank_ms}}',
+  'task_table_name', 'chrome_tasks',
+  'input_browser_interval_table_name', 'chrome_input_to_browser_intervals',
+  'function_prefix', '{{function_prefix}}'
+);
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_tasks_template.sql b/src/trace_processor/metrics/sql/chrome/chrome_tasks_template.sql
new file mode 100644
index 0000000..d2886f1
--- /dev/null
+++ b/src/trace_processor/metrics/sql/chrome/chrome_tasks_template.sql
@@ -0,0 +1,491 @@
+--
+-- Copyright 2022 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 IMPORT("common.slices");
+
+SELECT CREATE_FUNCTION(
+  '{{function_prefix}}EXTRACT_MOJO_IPC_HASH(slice_id INT)',
+  'INT',
+  '
+    SELECT EXTRACT_ARG(s2.arg_set_id, "chrome_mojo_event_info.ipc_hash")
+    FROM descendant_slice($slice_id) s2
+    WHERE s2.name="ScopedSetIpcHash"
+    ORDER BY s2.id
+    LIMIT 1
+  '
+);
+
+SELECT CREATE_FUNCTION(
+  '{{function_prefix}}EXTRACT_FRAME_TYPE(slice_id INT)',
+  'INT',
+  '
+    SELECT EXTRACT_ARG(descendants.arg_set_id, "render_frame_host.frame_type")
+    FROM descendant_slice($slice_id) descendants
+    WHERE descendants.name IN ("RenderFrameHostImpl::BeginNavigation",
+        "RenderFrameHostImpl::DidCommitProvisionalLoad",
+        "RenderFrameHostImpl::DidCommitSameDocumentNavigation",
+        "RenderFrameHostImpl::DidStopLoading")
+    LIMIT 1
+  '
+);
+
+-- Selects the ScheduledActionSendBeginMainFrame slices, used for root-level
+-- processing. In top-level/Java based slices, these will correspond with the
+-- ancestor of descendant slices; in long-task tracking, these tasks will be
+-- on a custom track and will need to be associated with children by timestamp
+-- and duration. Corresponds with the Choreographer root slices in
+-- chrome_choreographer_tasks below.
+--
+-- Schema:
+-- @column is            The slice id.
+-- @column kind          The type of Java slice.
+-- @column ts            The timestamp of the slice.
+-- @column name          The name of the slice.
+--
+-- Power states are encoded as non-negative integers, with zero representing
+-- full-power operation and positive values representing increasingly deep
+-- sleep states.
+SELECT CREATE_VIEW_FUNCTION(
+  'SELECT_BEGIN_MAIN_FRAME_JAVA_SLICES(name STRING)',
+  'id INT, kind STRING, ts LONG, dur LONG, name STRING',
+  'SELECT
+      id,
+      "SingleThreadProxy::BeginMainFrame" AS kind,
+      ts,
+      dur,
+      name
+    FROM slice
+    WHERE
+      (name = $name
+        AND EXTRACT_ARG(arg_set_id, "task.posted_from.file_name") = "cc/trees/single_thread_proxy.cc"
+        AND EXTRACT_ARG(arg_set_id, "task.posted_from.function_name") = "ScheduledActionSendBeginMainFrame")
+  '
+);
+
+SELECT CREATE_FUNCTION(
+  -- Function prototype: takes a formatted chrome scheduler task name and
+  -- returns a readable task name.
+  '{{function_prefix}}HUMAN_READABLE_NAVIGATION_TASK_NAME(full_name STRING)',
+  'STRING',
+  'SELECT
+    CASE
+      WHEN $full_name = "content.mojom.FrameHost message (hash=2168461044)" THEN "FrameHost::BeginNavigation"
+      WHEN $full_name = "content.mojom.FrameHost message (hash=3561497419)" THEN "FrameHost::DidCommitProvisionalLoad"
+      WHEN $full_name = "content.mojom.FrameHost message (hash=1421450774)" THEN "FrameHost::DidCommitSameDocumentNavigation"
+      WHEN $full_name = "content.mojom.FrameHost message (hash=368650583)" THEN "FrameHost::DidStopLoading"
+    END
+  '
+);
+
+SELECT CREATE_FUNCTION(
+  -- Function prototype: takes a task name and formats it correctly for
+  -- scheduler tasks.
+  '{{function_prefix}}FORMAT_SCHEDULER_TASK_NAME(full_name STRING)',
+  'STRING',
+  'SELECT
+    printf("RunTask(posted_from=%s)", $full_name)
+  '
+);
+
+SELECT CREATE_FUNCTION(
+  -- Function prototype: takes the category and determines whether it is "Java"
+  -- only, as opposed to "toplevel,Java".
+  '{{function_prefix}}JAVA_NOT_TOP_LEVEL_CATEGORY(category STRING)',
+  'BOOL',
+  'SELECT
+    $category GLOB "*Java*" AND $category not GLOB "*toplevel*"
+  '
+);
+
+SELECT CREATE_FUNCTION(
+  -- Function prototype: takes the category and determines whether is any valid
+  -- toplevel category or combination of categories.
+  '{{function_prefix}}ANY_TOP_LEVEL_CATEGORY(category STRING)',
+  'BOOL',
+  'SELECT
+    $category IN ("toplevel", "toplevel,viz", "toplevel,Java")
+  '
+);
+
+SELECT CREATE_FUNCTION(
+  -- Function prototype: takes a task name and formats it correctly for
+  -- scheduler tasks.
+  '{{function_prefix}}GET_JAVA_VIEWS_TASK_TYPE(kind STRING)',
+  'STRING',
+    'SELECT
+      (CASE $kind
+        WHEN "Choreographer" THEN "choreographer"
+        WHEN "SingleThreadProxy::BeginMainFrame" THEN "ui_thread_begin_main_frame"
+      END)
+    '
+);
+
+-- Create |chrome_mojo_slices_tbl| table, containing a subset of slice
+-- table with the slices corresponding to mojo messages.
+--
+-- Note: this might include messages received within a sync mojo call.
+DROP TABLE IF EXISTS chrome_mojo_slices_tbl;
+CREATE TABLE chrome_mojo_slices_tbl AS
+WITH
+-- Select all new-style (post crrev.com/c/3270337) mojo slices and
+-- generate |full_name| for them.
+-- If extended tracing is enabled, the slice name will have the full method
+-- name (i.e. "Receive content::mojom::FrameHost::DidStopLoading") and we
+-- should use it as a full name.
+-- If extended tracing is not enabled, we should include the interface name
+-- and method hash into the full name.
+new_mojo_slices AS (
+  SELECT
+    EXTRACT_ARG(s.arg_set_id, "chrome_mojo_event_info.mojo_interface_tag") AS interface_name,
+    EXTRACT_ARG(arg_set_id, "chrome_mojo_event_info.ipc_hash") AS ipc_hash,
+    CASE name
+      WHEN "Receive mojo message" THEN "message"
+      WHEN "Receive mojo reply" THEN "reply"
+    END AS message_type,
+    s.id
+  FROM {{slice_table_name}} s
+  WHERE
+    category GLOB "*toplevel*"
+    AND name GLOB 'Receive *'
+),
+-- Select old-style slices for channel-associated mojo events.
+old_associated_mojo_slices AS (
+  SELECT
+    s.name AS interface_name,
+    {{function_prefix}}EXTRACT_MOJO_IPC_HASH(s.id) AS ipc_hash,
+    "message" AS message_type,
+    s.id
+  FROM {{slice_table_name}} s
+  WHERE
+    category GLOB "*mojom*"
+    AND name GLOB '*.mojom.*'
+),
+-- Select old-style slices for non-(channel-associated) mojo events.
+old_non_associated_mojo_slices AS (
+  SELECT
+    COALESCE(
+      EXTRACT_ARG(s.arg_set_id, "chrome_mojo_event_info.watcher_notify_interface_tag"),
+      EXTRACT_ARG(s.arg_set_id, "chrome_mojo_event_info.mojo_interface_tag")
+    ) AS interface_name,
+    {{function_prefix}}EXTRACT_MOJO_IPC_HASH(s.id) AS ipc_hash,
+    "message" AS message_type,
+    s.id
+  FROM {{slice_table_name}} s
+  WHERE
+    category GLOB "*toplevel*" AND name = "Connector::DispatchMessage"
+)
+-- Merge all mojo slices.
+SELECT * FROM new_mojo_slices
+UNION ALL
+SELECT * FROM old_associated_mojo_slices
+UNION ALL
+SELECT * FROM old_non_associated_mojo_slices;
+
+-- As we lookup by ID on |chrome_mojo_slices_tbl| table, add an index on
+-- id to make lookups fast.
+DROP INDEX IF EXISTS chrome_mojo_slices_idx;
+CREATE INDEX chrome_mojo_slices_idx ON chrome_mojo_slices_tbl(id);
+
+-- This table contains a list of slices corresponding to the _representative_ Chrome Java views.
+-- These are the outermost Java view slices after filtering out generic framework views
+-- (like FitWindowsLinearLayout) and selecting the outermost slices from the remaining ones.
+DROP TABLE IF EXISTS chrome_java_views_internal;
+CREATE TABLE chrome_java_views_internal AS
+WITH
+-- .draw, .onLayout and .onMeasure parts of the java view names don't add much, strip them.
+java_slices_with_trimmed_names AS (
+  SELECT
+    id,
+    REPLACE(
+      REPLACE(
+        REPLACE(
+          REPLACE(
+            REPLACE(
+              s1.name,
+              ".draw", ""),
+            ".onLayout", ""),
+          ".onMeasure", ""),
+        ".Layout", ""),
+      ".Measure", "") AS name,
+      ts,
+      dur
+    FROM
+      {{slice_table_name}} s1
+    -- Ensure that toplevel Java slices are not included, as they may be logged
+    -- with either category = "toplevel" or category = "toplevel,Java".
+    WHERE {{function_prefix}}JAVA_NOT_TOP_LEVEL_CATEGORY(category) AND dur > 0
+  ),
+  -- We filter out generic slices from various UI frameworks which don't tell us much about
+  -- what exactly this view is doing.
+  interesting_java_slices AS (
+    SELECT
+      id, name, ts, dur
+    FROM java_slices_with_trimmed_names
+    WHERE NOT name IN (
+      -- AndroidX.
+      "FitWindowsFrameLayout",
+      "FitWindowsLinearLayout",
+      "ContentFrameLayout",
+      "CoordinatorLayout",
+      -- Other non-Chrome UI libraries.
+      "ComponentHost",
+      -- Generic Chrome frameworks.
+      "CompositorView:finalizeLayers",
+      "CompositorViewHolder",
+      "CompositorViewHolder:layout",
+      "CompositorViewHolder:updateContentViewChildrenDimension",
+      "CoordinatorLayoutForPointer",
+      "OptimizedFrameLayout",
+      "ViewResourceAdapter:getBitmap",
+      "ViewResourceFrameLayout",
+      -- Non-specific Chrome slices.
+      "AppCompatImageButton",
+      "ScrollingBottomViewResourceFrameLayout",
+      -- Screenshots get their custom annotations below.
+      "ViewResourceAdapter:captureWithHardwareDraw",
+      "ViewResourceAdapter:captureWithSoftwareDraw",
+      -- Non-bytecode generated slices.
+      "LayoutDriver:onUpdate"
+    )
+)
+SELECT
+  s1.*,
+  -- While the parent slices are too generic to be used by themselves,
+  -- they can provide some useful metadata.
+  HAS_PARENT_SLICE_WITH_NAME(
+    s1.id,
+    "ViewResourceAdapter:captureWithSoftwareDraw"
+  ) AS is_software_screenshot,
+  HAS_PARENT_SLICE_WITH_NAME(
+    s1.id,
+    "ViewResourceAdapter:captureWithHardwareDraw"
+  ) AS is_hardware_screenshot
+FROM interesting_java_slices s1
+WHERE (SELECT count()
+  FROM ancestor_slice(s1.id) s2
+  JOIN interesting_java_slices s3 ON s2.id = s3.id) = 0;
+
+-- |chrome_java_views| is a view over |chrome_java_views_internal| table, adding the necessary columns
+-- from |slice|.
+DROP VIEW IF EXISTS chrome_java_views;
+CREATE VIEW chrome_java_views AS
+SELECT
+  s1.name AS filtered_name,
+  s1.is_software_screenshot,
+  s1.is_hardware_screenshot,
+  s2.*
+FROM chrome_java_views_internal s1
+JOIN {{slice_table_name}} s2 USING (id);
+
+DROP VIEW IF EXISTS chrome_choreographer_tasks;
+CREATE VIEW chrome_choreographer_tasks
+AS
+SELECT
+  id,
+  "Choreographer" AS kind,
+  ts,
+  dur,
+  name
+FROM slice
+WHERE
+  name GLOB "Looper.dispatch: android.view.Choreographer$FrameHandler*";
+
+-- Most of java views will be triggered either by Chrome's BeginMainFrame
+-- or by Android's Choreographer.
+DROP VIEW IF EXISTS chrome_slices_with_java_views_internal;
+CREATE VIEW chrome_slices_with_java_views_internal AS
+WITH
+  -- Select UI thread BeginMainFrames and Choreographer frames.
+  root_slices AS (
+    SELECT *
+    FROM SELECT_BEGIN_MAIN_FRAME_JAVA_SLICES('ThreadControllerImpl::RunTask')
+    UNION ALL
+    SELECT * FROM chrome_choreographer_tasks
+  ),
+  -- Intermediate step to allow us to sort java view names.
+  root_slice_and_java_view_not_grouped AS (
+    SELECT
+      s1.id, s1.kind, s3.name AS java_view_name
+    FROM root_slices s1
+    JOIN descendant_slice(s1.id) s2
+    JOIN chrome_java_views_internal s3 ON s2.id = s3.id
+  )
+SELECT
+  s1.id,
+  s1.kind,
+  GROUP_CONCAT(DISTINCT s2.java_view_name) AS java_views
+FROM root_slices s1
+LEFT JOIN root_slice_and_java_view_not_grouped s2 USING (id)
+GROUP BY s1.id;
+
+-- Create |chrome_tasks| table, which contains a subset of slice
+-- table of the slices which should be considered top-level Chrome tasks with the
+-- additional scheduler_type |full_name| column, derived from subevents.
+DROP TABLE IF EXISTS chrome_tasks_internal;
+CREATE TABLE chrome_tasks_internal AS
+WITH
+-- Select slices from "toplevel" category which do not have another
+-- "toplevel" slice as ancestor. The possible cases include sync mojo messages
+-- and tasks in nested runloops. Toplevel events may also be logged as with
+-- the Java category.
+non_embedded_toplevel_slices AS (
+  SELECT * FROM {{slice_table_name}} s
+  WHERE
+    {{function_prefix}}ANY_TOP_LEVEL_CATEGORY(category)
+    AND (SELECT count() FROM ancestor_slice(s.id) s2
+      WHERE s2.category GLOB "*toplevel*" or s2.category GLOB "*toplevel.viz*") = 0
+),
+-- Select slices from "Java" category which do not have another "Java" or
+-- "toplevel" slice as parent. In the longer term they should probably belong
+-- to "toplevel" category as well, but for now this will have to do. Ensure
+-- that "Java" slices do not include "toplevel" slices as those would be
+-- handled elsewhere.
+non_embedded_java_slices AS (
+  SELECT name AS full_name, "java" AS task_type, id
+  FROM {{slice_table_name}} s
+  WHERE
+    {{function_prefix}}JAVA_NOT_TOP_LEVEL_CATEGORY(category)
+    AND (SELECT count()
+      FROM ancestor_slice(s.id) s2
+      WHERE s2.category GLOB "*toplevel*" OR s2.category GLOB "*Java*") = 0
+),
+raw_scheduler_tasks AS (
+  SELECT
+    EXTRACT_ARG(s.arg_set_id, "task.posted_from.file_name") AS posted_from_file_name,
+    EXTRACT_ARG(s.arg_set_id, "task.posted_from.function_name") AS posted_from_function_name,
+    (CASE name
+        WHEN "ThreadControllerImpl::RunTask" THEN "SequenceManager"
+        WHEN "ThreadPool_RunTask" THEN "ThreadPool"
+      END) AS scheduler_type,
+    s.id
+  FROM {{slice_table_name}} s
+  WHERE
+    category GLOB "*toplevel*"
+    AND (name = "ThreadControllerImpl::RunTask" OR name = "ThreadPool_RunTask")
+),
+scheduler_tasks AS (
+  SELECT
+    s1.posted_from_file_name || ":" || s1.posted_from_function_name AS posted_from,
+    s1.posted_from_file_name,
+    s1.posted_from_function_name,
+    s1.scheduler_type,
+    s1.id
+  FROM raw_scheduler_tasks s1
+),
+-- Generate full names for scheduler tasks.
+scheduler_tasks_with_full_names AS (
+  SELECT
+    {{function_prefix}}FORMAT_SCHEDULER_TASK_NAME(s.posted_from) AS full_name,
+    "scheduler" AS task_type,
+    s.id
+  FROM scheduler_tasks s
+),
+-- Generate full names for mojo slices.
+mojo_slices AS (
+  SELECT
+    printf('%s %s (hash=%d)',
+      interface_name, message_type, ipc_hash) AS full_name,
+    "mojo" AS task_type,
+    id
+  FROM chrome_mojo_slices_tbl
+),
+-- Generate full names for tasks with java views.
+java_views_tasks AS (
+  SELECT
+    printf('%s(java_views=%s)', kind, java_views) AS full_name,
+    {{function_prefix}}GET_JAVA_VIEWS_TASK_TYPE(kind) AS task_type,
+    id
+  FROM chrome_slices_with_java_views_internal
+),
+-- Select scheduler tasks which are used to run mojo messages and use the mojo names
+-- as full names for these slices.
+-- We restrict this to specific scheduler tasks which are expected to run mojo
+-- tasks due to sync mojo events, which also emit similar events.
+scheduler_tasks_with_mojo AS (
+  SELECT
+    (SELECT s3.full_name
+      FROM descendant_slice(s1.id) s2
+      JOIN mojo_slices s3 USING (id)
+      ORDER BY s2.depth LIMIT 1) AS full_name,
+    "mojo" AS task_type,
+    s1.id
+  FROM
+    scheduler_tasks s1
+  WHERE
+    s1.posted_from IN (
+      "mojo/public/cpp/system/simple_watcher.cc:Notify",
+      "mojo/public/cpp/bindings/lib/connector.cc:PostDispatchNextMessageFromPipe",
+      "ipc/ipc_mojo_bootstrap.cc:Accept")
+),
+navigation_tasks AS (
+  SELECT
+    printf("%s (%s)",
+      {{function_prefix}}HUMAN_READABLE_NAVIGATION_TASK_NAME(full_name),
+      IFNULL({{function_prefix}}EXTRACT_FRAME_TYPE(id), 'unknown frame type')) AS full_name,
+    'navigation_task' AS task_type,
+    id
+  FROM (
+    SELECT * FROM scheduler_tasks_with_mojo
+    WHERE {{function_prefix}}HUMAN_READABLE_NAVIGATION_TASK_NAME(full_name) IS NOT NULL
+    )
+),
+-- Add scheduler and mojo full names to non-embedded slices from
+-- the "toplevel" category, with mojo ones taking precedence.
+non_embedded_toplevel_slices_with_full_name AS (
+  SELECT
+    COALESCE(s5.full_name, s4.full_name, s2.full_name, s3.full_name, s1.name) AS full_name,
+    COALESCE(s5.task_type, s4.task_type, s2.task_type, s3.task_type, "other") AS task_type,
+    s1.id AS id
+  FROM non_embedded_toplevel_slices s1
+  LEFT JOIN scheduler_tasks_with_mojo s2 ON s2.id = s1.id
+  LEFT JOIN scheduler_tasks_with_full_names s3 ON s3.id = s1.id
+  LEFT JOIN java_views_tasks s4 ON s4.id = s1.id
+  LEFT JOIN navigation_tasks s5 ON s5.id = s1.id
+)
+-- Merge slices from toplevel and Java categories.
+SELECT * FROM non_embedded_toplevel_slices_with_full_name
+UNION ALL
+SELECT * FROM non_embedded_java_slices;
+
+DROP VIEW IF EXISTS chrome_tasks;
+CREATE VIEW chrome_tasks AS
+SELECT
+  full_name,
+  task_type,
+  thread.name AS thread_name,
+  thread.utid,
+  process.name AS process_name,
+  thread.upid,
+  ts.*
+FROM chrome_tasks_internal cti
+JOIN {{slice_table_name}} ts USING (id)
+JOIN thread_track tt ON ts.track_id = tt.id
+JOIN thread USING (utid)
+JOIN process USING (upid);
+
+-- A helper view into Chrome thread slices which don't have a parent task. 
+-- TODO(altimin): Use chrome_thread here once it's reliable.
+DROP VIEW IF EXISTS chrome_slices_without_parent_task;
+CREATE VIEW chrome_slices_without_parent_task AS
+SELECT
+  s1.*
+FROM {{slice_table_name}} s1
+LEFT JOIN chrome_tasks s2 USING (id)
+WHERE
+  (SELECT count()
+          FROM ancestor_slice(s1.id) s3
+          JOIN chrome_tasks s4 ON s3.id = s4.id) = 0
+  AND s2.id IS NULL;
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_thread_slice.sql b/src/trace_processor/metrics/sql/chrome/chrome_thread_slice.sql
index 273d3e7..6ab7201 100644
--- a/src/trace_processor/metrics/sql/chrome/chrome_thread_slice.sql
+++ b/src/trace_processor/metrics/sql/chrome/chrome_thread_slice.sql
@@ -19,20 +19,20 @@
 -- Grab all the thread tracks which are found in chrome threads.
 DROP VIEW IF EXISTS chrome_track;
 CREATE VIEW chrome_track AS
-  SELECT
-    *
-  FROM thread_track
-  WHERE utid IN (SELECT utid FROM chrome_thread);
+SELECT
+  *
+FROM thread_track
+WHERE utid IN (SELECT utid FROM chrome_thread);
 
 -- From all the chrome thread tracks select all the slice details for thread
 -- slices.
 DROP VIEW IF EXISTS chrome_thread_slice;
 CREATE VIEW chrome_thread_slice AS
-  SELECT
-    thread_slice.*
-  FROM
-    thread_slice JOIN
-    chrome_track ON
-        chrome_track.id = thread_slice.track_id
-  WHERE
-    track_id in (SELECT id FROM chrome_track);
\ No newline at end of file
+SELECT
+  slice.*
+FROM
+  slice JOIN
+  chrome_track ON
+    chrome_track.id = slice.track_id
+WHERE
+  track_id IN (SELECT id FROM chrome_track);
diff --git a/src/trace_processor/metrics/sql/chrome/cpu_time_by_category.sql b/src/trace_processor/metrics/sql/chrome/cpu_time_by_category.sql
index 2981ebd..04f7f55 100644
--- a/src/trace_processor/metrics/sql/chrome/cpu_time_by_category.sql
+++ b/src/trace_processor/metrics/sql/chrome/cpu_time_by_category.sql
@@ -21,7 +21,7 @@
 -- category: name of the category column in the input table, which will be
 --   preserved in the output
 
-SELECT RUN_METRIC('chrome/chrome_processes.sql') ;
+SELECT RUN_METRIC('chrome/chrome_processes.sql');
 
 -- CPU time slices for Chrome threads.
 DROP VIEW IF EXISTS chrome_cpu_slices;
@@ -43,7 +43,7 @@
   (
     SELECT thread_counter_track.id
     FROM chrome_thread
-      JOIN thread_counter_track ON chrome_thread.utid = thread_counter_track.utid
+    JOIN thread_counter_track ON chrome_thread.utid = thread_counter_track.utid
   ) AS t
 WHERE t.id = track_id;
 
@@ -66,6 +66,6 @@
   s.{{category}},
   SUM(cpu_dur) AS cpu_dur
 FROM {{input}}_cpu_time r
-  JOIN {{input}} s
+JOIN {{input}} s
 WHERE r.id = s.id
 GROUP BY r.id;
diff --git a/src/trace_processor/metrics/sql/chrome/cpu_time_by_rail_mode.sql b/src/trace_processor/metrics/sql/chrome/cpu_time_by_rail_mode.sql
index 426bd25..5c4357c 100644
--- a/src/trace_processor/metrics/sql/chrome/cpu_time_by_rail_mode.sql
+++ b/src/trace_processor/metrics/sql/chrome/cpu_time_by_rail_mode.sql
@@ -14,7 +14,7 @@
 -- limitations under the License.
 --
 
-SELECT RUN_METRIC('chrome/rail_modes.sql') AS suppress_query_output;
+SELECT RUN_METRIC('chrome/rail_modes.sql');
 
 -- Creates a view cpu_time_by_rail_mode containing the CPU time used (across all
 -- cores) for each RAIL Mode slice in combined_overall_rail_slices.
diff --git a/src/trace_processor/metrics/sql/chrome/estimated_power_by_category.sql b/src/trace_processor/metrics/sql/chrome/estimated_power_by_category.sql
index 506d2a1..68d6796 100644
--- a/src/trace_processor/metrics/sql/chrome/estimated_power_by_category.sql
+++ b/src/trace_processor/metrics/sql/chrome/estimated_power_by_category.sql
@@ -39,7 +39,7 @@
   upid,
   is_main_thread
 FROM power_per_thread
-  JOIN chrome_thread
+JOIN chrome_thread
 WHERE power_per_thread.utid = chrome_thread.utid;
 
 DROP TABLE IF EXISTS {{input}}_power;
@@ -65,7 +65,7 @@
       s.{{category}},
       SUM(r.power_ma * r.dur) / 1e9 AS mas
     FROM {{input}}_power r
-      JOIN {{input}} s
-    WHERE r.id == s.id
+    JOIN {{input}} s
+    WHERE r.id = s.id
     GROUP BY s.id
   );
diff --git a/src/trace_processor/metrics/sql/chrome/event_latency_scroll_jank.sql b/src/trace_processor/metrics/sql/chrome/event_latency_scroll_jank.sql
new file mode 100644
index 0000000..7a45fff
--- /dev/null
+++ b/src/trace_processor/metrics/sql/chrome/event_latency_scroll_jank.sql
@@ -0,0 +1,170 @@
+--
+-- Copyright 2022 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.
+--
+-- A scroll jank metric based on EventLatency slices.
+--
+-- We define an update to be janky if comparing forwards or backwards (ignoring
+-- coalesced and not shown on the screen updates) a given updates exceeds the duration
+-- of its predecessor or successor by 50% of a vsync interval (defaulted to 60 FPS).
+--
+-- WARNING: This metric should not be used as a source of truth. It is under
+--          active development and the values & meaning might change without
+--          notice.
+
+SELECT RUN_METRIC('chrome/jank_utilities.sql');
+SELECT RUN_METRIC('chrome/event_latency_to_breakdowns.sql');
+SELECT RUN_METRIC('chrome/vsync_intervals.sql');
+
+-- Creates table view where each EventLatency event has its upid.
+DROP VIEW IF EXISTS event_latency_with_track;
+CREATE VIEW event_latency_with_track
+AS
+SELECT
+  slice.*,
+  process_track.upid AS upid
+FROM slice JOIN process_track
+  ON slice.track_id = process_track.id
+WHERE slice.name = "EventLatency";
+
+-- Select scroll EventLatency events that were shown on the screen.
+-- An update event was shown on the screen if and only if
+-- it has a "SubmitCompositorFrameToPresentationCompositorFrame" breakdown.
+-- But this logic is not applied for begin events, because a begin event is an artifical marker
+-- and never gets shown to the screen because it doesn't contain any update.
+-- Also it automaticly only includes non-coalesced EventLatency events,
+-- because coalesced ones are not shown on the screen.
+DROP VIEW IF EXISTS filtered_scroll_event_latency;
+CREATE VIEW filtered_scroll_event_latency
+AS
+WITH shown_on_display_event_latency_ids AS (
+  SELECT
+  event_latency_id
+  FROM event_latency_breakdowns
+  WHERE name = "SubmitCompositorFrameToPresentationCompositorFrame" OR event_type = "GESTURE_SCROLL_BEGIN"
+)
+SELECT
+  event_latency_with_track.id,
+  event_latency_with_track.track_id,
+  event_latency_with_track.upid,
+  event_latency_with_track.ts,
+  event_latency_with_track.dur,
+  EXTRACT_ARG(event_latency_with_track.arg_set_id, "event_latency.event_type") AS event_type
+FROM event_latency_with_track JOIN shown_on_display_event_latency_ids
+  ON event_latency_with_track.id = shown_on_display_event_latency_ids.event_latency_id
+WHERE
+  event_type IN (
+    "GESTURE_SCROLL_BEGIN", "GESTURE_SCROLL_UPDATE",
+    "INERTIAL_GESTURE_SCROLL_UPDATE", "FIRST_GESTURE_SCROLL_UPDATE");
+
+-- Select begin events and it's next begin event witin the same process (same upid).
+--
+-- Note: Must be a TABLE because it uses a window function which can behave
+--       strangely in views.
+DROP TABLE IF EXISTS scroll_event_latency_begins;
+CREATE TABLE scroll_event_latency_begins
+AS
+SELECT
+  *,
+  LEAD(ts) OVER sorted_begins AS next_gesture_begin_ts
+FROM filtered_scroll_event_latency
+WHERE event_type = "GESTURE_SCROLL_BEGIN"
+WINDOW sorted_begins AS (PARTITION BY upid ORDER BY ts ASC);
+
+-- For each scroll update event finds it's begin event.
+-- Pair [upid, next_gesture_begin_ts] represent a gesture key.
+-- We need to know the gesture key of gesture scroll to calculate a jank only within this gesture scroll.
+-- Because different gesture scrolls can have different properties.
+DROP VIEW IF EXISTS scroll_event_latency_updates;
+CREATE VIEW scroll_event_latency_updates
+AS
+SELECT
+  filtered_scroll_event_latency.*,
+  scroll_event_latency_begins.ts AS gesture_begin_ts,
+  scroll_event_latency_begins.next_gesture_begin_ts AS next_gesture_begin_ts
+FROM filtered_scroll_event_latency LEFT JOIN scroll_event_latency_begins
+  ON filtered_scroll_event_latency.ts >= scroll_event_latency_begins.ts
+     AND (filtered_scroll_event_latency.ts < next_gesture_begin_ts OR next_gesture_begin_ts IS NULL)
+     AND filtered_scroll_event_latency.upid = scroll_event_latency_begins.upid
+WHERE filtered_scroll_event_latency.id != scroll_event_latency_begins.id
+      AND filtered_scroll_event_latency.event_type != "GESTURE_SCROLL_BEGIN";
+
+-- Find the last EventLatency scroll update event in the scroll.
+-- We will use the last EventLatency event insted of "InputLatency::GestureScrollEnd" event.
+-- We need to know when the scroll gesture ends so that we can later calculate
+-- the average vsync interval just up to the end of the gesture.
+DROP VIEW IF EXISTS scroll_event_latency_updates_ends;
+CREATE VIEW scroll_event_latency_updates_ends
+AS
+SELECT
+  id,
+  upid,
+  gesture_begin_ts,
+  ts,
+  dur,
+  MAX(ts + dur) AS gesture_end_ts
+FROM scroll_event_latency_updates
+GROUP BY upid, gesture_begin_ts;
+
+DROP VIEW IF EXISTS scroll_event_latency_updates_with_ends;
+CREATE VIEW scroll_event_latency_updates_with_ends
+AS
+SELECT
+  scroll_event_latency_updates.*,
+  scroll_event_latency_updates_ends.gesture_end_ts AS gesture_end_ts
+FROM scroll_event_latency_updates LEFT JOIN scroll_event_latency_updates_ends
+  ON scroll_event_latency_updates.upid = scroll_event_latency_updates_ends.upid
+    AND scroll_event_latency_updates.gesture_begin_ts = scroll_event_latency_updates_ends.gesture_begin_ts;
+
+-- Creates table where each event contains info about it's previous and next events.
+-- We consider only previous and next events from the same scroll id
+-- to don't calculate a jank between different scrolls.
+--
+-- Note: Must be a TABLE because it uses a window function which can behave
+--       strangely in views.
+DROP TABLE IF EXISTS scroll_event_latency_with_neighbours;
+CREATE TABLE scroll_event_latency_with_neighbours
+AS
+SELECT
+  *,
+  LEAD(id) OVER sorted_events AS next_id,
+  LEAD(ts) OVER sorted_events AS next_ts,
+  LEAD(dur) OVER sorted_events AS next_dur,
+  LAG(id) OVER sorted_events AS prev_id,
+  LAG(ts) OVER sorted_events AS prev_ts,
+  LAG(dur) OVER sorted_events AS prev_dur,
+  CalculateAvgVsyncInterval(gesture_begin_ts, gesture_end_ts) AS avg_vsync_interval
+FROM scroll_event_latency_updates_with_ends
+WINDOW sorted_events AS (PARTITION BY upid, next_gesture_begin_ts ORDER BY id ASC, ts ASC);
+
+DROP VIEW IF EXISTS scroll_event_latency_neighbors_jank;
+CREATE VIEW scroll_event_latency_neighbors_jank
+AS
+SELECT
+  IsJankyFrame(gesture_begin_ts, gesture_begin_ts, next_ts,
+    gesture_begin_ts, gesture_end_ts, dur / avg_vsync_interval, next_dur / avg_vsync_interval) AS next_jank,
+  IsJankyFrame(gesture_begin_ts, gesture_begin_ts, prev_ts,
+    gesture_begin_ts, gesture_end_ts, dur / avg_vsync_interval, prev_dur / avg_vsync_interval) AS prev_jank,
+  scroll_event_latency_with_neighbours.*
+FROM scroll_event_latency_with_neighbours;
+
+-- Creates a view where each event contains information about whether it is janky
+-- with respect to previous and next events within the same scroll.
+DROP VIEW IF EXISTS scroll_event_latency_jank;
+CREATE VIEW scroll_event_latency_jank
+AS
+SELECT
+  (next_jank IS NOT NULL AND next_jank) OR (prev_jank IS NOT NULL AND prev_jank) AS jank,
+  *
+FROM scroll_event_latency_neighbors_jank;
diff --git a/src/trace_processor/metrics/sql/chrome/event_latency_scroll_jank_cause.sql b/src/trace_processor/metrics/sql/chrome/event_latency_scroll_jank_cause.sql
new file mode 100644
index 0000000..38187d4
--- /dev/null
+++ b/src/trace_processor/metrics/sql/chrome/event_latency_scroll_jank_cause.sql
@@ -0,0 +1,176 @@
+--
+-- Copyright 2022 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.
+--
+-- Calculating how often each breakdown causes a scroll jank.
+-- We define that a breakdown causes a scroll jank in the janky EventLatency event if it increased
+-- more than other breakdowns relatevly to its neigbour EventLatency events.
+--
+-- WARNING: This metric should not be used as a source of truth. It is under
+--          active development and the values & meaning might change without
+--          notice.
+
+
+SELECT RUN_METRIC('chrome/event_latency_scroll_jank.sql');
+
+-- Calculating the jank delta for EventLatency events which are janky relatevly to its next EventLatency event.
+-- For breakdowns that exist in the current EventLatency but not the next EventLatency
+-- we use a default of 0 so that the full duration is considered when looking for the maximum increase.
+-- Breakdowns that exist in the next EventLatency event, but not in the current EventLatency event,
+-- are ignored because they do not cause a jank anyway.
+DROP VIEW IF EXISTS event_latency_scroll_breakdowns_next_jank_deltas;
+CREATE VIEW event_latency_scroll_breakdowns_next_jank_deltas
+AS
+SELECT
+    cur_breakdowns.*,
+    next_breakdowns.event_type as next_event_type,
+    next_breakdowns.slice_id as next_breakdown_id,
+    next_breakdowns.dur as next_dur,
+    cur_breakdowns.dur - COALESCE(next_breakdowns.dur, 0) as delta_dur_ns
+FROM event_latency_scroll_breakdowns_jank as cur_breakdowns LEFT JOIN event_latency_scroll_breakdowns_jank as next_breakdowns
+ON cur_breakdowns.next_event_latency_id = next_breakdowns.event_latency_id AND
+    cur_breakdowns.name = next_breakdowns.name
+WHERE cur_breakdowns.next_jank = 1;
+
+-- Calculating the jank delta for EventLatency events which are janky relatevly to its prev EventLatency event.
+-- For breakdowns that exist in the current EventLatency but not the prev EventLatency
+-- we use a default of 0 so that the full duration is considered when looking for the maximum increase.
+-- Breakdowns that exist in the prev EventLatency event, but not in the current EventLatency event,
+-- are ignored because they do not cause a jank anyway.
+DROP VIEW IF EXISTS event_latency_scroll_breakdowns_prev_jank_deltas;
+CREATE VIEW event_latency_scroll_breakdowns_prev_jank_deltas
+AS
+SELECT
+    cur_breakdowns.*,
+    prev_breakdowns.event_type as prev_event_type,
+    prev_breakdowns.slice_id as prev_breakdown_id,
+    prev_breakdowns.dur as prev_dur,
+    cur_breakdowns.dur - COALESCE(prev_breakdowns.dur, 0) as delta_dur_ns
+FROM event_latency_scroll_breakdowns_jank as cur_breakdowns LEFT JOIN event_latency_scroll_breakdowns_jank as prev_breakdowns
+ON cur_breakdowns.prev_event_latency_id = prev_breakdowns.event_latency_id AND
+    cur_breakdowns.name = prev_breakdowns.name
+WHERE cur_breakdowns.prev_jank = 1;
+
+-- Add a jank indicator to each breakdown. Jank indicator is related to an entire EventLatency envent, not only to a breakdown.
+DROP VIEW IF EXISTS event_latency_scroll_breakdowns_jank;
+CREATE VIEW event_latency_scroll_breakdowns_jank
+AS
+SELECT
+  event_latency_breakdowns.*,
+  scroll_event_latency_jank.jank,
+  scroll_event_latency_jank.next_jank,
+  scroll_event_latency_jank.prev_jank,
+  scroll_event_latency_jank.next_id as next_event_latency_id,
+  scroll_event_latency_jank.prev_id as prev_event_latency_id
+FROM event_latency_breakdowns JOIN scroll_event_latency_jank
+ON event_latency_breakdowns.event_latency_id = scroll_event_latency_jank.id
+WHERE event_latency_breakdowns.event_type in ("GESTURE_SCROLL_UPDATE", "FIRST_GESTURE_SCROLL_UPDATE", "INERTIAL_GESTURE_SCROLL_UPDATE");
+
+-- Merge breakdowns from the |event_latency_scroll_breakdowns_next_jank_deltas|
+-- and |event_latency_scroll_breakdowns_prev_jank_deltas| tables and select the maximum |delta_dur_ns| of them.
+-- This is necessary in order to get a single reason for the jank for the event later.
+DROP VIEW IF EXISTS event_latency_scroll_breakdowns_max_jank_deltas;
+CREATE VIEW event_latency_scroll_breakdowns_max_jank_deltas
+AS
+SELECT
+  COALESCE(next.slice_id, prev.slice_id) as slice_id,
+  COALESCE(next.name, prev.name) as name,
+  COALESCE(next.event_latency_id, prev.event_latency_id) as event_latency_id,
+  COALESCE(next.event_latency_track_id, prev.event_latency_track_id) as track_id,
+  COALESCE(next.event_latency_dur, prev.event_latency_dur) as event_latency_dur,
+  COALESCE(next.event_latency_ts, prev.event_latency_ts) as event_latency_ts,
+  COALESCE(next.event_type, prev.event_type) as event_type,
+  COALESCE(next.event_latency_ts, prev.event_latency_ts) as ts,
+  COALESCE(next.event_latency_dur, prev.event_latency_dur) as dur,
+  COALESCE(next.jank, prev.jank) as jank,
+  COALESCE(next.next_jank, 0) as next_jank,
+  COALESCE(prev.prev_jank, 0) as prev_jank,
+  next.next_event_latency_id as next_event_latency_id,
+  prev.prev_event_latency_id as prev_event_latency_id,
+  next.delta_dur_ns as next_delta_dur_ns,
+  prev.delta_dur_ns as prev_delta_dur_ns,
+  CASE
+    WHEN prev.delta_dur_ns IS NULL OR next.delta_dur_ns >  prev.delta_dur_ns
+      THEN next.delta_dur_ns
+    ELSE prev.delta_dur_ns
+  END as delta_dur_ns,
+  CASE
+    WHEN prev.delta_dur_ns IS NULL OR next.delta_dur_ns >  prev.delta_dur_ns
+      THEN next.next_breakdown_id
+    ELSE prev.prev_breakdown_id
+  END as max_jank_neigbour_breakdown_id
+FROM event_latency_scroll_breakdowns_next_jank_deltas as next
+FULL JOIN event_latency_scroll_breakdowns_prev_jank_deltas as prev
+ON next.slice_id = prev.slice_id;
+
+-- Selecting breakdowns which have a maximum ns duration delta as a main causes of a jank for this EventLatency event.
+DROP VIEW IF EXISTS event_latency_scroll_jank_cause_top_level;
+CREATE VIEW event_latency_scroll_jank_cause_top_level
+AS
+SELECT
+  event_latency_id as slice_id,
+  track_id,
+  event_latency_dur as dur,
+  event_latency_ts as ts,
+  event_type,
+  next_jank,
+  prev_jank,
+  next_event_latency_id,
+  prev_event_latency_id,
+  next_delta_dur_ns,
+  prev_delta_dur_ns,
+  name as cause_of_jank,
+  slice_id as max_jank_breakdown_id,
+  max_jank_neigbour_breakdown_id,
+  MAX(delta_dur_ns) as max_delta_dur_ns
+FROM event_latency_scroll_breakdowns_max_jank_deltas
+GROUP BY event_latency_id;
+
+-- Selecting sub-breakdowns of the main causes of a jank which have a maximum ns duration delta with a neighbour event's breakdowns.
+DROP VIEW IF EXISTS event_latency_scroll_sub_breakdowns_max_deltas;
+CREATE VIEW event_latency_scroll_sub_breakdowns_max_deltas
+AS
+SELECT
+  cur_event_latency.slice_id as event_latency_id,
+  cur_sub_breakdowns.name as sub_breakdown_name,
+  MAX(cur_sub_breakdowns.dur - neighbour_sub_breakdowns.dur) as max_sub_breakdown_delta_dur_ns
+FROM event_latency_scroll_jank_cause_top_level as cur_event_latency
+LEFT JOIN slices as cur_sub_breakdowns
+  ON cur_event_latency.max_jank_breakdown_id = cur_sub_breakdowns.parent_id
+LEFT JOIN slices as neighbour_sub_breakdowns
+  ON cur_event_latency.max_jank_neigbour_breakdown_id = neighbour_sub_breakdowns.parent_id AND
+   cur_sub_breakdowns.name = neighbour_sub_breakdowns.name
+GROUP BY cur_event_latency.slice_id, cur_event_latency.max_jank_breakdown_id;
+
+-- Selecting the main cause of jank and its sub-cause of jank.
+DROP VIEW IF EXISTS event_latency_scroll_jank_cause;
+CREATE VIEW event_latency_scroll_jank_cause
+AS
+SELECT
+  event_latency_scroll_jank_cause_top_level.*,
+  max_sub_breakdowns.sub_breakdown_name as sub_cause_of_jank
+FROM event_latency_scroll_jank_cause_top_level
+LEFT JOIN event_latency_scroll_sub_breakdowns_max_deltas as max_sub_breakdowns
+ON event_latency_scroll_jank_cause_top_level.slice_id = max_sub_breakdowns.event_latency_id;
+
+-- Calculate how often each breakdown is a main cause of a jank for EventLatency events.
+DROP VIEW IF EXISTS event_latency_scroll_jank_cause_cnt;
+CREATE VIEW event_latency_scroll_jank_cause_cnt
+AS
+SELECT
+  cause_of_jank,
+  sub_cause_of_jank,
+  COUNT(*) as cnt
+FROM event_latency_scroll_jank_cause
+GROUP BY cause_of_jank, sub_cause_of_jank;
diff --git a/src/trace_processor/metrics/sql/chrome/event_latency_to_breakdowns.sql b/src/trace_processor/metrics/sql/chrome/event_latency_to_breakdowns.sql
new file mode 100644
index 0000000..da02110
--- /dev/null
+++ b/src/trace_processor/metrics/sql/chrome/event_latency_to_breakdowns.sql
@@ -0,0 +1,140 @@
+--
+-- Copyright 2022 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.
+--
+-- Creates metric with info about breakdowns and jank for GestureScrollBegin and GestureScrollUpdate.
+
+-- Select EventLatency events.
+DROP VIEW IF EXISTS event_latency;
+CREATE VIEW event_latency
+AS
+SELECT
+  *,
+  EXTRACT_ARG(arg_set_id, "event_latency.event_type") AS event_type
+FROM slice
+WHERE
+  name = "EventLatency";
+
+-- Select breakdowns related to EventLatencies from `event_latency` table.
+DROP VIEW IF EXISTS event_latency_breakdowns;
+CREATE VIEW event_latency_breakdowns
+AS
+SELECT
+  slice.id AS slice_id,
+  slice.name AS name,
+  slice.dur AS dur,
+  slice.track_id AS track_id,
+  slice.ts AS ts,
+  event_latency.slice_id AS event_latency_id,
+  event_latency.track_id AS event_latency_track_id,
+  event_latency.ts AS event_latency_ts,
+  event_latency.dur AS event_latency_dur,
+  event_latency.event_type AS event_type
+FROM slice JOIN event_latency
+  ON slice.parent_id = event_latency.slice_id;
+
+-- The function takes a breakdown name and checks if the breakdown name is known or not.
+SELECT CREATE_FUNCTION(
+  'InvalidNameOrNull(name STRING)',
+  -- Returns the input breakdown name if it's an unknown breakdown, NULL otherwise.
+  'STRING',
+  'SELECT
+    CASE
+      WHEN
+      $name not in (
+        "GenerationToBrowserMain", "GenerationToRendererCompositor",
+        "BrowserMainToRendererCompositor", "RendererCompositorQueueingDelay",
+        "RendererCompositorToMain", "RendererCompositorProcessing",
+        "RendererMainProcessing", "EndActivateToSubmitCompositorFrame",
+        "SubmitCompositorFrameToPresentationCompositorFrame",
+        "ArrivedInRendererCompositorToTermination",
+        "RendererCompositorStartedToTermination",
+        "RendererMainFinishedToTermination",
+        "RendererCompositorFinishedToTermination",
+        "RendererMainStartedToTermination",
+        "RendererCompositorFinishedToBeginImplFrame",
+        "RendererCompositorFinishedToCommit",
+        "RendererCompositorFinishedToEndCommit",
+        "RendererCompositorFinishedToActivation",
+        "RendererCompositorFinishedToEndActivate", 
+        "RendererCompositorFinishedToSubmitCompositorFrame",
+        "RendererMainFinishedToBeginImplFrame",
+        "RendererMainFinishedToSendBeginMainFrame",
+        "RendererMainFinishedToCommit", "RendererMainFinishedToEndCommit",
+        "RendererMainFinishedToActivation", "RendererMainFinishedToEndActivate",
+        "RendererMainFinishedToSubmitCompositorFrame",
+        "BeginImplFrameToSendBeginMainFrame",
+        "RendererCompositorFinishedToSendBeginMainFrame",
+        "SendBeginMainFrameToCommit", "Commit",
+        "EndCommitToActivation", "Activation")
+        THEN $name
+      ELSE NULL
+    END'
+);
+
+-- Creates a view where each row contains information about one EventLatency event. Columns are duration of breakdowns.
+-- In the result it will be something like this:
+-- | event_latency_id | event_latency_ts | event_latency_dur | event_type       | GenerationToBrowserMainNs | BrowserMainToRendererCompositorNs |...|
+-- |------------------|------------------|-------------------|------------------|----------------------------|------------------------------------|---|
+-- | 123              | 1661947470       | 20                | 1234567          | 30                         | 50                                 |   |
+DROP VIEW IF EXISTS event_latency_to_breakdowns;
+CREATE VIEW event_latency_to_breakdowns
+AS
+SELECT
+  event_latency_id,
+  event_latency_track_id,
+  event_latency_ts,
+  event_latency_dur,
+  event_type,
+  max(CASE WHEN name = "GenerationToRendererCompositor" THEN dur END) AS GenerationToRendererCompositorNs,
+  max(CASE WHEN name = "GenerationToBrowserMain" THEN dur END) AS GenerationToBrowserMainNs,
+  max(CASE WHEN name = "BrowserMainToRendererCompositor" THEN dur END) AS BrowserMainToRendererCompositorNs,
+  max(CASE WHEN name = "RendererCompositorQueueingDelay" THEN dur END) AS RendererCompositorQueueingDelayNs,
+  max(CASE WHEN name = "RendererCompositorProcessing" THEN dur END) AS RendererCompositorProcessingNs,
+  max(CASE WHEN name = "RendererCompositorToMain" THEN dur END) AS RendererCompositorToMainNs,
+  max(CASE WHEN name = "RendererMainProcessing" THEN dur END) AS RendererMainProcessingNs,
+
+  max(CASE WHEN name = "ArrivedInRendererCompositorToTermination" THEN dur END) AS ArrivedInRendererCompositorToTerminationNs,
+  max(CASE WHEN name = "RendererCompositorStartedToTermination" THEN dur END) AS RendererCompositorStartedToTerminationNs,
+  max(CASE WHEN name = "RendererCompositorFinishedToTermination" THEN dur END) AS RendererCompositorFinishedToTerminationNs,
+  max(CASE WHEN name = "RendererMainStartedToTermination" THEN dur END) AS RendererMainStartedToTerminationNs,
+  max(CASE WHEN name = "RendererMainFinishedToTermination" THEN dur END) AS RendererMainFinishedToTerminationNs,
+
+  max(CASE WHEN name = "BeginImplFrameToSendBeginMainFrame" THEN dur END) AS BeginImplFrameToSendBeginMainFrameNs,
+  max(CASE WHEN name = "RendererCompositorFinishedToSendBeginMainFrame" THEN dur END) AS RendererCompositorFinishedToSendBeginMainFrameNs,
+  max(CASE WHEN name = "RendererCompositorFinishedToBeginImplFrame" THEN dur END) AS RendererCompositorFinishedToBeginImplFrameNs,
+  max(CASE WHEN name = "RendererCompositorFinishedToCommit" THEN dur END) AS RendererCompositorFinishedToCommitNs,
+  max(CASE WHEN name = "RendererCompositorFinishedToEndCommit" THEN dur END) AS RendererCompositorFinishedToEndCommitNs,
+  max(CASE WHEN name = "RendererCompositorFinishedToActivation" THEN dur END) AS RendererCompositorFinishedToActivationNs,
+  max(CASE WHEN name = "RendererCompositorFinishedToEndActivate" THEN dur END) AS RendererCompositorFinishedToEndActivateNs,
+  max(CASE WHEN name = "RendererCompositorFinishedToSubmitCompositorFrame" THEN dur END) AS RendererCompositorFinishedToSubmitCompositorFrameNs,
+  max(CASE WHEN name = "RendererMainFinishedToBeginImplFrame" THEN dur END) AS RendererMainFinishedToBeginImplFrameNs,
+  max(CASE WHEN name = "RendererMainFinishedToSendBeginMainFrame" THEN dur END) AS RendererMainFinishedToSendBeginMainFrameNs,
+  max(CASE WHEN name = "RendererMainFinishedToCommit" THEN dur END) AS RendererMainFinishedToCommitNs,
+  max(CASE WHEN name = "RendererMainFinishedToEndCommit" THEN dur END) AS RendererMainFinishedToEndCommitNs,
+  max(CASE WHEN name = "RendererMainFinishedToActivation" THEN dur END) AS RendererMainFinishedToActivationNs,
+  max(CASE WHEN name = "RendererMainFinishedToEndActivate" THEN dur END) AS RendererMainFinishedToEndActivateNs,
+  max(CASE WHEN name = "RendererMainFinishedToSubmitCompositorFrame" THEN dur END) AS RendererMainFinishedToSubmitCompositorFrameNs,
+
+  max(CASE WHEN name = "EndActivateToSubmitCompositorFrame" THEN dur END) AS EndActivateToSubmitCompositorFrameNs,
+  max(CASE WHEN name = "SubmitCompositorFrameToPresentationCompositorFrame" THEN dur END) AS SubmitCompositorFrameToPresentationCompositorFrameNs,
+  max(CASE WHEN name = "SendBeginMainFrameToCommit" THEN dur END) AS SendBeginMainFrameToCommitNs,
+  max(CASE WHEN name = "Commit" THEN dur END) AS CommitNs,
+  max(CASE WHEN name = "EndCommitToActivation" THEN dur END) AS EndCommitToActivationNs,
+  max(CASE WHEN name = "Activation" THEN dur END) AS ActivationNs,
+  -- This column indicates whether there are unknown breakdowns.
+  -- Contains: NULL if there are no unknown breakdowns, otherwise a list of unknown breakdows.
+  group_concat(InvalidNameOrNull(name), ', ') AS unknown_stages_seen
+FROM event_latency_breakdowns
+GROUP BY event_latency_id;
diff --git a/src/trace_processor/metrics/sql/chrome/experimental_reliable_chrome_tasks_delaying_input_processing.sql b/src/trace_processor/metrics/sql/chrome/experimental_reliable_chrome_tasks_delaying_input_processing.sql
new file mode 100644
index 0000000..8cb139e
--- /dev/null
+++ b/src/trace_processor/metrics/sql/chrome/experimental_reliable_chrome_tasks_delaying_input_processing.sql
@@ -0,0 +1,34 @@
+--
+-- Copyright 2022 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.
+
+-- Script params:
+-- {{duration_causing_jank_ms}} : The duration of a single task that would cause
+-- jank, by delaying input from being handled on the main thread.
+
+SELECT RUN_METRIC('chrome/chrome_reliable_range.sql') AS suppress_query_output;
+
+DROP VIEW IF EXISTS chrome_reliable_slice;
+
+CREATE VIEW chrome_reliable_slice AS
+SELECT *
+FROM slice
+WHERE ts + dur >= (SELECT start FROM chrome_reliable_range);
+
+SELECT RUN_METRIC(
+  'chrome/chrome_tasks_delaying_input_processing_template.sql',
+  'duration_causing_jank_ms', '{{duration_causing_jank_ms}}',
+  'slice_table_name', 'chrome_reliable_slice',
+  'function_prefix', 'EXPERIMENTAL_RELIABLE_'
+);
diff --git a/src/trace_processor/metrics/sql/chrome/gesture_flow_event.sql b/src/trace_processor/metrics/sql/chrome/gesture_flow_event.sql
index 69b824d..516f31c 100644
--- a/src/trace_processor/metrics/sql/chrome/gesture_flow_event.sql
+++ b/src/trace_processor/metrics/sql/chrome/gesture_flow_event.sql
@@ -34,9 +34,9 @@
 -- for this table.
 DROP VIEW IF EXISTS {{prefix}}_latency_info_flow_step_and_ancestors;
 CREATE VIEW {{prefix}}_latency_info_flow_step_and_ancestors AS
-  SELECT
-    *
-  FROM (
+SELECT
+  *
+FROM (
     SELECT
       slice.name,
       slice.id,
@@ -56,67 +56,67 @@
     FROM
       slice LEFT JOIN
       ancestor_slice(slice.id) AS ancestor_zero
-          ON ancestor_zero.depth = 0 LEFT JOIN
+      ON ancestor_zero.depth = 0 LEFT JOIN
       ancestor_slice(slice.id) AS ancestor_one ON ancestor_one.depth = 1
     WHERE
-      slice.name = 'LatencyInfo.Flow' AND
-      EXTRACT_ARG(slice.arg_set_id, 'chrome_latency_info.trace_id') != -1
+      slice.name = 'LatencyInfo.Flow'
+      AND EXTRACT_ARG(slice.arg_set_id, 'chrome_latency_info.trace_id') != -1
   ) flow JOIN (
-      SELECT
-        id AS gesture_slice_id,
-        ts AS gesture_ts,
-        dur AS {{prefix}}_dur,
-        track_id AS gesture_track_id,
-        trace_id AS {{prefix}}_trace_id,
-        jank,
-        {{id_field}},
-        avg_vsync_interval
-      FROM {{prefix}}_jank
+    SELECT
+      id AS gesture_slice_id,
+      ts AS gesture_ts,
+      dur AS {{prefix}}_dur,
+      track_id AS gesture_track_id,
+      trace_id AS {{prefix}}_trace_id,
+      jank,
+      {{id_field}},
+      avg_vsync_interval
+    FROM {{prefix}}_jank
   ) gesture ON
     flow.trace_id = gesture.{{prefix}}_trace_id
-  UNION ALL
-  SELECT
-    'InputLatency::{{gesture_update}}' AS name,
-    id,
-    ts,
-    dur,
-    track_id,
-    trace_id,
-    'AsyncBegin' AS step,
-    'InputLatency::{{gesture_update}}' AS ancestor_name_zero,
-    id AS ancestor_id_zero,
-    ts AS ancestor_ts_zero,
-    0 AS ancestor_dur_zero,
-    'InputLatency::{{gesture_update}}' AS ancestor_name_one,
-    id AS ancestor_id_one,
-    ts AS ancestor_ts_one,
-    0 AS ancestor_dur_one,
-    id AS gesture_slice_id,
-    ts AS gesture_ts,
-    dur AS {{prefix}}_dur,
-    track_id AS gesture_track_id,
-    trace_id AS {{prefix}}_trace_id,
-    jank,
-    {{id_field}},
-    avg_vsync_interval
-  FROM {{prefix}}_jank
-  ORDER BY {{id_field}} ASC, trace_id ASC, ts ASC;
+UNION ALL
+SELECT
+  'InputLatency::{{gesture_update}}' AS name,
+  id,
+  ts,
+  dur,
+  track_id,
+  trace_id,
+  'AsyncBegin' AS step,
+  'InputLatency::{{gesture_update}}' AS ancestor_name_zero,
+  id AS ancestor_id_zero,
+  ts AS ancestor_ts_zero,
+  0 AS ancestor_dur_zero,
+  'InputLatency::{{gesture_update}}' AS ancestor_name_one,
+  id AS ancestor_id_one,
+  ts AS ancestor_ts_one,
+  0 AS ancestor_dur_one,
+  id AS gesture_slice_id,
+  ts AS gesture_ts,
+  dur AS {{prefix}}_dur,
+  track_id AS gesture_track_id,
+  trace_id AS {{prefix}}_trace_id,
+  jank,
+  {{id_field}},
+  avg_vsync_interval
+FROM {{prefix}}_jank
+ORDER BY {{id_field}} ASC, trace_id ASC, ts ASC;
 
 -- See b/184134310, but "ThreadController active" spans multiple tasks and when
 -- the top level parent is this event we should use the second event instead.
 DROP VIEW IF EXISTS {{prefix}}_latency_info_flow_step;
 CREATE VIEW {{prefix}}_latency_info_flow_step AS
-  SELECT
-    *,
-    CASE WHEN ancestor_name_zero != "ThreadController active" THEN
+SELECT
+  *,
+  CASE WHEN ancestor_name_zero != "ThreadController active" THEN
       ancestor_name_zero ELSE ancestor_name_one END AS ancestor_name,
-    CASE WHEN ancestor_name_zero != "ThreadController active" THEN
+  CASE WHEN ancestor_name_zero != "ThreadController active" THEN
       ancestor_id_zero ELSE ancestor_id_one END AS ancestor_id,
-    CASE WHEN ancestor_name_zero != "ThreadController active" THEN
+  CASE WHEN ancestor_name_zero != "ThreadController active" THEN
       ancestor_ts_zero ELSE ancestor_ts_one END AS ancestor_ts,
-    CASE WHEN ancestor_name_zero != "ThreadController active" THEN
+  CASE WHEN ancestor_name_zero != "ThreadController active" THEN
       ancestor_dur_zero ELSE ancestor_dur_one END AS ancestor_dur
-  FROM {{prefix}}_latency_info_flow_step_and_ancestors;
+FROM {{prefix}}_latency_info_flow_step_and_ancestors;
 
 -- This is a heuristic to figure out which flow event properly joins this
 -- {{gesture_update}}. This heuristic is only needed in traces before we added
@@ -135,14 +135,14 @@
 -- both browsers but this is hopefully unlikely.
 DROP VIEW IF EXISTS {{prefix}}_max_latency_info_ts_per_trace_id;
 CREATE VIEW {{prefix}}_max_latency_info_ts_per_trace_id AS
-  SELECT
-    gesture_slice_id,
-    MIN(ts) AS max_flow_ts
-  FROM {{prefix}}_latency_info_flow_step
-  WHERE
-    trace_id = {{prefix}}_trace_id AND
-    ts > gesture_ts + {{prefix}}_dur
-  GROUP BY gesture_slice_id;
+SELECT
+  gesture_slice_id,
+  MIN(ts) AS max_flow_ts
+FROM {{prefix}}_latency_info_flow_step
+WHERE
+  trace_id = {{prefix}}_trace_id
+  AND ts > gesture_ts + {{prefix}}_dur
+GROUP BY gesture_slice_id;
 
 -- As described by the comments about this uses the heuristic to remove any flow
 -- events that aren't contained within the |max_flow_ts| and the beginning of
@@ -153,18 +153,18 @@
 --       strangely in views.
 DROP TABLE IF EXISTS {{prefix}}_latency_info_flow_step_filtered;
 CREATE TABLE {{prefix}}_latency_info_flow_step_filtered AS
-  SELECT
-    ROW_NUMBER() OVER (ORDER BY
+SELECT
+  ROW_NUMBER() OVER (ORDER BY
       flow.{{id_field}} ASC, trace_id ASC, ts ASC) AS row_number,
-    *
-  FROM
-    {{prefix}}_latency_info_flow_step flow JOIN
-    {{prefix}}_max_latency_info_ts_per_trace_id max_flow on
+  *
+FROM
+  {{prefix}}_latency_info_flow_step flow JOIN
+  {{prefix}}_max_latency_info_ts_per_trace_id max_flow ON
     max_flow.gesture_slice_id = flow.gesture_slice_id
-  WHERE
-    ts >= gesture_ts AND
-    ts <= max_flow_ts
-  ORDER BY flow.{{id_field}} ASC, flow.trace_id ASC, flow.ts ASC;
+WHERE
+  ts >= gesture_ts
+  AND ts <= max_flow_ts
+ORDER BY flow.{{id_field}} ASC, flow.trace_id ASC, flow.ts ASC;
 
 -- Take all the LatencyInfo.Flow events and within a |trace_id| join it with the
 -- previous and nextflows. Some events are 'Unknown' when they don't have a step
@@ -175,8 +175,8 @@
 --       strangely in views.
 DROP TABLE IF EXISTS {{prefix}}_latency_info_flow_null_step_removed;
 CREATE TABLE {{prefix}}_latency_info_flow_null_step_removed AS
-  SELECT
-    ROW_NUMBER() OVER (ORDER BY
+SELECT
+  ROW_NUMBER() OVER (ORDER BY
       curr.{{id_field}} ASC, curr.trace_id ASC, curr.ts ASC
     ) AS row_number,
     curr.id,
@@ -195,24 +195,25 @@
     curr.ancestor_ts,
     curr.ancestor_dur,
     curr.ancestor_ts + curr.ancestor_dur AS ancestor_end,
-    CASE WHEN curr.step IS NULL THEN
+    COALESCE(
+      curr.step,
       CASE WHEN
-          prev.{{id_field}} != curr.{{id_field}} OR
-          prev.trace_id != curr.trace_id OR
-          prev.trace_id IS NULL OR
-          prev.step = 'AsyncBegin' THEN
+          prev.{{id_field}} != curr.{{id_field}}
+          OR prev.trace_id != curr.trace_id
+          OR prev.trace_id IS NULL
+          OR prev.step = 'AsyncBegin' THEN
         'Begin'
       ELSE
         CASE WHEN
-            next.{{id_field}} != curr.{{id_field}} OR
-            next.trace_id != curr.trace_id OR
-            next.trace_id IS NULL THEN
+          next.{{id_field}} != curr.{{id_field}}
+          OR next.trace_id != curr.trace_id
+          OR next.trace_id IS NULL THEN
           'End'
-        ELSE
-         'Unknown'
+          ELSE
+            'Unknown'
         END
       END
-    ELSE curr.step END AS step
+    ) AS step
   FROM
     {{prefix}}_latency_info_flow_step_filtered curr LEFT JOIN
     {{prefix}}_latency_info_flow_step_filtered prev ON
@@ -226,37 +227,37 @@
 -- step and the beginning of the next step.
 DROP VIEW IF EXISTS {{prefix}}_flow_event;
 CREATE VIEW {{prefix}}_flow_event AS
-  SELECT
-    curr.trace_id,
-    curr.id,
-    curr.ts,
-    curr.dur,
-    curr.track_id,
-    curr.{{id_field}},
-    curr.avg_vsync_interval,
-    curr.gesture_slice_id AS {{prefix}}_slice_id,
-    curr.gesture_ts AS {{prefix}}_ts,
-    curr.{{prefix}}_dur AS {{prefix}}_dur,
-    curr.gesture_track_id AS {{prefix}}_track_id,
-    curr.jank,
-    curr.step,
-    curr.ancestor_id,
-    curr.ancestor_ts,
-    curr.ancestor_dur,
-    curr.ancestor_end,
-    next.id as next_id,
-    next.ts AS next_ts,
-    next.dur AS next_dur,
-    next.track_id AS next_track_id,
-    next.trace_id AS next_trace_id,
-    next.step AS next_step,
-    CASE WHEN next.trace_id = curr.trace_id THEN
+SELECT
+  curr.trace_id,
+  curr.id,
+  curr.ts,
+  curr.dur,
+  curr.track_id,
+  curr.{{id_field}},
+  curr.avg_vsync_interval,
+  curr.gesture_slice_id AS {{prefix}}_slice_id,
+  curr.gesture_ts AS {{prefix}}_ts,
+  curr.{{prefix}}_dur AS {{prefix}}_dur,
+  curr.gesture_track_id AS {{prefix}}_track_id,
+  curr.jank,
+  curr.step,
+  curr.ancestor_id,
+  curr.ancestor_ts,
+  curr.ancestor_dur,
+  curr.ancestor_end,
+  next.id AS next_id,
+  next.ts AS next_ts,
+  next.dur AS next_dur,
+  next.track_id AS next_track_id,
+  next.trace_id AS next_trace_id,
+  next.step AS next_step,
+  CASE WHEN next.trace_id = curr.trace_id THEN
       next.ancestor_ts
     ELSE
       NULL
-    END AS maybe_next_ancestor_ts
-  FROM
-    {{prefix}}_latency_info_flow_null_step_removed curr LEFT JOIN
-    {{prefix}}_latency_info_flow_null_step_removed next ON
+  END AS maybe_next_ancestor_ts
+FROM
+  {{prefix}}_latency_info_flow_null_step_removed curr LEFT JOIN
+  {{prefix}}_latency_info_flow_null_step_removed next ON
     curr.row_number + 1 = next.row_number
-  ORDER BY curr.{{id_field}}, curr.trace_id, curr.ts;
+ORDER BY curr.{{id_field}}, curr.trace_id, curr.ts;
diff --git a/src/trace_processor/metrics/sql/chrome/gesture_flow_event_queuing_delay.sql b/src/trace_processor/metrics/sql/chrome/gesture_flow_event_queuing_delay.sql
index e2c069c..9040f91 100644
--- a/src/trace_processor/metrics/sql/chrome/gesture_flow_event_queuing_delay.sql
+++ b/src/trace_processor/metrics/sql/chrome/gesture_flow_event_queuing_delay.sql
@@ -28,49 +28,49 @@
 DROP VIEW IF EXISTS {{prefix}}_flow_event_queuing_delay;
 
 CREATE VIEW {{prefix}}_flow_event_queuing_delay AS
-  SELECT
-    trace_id,
-    id,
-    ts,
-    dur,
-    track_id,
-    {{id_field}},
-    avg_vsync_interval,
-    {{prefix}}_slice_id,
-    {{prefix}}_ts,
-    {{prefix}}_dur,
-    {{prefix}}_track_id,
-    jank,
-    step,
-    ancestor_id,
-    ancestor_ts,
-    ancestor_end,
-    next_id,
-    next_step,
-    maybe_next_ancestor_ts,
-    next_track_id,
-      CASE WHEN trace_id = next_trace_id THEN
-      'InputLatency.LatencyInfo.Flow.QueuingDelay.' ||
-      CASE WHEN
-        jank IS NOT NULL AND
-        jank = 1
-      THEN
-          'Jank.'
-      ELSE
+SELECT
+  trace_id,
+  id,
+  ts,
+  dur,
+  track_id,
+  {{id_field}},
+  avg_vsync_interval,
+  {{prefix}}_slice_id,
+  {{prefix}}_ts,
+  {{prefix}}_dur,
+  {{prefix}}_track_id,
+  jank,
+  step,
+  ancestor_id,
+  ancestor_ts,
+  ancestor_end,
+  next_id,
+  next_step,
+  maybe_next_ancestor_ts,
+  next_track_id,
+  CASE WHEN trace_id = next_trace_id THEN
+      'InputLatency.LatencyInfo.Flow.QueuingDelay.'
+      || CASE WHEN
+        jank IS NOT NULL
+        AND jank = 1
+        THEN
+        'Jank.'
+        ELSE
           'NoJank.'
       END
       || step || '-to-' || next_step
     ELSE
       step
-    END AS description,
-    CASE WHEN maybe_next_ancestor_ts IS NULL THEN
+  END AS description,
+  CASE WHEN maybe_next_ancestor_ts IS NULL THEN
       NULL
     ELSE
       CASE WHEN maybe_next_ancestor_ts > ancestor_end THEN
         (maybe_next_ancestor_ts - ancestor_end)
-      ELSE
-        0
+        ELSE
+          0
       END
-    END AS queuing_time_ns
-  FROM {{prefix}}_flow_event
-  ORDER BY {{id_field}}, trace_id, ts;
+  END AS queuing_time_ns
+FROM {{prefix}}_flow_event
+ORDER BY {{id_field}}, trace_id, ts;
diff --git a/src/trace_processor/metrics/sql/chrome/gesture_jank.sql b/src/trace_processor/metrics/sql/chrome/gesture_jank.sql
index 6e7ea1a..82d0fd0 100644
--- a/src/trace_processor/metrics/sql/chrome/gesture_jank.sql
+++ b/src/trace_processor/metrics/sql/chrome/gesture_jank.sql
@@ -31,43 +31,30 @@
 --       TraceEvents and this table will be empty.
 
 SELECT RUN_METRIC('chrome/jank_utilities.sql');
-
--- Note: Must be a TABLE because it uses a window function which can behave
---       strangely in views.
-DROP TABLE IF EXISTS vsync_intervals;
-CREATE TABLE vsync_intervals AS
-  SELECT
-    slice_id,
-    ts,
-    dur,
-    track_id,
-    LEAD(ts) OVER(PARTITION BY track_id ORDER BY ts) - ts AS time_to_next_vsync
-  FROM slice
-  WHERE name = "VSync"
-  ORDER BY track_id, ts;
+SELECT RUN_METRIC('chrome/vsync_intervals.sql');
 
 -- Get all the "begin" and "end" events. We take their IDs to group them
 -- together into gestures later and the timestamp and duration to compute the
 -- duration of the gesture.
 DROP VIEW IF EXISTS {{prefix}}_begin_and_end;
 CREATE VIEW {{prefix}}_begin_and_end AS
-  SELECT
-    slice.name,
-    slice.id,
-    slice.ts,
-    slice.dur,
-    slice.track_id,
-    EXTRACT_ARG(arg_set_id, 'chrome_latency_info.{{id_field}}')
-        AS {{id_field}},
-    EXTRACT_ARG(arg_set_id, "chrome_latency_info.trace_id") AS trace_id
-  FROM
-    slice
-  WHERE
-    slice.name IN (
-      'InputLatency::{{gesture_start}}',
-      'InputLatency::{{gesture_end}}'
-    )
-  ORDER BY ts;
+SELECT
+  slice.name,
+  slice.id,
+  slice.ts,
+  slice.dur,
+  slice.track_id,
+  EXTRACT_ARG(arg_set_id, 'chrome_latency_info.{{id_field}}')
+  AS {{id_field}},
+  EXTRACT_ARG(arg_set_id, "chrome_latency_info.trace_id") AS trace_id
+FROM
+  slice
+WHERE
+  slice.name IN (
+    'InputLatency::{{gesture_start}}',
+    'InputLatency::{{gesture_end}}'
+  )
+ORDER BY ts;
 
 -- Now we take the "begin" and the "end" events and join the information into a
 -- single row per gesture. We also compute the average Vysnc interval of the
@@ -77,68 +64,60 @@
 -- the COALESCE which corresponds to 16 ms or 60 FPS).
 DROP VIEW IF EXISTS joined_{{prefix}}_begin_and_end;
 CREATE VIEW joined_{{prefix}}_begin_and_end AS
-  SELECT
-    begin.id AS begin_id,
-    begin.ts AS begin_ts,
-    begin.dur AS begin_dur,
-    begin.track_id AS begin_track_id,
-    begin.trace_id AS begin_trace_id,
-    COALESCE(begin.{{id_field}}, begin.trace_id)
-        AS begin_{{id_field}},
-    end.ts AS end_ts,
-    end.ts + end.dur AS end_ts_and_dur,
-    end.trace_id AS end_trace_id,
-    COALESCE((
-      SELECT
-        CAST(AVG(time_to_next_vsync) AS FLOAT)
-      FROM vsync_intervals in_query
-      WHERE
-        time_to_next_vsync IS NOT NULL AND
-        in_query.ts > begin.ts AND
-        in_query.ts < end.ts
-    ), 1e+9 / 60) AS avg_vsync_interval
-  FROM {{prefix}}_begin_and_end begin JOIN {{prefix}}_begin_and_end end ON
-    begin.trace_id < end.trace_id AND
-    begin.name = 'InputLatency::{{gesture_start}}' AND
-    end.name = 'InputLatency::{{gesture_end}}' AND (
+SELECT
+  begin.id AS begin_id,
+  begin.ts AS begin_ts,
+  begin.dur AS begin_dur,
+  begin.track_id AS begin_track_id,
+  begin.trace_id AS begin_trace_id,
+  COALESCE(begin.{{id_field}}, begin.trace_id)
+  AS begin_{{id_field}},
+  end.ts AS end_ts,
+  end.ts + end.dur AS end_ts_and_dur,
+  end.trace_id AS end_trace_id,
+  CalculateAvgVsyncInterval(begin.ts, end.ts) AS avg_vsync_interval
+FROM {{prefix}}_begin_and_end begin JOIN {{prefix}}_begin_and_end end ON
+    begin.trace_id < end.trace_id
+    AND begin.name = 'InputLatency::{{gesture_start}}'
+    AND end.name = 'InputLatency::{{gesture_end}}' AND (
       (
-        begin.{{id_field}} IS NULL AND
-        end.trace_id = (
+        begin.{{id_field}} IS NULL
+        AND end.trace_id = (
           SELECT MIN(trace_id)
           FROM {{prefix}}_begin_and_end in_query
           WHERE
-            name = 'InputLatency::{{gesture_end}}' AND
-          in_query.trace_id > begin.trace_id
+            name = 'InputLatency::{{gesture_end}}'
+            AND in_query.trace_id > begin.trace_id
         )
-      ) OR
-      end.{{id_field}} = begin.{{id_field}}
+      )
+      OR end.{{id_field}} = begin.{{id_field}}
     )
-  ORDER BY begin.ts;
+ORDER BY begin.ts;
 
 -- Prepare all gesture updates that were not coalesced to be joined with their
 -- respective scrolls to calculate jank
 DROP VIEW IF EXISTS gesture_update;
 CREATE VIEW gesture_update AS
-  SELECT
-      EXTRACT_ARG(arg_set_id, "chrome_latency_info.trace_id") AS trace_id,
-      EXTRACT_ARG(arg_set_id, 'chrome_latency_info.{{id_field}}')
-          AS {{id_field}},
-      *
-  FROM
-    slice JOIN track ON slice.track_id = track.id
-  WHERE
-    slice.name = 'InputLatency::{{gesture_update}}' AND
-    slice.dur != -1 AND
-    NOT COALESCE(
-            EXTRACT_ARG(arg_set_id, "chrome_latency_info.is_coalesced"),
-            TRUE)
-    AND slice.arg_set_id IN (
-      SELECT arg_set_id
-      FROM args
-      WHERE args.arg_set_id = slice.arg_set_id
+SELECT
+  EXTRACT_ARG(arg_set_id, "chrome_latency_info.trace_id") AS trace_id,
+  EXTRACT_ARG(arg_set_id, 'chrome_latency_info.{{id_field}}')
+  AS {{id_field}},
+  *
+FROM
+  slice JOIN track ON slice.track_id = track.id
+WHERE
+  slice.name = 'InputLatency::{{gesture_update}}'
+  AND slice.dur != -1
+  AND NOT COALESCE(
+    EXTRACT_ARG(arg_set_id, "chrome_latency_info.is_coalesced"),
+    TRUE)
+  AND slice.arg_set_id IN (
+    SELECT arg_set_id
+    FROM args
+    WHERE args.arg_set_id = slice.arg_set_id
       AND flat_key = 'chrome_latency_info.component_info.component_type'
       AND string_value = 'COMPONENT_INPUT_EVENT_GPU_SWAP_BUFFER'
-    );
+  );
 
 -- Get the "update" events by name ordered by the |{{id_field}}|, and
 -- timestamp. Then compute the number of frames (relative to vsync interval)
@@ -147,40 +126,41 @@
 -- {{gesture_update}} event to the information about its "begin" and "end"
 -- events for easy computation later.
 --
--- We remove updates with |dur| == -1 because this means we have no "end" event
+-- We remove updates with |dur| = -1 because this means we have no "end" event
 -- and can't reasonably determine what it should be. We have separate tracking
 -- to ensure this only happens at the end of the trace where its expected.
 DROP VIEW IF EXISTS {{id_field}}_update;
 CREATE VIEW {{id_field}}_update AS
-  SELECT
-    begin_id,
-    begin_ts,
-    begin_dur,
-    begin_track_id,
-    begin_trace_id,
-    COALESCE({{id_field}}, begin_trace_id) AS {{id_field}},
-    CASE WHEN
+SELECT
+  begin_id,
+  begin_ts,
+  begin_dur,
+  begin_track_id,
+  begin_trace_id,
+  COALESCE({{id_field}}, begin_trace_id) AS {{id_field}},
+  end_ts,
+  CASE WHEN
       end_ts_and_dur > ts + dur THEN
-        end_ts_and_dur
-      ELSE
-        ts + dur
-    END AS maybe_gesture_end,
-    id,
-    ts,
-    dur,
-    track_id,
-    trace_id,
-    dur/avg_vsync_interval AS gesture_frames_exact,
-    avg_vsync_interval
-  FROM joined_{{prefix}}_begin_and_end begin_and_end JOIN gesture_update ON
-  gesture_update.ts <= begin_and_end.end_ts AND
-  gesture_update.ts >= begin_and_end.begin_ts AND
-  gesture_update.trace_id > begin_and_end.begin_trace_id AND
-  gesture_update.trace_id < begin_and_end.end_trace_id AND (
-    gesture_update.{{id_field}} IS NULL OR
-    gesture_update.{{id_field}} = begin_and_end.begin_{{id_field}}
+      end_ts_and_dur
+    ELSE
+      ts + dur
+  END AS maybe_gesture_end,
+  id,
+  ts,
+  dur,
+  track_id,
+  trace_id,
+  dur / avg_vsync_interval AS gesture_frames_exact,
+  avg_vsync_interval
+FROM joined_{{prefix}}_begin_and_end begin_and_end JOIN gesture_update ON
+  gesture_update.ts <= begin_and_end.end_ts
+  AND gesture_update.ts >= begin_and_end.begin_ts
+  AND gesture_update.trace_id > begin_and_end.begin_trace_id
+  AND gesture_update.trace_id < begin_and_end.end_trace_id AND (
+    gesture_update.{{id_field}} IS NULL
+    OR gesture_update.{{id_field}} = begin_and_end.begin_{{id_field}}
   )
-  ORDER BY {{id_field}} ASC, ts ASC;
+ORDER BY {{id_field}} ASC, ts ASC;
 
 -- This takes the "update" events and get to the previous "update" event through LAG
 -- (previous row and NULL if there isn't one) and the next "update" event through LEAD
@@ -188,7 +168,7 @@
 -- event (relative to fps).
 --
 -- We only compare an "update" event to another event within the same gesture
--- ({{id_field}} == prev/next {{id_field}}). This controls somewhat for
+-- ({{id_field}} = prev/next {{id_field}}). This controls somewhat for
 -- variability of gestures.
 --
 -- Note: Must be a TABLE because it uses a window function which can behave
@@ -196,18 +176,18 @@
 
 DROP TABLE IF EXISTS {{prefix}}_jank_maybe_null_prev_and_next_without_precompute;
 CREATE TABLE {{prefix}}_jank_maybe_null_prev_and_next_without_precompute AS
-  SELECT
-    *,
-    maybe_gesture_end - begin_ts AS {{prefix}}_dur,
-    LAG(ts) OVER sorted_frames AS prev_ts,
-    LAG({{id_field}}) OVER sorted_frames AS prev_{{id_field}},
-    LAG(gesture_frames_exact) OVER sorted_frames AS prev_gesture_frames_exact,
-    LEAD(ts) OVER sorted_frames AS next_ts,
-    LEAD({{id_field}}) OVER sorted_frames AS next_{{id_field}},
-    LEAD(gesture_frames_exact) OVER sorted_frames AS next_gesture_frames_exact
-  FROM {{id_field}}_update
-  WINDOW sorted_frames AS (ORDER BY {{id_field}} ASC, ts ASC)
-  ORDER BY {{id_field}} ASC, ts ASC;
+SELECT
+  *,
+  maybe_gesture_end - begin_ts AS {{prefix}}_dur,
+  LAG(ts) OVER sorted_frames AS prev_ts,
+  LAG({{id_field}}) OVER sorted_frames AS prev_{{id_field}},
+  LAG(gesture_frames_exact) OVER sorted_frames AS prev_gesture_frames_exact,
+  LEAD(ts) OVER sorted_frames AS next_ts,
+  LEAD({{id_field}}) OVER sorted_frames AS next_{{id_field}},
+  LEAD(gesture_frames_exact) OVER sorted_frames AS next_gesture_frames_exact
+FROM {{id_field}}_update
+WINDOW sorted_frames AS (ORDER BY {{id_field}} ASC, ts ASC)
+ORDER BY {{id_field}} ASC, ts ASC;
 
 
 -- We compute the duration of the event (relative to fps) and see if it
@@ -224,16 +204,16 @@
 -- Note: Logic is inside the IsJankyFrame function found in jank_utilities.sql.
 DROP VIEW IF EXISTS {{prefix}}_jank_maybe_null_prev_and_next;
 CREATE VIEW {{prefix}}_jank_maybe_null_prev_and_next AS
-  SELECT
-    *,
-     IsJankyFrame({{id_field}}, prev_{{id_field}},
-     prev_ts, begin_ts, maybe_gesture_end,
-     gesture_frames_exact, prev_gesture_frames_exact) AS prev_jank,
-    IsJankyFrame({{id_field}}, next_{{id_field}},
-     next_ts, begin_ts, maybe_gesture_end,
-     gesture_frames_exact, next_gesture_frames_exact) AS next_jank
-  FROM {{prefix}}_jank_maybe_null_prev_and_next_without_precompute
-  ORDER BY {{id_field}} ASC, ts ASC;
+SELECT
+  *,
+  IsJankyFrame({{id_field}}, prev_{{id_field}},
+    prev_ts, begin_ts, maybe_gesture_end,
+    gesture_frames_exact, prev_gesture_frames_exact) AS prev_jank,
+  IsJankyFrame({{id_field}}, next_{{id_field}},
+    next_ts, begin_ts, maybe_gesture_end,
+    gesture_frames_exact, next_gesture_frames_exact) AS next_jank
+FROM {{prefix}}_jank_maybe_null_prev_and_next_without_precompute
+ORDER BY {{id_field}} ASC, ts ASC;
 
 -- This just uses prev_jank and next_jank to see if each "update" event is a
 -- jank.
@@ -248,46 +228,46 @@
 -- Note: Logic is inside the JankBudget function found in jank_utilities.sql.
 DROP VIEW IF EXISTS {{prefix}}_jank;
 CREATE VIEW {{prefix}}_jank AS
-  SELECT
-    id AS slice_id,
-    (next_jank IS NOT NULL AND next_jank) OR
-    (prev_jank IS NOT NULL AND prev_jank)
-    AS jank,
-    JankBudget(gesture_frames_exact, prev_gesture_frames_exact,
-      next_gesture_frames_exact) * avg_vsync_interval AS jank_budget,
-    *
-  FROM {{prefix}}_jank_maybe_null_prev_and_next
-  ORDER BY {{id_field}} ASC, ts ASC;
+SELECT
+  id AS slice_id,
+  (next_jank IS NOT NULL AND next_jank)
+  OR (prev_jank IS NOT NULL AND prev_jank)
+  AS jank,
+  JankBudget(gesture_frames_exact, prev_gesture_frames_exact,
+    next_gesture_frames_exact) * avg_vsync_interval AS jank_budget,
+  *
+FROM {{prefix}}_jank_maybe_null_prev_and_next
+ORDER BY {{id_field}} ASC, ts ASC;
 
 DROP VIEW IF EXISTS {{prefix}}_jank_output;
 CREATE VIEW {{prefix}}_jank_output AS
-  SELECT
-    {{proto_name}}(
-      '{{prefix}}_jank_percentage', (
+SELECT
+  {{proto_name}}(
+    '{{prefix}}_jank_percentage', (
+      SELECT
+        (
+          SUM(CASE WHEN jank THEN dur ELSE 0 END) / CAST(SUM(dur) AS REAL)
+        ) * 100.0
+      FROM {{prefix}}_jank
+    ),
+    '{{prefix}}_ms', (
+      SELECT
+        CAST(SUM({{prefix}}_dur) / 1e6 AS REAL)
+      FROM (
         SELECT
-          (
-            SUM(CASE WHEN jank THEN dur ELSE 0 END)/CAST(SUM(dur) AS REAL)
-          ) * 100.0
+          MAX({{prefix}}_dur) AS {{prefix}}_dur
         FROM {{prefix}}_jank
-      ),
-      '{{prefix}}_ms', (
-        SELECT
-          CAST(SUM({{prefix}}_dur)/1e6 AS REAL)
-        FROM (
-          SELECT
-            MAX({{prefix}}_dur) AS {{prefix}}_dur
-          FROM {{prefix}}_jank
-          GROUP BY {{id_field}}
-        )
-      ),
-      '{{prefix}}_processing_ms', CAST(SUM(dur)/1e6 AS REAL),
-      '{{prefix}}_jank_processing_ms', (
-        SELECT CAST(SUM(dur)/1e6 AS REAL) FROM {{prefix}}_jank WHERE jank
-      ),
-      'num_{{prefix}}_update_count', COUNT(*),
-      'num_{{prefix}}_update_jank_count', SUM(jank),
-      '{{prefix}}_jank_budget_ms', (
-        SELECT CAST(SUM(jank_budget) AS REAL) FROM {{prefix}}_jank WHERE jank
+        GROUP BY {{id_field}}
       )
+    ),
+    '{{prefix}}_processing_ms', CAST(SUM(dur) / 1e6 AS REAL),
+    '{{prefix}}_jank_processing_ms', (
+      SELECT CAST(SUM(dur) / 1e6 AS REAL) FROM {{prefix}}_jank WHERE jank
+    ),
+    'num_{{prefix}}_update_count', COUNT(*),
+    'num_{{prefix}}_update_jank_count', SUM(jank),
+    '{{prefix}}_jank_budget_ms', (
+      SELECT CAST(SUM(jank_budget) AS REAL) FROM {{prefix}}_jank WHERE jank
     )
-  FROM {{prefix}}_jank;
+  )
+FROM {{prefix}}_jank;
diff --git a/src/trace_processor/metrics/sql/chrome/jank_utilities.sql b/src/trace_processor/metrics/sql/chrome/jank_utilities.sql
index 44edb42..4d27f4f 100644
--- a/src/trace_processor/metrics/sql/chrome/jank_utilities.sql
+++ b/src/trace_processor/metrics/sql/chrome/jank_utilities.sql
@@ -29,17 +29,17 @@
   -- Function : function takes scroll ids of frames to verify it's from
   -- the same scroll, and makes sure the frame ts occured within the scroll
   -- timestamp of the neighbour and computes whether the frame was janky or not.
-  'IsJankyFrame(cur_id LONG,next_id LONG,neighbour_ts LONG,' ||
-  'cur_begin_ts LONG,cur_gesture_end LONG,cur_frame_exact FLOAT,' ||
-  'neighbour_frame_exact FLOAT)',
+  'IsJankyFrame(cur_gesture_id LONG,neighbour_gesture_id LONG,neighbour_ts LONG,'
+  || 'cur_gesture_begin_ts LONG,cur_gesture_end_ts LONG,cur_frame_exact FLOAT,'
+  || 'neighbour_frame_exact FLOAT)',
   -- Returns true if the frame was janky, false otherwise
   'BOOL',
   'SELECT
     CASE WHEN
-      $cur_id != $next_id OR
+      $cur_gesture_id != $neighbour_gesture_id OR
       $neighbour_ts IS NULL OR
-      $neighbour_ts < $cur_begin_ts OR
-      $neighbour_ts > $cur_gesture_end THEN
+      $neighbour_ts < $cur_gesture_begin_ts OR
+      $neighbour_ts > $cur_gesture_end_ts THEN
         FALSE ELSE
         $cur_frame_exact > $neighbour_frame_exact + 0.5 + 1e-9
     END'
@@ -52,8 +52,8 @@
   --
   -- JankBudget is the minimum amount of frames/time we need to reduce the frame
   -- duration by for it to be no longer considered janky.
-  'JankBudget(cur_frame_exact FLOAT, prev_frame_exact FLOAT, ' ||
-  ' next_frame_exact FLOAT)',
+  'JankBudget(cur_frame_exact FLOAT, prev_frame_exact FLOAT, '
+  || ' next_frame_exact FLOAT)',
   -- Returns the jank budget in percentage (i.e. 0.75) of vsync interval
   -- percentage.
   --
@@ -81,3 +81,25 @@
       -- Otherwise return null
     ) - 0.5 - 1e-9'
 );
+
+-- Extract mojo information for the long-task-tracking scenario for specific
+-- names. For example, LongTaskTracker slices may have associated IPC
+-- metadata, or InterestingTask slices for input may have associated IPC to
+-- determine whether the task is fling/etc.
+SELECT CREATE_VIEW_FUNCTION(
+  'SELECT_LONG_TASK_SLICES(name STRING)',
+  'interface_name STRING, ipc_hash INT, message_type STRING, id INT',
+  'SELECT
+      EXTRACT_ARG(s.arg_set_id, "chrome_mojo_event_info.mojo_interface_tag") AS interface_name,
+      EXTRACT_ARG(arg_set_id, "chrome_mojo_event_info.ipc_hash") AS ipc_hash,
+      CASE
+        WHEN EXTRACT_ARG(arg_set_id, "chrome_mojo_event_info.is_reply") THEN "reply"
+        ELSE "message"
+      END AS message_type,
+      s.id
+    FROM slice s
+    WHERE
+      category GLOB "*scheduler.long_tasks*"
+      AND name = $name
+  '
+);
diff --git a/src/trace_processor/metrics/sql/chrome/rail_modes.sql b/src/trace_processor/metrics/sql/chrome/rail_modes.sql
index d28f665..625a7f4 100644
--- a/src/trace_processor/metrics/sql/chrome/rail_modes.sql
+++ b/src/trace_processor/metrics/sql/chrome/rail_modes.sql
@@ -14,8 +14,8 @@
 -- limitations under the License.
 --
 
-SELECT RUN_METRIC('chrome/chrome_processes.sql') AS suppress_query_output;
-SELECT RUN_METRIC('chrome/chrome_event_metadata.sql') AS suppress_query_output;
+SELECT RUN_METRIC('chrome/chrome_processes.sql');
+SELECT RUN_METRIC('chrome/chrome_event_metadata.sql');
 
 -- Priority order for RAIL modes where response has the highest priority and
 -- idle has the lowest.
@@ -38,15 +38,15 @@
 -- https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/platform/scheduler/public/rail_mode_observer.h
 INSERT INTO rail_modes
 VALUES ('RAIL_MODE_IDLE', 0, 'background'),
-  ('RAIL_MODE_ANIMATION', 1, "animation"),
-  ('RAIL_MODE_LOAD', 2, "load"),
-  ('RAIL_MODE_RESPONSE', 3, "response");
+('RAIL_MODE_ANIMATION', 1, "animation"),
+('RAIL_MODE_LOAD', 2, "load"),
+('RAIL_MODE_RESPONSE', 3, "response");
 
 
 -- Find the max ts + dur for every process
 DROP TABLE IF EXISTS max_ts_per_process;
 CREATE TABLE max_ts_per_process AS
- -- MAX(dur, 0) means unclosed slices just contribute their start time.
+-- MAX(dur, 0) means unclosed slices just contribute their start time.
 SELECT upid,
   MAX(ts + MAX(dur, 0)) AS ts
 FROM (
@@ -54,15 +54,15 @@
       ts,
       dur
     FROM process_track t
-      JOIN slice s
+    JOIN slice s
     WHERE s.track_id = t.id
     UNION ALL
     SELECT upid,
       ts,
       dur
     FROM thread_track t
-      JOIN thread
-      JOIN slice
+    JOIN thread
+    JOIN slice
     WHERE slice.track_id = t.id
       AND thread.utid = t.utid
   )
@@ -77,7 +77,7 @@
   CASE
     -- Add 1 to the duration to ensure you cannot get a zero-sized RAIL mode
     -- slice, which can throw off the later queries.
-    WHEN dur == -1 THEN max_ts_per_process.ts - slice.ts + 1
+    WHEN dur = -1 THEN max_ts_per_process.ts - slice.ts + 1
     ELSE dur
   END AS dur,
   track_id,
@@ -105,10 +105,10 @@
 DROP VIEW IF EXISTS rail_mode_slices;
 CREATE VIEW rail_mode_slices AS
 SELECT ts, dur, track_id,
-    CASE
-      WHEN rail_mode == "RAIL_MODE_LOAD" THEN "RAIL_MODE_ANIMATION"
-      ELSE rail_mode
-    END AS rail_mode
+  CASE
+    WHEN rail_mode = "RAIL_MODE_LOAD" THEN "RAIL_MODE_ANIMATION"
+    ELSE rail_mode
+  END AS rail_mode
 FROM original_rail_mode_slices;
 
 -- View containing a collapsed view of rail_mode_slices where there is only one
@@ -141,7 +141,7 @@
       s.end_ts > r.ts AND s.end_ts <= r.ts + r.dur
     )
   )
-  AND r.rail_mode == rail_modes.mode
+  AND r.rail_mode = rail_modes.mode
   AND trace_has_realistic_length.value
 GROUP BY s.ts;
 
@@ -214,14 +214,14 @@
   dur,
   1
 FROM (SELECT start_ts AS ts,
-          COALESCE((
-              SELECT MIN(ts)
-              FROM slice
-              WHERE name = "VSync"
-            ) - start_ts - const.vsync_padding,
-            end_ts - start_ts
-          ) AS dur
-FROM trace_bounds, const)
+  COALESCE((
+    SELECT MIN(ts)
+    FROM slice
+    WHERE name = "VSync"
+  ) - start_ts - const.vsync_padding,
+  end_ts - start_ts
+  ) AS dur
+  FROM trace_bounds, const)
 WHERE dur > 0
 UNION
 -- Insert a slice between the last vsync and end_ts
@@ -264,30 +264,30 @@
 );
 
 INSERT
-  OR IGNORE INTO input_latency_begin_end_names
+OR IGNORE INTO input_latency_begin_end_names
 VALUES
-  ("InputLatency::GestureScrollBegin",
-     "InputLatency::GestureScroll", 1, 0, 0, 0, 0, 0),
-  ("InputLatency::GestureScrollEnd",
-     "InputLatency::GestureScroll", -1, 0, 0, 0, 0, 1),
-  ("InputLatency::GesturePinchBegin",
-     "InputLatency::GesturePinch", 0, 1, 0, 0, 0, 0),
-  ("InputLatency::GesturePinchEnd",
-     "InputLatency::GesturePinch", 0, -1, 0, 0, 0, 1),
-  ("InputLatency::TouchStart",
-     "InputLatency::Touch", 0, 0, 1, 0, 0, 0),
-  ("InputLatency::TouchEnd",
-     "InputLatency::Touch", 0, 0, -1, 0, 0, 1),
-  ("InputLatency::GestureFlingStart",
-     "InputLatency::GestureFling", 0, 0, 0, 1, 0, 0),
-  ("InputLatency::GestureFlingCancel",
-     "InputLatency::GestureFling", 0, 0, 0, -1, 0, 1),
-  ("InputLatency::PointerDown",
-     "InputLatency::Pointer", 0, 0, 0, 0, 1, 0),
-  ("InputLatency::PointerUp",
-     "InputLatency::Pointer", 0, 0, 0, 0, -1, 1),
-  ("InputLatency::PointerCancel",
-     "InputLatency::Pointer", 0, 0, 0, 0, -1, 1);
+("InputLatency::GestureScrollBegin",
+  "InputLatency::GestureScroll", 1, 0, 0, 0, 0, 0),
+("InputLatency::GestureScrollEnd",
+  "InputLatency::GestureScroll", -1, 0, 0, 0, 0, 1),
+("InputLatency::GesturePinchBegin",
+  "InputLatency::GesturePinch", 0, 1, 0, 0, 0, 0),
+("InputLatency::GesturePinchEnd",
+  "InputLatency::GesturePinch", 0, -1, 0, 0, 0, 1),
+("InputLatency::TouchStart",
+  "InputLatency::Touch", 0, 0, 1, 0, 0, 0),
+("InputLatency::TouchEnd",
+  "InputLatency::Touch", 0, 0, -1, 0, 0, 1),
+("InputLatency::GestureFlingStart",
+  "InputLatency::GestureFling", 0, 0, 0, 1, 0, 0),
+("InputLatency::GestureFlingCancel",
+  "InputLatency::GestureFling", 0, 0, 0, -1, 0, 1),
+("InputLatency::PointerDown",
+  "InputLatency::Pointer", 0, 0, 0, 0, 1, 0),
+("InputLatency::PointerUp",
+  "InputLatency::Pointer", 0, 0, 0, 0, -1, 1),
+("InputLatency::PointerCancel",
+  "InputLatency::Pointer", 0, 0, 0, 0, -1, 1);
 
 -- Find all the slices that have split "begin" and "end" slices and maintain a
 -- running total for each type, where >0 means that type of input event is
@@ -311,11 +311,11 @@
 DROP VIEW IF EXISTS unified_input_pair_increments;
 CREATE VIEW unified_input_pair_increments AS
 SELECT ts,
-  scroll_increment +
-  pinch_increment +
-  touch_increment +
-  fling_increment +
-  pointer_increment AS increment
+  scroll_increment
+  + pinch_increment
+  + touch_increment
+  + fling_increment
+  + pointer_increment AS increment
 FROM input_begin_end_slices;
 
 -- It's possible there's an end slice without a start slice (as it occurred
@@ -328,18 +328,18 @@
 DROP VIEW IF EXISTS initial_paired_increment;
 CREATE VIEW initial_paired_increment AS
 SELECT ts,
-  MIN(0, MIN(scroll_total)) +
-  MIN(0, MIN(pinch_total))  +
-  MIN(0, MIN(touch_total))  +
-  MIN(0, MIN(fling_total))  +
-  MIN(0, MIN(pointer_total)) AS offset
+  MIN(0, MIN(scroll_total))
+  + MIN(0, MIN(pinch_total))
+  + MIN(0, MIN(touch_total))
+  + MIN(0, MIN(fling_total))
+  + MIN(0, MIN(pointer_total)) AS offset
 FROM (
     SELECT ts,
       SUM(scroll_increment) OVER(ROWS UNBOUNDED PRECEDING) AS scroll_total,
-      SUM(pinch_increment)  OVER(ROWS UNBOUNDED PRECEDING) AS pinch_total,
-      SUM(touch_increment)  OVER(ROWS UNBOUNDED PRECEDING) AS touch_total,
-      SUM(fling_increment)  OVER(ROWS UNBOUNDED PRECEDING) AS fling_total,
-      SUM(pointer_increment)  OVER(ROWS UNBOUNDED PRECEDING) AS pointer_total
+      SUM(pinch_increment) OVER(ROWS UNBOUNDED PRECEDING) AS pinch_total,
+      SUM(touch_increment) OVER(ROWS UNBOUNDED PRECEDING) AS touch_total,
+      SUM(fling_increment) OVER(ROWS UNBOUNDED PRECEDING) AS fling_total,
+      SUM(pointer_increment) OVER(ROWS UNBOUNDED PRECEDING) AS pointer_total
     FROM input_begin_end_slices
   );
 
@@ -356,8 +356,8 @@
   AND NOT EXISTS (
     SELECT 1
     FROM slice
-      JOIN input_latency_begin_end_names
-    WHERE s.name == full_name
+    JOIN input_latency_begin_end_names
+    WHERE s.name = full_name
   );
 
 -- Turn the simple input slices into +1s and -1s at the start and end of each
@@ -420,7 +420,7 @@
               lag(input_total) OVER() AS prev_input_total
             FROM all_input_totals
           )
-        WHERE (input_total > 0 <> prev_input_total > 0)
+        WHERE (input_total > 0 != prev_input_total > 0)
           OR prev_input_total IS NULL
       )
   )
@@ -452,8 +452,8 @@
 SELECT (
     SELECT value
     FROM chrome_event_metadata
-    WHERE name == "os-name"
-  ) == "Android" AS value;
+    WHERE name = "os-name"
+  ) = "Android" AS value;
 
 -- Mapping to allow CamelCased names to be produced from the modified rail
 -- modes.
@@ -464,10 +464,10 @@
 );
 INSERT INTO modified_rail_mode_prettier
 VALUES ("background", "Background"),
-  ("foreground_idle", "ForegroundIdle"),
-  ("animation", "Animation"),
-  ("load", "Load"),
-  ("response", "Response");
+("foreground_idle", "ForegroundIdle"),
+("animation", "Animation"),
+("load", "Load"),
+("response", "Response");
 
 -- When the RAIL mode is animation, use input/vsync data to conditionally change
 -- the mode to response or foreground_idle.
@@ -491,12 +491,12 @@
       dur,
       rail_mode AS mode
     FROM combined_overall_rail_slices
-    WHERE rail_mode <> "animation"
+    WHERE rail_mode != "animation"
   )
-  -- Since VSync events are only emitted on Android (and the concept of a
-  -- unified RAIL mode only makes sense if there's just a single Chrome window),
-  -- don't output anything on other platforms. This will result in all the power
-  -- and cpu time tables being empty rather than containing bogus results.
+-- Since VSync events are only emitted on Android (and the concept of a
+-- unified RAIL mode only makes sense if there's just a single Chrome window),
+-- don't output anything on other platforms. This will result in all the power
+-- and cpu time tables being empty rather than containing bogus results.
 WHERE (
     SELECT value
     FROM has_modified_rail_slices
@@ -510,9 +510,10 @@
 DROP TABLE IF EXISTS modified_rail_slices;
 CREATE TABLE modified_rail_slices AS
 WITH const (end_ts) AS (SELECT ts + dur
-              FROM unmerged_modified_rail_slices
-              ORDER BY ts DESC
-              LIMIT 1)
+  FROM unmerged_modified_rail_slices
+  ORDER BY ts DESC
+  LIMIT 1
+)
 SELECT ROW_NUMBER() OVER () AS id, lag(next_ts) OVER() AS ts,
   ts + dur - lag(next_ts) OVER() AS dur,
   mode AS mode
@@ -557,6 +558,6 @@
         LIMIT 1
       )
   )
-WHERE mode <> next_mode
+WHERE mode != next_mode
 -- Retrieve all but the first row.
 LIMIT -1 OFFSET 1;
diff --git a/src/trace_processor/metrics/sql/chrome/scroll_flow_event.sql b/src/trace_processor/metrics/sql/chrome/scroll_flow_event.sql
index 083c35d..62cffa9 100644
--- a/src/trace_processor/metrics/sql/chrome/scroll_flow_event.sql
+++ b/src/trace_processor/metrics/sql/chrome/scroll_flow_event.sql
@@ -27,5 +27,5 @@
     'chrome/gesture_flow_event.sql',
     'prefix', 'scroll',
     'gesture_update', 'GestureScrollUpdate',
-    'id_field', 'gesture_scroll_id' 
-    );
+    'id_field', 'gesture_scroll_id'
+);
diff --git a/src/trace_processor/metrics/sql/chrome/scroll_flow_event_queuing_delay.sql b/src/trace_processor/metrics/sql/chrome/scroll_flow_event_queuing_delay.sql
index 94a8f48..49edce4 100644
--- a/src/trace_processor/metrics/sql/chrome/scroll_flow_event_queuing_delay.sql
+++ b/src/trace_processor/metrics/sql/chrome/scroll_flow_event_queuing_delay.sql
@@ -24,4 +24,3 @@
 SELECT RUN_METRIC('chrome/gesture_flow_event_queuing_delay.sql',
     'prefix', 'scroll',
     'id_field', 'gesture_scroll_id');
-
diff --git a/src/trace_processor/metrics/sql/chrome/scroll_jank_cause.sql b/src/trace_processor/metrics/sql/chrome/scroll_jank_cause.sql
index 030ab32..220cca2 100644
--- a/src/trace_processor/metrics/sql/chrome/scroll_jank_cause.sql
+++ b/src/trace_processor/metrics/sql/chrome/scroll_jank_cause.sql
@@ -35,50 +35,44 @@
 
 DROP VIEW IF EXISTS scroll_jank_cause_joined;
 CREATE VIEW scroll_jank_cause_joined AS
-  SELECT
-    COALESCE(move.blocking_touch_move, 0) AS blocking_touch_move,
-    COALESCE(task.blocked_by_language_detection, 0)
-        AS blocked_by_language_detection,
-    COALESCE(task.blocked_by_copy_request, 0) AS blocked_by_copy_request,
-    COALESCE(bitmap.blocked_by_bitmap, 0) AS blocked_by_bitmap,
-    COALESCE(bitmap.blocked_by_toolbar, 0) AS blocked_by_toolbar,
-    COALESCE(bitmap.blocked_by_bitmap_no_toolbar, 0)
-        AS blocked_by_bitmap_no_toolbar,
-    jank.*
-  FROM
-    scroll_jank jank LEFT JOIN
-    scroll_jank_cause_blocking_touch_move move
-        ON jank.id = move.scroll_id LEFT JOIN
-    scroll_jank_cause_blocking_task task
-        ON jank.id = task.scroll_id LEFT JOIN
-    scroll_jank_cause_get_bitmap bitmap
-        ON jank.id = bitmap.scroll_id;
+SELECT
+  COALESCE(move.blocking_touch_move, 0) AS blocking_touch_move,
+  COALESCE(task.blocked_by_language_detection, 0)
+  AS blocked_by_language_detection,
+  COALESCE(task.blocked_by_copy_request, 0) AS blocked_by_copy_request,
+  COALESCE(bitmap.blocked_by_bitmap, 0) AS blocked_by_bitmap,
+  COALESCE(bitmap.blocked_by_toolbar, 0) AS blocked_by_toolbar,
+  COALESCE(bitmap.blocked_by_bitmap_no_toolbar, 0)
+  AS blocked_by_bitmap_no_toolbar,
+  jank.*
+FROM
+  scroll_jank jank LEFT JOIN
+  scroll_jank_cause_blocking_touch_move move
+  ON jank.id = move.scroll_id LEFT JOIN
+  scroll_jank_cause_blocking_task task
+  ON jank.id = task.scroll_id LEFT JOIN
+  scroll_jank_cause_get_bitmap bitmap
+  ON jank.id = bitmap.scroll_id;
 
 DROP VIEW IF EXISTS scroll_jank_cause_explained_jank;
 CREATE VIEW scroll_jank_cause_explained_jank AS
-  SELECT
-    CASE WHEN
+SELECT
+  CASE WHEN
       NOT jank
-    THEN
+      THEN
       FALSE
     ELSE
-      CASE WHEN
-        blocking_touch_move OR
-        blocked_by_language_detection OR
-        blocked_by_copy_request OR
-        blocked_by_bitmap
-      THEN
-        TRUE
-      ELSE
-        FALSE
-      END
-    END AS explained_jank,
-    jank.*
-  FROM scroll_jank_cause_joined jank;
+      COALESCE(blocking_touch_move
+        OR blocked_by_language_detection
+        OR blocked_by_copy_request
+        OR blocked_by_bitmap, FALSE)
+  END AS explained_jank,
+  jank.*
+FROM scroll_jank_cause_joined jank;
 
 DROP VIEW IF EXISTS scroll_jank_cause;
 CREATE VIEW scroll_jank_cause AS
-  SELECT
-    jank AND NOT explained_jank AS unexplained_jank,
-    jank.*
-  FROM scroll_jank_cause_explained_jank jank;
+SELECT
+  jank AND NOT explained_jank AS unexplained_jank,
+  jank.*
+FROM scroll_jank_cause_explained_jank jank;
diff --git a/src/trace_processor/metrics/sql/chrome/scroll_jank_cause_blocking_task.sql b/src/trace_processor/metrics/sql/chrome/scroll_jank_cause_blocking_task.sql
index 14ddd83..d8af338 100644
--- a/src/trace_processor/metrics/sql/chrome/scroll_jank_cause_blocking_task.sql
+++ b/src/trace_processor/metrics/sql/chrome/scroll_jank_cause_blocking_task.sql
@@ -58,7 +58,7 @@
 FROM slice
 WHERE
   EXTRACT_ARG(arg_set_id, "task.posted_from.file_name") GLOB
-      "*gpu/command_buffer/service/scheduler.cc"
+  "*gpu/command_buffer/service/scheduler.cc"
 LIMIT 1;
 
 -- TODO(nuskos): Determine a good way to get all the renderer track_ids (each
@@ -81,9 +81,9 @@
 WHERE
   track_id = (
     SELECT id FROM browser_main_track_id
-  ) AND
-  name = "LatencyInfo.Flow"
-  GROUP BY trace_id;
+  )
+  AND name = "LatencyInfo.Flow"
+GROUP BY trace_id;
 
 -- Grab the last LatencyInfo.Flow for each trace_id on the VizCompositor.
 DROP VIEW IF EXISTS viz_flows;
@@ -97,9 +97,9 @@
 WHERE
   track_id = (
     SELECT id FROM viz_compositor_track_id
-  ) AND
-  name = "LatencyInfo.Flow"
-  GROUP BY trace_id;
+  )
+  AND name = "LatencyInfo.Flow"
+GROUP BY trace_id;
 
 -- Grab the last LatencyInfo.Flow for each trace_id on the GPU main.
 DROP VIEW IF EXISTS gpu_flows;
@@ -113,9 +113,9 @@
 WHERE
   track_id = (
     SELECT id FROM gpu_main_track_id
-  ) AND
-  name = "LatencyInfo.Flow"
-  GROUP BY trace_id;
+  )
+  AND name = "LatencyInfo.Flow"
+GROUP BY trace_id;
 
 --------------------------------------------------------------------------------
 -- Finally join the relevant tracks/flows to the individual scrolls.
@@ -149,9 +149,9 @@
     track_id
   FROM scroll_jank
 ) scroll JOIN browser_flows ON
-    scroll.trace_id = browser_flows.trace_id
-  JOIN viz_flows ON viz_flows.trace_id = scroll.trace_id
-  JOIN gpu_flows ON gpu_flows.trace_id = scroll.trace_id;
+  scroll.trace_id = browser_flows.trace_id
+JOIN viz_flows ON viz_flows.trace_id = scroll.trace_id
+JOIN gpu_flows ON gpu_flows.trace_id = scroll.trace_id;
 
 --------------------------------------------------------------------------------
 -- Below we determine individual causes of blocking tasks.
@@ -173,17 +173,17 @@
 WHERE
   (
     (
-      name = "viz.mojom.CopyOutputResultSender" OR
-      name = "GLRenderer::CopyDrawnRenderPass"
-    ) AND
-    track_id = (SELECT id FROM browser_main_track_id)
+      name = "viz.mojom.CopyOutputResultSender"
+      OR name = "GLRenderer::CopyDrawnRenderPass"
+    )
+    AND track_id = (SELECT id FROM browser_main_track_id)
   ) OR (
     EXTRACT_ARG(arg_set_id, "task.posted_from.file_name") GLOB
-        "*components/viz/common/frame_sinks/copy_output_request.cc" AND
-    track_id = (SELECT id FROM viz_compositor_track_id)
+    "*components/viz/common/frame_sinks/copy_output_request.cc"
+    AND track_id = (SELECT id FROM viz_compositor_track_id)
   ) OR (
-    name = "SkiaOutputSurfaceImplOnGpu::CopyOutput" AND
-    track_id = (SELECT id FROM gpu_main_track_id)
+    name = "SkiaOutputSurfaceImplOnGpu::CopyOutput"
+    AND track_id = (SELECT id FROM gpu_main_track_id)
   );
 
 -- Determine based on the LatencyInfo.Flow timestamp and the copy task overlap
@@ -198,31 +198,19 @@
   copy.dur,
   copy.track_id,
   CASE WHEN copy.track_id = scroll.browser_track_id THEN
-    CASE WHEN copy.ts < scroll.browser_flow_ts THEN
-      TRUE
+    COALESCE(copy.ts < scroll.browser_flow_ts, FALSE)
+    WHEN copy.track_id = scroll.viz_track_id THEN
+      COALESCE(copy.ts < scroll.viz_flow_ts, FALSE)
+    WHEN copy.track_id = scroll.gpu_track_id THEN
+      COALESCE(copy.ts < scroll.gpu_flow_ts, FALSE)
     ELSE
       FALSE
-    END
-  WHEN copy.track_id = scroll.viz_track_id THEN
-    CASE WHEN copy.ts < scroll.viz_flow_ts THEN
-      TRUE
-    ELSE
-      FALSE
-    END
-  WHEN copy.track_id = scroll.gpu_track_id THEN
-    CASE WHEN copy.ts < scroll.gpu_flow_ts THEN
-      TRUE
-    ELSE
-      FALSE
-    END
-  ELSE
-    FALSE
   END AS blocked_by_copy
 FROM
   scroll_with_browser_gpu_and_viz_flows scroll JOIN
   blocking_browser_gpu_and_viz_copies copy ON
-  scroll.ts + scroll.dur >= copy.ts AND
-  copy.ts + copy.dur >= scroll.ts;
+    scroll.ts + scroll.dur >= copy.ts
+    AND copy.ts + copy.dur >= scroll.ts;
 
 -- Group by scroll so we can equally join one reply to the ScrollJankAndCauses
 -- view.
@@ -246,8 +234,8 @@
 FROM slice
 WHERE
   (
-    name = "language_detection.mojom.LanguageDetectionService" AND
-    track_id = (SELECT id FROM browser_main_track_id)
+    name = "language_detection.mojom.LanguageDetectionService"
+    AND track_id = (SELECT id FROM browser_main_track_id)
   );
 
 DROP VIEW IF EXISTS blocking_language_detection_tasks;
@@ -260,17 +248,13 @@
   lang.dur,
   lang.track_id,
   CASE WHEN lang.track_id = scroll.browser_track_id THEN
-    CASE WHEN lang.ts < scroll.browser_flow_ts THEN
-      TRUE
-    ELSE
-      FALSE
-    END
+    COALESCE(lang.ts < scroll.browser_flow_ts, FALSE)
   END AS blocked_by_language_detection
 FROM
   scroll_with_browser_gpu_and_viz_flows scroll JOIN
   blocking_browser_language_detection lang ON
-  scroll.ts + scroll.dur >= lang.ts AND
-  lang.ts + lang.dur >= scroll.ts;
+    scroll.ts + scroll.dur >= lang.ts
+    AND lang.ts + lang.dur >= scroll.ts;
 
 DROP VIEW IF EXISTS language_detection_overlapping_scrolls;
 CREATE VIEW language_detection_overlapping_scrolls AS
@@ -285,10 +269,10 @@
 --------------------------------------------------------------------------------
 DROP VIEW IF EXISTS scroll_jank_cause_blocking_task;
 CREATE VIEW scroll_jank_cause_blocking_task AS
-  SELECT
-    lang.scroll_id,
-    lang.blocked_by_language_detection,
-    copy.blocked_by_copy_request
-  FROM
-    language_detection_overlapping_scrolls lang JOIN
-    screenshot_overlapping_scrolls copy ON copy.scroll_id = lang.scroll_id;
+SELECT
+  lang.scroll_id,
+  lang.blocked_by_language_detection,
+  copy.blocked_by_copy_request
+FROM
+  language_detection_overlapping_scrolls lang JOIN
+  screenshot_overlapping_scrolls copy ON copy.scroll_id = lang.scroll_id;
diff --git a/src/trace_processor/metrics/sql/chrome/scroll_jank_cause_blocking_touch_move.sql b/src/trace_processor/metrics/sql/chrome/scroll_jank_cause_blocking_touch_move.sql
index e07bb32..6f06f71 100644
--- a/src/trace_processor/metrics/sql/chrome/scroll_jank_cause_blocking_touch_move.sql
+++ b/src/trace_processor/metrics/sql/chrome/scroll_jank_cause_blocking_touch_move.sql
@@ -20,12 +20,12 @@
 -- the data from the first flow event for that TouchMove.
 DROP TABLE IF EXISTS touch_move_and_begin_flow;
 CREATE TABLE touch_move_and_begin_flow AS
-  SELECT
-    flow.begin_flow_id,
-    flow.begin_flow_ts,
-    flow.begin_flow_track_id,
-    move.*
-  FROM (
+SELECT
+  flow.begin_flow_id,
+  flow.begin_flow_ts,
+  flow.begin_flow_track_id,
+  move.*
+FROM (
     SELECT
       EXTRACT_ARG(arg_set_id, "chrome_latency_info.trace_id") AS trace_id,
       *
@@ -37,11 +37,11 @@
       track_id AS begin_flow_track_id,
       ts AS begin_flow_ts,
       EXTRACT_ARG(arg_set_id, "chrome_latency_info.trace_id")
-          AS begin_flow_trace_id
+      AS begin_flow_trace_id
     FROM slice
     WHERE
-      name = "LatencyInfo.Flow" AND
-      EXTRACT_ARG(arg_set_id, "chrome_latency_info.step") IS NULL
+      name = "LatencyInfo.Flow"
+      AND EXTRACT_ARG(arg_set_id, "chrome_latency_info.step") IS NULL
     GROUP BY begin_flow_trace_id
   ) flow ON flow.begin_flow_trace_id = move.trace_id;
 
@@ -51,45 +51,45 @@
 -- weren't blocking.
 DROP TABLE IF EXISTS touch_move_begin_and_end_flow;
 CREATE TABLE touch_move_begin_and_end_flow AS
-  SELECT
-    flow.end_flow_id,
-    flow.end_flow_ts,
-    flow.end_flow_track_id,
-    move.*
-  FROM touch_move_and_begin_flow move LEFT JOIN (
+SELECT
+  flow.end_flow_id,
+  flow.end_flow_ts,
+  flow.end_flow_track_id,
+  move.*
+FROM touch_move_and_begin_flow move LEFT JOIN (
     SELECT
       MAX(id) AS end_flow_id,
       ts AS end_flow_ts,
       track_id AS end_flow_track_id,
       EXTRACT_ARG(arg_set_id, "chrome_latency_info.trace_id")
-          AS end_flow_trace_id
+      AS end_flow_trace_id
     FROM slice
     WHERE
-      name = "LatencyInfo.Flow" AND
-      EXTRACT_ARG(arg_set_id, "chrome_latency_info.step") IS NULL
+      name = "LatencyInfo.Flow"
+      AND EXTRACT_ARG(arg_set_id, "chrome_latency_info.step") IS NULL
     GROUP BY end_flow_trace_id
   ) flow ON
-      flow.end_flow_trace_id = move.trace_id AND
-      move.begin_flow_track_id = flow.end_flow_track_id AND
-      flow.end_flow_id != move.begin_flow_id
-  WHERE flow.end_flow_id IS NOT NULL;
+    flow.end_flow_trace_id = move.trace_id
+    AND move.begin_flow_track_id = flow.end_flow_track_id
+    AND flow.end_flow_id != move.begin_flow_id
+WHERE flow.end_flow_id IS NOT NULL;
 
 -- Now that we have the begin and the end we need to find the parent stack of
 -- both. If the end didn't happen on the browser (end is NULL), then we can
 -- ignore it because it couldn't have generated a GestureScrollUpdate.
 DROP TABLE IF EXISTS touch_move_with_ancestor;
 CREATE TABLE touch_move_with_ancestor AS
-  SELECT
-    begin.id AS begin_ancestor_id,
-    end.id AS end_ancestor_id,
-    end.ts AS end_ancestor_ts,
-    end.dur AS end_ancestor_dur,
-    end.track_id AS end_ancestor_track_id,
-    move.*
-  FROM
-    touch_move_begin_and_end_flow move JOIN
-    ancestor_slice(begin_flow_id) begin ON begin.depth = 0 LEFT JOIN
-    ancestor_slice(end_flow_id) end ON end.depth = 0;
+SELECT
+  begin.id AS begin_ancestor_id,
+  end.id AS end_ancestor_id,
+  end.ts AS end_ancestor_ts,
+  end.dur AS end_ancestor_dur,
+  end.track_id AS end_ancestor_track_id,
+  move.*
+FROM
+  touch_move_begin_and_end_flow move JOIN
+  ancestor_slice(begin_flow_id) begin ON begin.depth = 0 LEFT JOIN
+  ancestor_slice(end_flow_id) end ON end.depth = 0;
 
 -- Now take the parent stack for the end and find if a GestureScrollUpdate was
 -- launched that share the same parent as the end flow event for the TouchMove.
@@ -97,66 +97,66 @@
 -- depending on if the begin flow event is in the same stack.
 DROP TABLE IF EXISTS blocking_touch_move_with_scroll_update;
 CREATE TABLE blocking_touch_move_with_scroll_update AS
-  SELECT
-      move.begin_ancestor_id != move.end_ancestor_id AS blocking_touch_move,
-      scroll.scroll_begin_flow_id,
-      scroll.scroll_begin_flow_trace_id,
-      scroll.scroll_id,
-      move.*
-    FROM touch_move_with_ancestor move LEFT JOIN (
-      SELECT in_flow.*, in_scroll.scroll_id FROM (
-        SELECT
-          MIN(slice.id) AS scroll_begin_flow_id,
-          slice.ts AS scroll_begin_flow_ts,
-          slice.track_id AS scroll_begin_flow_track_id,
-          EXTRACT_ARG(slice.arg_set_id, "chrome_latency_info.trace_id")
-              AS scroll_begin_flow_trace_id,
-          ancestor.id AS scroll_begin_flow_ancestor_id
-        FROM
-          slice LEFT JOIN
-          ancestor_slice(slice.id) AS ancestor ON ancestor.depth = 0
-        WHERE
-          slice.name = "LatencyInfo.Flow" AND
-          EXTRACT_ARG(slice.arg_set_id, "chrome_latency_info.step") IS NULL
-        GROUP BY scroll_begin_flow_trace_id
-      ) in_flow JOIN (
-        SELECT
-          id AS scroll_id,
-          EXTRACT_ARG(arg_set_id, "chrome_latency_info.trace_id")
-              AS scroll_trace_id
-        FROM slice in_scroll
-        WHERE
-          name = "InputLatency::GestureScrollUpdate" AND
-          dur != -1 AND
-          NOT EXTRACT_ARG(arg_set_id, "chrome_latency_info.is_coalesced")
-      ) in_scroll ON
-          in_scroll.scroll_trace_id = in_flow.scroll_begin_flow_trace_id
-    ) scroll ON
-      scroll.scroll_begin_flow_track_id = move.end_ancestor_track_id AND
-      scroll.scroll_begin_flow_ancestor_id = move.end_ancestor_id AND
-      scroll.scroll_begin_flow_ts > move.end_ancestor_ts AND
-      scroll.scroll_begin_flow_ts < move.end_ancestor_ts + move.end_ancestor_dur AND
-      scroll.scroll_begin_flow_id > move.end_ancestor_id
-    WHERE scroll.scroll_id IS NOT NULL;
+SELECT
+  move.begin_ancestor_id != move.end_ancestor_id AS blocking_touch_move,
+  scroll.scroll_begin_flow_id,
+  scroll.scroll_begin_flow_trace_id,
+  scroll.scroll_id,
+  move.*
+FROM touch_move_with_ancestor move LEFT JOIN (
+  SELECT in_flow.*, in_scroll.scroll_id FROM (
+    SELECT
+      MIN(slice.id) AS scroll_begin_flow_id,
+      slice.ts AS scroll_begin_flow_ts,
+      slice.track_id AS scroll_begin_flow_track_id,
+      EXTRACT_ARG(slice.arg_set_id, "chrome_latency_info.trace_id")
+      AS scroll_begin_flow_trace_id,
+      ancestor.id AS scroll_begin_flow_ancestor_id
+    FROM
+      slice LEFT JOIN
+      ancestor_slice(slice.id) AS ancestor ON ancestor.depth = 0
+    WHERE
+      slice.name = "LatencyInfo.Flow"
+      AND EXTRACT_ARG(slice.arg_set_id, "chrome_latency_info.step") IS NULL
+    GROUP BY scroll_begin_flow_trace_id
+  ) in_flow JOIN (
+    SELECT
+      id AS scroll_id,
+      EXTRACT_ARG(arg_set_id, "chrome_latency_info.trace_id")
+      AS scroll_trace_id
+    FROM slice in_scroll
+    WHERE
+      name = "InputLatency::GestureScrollUpdate"
+      AND dur != -1
+      AND NOT EXTRACT_ARG(arg_set_id, "chrome_latency_info.is_coalesced")
+  ) in_scroll ON
+    in_scroll.scroll_trace_id = in_flow.scroll_begin_flow_trace_id
+) scroll ON
+  scroll.scroll_begin_flow_track_id = move.end_ancestor_track_id
+  AND scroll.scroll_begin_flow_ancestor_id = move.end_ancestor_id
+  AND scroll.scroll_begin_flow_ts > move.end_ancestor_ts
+  AND scroll.scroll_begin_flow_ts < move.end_ancestor_ts + move.end_ancestor_dur
+  AND scroll.scroll_begin_flow_id > move.end_ancestor_id
+WHERE scroll.scroll_id IS NOT NULL;
 
 -- Now filter out any TouchMoves that weren't during a complete scroll. Most of
 -- the other ones will be null anyway since they won't have
 -- GestureScrollUpdates.
 DROP VIEW IF EXISTS scroll_jank_cause_blocking_touch_move;
 CREATE VIEW scroll_jank_cause_blocking_touch_move AS
-   SELECT
-      id,
-      ts,
-      dur,
-      track_id,
-      blocking_touch_move,
-      scroll_id
-  FROM joined_scroll_begin_and_end begin_and_end JOIN (
+SELECT
+  id,
+  ts,
+  dur,
+  track_id,
+  blocking_touch_move,
+  scroll_id
+FROM joined_scroll_begin_and_end begin_and_end JOIN (
     SELECT
       *
     FROM blocking_touch_move_with_scroll_update
   ) touch ON
-    touch.ts <= begin_and_end.end_ts AND
-    touch.ts > begin_and_end.begin_ts + begin_and_end.begin_dur AND
-    touch.trace_id > begin_and_end.begin_trace_id AND
-    touch.trace_id < begin_and_end.end_trace_id;
+    touch.ts <= begin_and_end.end_ts
+    AND touch.ts > begin_and_end.begin_ts + begin_and_end.begin_dur
+    AND touch.trace_id > begin_and_end.begin_trace_id
+    AND touch.trace_id < begin_and_end.end_trace_id;
diff --git a/src/trace_processor/metrics/sql/chrome/scroll_jank_cause_get_bitmap.sql b/src/trace_processor/metrics/sql/chrome/scroll_jank_cause_get_bitmap.sql
index e0a67e0..fd57296 100644
--- a/src/trace_processor/metrics/sql/chrome/scroll_jank_cause_get_bitmap.sql
+++ b/src/trace_processor/metrics/sql/chrome/scroll_jank_cause_get_bitmap.sql
@@ -46,9 +46,9 @@
 WHERE
   track_id = (
     SELECT id FROM browser_main_track_id
-  ) AND
-  name = "LatencyInfo.Flow"
-  GROUP BY trace_id;
+  )
+  AND name = "LatencyInfo.Flow"
+GROUP BY trace_id;
 
 --------------------------------------------------------------------------------
 -- Join the relevant tracks/flows to the individual scrolls.
@@ -76,55 +76,55 @@
     track_id
   FROM scroll_jank
 ) scroll JOIN browser_flows ON
-    scroll.trace_id = browser_flows.trace_id;
+  scroll.trace_id = browser_flows.trace_id;
 
 --------------------------------------------------------------------------------
 -- Below we determine if there was any bitmaps taken on the browser main.
 --------------------------------------------------------------------------------
 DROP VIEW IF EXISTS get_bitmap_calls;
 CREATE VIEW get_bitmap_calls AS
-  SELECT
-    id,
-    ts,
-    dur,
-    track_id
-  FROM slice
-  WHERE
-    slice.name = "ViewResourceAdapter:getBitmap" AND
-    track_id = (SELECT id FROM browser_main_track_id);
+SELECT
+  id,
+  ts,
+  dur,
+  track_id
+FROM slice
+WHERE
+  slice.name = "ViewResourceAdapter:getBitmap"
+  AND track_id = (SELECT id FROM browser_main_track_id);
 
 DROP VIEW IF EXISTS toolbar_bitmaps;
 CREATE VIEW toolbar_bitmaps AS
-  SELECT
-    slice.id,
-    slice.ts,
-    slice.dur,
-    slice.track_id,
-    ancestor.id AS ancestor_id
-  FROM
-    slice JOIN
-    ancestor_slice(slice.id) AS ancestor ON
-      ancestor.depth = slice.depth - 1
-  WHERE
-    slice.name = "ToolbarLayout.draw" AND
-    ancestor.name = "ViewResourceAdapter:getBitmap" AND
-    slice.track_id = (SELECT id FROM browser_main_track_id);
+SELECT
+  slice.id,
+  slice.ts,
+  slice.dur,
+  slice.track_id,
+  ancestor.id AS ancestor_id
+FROM
+  slice JOIN
+  ancestor_slice(slice.id) AS ancestor ON
+    ancestor.depth = slice.depth - 1
+WHERE
+  slice.name = "ToolbarLayout.draw"
+  AND ancestor.name = "ViewResourceAdapter:getBitmap"
+  AND slice.track_id = (SELECT id FROM browser_main_track_id);
 
 DROP VIEW IF EXISTS get_bitmaps_and_toolbar;
 CREATE VIEW get_bitmaps_and_toolbar AS
-  SELECT
-    bitmap.id AS id,
-    bitmap.ts AS ts,
-    bitmap.dur AS dur,
-    bitmap.track_id AS track_id,
-    toolbar.id AS toolbar_id,
-    toolbar.ts AS toolbar_ts,
-    toolbar.dur AS toolbar_dur,
-    toolbar.track_id AS toolbar_track_id
-  FROM
-    get_bitmap_calls bitmap LEFT JOIN
-    toolbar_bitmaps toolbar ON
-      toolbar.ancestor_id = bitmap.id;
+SELECT
+  bitmap.id AS id,
+  bitmap.ts AS ts,
+  bitmap.dur AS dur,
+  bitmap.track_id AS track_id,
+  toolbar.id AS toolbar_id,
+  toolbar.ts AS toolbar_ts,
+  toolbar.dur AS toolbar_dur,
+  toolbar.track_id AS toolbar_track_id
+FROM
+  get_bitmap_calls bitmap LEFT JOIN
+  toolbar_bitmaps toolbar ON
+    toolbar.ancestor_id = bitmap.id;
 
 --------------------------------------------------------------------------------
 -- Take bitmaps and determine if it could have been blocked by a scroll. I.E. if
@@ -141,34 +141,19 @@
   bitmap.ts,
   bitmap.dur,
   bitmap.track_id,
-  CASE WHEN
-      bitmap.track_id = scroll.browser_track_id AND
-      bitmap.ts < scroll.browser_flow_ts THEN
-    TRUE
-  ELSE
-    FALSE
-  END AS blocked_by_bitmap,
-  CASE WHEN
-      bitmap.track_id = scroll.browser_track_id AND
-      bitmap.toolbar_id IS NOT NULL AND
-      bitmap.ts < scroll.browser_flow_ts THEN
-    TRUE
-  ELSE
-    FALSE
-  END AS blocked_by_toolbar,
-  CASE WHEN
-      bitmap.track_id = scroll.browser_track_id AND
-      bitmap.toolbar_id IS NULL AND
-      bitmap.ts < scroll.browser_flow_ts THEN
-    TRUE
-  ELSE
-    FALSE
-  END AS blocked_by_bitmap_no_toolbar
+  COALESCE(bitmap.track_id = scroll.browser_track_id
+    AND bitmap.ts < scroll.browser_flow_ts, FALSE) AS blocked_by_bitmap,
+  COALESCE(bitmap.track_id = scroll.browser_track_id
+    AND bitmap.toolbar_id IS NOT NULL
+    AND bitmap.ts < scroll.browser_flow_ts, FALSE) AS blocked_by_toolbar,
+  COALESCE(bitmap.track_id = scroll.browser_track_id
+    AND bitmap.toolbar_id IS NULL
+    AND bitmap.ts < scroll.browser_flow_ts, FALSE) AS blocked_by_bitmap_no_toolbar
 FROM
   scroll_with_browser_flows scroll JOIN
   get_bitmaps_and_toolbar bitmap ON
-  scroll.ts + scroll.dur >= bitmap.ts AND
-  bitmap.ts + bitmap.dur >= scroll.ts;
+    scroll.ts + scroll.dur >= bitmap.ts
+    AND bitmap.ts + bitmap.dur >= scroll.ts;
 
 
 --------------------------------------------------------------------------------
diff --git a/src/trace_processor/metrics/sql/chrome/scroll_jank_cause_queuing_delay.sql b/src/trace_processor/metrics/sql/chrome/scroll_jank_cause_queuing_delay.sql
index 9b45876..1e2ecd2 100644
--- a/src/trace_processor/metrics/sql/chrome/scroll_jank_cause_queuing_delay.sql
+++ b/src/trace_processor/metrics/sql/chrome/scroll_jank_cause_queuing_delay.sql
@@ -19,97 +19,101 @@
 -- See b/184134310 why we remove ThreadController active.
 DROP VIEW IF EXISTS blocking_tasks_no_threadcontroller_active;
 CREATE VIEW blocking_tasks_no_threadcontroller_active AS
- SELECT
-    slice.*,
-    ancestor.id AS task_ancestor_id,
-    ancestor.name AS task_ancestor_name
-  FROM
-    chrome_thread_slice AS slice LEFT JOIN
-    ancestor_slice(slice.id) as ancestor ON ancestor.id = slice.parent_id
-  WHERE
-    slice.name != "ThreadController active" AND
-    (slice.depth = 0 OR ancestor.name = "ThreadController active");
+SELECT
+  slice.*,
+  ancestor.id AS task_ancestor_id,
+  ancestor.name AS task_ancestor_name
+FROM
+  chrome_thread_slice AS slice LEFT JOIN
+  ancestor_slice(slice.id) AS ancestor ON ancestor.id = slice.parent_id
+WHERE
+  slice.name != "ThreadController active"
+  AND (slice.depth = 0 OR ancestor.name = "ThreadController active");
 
 -- Sort track ids to optimize joining with slices
 -- as engine doesn't do the sort to join in O(LogN)
 -- per row by default
-DROP VIEW IF EXISTS chrome_annotated_threads_and_processes;
-CREATE VIEW chrome_annotated_threads_and_processes AS
-  SELECT
-    thread_track.id AS track_id,
-    chrome_thread.canonical_name AS thread_name,
-    chrome_process.process_type AS process_name
-  FROM
-    thread_track JOIN
-    chrome_thread JOIN
-    chrome_process ON
-    thread_track.utid = chrome_thread.utid AND
-    chrome_thread.upid = chrome_process.upid
-  ORDER BY
-    track_id ASC;
+-- TODO(243897379): switch this back to a view once we understand why rolling SQLite to
+-- 3.39.2 causes slowdowns.
+DROP TABLE IF EXISTS chrome_annotated_threads_and_processes;
+CREATE TABLE chrome_annotated_threads_and_processes AS
+SELECT
+  thread_track.id AS track_id,
+  chrome_thread.canonical_name AS thread_name,
+  chrome_process.process_type AS process_name
+FROM
+  thread_track JOIN
+  chrome_thread JOIN
+  chrome_process ON
+    thread_track.utid = chrome_thread.utid
+    AND chrome_thread.upid = chrome_process.upid
+ORDER BY
+  track_id ASC;
 
 -- See b/166441398 & crbug/1094361 for why we remove threadpool (originally
 -- the -to-End step). In essence -to-End is often reported on the ThreadPool
 -- after the fact with explicit timestamps so it being blocked isn't noteworthy.
-DROP VIEW IF EXISTS blocking_chrome_tasks_without_threadpool;
-CREATE VIEW blocking_chrome_tasks_without_threadpool AS
-  SELECT
-     slice.*,
-     annotations.thread_name AS thread_name,
-     annotations.process_name AS process_name
-  FROM
-    blocking_tasks_no_threadcontroller_active AS slice JOIN
-    chrome_annotated_threads_and_processes AS annotations ON
+-- TODO(243897379): switch this back to a view once we understand why rolling SQLite to
+-- 3.39.2 causes slowdowns.
+DROP TABLE IF EXISTS blocking_chrome_tasks_without_threadpool;
+CREATE TABLE blocking_chrome_tasks_without_threadpool AS
+SELECT
+  slice.*,
+  annotations.thread_name AS thread_name,
+  annotations.process_name AS process_name
+FROM
+  blocking_tasks_no_threadcontroller_active AS slice JOIN
+  chrome_annotated_threads_and_processes AS annotations ON
     annotations.track_id = slice.track_id
-  WHERE
-    NOT (thread_name GLOB "*ThreadPool*");
+WHERE
+  NOT(thread_name GLOB "*ThreadPool*");
 
 -- This view grabs any slice that could have prevented any GestureScrollUpdate
 -- flow event from being run (queuing delays). For RunTask we know that its
 -- generic (and thus hard to figure out whats the cause) so we grab the src
 -- location to make it more meaningful.
 --
--- See b/184134310 for why we allow depth == 1 and ancestor.id is null (which
+-- See b/184134310 for why we allow depth = 1 and ancestor.id is null (which
 -- implies its a "ThreadController active" slice because we removed it
 -- previously).
 DROP TABLE IF EXISTS blocking_tasks_queuing_delay;
 CREATE TABLE blocking_tasks_queuing_delay AS
-  SELECT
-    EXTRACT_ARG(slice.arg_set_id, "task.posted_from.file_name") as file,
-    EXTRACT_ARG(slice.arg_set_id, "task.posted_from.function_name") as function,
-    trace_id,
-    queuing_time_ns,
-    avg_vsync_interval,
-    next_track_id,
-    CASE WHEN queuing.ancestor_end <= slice.ts THEN
+SELECT
+  EXTRACT_ARG(slice.arg_set_id, "task.posted_from.file_name") AS file,
+  EXTRACT_ARG(slice.arg_set_id, "task.posted_from.function_name") AS function,
+  trace_id,
+  queuing_time_ns,
+  avg_vsync_interval,
+  next_track_id,
+  CASE WHEN queuing.ancestor_end <= slice.ts THEN
       CASE WHEN slice.ts + slice.dur <= queuing.maybe_next_ancestor_ts THEN
         slice.dur
-      ELSE
-        queuing.maybe_next_ancestor_ts - slice.ts
+        ELSE
+          queuing.maybe_next_ancestor_ts - slice.ts
       END
     ELSE
       CASE WHEN slice.ts + slice.dur <= queuing.maybe_next_ancestor_ts THEN
         slice.ts + slice.dur - queuing.ancestor_end
-      ELSE
-        queuing.maybe_next_ancestor_ts - queuing.ancestor_end
+        ELSE
+          queuing.maybe_next_ancestor_ts - queuing.ancestor_end
       END
-    END AS dur_overlapping_ns,
-    description,
-    scroll_slice_id,
-    scroll_ts,
-    scroll_dur,
-    scroll_track_id,
-    jank,
-    slice.*
-  FROM
-    scroll_flow_event_queuing_delay queuing JOIN
-    blocking_chrome_tasks_without_threadpool AS slice ON
-        slice.ts + slice.dur > queuing.ancestor_end AND
-        queuing.maybe_next_ancestor_ts > slice.ts AND
-        slice.track_id = queuing.next_track_id
-  WHERE
-    queuing_time_ns IS NOT NULL AND
-    queuing_time_ns > 0;
+  END AS dur_overlapping_ns,
+  description,
+  scroll_slice_id,
+  scroll_ts,
+  scroll_dur,
+  scroll_track_id,
+  jank,
+  slice.*
+FROM
+  scroll_flow_event_queuing_delay queuing JOIN
+  blocking_chrome_tasks_without_threadpool AS slice ON
+    slice.ts + slice.dur > queuing.ancestor_end
+    AND queuing.maybe_next_ancestor_ts > slice.ts
+    AND slice.track_id = queuing.next_track_id
+WHERE
+  queuing_time_ns IS NOT NULL
+  AND queuing_time_ns > 0;
 
 -- Now for each toplevel task (depth = 0 from above) we want to grab all their
 -- children slices. This is done by joining on descendant_slice which is a
@@ -121,52 +125,52 @@
 -- "interface_name" since that is more descriptive for our jank purposes.
 DROP VIEW IF EXISTS all_descendant_blocking_tasks_queuing_delay;
 CREATE VIEW all_descendant_blocking_tasks_queuing_delay AS
-  SELECT
-    descendant.id AS descendant_id,
-    descendant.ts AS descendant_ts,
-    descendant.dur AS descendant_dur,
-    COALESCE(
-      IIF(descendant.arg_set_id IS NOT NULL,
-          EXTRACT_ARG(descendant.arg_set_id,
-              "chrome_mojo_event_info.watcher_notify_interface_tag"),
-          NULL),
-      IIF(descendant.arg_set_id IS NOT NULL,
-          EXTRACT_ARG(descendant.arg_set_id,
-              "chrome_mojo_event_info.mojo_interface_tag"),
-          NULL),
-      descendant.name) AS descendant_name,
-    EXTRACT_ARG(descendant.arg_set_id,
-        "chrome_mojo_event_info.ipc_hash") AS descendant_ipc_hash,
-    descendant.parent_id As descendant_parent_id,
-    descendant.depth AS descendant_depth,
-    descendant.category AS descendant_category,
-    base.*
-  FROM
-    blocking_tasks_queuing_delay base LEFT JOIN
-    descendant_slice(base.id) AS descendant;
+SELECT
+  descendant.id AS descendant_id,
+  descendant.ts AS descendant_ts,
+  descendant.dur AS descendant_dur,
+  COALESCE(
+    IIF(descendant.arg_set_id IS NOT NULL,
+      EXTRACT_ARG(descendant.arg_set_id,
+        "chrome_mojo_event_info.watcher_notify_interface_tag"),
+      NULL),
+    IIF(descendant.arg_set_id IS NOT NULL,
+      EXTRACT_ARG(descendant.arg_set_id,
+        "chrome_mojo_event_info.mojo_interface_tag"),
+      NULL),
+    descendant.name) AS descendant_name,
+  EXTRACT_ARG(descendant.arg_set_id,
+    "chrome_mojo_event_info.ipc_hash") AS descendant_ipc_hash,
+  descendant.parent_id AS descendant_parent_id,
+  descendant.depth AS descendant_depth,
+  descendant.category AS descendant_category,
+  base.*
+FROM
+  blocking_tasks_queuing_delay base LEFT JOIN
+  descendant_slice(base.id) AS descendant;
 
 DROP TABLE IF EXISTS all_descendant_blocking_tasks_queuing_delay_with_cpu_time;
 CREATE TABLE all_descendant_blocking_tasks_queuing_delay_with_cpu_time AS
-  SELECT
-    cpu.thread_dur AS descendant_thread_dur,
-    CAST(cpu.thread_dur AS REAL) / descendant.thread_dur
-        AS descendant_cpu_percentage,
-    CAST(cpu.thread_dur AS REAL) /
-        (descendant.thread_dur /
-          (1 << (descendant.descendant_depth - 1))) > 0.5
-            AS descendant_cpu_time_above_relative_threshold,
-    descendant_dur / descendant.dur AS descendant_dur_percentage,
-    descendant_dur /
-        (descendant.dur / (1 << (descendant.descendant_depth - 1))) > 0.5
-        AS descendant_dur_above_relative_threshold,
-    descendant.*
-  FROM
-    all_descendant_blocking_tasks_queuing_delay descendant LEFT JOIN (
-      SELECT
-        id, thread_dur
-      FROM chrome_thread_slice
-    ) AS cpu ON
-        cpu.id = descendant.descendant_id;
+SELECT
+  cpu.thread_dur AS descendant_thread_dur,
+  CAST(cpu.thread_dur AS REAL) / descendant.thread_dur
+  AS descendant_cpu_percentage,
+  CAST(cpu.thread_dur AS REAL)
+  / (descendant.thread_dur
+    / (1 << (descendant.descendant_depth - 1))) > 0.5
+  AS descendant_cpu_time_above_relative_threshold,
+  descendant_dur / descendant.dur AS descendant_dur_percentage,
+  descendant_dur
+  / (descendant.dur / (1 << (descendant.descendant_depth - 1))) > 0.5
+  AS descendant_dur_above_relative_threshold,
+  descendant.*
+FROM
+  all_descendant_blocking_tasks_queuing_delay descendant LEFT JOIN (
+    SELECT
+      id, thread_dur
+    FROM chrome_thread_slice
+  ) AS cpu ON
+    cpu.id = descendant.descendant_id;
 
 -- Now that we've generated the descendant count how many siblings each row
 -- has. Recall that all the top level tasks are repeated but each row represents
@@ -176,19 +180,19 @@
 -- the current slice.
 DROP VIEW IF EXISTS counted_descendant_blocking_tasks_queuing_delay;
 CREATE VIEW counted_descendant_blocking_tasks_queuing_delay AS
-  SELECT
-    base.*,
-    COALESCE(single_descendant.number_of_siblings, 0) AS number_of_siblings
-  FROM
-    all_descendant_blocking_tasks_queuing_delay_with_cpu_time base LEFT JOIN (
-      SELECT
-        descendant_parent_id,
-        COUNT(*) - 1 AS number_of_siblings
-      FROM all_descendant_blocking_tasks_queuing_delay_with_cpu_time
-      WHERE descendant_parent_id IS NOT NULL
-      GROUP BY 1
+SELECT
+  base.*,
+  COALESCE(single_descendant.number_of_siblings, 0) AS number_of_siblings
+FROM
+  all_descendant_blocking_tasks_queuing_delay_with_cpu_time base LEFT JOIN (
+    SELECT
+      descendant_parent_id,
+      COUNT(*) - 1 AS number_of_siblings
+    FROM all_descendant_blocking_tasks_queuing_delay_with_cpu_time
+    WHERE descendant_parent_id IS NOT NULL
+    GROUP BY 1
   ) single_descendant ON
-      single_descendant.descendant_parent_id = base.descendant_parent_id;
+    single_descendant.descendant_parent_id = base.descendant_parent_id;
 
 -- Now we group by the |id| which is the top level task id and find the first
 -- descendant_depth where we have a sibling. We need this because we only want
@@ -196,29 +200,29 @@
 -- reason about what that code is doing.
 DROP VIEW IF EXISTS blocking_tasks_queuing_delay_with_invalid_depth;
 CREATE VIEW blocking_tasks_queuing_delay_with_invalid_depth AS
-  SELECT
-    base.*,
-    (
-      descendant_cpu_time_above_relative_threshold AND
-      descendant_cpu_percentage > 0.05
-    ) OR (
-      descendant_dur_above_relative_threshold AND
-      descendant_dur_percentage > 0.05
-    ) AS descendant_major_slice,
-    COALESCE(depth.invalid_depth, 10) AS invalid_depth
-  FROM
-    counted_descendant_blocking_tasks_queuing_delay base LEFT JOIN (
-      SELECT
-        id,
-        MIN(descendant_depth) AS invalid_depth
-      FROM counted_descendant_blocking_tasks_queuing_delay
-      WHERE number_of_siblings >= 1
-      GROUP BY 1
-    ) AS depth ON base.id = depth.id
-  ORDER BY
-    descendant_depth ASC,
-    descendant_cpu_percentage DESC,
-    descendant_dur_percentage DESC;
+SELECT
+  base.*,
+  (
+    descendant_cpu_time_above_relative_threshold
+    AND descendant_cpu_percentage > 0.05
+  ) OR (
+    descendant_dur_above_relative_threshold
+    AND descendant_dur_percentage > 0.05
+  ) AS descendant_major_slice,
+  COALESCE(depth.invalid_depth, 10) AS invalid_depth
+FROM
+  counted_descendant_blocking_tasks_queuing_delay base LEFT JOIN (
+    SELECT
+      id,
+      MIN(descendant_depth) AS invalid_depth
+    FROM counted_descendant_blocking_tasks_queuing_delay
+    WHERE number_of_siblings >= 1
+    GROUP BY 1
+  ) AS depth ON base.id = depth.id
+ORDER BY
+  descendant_depth ASC,
+  descendant_cpu_percentage DESC,
+  descendant_dur_percentage DESC;
 
 -- Now to get back to a single output per top level task we group by all the
 -- toplevel fields and aggregate the descendant fields. We only include the
@@ -226,97 +230,97 @@
 -- |invalid_depth|).
 DROP VIEW IF EXISTS descendant_blocking_tasks_queuing_delay;
 CREATE VIEW descendant_blocking_tasks_queuing_delay AS
-  SELECT
-    id,
-    ts,
-    dur,
-    track_id,
-    trace_id,
-    name,
-    category,
-    scroll_slice_id AS scroll_id,
-    scroll_ts,
-    scroll_dur,
-    scroll_track_id,
-    jank,
-    queuing_time_ns,
-    dur_overlapping_ns,
-    description,
-    replace(file, rtrim(file, replace(file, '/', '')), '') AS file,
-    thread_name,
-    process_name,
-    function,
-    avg_vsync_interval,
-    GROUP_CONCAT(
-      CASE WHEN descendant_depth < invalid_depth OR descendant_major_slice THEN
+SELECT
+  id,
+  ts,
+  dur,
+  track_id,
+  trace_id,
+  name,
+  category,
+  scroll_slice_id AS scroll_id,
+  scroll_ts,
+  scroll_dur,
+  scroll_track_id,
+  jank,
+  queuing_time_ns,
+  dur_overlapping_ns,
+  description,
+  replace(file, rtrim(file, replace(file, '/', '')), '') AS file,
+  thread_name,
+  process_name,
+  function,
+  avg_vsync_interval,
+  GROUP_CONCAT(
+    CASE WHEN descendant_depth < invalid_depth OR descendant_major_slice THEN
         descendant_id
       ELSE
         NULL
-      END
-    , "-") AS descendant_id,
-    GROUP_CONCAT(
-      CASE WHEN descendant_depth < invalid_depth OR descendant_major_slice THEN
+    END,
+    "-") AS descendant_id,
+  GROUP_CONCAT(
+    CASE WHEN descendant_depth < invalid_depth OR descendant_major_slice THEN
         descendant_ts
       ELSE
         NULL
-      END
-    , "-") AS descendant_ts,
-    GROUP_CONCAT(
-      CASE WHEN descendant_depth < invalid_depth OR descendant_major_slice THEN
+    END,
+    "-") AS descendant_ts,
+  GROUP_CONCAT(
+    CASE WHEN descendant_depth < invalid_depth OR descendant_major_slice THEN
         descendant_dur
       ELSE
         NULL
-      END
-    , "-") AS descendant_dur,
-    GROUP_CONCAT(
-      CASE WHEN descendant_depth < invalid_depth OR descendant_major_slice THEN
+    END,
+    "-") AS descendant_dur,
+  GROUP_CONCAT(
+    CASE WHEN descendant_depth < invalid_depth OR descendant_major_slice THEN
         descendant_name
       ELSE
         NULL
-      END, "-") AS descendant_name,
-    GROUP_CONCAT(
-      CASE WHEN descendant_depth < invalid_depth OR descendant_major_slice THEN
+    END, "-") AS descendant_name,
+  GROUP_CONCAT(
+    CASE WHEN descendant_depth < invalid_depth OR descendant_major_slice THEN
         descendant_thread_dur
       ELSE
         NULL
-      END
-    , "-") AS descendant_thread_dur,
-    GROUP_CONCAT(
-      CASE WHEN descendant_depth < invalid_depth OR descendant_major_slice THEN
+    END,
+    "-") AS descendant_thread_dur,
+  GROUP_CONCAT(
+    CASE WHEN descendant_depth < invalid_depth OR descendant_major_slice THEN
         descendant_cpu_percentage
       ELSE
         NULL
-      END
-    , "-") AS descendant_cpu_time,
-    GROUP_CONCAT(
-      CASE WHEN descendant_category = "mojom" THEN
+    END,
+    "-") AS descendant_cpu_time,
+  GROUP_CONCAT(
+    CASE WHEN descendant_category = "mojom" THEN
         descendant_name
       ELSE
         NULL
-      END
-    , "-") AS mojom_name,
-    -- All ipc_hashes should be equal so just select the first non-null one.
-    MIN(descendant_ipc_hash) AS mojom_ipc_hash,
-    GROUP_CONCAT(
-      CASE WHEN
-        descendant_category = "toplevel" AND
-        descendant_name NOT GLOB "*ThreadController*" THEN
-          descendant_name
-      ELSE
-          NULL
-      END
-    , "-") AS toplevel_name,
-    GROUP_CONCAT(
-      CASE WHEN descendant_category = "Java" THEN
+    END,
+    "-") AS mojom_name,
+  -- All ipc_hashes should be equal so just select the first non-null one.
+  MIN(descendant_ipc_hash) AS mojom_ipc_hash,
+  GROUP_CONCAT(
+    CASE WHEN
+        descendant_category = "toplevel"
+        AND descendant_name NOT GLOB "*ThreadController*" THEN
         descendant_name
       ELSE
         NULL
-      END
-    , "-") AS java_name
-  FROM
-    blocking_tasks_queuing_delay_with_invalid_depth
-  GROUP BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
-  ORDER BY descendant_cpu_percentage DESC;
+    END,
+    "-") AS toplevel_name,
+  GROUP_CONCAT(
+    CASE WHEN descendant_category = "Java" THEN
+        descendant_name
+      ELSE
+        NULL
+    END,
+    "-") AS java_name
+FROM
+  blocking_tasks_queuing_delay_with_invalid_depth
+GROUP BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
+ORDER BY descendant_cpu_percentage DESC;
 
 SELECT CREATE_FUNCTION(
   -- Function prototype: takes a '-' separated list of slice names (formed by
@@ -443,70 +447,70 @@
 -- Create a common name for each "cause" based on the slice stack we found.
 DROP VIEW IF EXISTS scroll_jank_cause_queuing_delay_temp;
 CREATE VIEW scroll_jank_cause_queuing_delay_temp AS
-  SELECT
-    TopLevelName(name, function, file) || COALESCE(
-      "-" || descendant_name, "") AS location,
-    TopLevelName(name, function, file) || COALESCE(
-      "-" || GetFirstSliceNameOrNull(mojom_name)
-          || COALESCE("(ipc=" || mojom_ipc_hash || ")", ""),
-      "-" || GetFirstSliceNameOrNull(toplevel_name)
-          || COALESCE("(ipc=" || mojom_ipc_hash || ")", ""),
-      "-" || GetJavaSliceSummaryOrNull(java_name),
-      UnknownEventOrEmptyString(name, category, descendant_name)
-    ) AS restricted_location,
-    base.*
-  FROM descendant_blocking_tasks_queuing_delay base;
+SELECT
+  TopLevelName(name, function, file) || COALESCE(
+    "-" || descendant_name, "") AS location,
+  TopLevelName(name, function, file) || COALESCE(
+    "-" || GetFirstSliceNameOrNull(mojom_name)
+    || COALESCE("(ipc=" || mojom_ipc_hash || ")", ""),
+    "-" || GetFirstSliceNameOrNull(toplevel_name)
+    || COALESCE("(ipc=" || mojom_ipc_hash || ")", ""),
+    "-" || GetJavaSliceSummaryOrNull(java_name),
+    UnknownEventOrEmptyString(name, category, descendant_name)
+  ) AS restricted_location,
+  base.*
+FROM descendant_blocking_tasks_queuing_delay base;
 
 -- Figure out the average time taken during non-janky scrolls updates for each
 -- TraceEvent (metric_name) stack.
 DROP VIEW IF EXISTS scroll_jank_cause_queuing_delay_average_no_jank_time;
 CREATE VIEW scroll_jank_cause_queuing_delay_average_no_jank_time AS
-  SELECT
-    location,
-    AVG(dur_overlapping_ns) as avg_dur_overlapping_ns
-  FROM scroll_jank_cause_queuing_delay_temp
-  WHERE NOT jank
-  GROUP BY 1;
+SELECT
+  location,
+  AVG(dur_overlapping_ns) AS avg_dur_overlapping_ns
+FROM scroll_jank_cause_queuing_delay_temp
+WHERE NOT jank
+GROUP BY 1;
 
 -- Again figure out the average time, but based on a more restricted set of
 -- trace events.
 DROP VIEW IF EXISTS scroll_jank_cause_queuing_delay_average_no_jank_time_restricted;
 CREATE VIEW scroll_jank_cause_queuing_delay_average_no_jank_time_restricted AS
-  SELECT
-    restricted_location,
-    AVG(dur_overlapping_ns) as avg_dur_overlapping_ns_restricted
-  FROM scroll_jank_cause_queuing_delay_temp
-  WHERE NOT jank
-  GROUP BY 1;
+SELECT
+  restricted_location,
+  AVG(dur_overlapping_ns) AS avg_dur_overlapping_ns_restricted
+FROM scroll_jank_cause_queuing_delay_temp
+WHERE NOT jank
+GROUP BY 1;
 
 
 -- Join every row (jank and non-jank with the average non-jank time for the
 -- given metric_name).
 DROP VIEW IF EXISTS scroll_jank_cause_queuing_delay_unannotated;
 CREATE VIEW scroll_jank_cause_queuing_delay_unannotated AS
-  SELECT
-    base.*,
-    'InputLatency.LatencyInfo.Flow.QueuingDelay.' ||
-    CASE WHEN jank THEN 'Jank' ELSE 'NoJank' END || '.BlockingTasksUs.' ||
-      base.location as metric_name,
-    COALESCE(avg_no_jank.avg_dur_overlapping_ns, 0)
-        AS avg_no_jank_dur_overlapping_ns
-  FROM
-    scroll_jank_cause_queuing_delay_temp base LEFT JOIN
-    scroll_jank_cause_queuing_delay_average_no_jank_time avg_no_jank ON
-        base.location = avg_no_jank.location;
+SELECT
+  base.*,
+  'InputLatency.LatencyInfo.Flow.QueuingDelay.'
+  || CASE WHEN jank THEN 'Jank' ELSE 'NoJank' END || '.BlockingTasksUs.'
+  || base.location AS metric_name,
+  COALESCE(avg_no_jank.avg_dur_overlapping_ns, 0)
+  AS avg_no_jank_dur_overlapping_ns
+FROM
+  scroll_jank_cause_queuing_delay_temp base LEFT JOIN
+  scroll_jank_cause_queuing_delay_average_no_jank_time avg_no_jank ON
+    base.location = avg_no_jank.location;
 
 -- Join in the restricted set of trace events average as well to form the final output.
 DROP VIEW IF EXISTS scroll_jank_cause_queuing_delay;
 CREATE VIEW scroll_jank_cause_queuing_delay AS
-  SELECT
-    base.*,
-    'QueuingDelay.' ||
-    CASE WHEN jank THEN 'Jank' ELSE 'NoJank' END || '.BlockingTasksUs.' ||
-      base.restricted_location AS restricted_metric_name,
-    COALESCE(avg_no_jank.avg_dur_overlapping_ns_restricted, 0)
-        AS avg_no_jank_dur_overlapping_ns_restricted
-  FROM
-    scroll_jank_cause_queuing_delay_unannotated base LEFT JOIN
-    scroll_jank_cause_queuing_delay_average_no_jank_time_restricted avg_no_jank ON
-        base.restricted_location = avg_no_jank.restricted_location;
+SELECT
+  base.*,
+  'QueuingDelay.'
+  || CASE WHEN jank THEN 'Jank' ELSE 'NoJank' END || '.BlockingTasksUs.'
+  || base.restricted_location AS restricted_metric_name,
+  COALESCE(avg_no_jank.avg_dur_overlapping_ns_restricted, 0)
+  AS avg_no_jank_dur_overlapping_ns_restricted
+FROM
+  scroll_jank_cause_queuing_delay_unannotated base LEFT JOIN
+  scroll_jank_cause_queuing_delay_average_no_jank_time_restricted avg_no_jank ON
+    base.restricted_location = avg_no_jank.restricted_location;
diff --git a/src/trace_processor/metrics/sql/chrome/sufficient_chrome_processes.sql b/src/trace_processor/metrics/sql/chrome/sufficient_chrome_processes.sql
index c44270e..8d727a8 100644
--- a/src/trace_processor/metrics/sql/chrome/sufficient_chrome_processes.sql
+++ b/src/trace_processor/metrics/sql/chrome/sufficient_chrome_processes.sql
@@ -26,23 +26,23 @@
 -- See b/151077536 for historical context.
 DROP VIEW IF EXISTS sufficient_chrome_processes;
 CREATE VIEW sufficient_chrome_processes AS
-  SELECT
-    CASE WHEN (
+SELECT
+  CASE WHEN (
       SELECT COUNT(*) FROM chrome_process) = 0
     THEN
-      FALSE
+    FALSE
     ELSE (
       SELECT COUNT(*) >= 3 FROM (
         SELECT name FROM chrome_process
         WHERE
-          name GLOB "Browser" OR
-          name GLOB "Renderer" OR
-          name GLOB "Gpu" OR
-          name GLOB 'com.android.chrome*' OR
-          name GLOB 'com.chrome.beta*' OR
-          name GLOB 'com.chrome.dev*' OR
-          name GLOB 'com.chrome.canary*' OR
-          name GLOB 'com.google.android.apps.chrome*' OR
-          name GLOB 'org.chromium.chrome*'
+          name GLOB "Browser"
+          OR name GLOB "Renderer"
+          OR name GLOB "Gpu"
+          OR name GLOB 'com.android.chrome*'
+          OR name GLOB 'com.chrome.beta*'
+          OR name GLOB 'com.chrome.dev*'
+          OR name GLOB 'com.chrome.canary*'
+          OR name GLOB 'com.google.android.apps.chrome*'
+          OR name GLOB 'org.chromium.chrome*'
         GROUP BY name
-    )) END AS have_enough_chrome_processes;
\ No newline at end of file
+      )) END AS have_enough_chrome_processes;
diff --git a/src/trace_processor/metrics/sql/chrome/test_chrome_metric.sql b/src/trace_processor/metrics/sql/chrome/test_chrome_metric.sql
index 2e9ee32..af415a4 100644
--- a/src/trace_processor/metrics/sql/chrome/test_chrome_metric.sql
+++ b/src/trace_processor/metrics/sql/chrome/test_chrome_metric.sql
@@ -15,4 +15,4 @@
 
 DROP VIEW IF EXISTS test_chrome_metric_output;
 CREATE VIEW test_chrome_metric_output AS
-SELECT TestChromeMetric('test_value', 1)
+SELECT TestChromeMetric('test_value', 1);
diff --git a/src/trace_processor/metrics/sql/chrome/touch_flow_event.sql b/src/trace_processor/metrics/sql/chrome/touch_flow_event.sql
index 3318ff7..0bbfcc3 100644
--- a/src/trace_processor/metrics/sql/chrome/touch_flow_event.sql
+++ b/src/trace_processor/metrics/sql/chrome/touch_flow_event.sql
@@ -28,4 +28,4 @@
     'prefix', 'touch',
     'gesture_update', 'TouchMove',
     'id_field', 'touch_id'
-    );
+);
diff --git a/src/trace_processor/metrics/sql/chrome/touch_flow_event_queuing_delay.sql b/src/trace_processor/metrics/sql/chrome/touch_flow_event_queuing_delay.sql
index 2c493d1..66b5017 100644
--- a/src/trace_processor/metrics/sql/chrome/touch_flow_event_queuing_delay.sql
+++ b/src/trace_processor/metrics/sql/chrome/touch_flow_event_queuing_delay.sql
@@ -23,4 +23,3 @@
 SELECT RUN_METRIC('chrome/gesture_flow_event_queuing_delay.sql',
     'prefix', 'touch',
     'id_field', 'touch_id');
-
diff --git a/src/trace_processor/metrics/sql/chrome/vsync_intervals.sql b/src/trace_processor/metrics/sql/chrome/vsync_intervals.sql
new file mode 100644
index 0000000..3434056
--- /dev/null
+++ b/src/trace_processor/metrics/sql/chrome/vsync_intervals.sql
@@ -0,0 +1,42 @@
+-- A simple table that checks the time between VSync (this can be used to
+-- determine if we're refreshing at 90 FPS or 60 FPS).
+--
+-- Note: In traces without the "Java" category there will be no VSync
+--       TraceEvents and this table will be empty.
+DROP TABLE IF EXISTS vsync_intervals;
+CREATE TABLE vsync_intervals AS
+SELECT
+  slice_id,
+  ts,
+  dur,
+  track_id,
+  LEAD(ts) OVER(PARTITION BY track_id ORDER BY ts) - ts AS time_to_next_vsync
+FROM slice
+WHERE name = "VSync"
+ORDER BY track_id, ts;
+
+SELECT CREATE_FUNCTION(
+  -- Function: compute the average Vysnc interval of the
+  -- gesture (hopefully this would be either 60 FPS for the whole gesture or 90
+  -- FPS but that isn't always the case) on the given time segment.
+  -- If the trace doesn't contain the VSync TraceEvent we just fall back on
+  -- assuming its 60 FPS (this is the 1.6e+7 in the COALESCE which
+  -- corresponds to 16 ms or 60 FPS).
+  --
+  -- begin_ts: segment start time
+  -- end_ts: segment end time
+  'CalculateAvgVsyncInterval(begin_ts LONG, end_ts LONG)',
+  -- Returns: the average Vysnc interval on this time segment
+  -- or 1.6e+7, if trace doesn't contain the VSync TraceEvent.
+  'FLOAT',
+  'SELECT
+    COALESCE((
+      SELECT
+        CAST(AVG(time_to_next_vsync) AS FLOAT)
+      FROM vsync_intervals in_query
+      WHERE
+        time_to_next_vsync IS NOT NULL AND
+        in_query.ts > $begin_ts AND
+        in_query.ts < $end_ts
+    ), 1e+9 / 60)'
+);
diff --git a/src/trace_processor/metrics/sql/common/BUILD.gn b/src/trace_processor/metrics/sql/common/BUILD.gn
new file mode 100644
index 0000000..61c7ab7
--- /dev/null
+++ b/src/trace_processor/metrics/sql/common/BUILD.gn
@@ -0,0 +1,22 @@
+# Copyright (C) 2022 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/perfetto_sql.gni")
+
+assert(enable_perfetto_trace_processor_sqlite)
+
+perfetto_sql_source_set("common") {
+  sources = [ "parent_slice.sql" ]
+}
diff --git a/src/trace_processor/metrics/sql/common/parent_slice.sql b/src/trace_processor/metrics/sql/common/parent_slice.sql
new file mode 100644
index 0000000..23e99ca
--- /dev/null
+++ b/src/trace_processor/metrics/sql/common/parent_slice.sql
@@ -0,0 +1,17 @@
+--
+-- Copyright 2022 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 IMPORT('common.slices');
diff --git a/src/trace_processor/metrics/sql/experimental/BUILD.gn b/src/trace_processor/metrics/sql/experimental/BUILD.gn
new file mode 100644
index 0000000..1431bd3
--- /dev/null
+++ b/src/trace_processor/metrics/sql/experimental/BUILD.gn
@@ -0,0 +1,29 @@
+# Copyright (C) 2022 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/perfetto_sql.gni")
+
+assert(enable_perfetto_trace_processor_sqlite)
+
+perfetto_sql_source_set("experimental") {
+  sources = [
+    "blink_gc_metric.sql",
+    "chrome_dropped_frames.sql",
+    "chrome_long_latency.sql",
+    "frame_times.sql",
+    "media_metric.sql",
+    "reported_by_page.sql",
+  ]
+}
diff --git a/src/trace_processor/metrics/sql/experimental/blink_gc_metric.sql b/src/trace_processor/metrics/sql/experimental/blink_gc_metric.sql
index c260332..e8307b6 100644
--- a/src/trace_processor/metrics/sql/experimental/blink_gc_metric.sql
+++ b/src/trace_processor/metrics/sql/experimental/blink_gc_metric.sql
@@ -61,14 +61,14 @@
 DROP TABLE IF EXISTS blink_gc_cpu_slice;
 CREATE TABLE blink_gc_cpu_slice AS
 SELECT
-  CASE WHEN dur != 0 THEN cpuDurNs/1e6 ELSE 0.0 END AS cpuDurMs,
+  CASE WHEN dur != 0 THEN cpuDurNs / 1e6 ELSE 0.0 END AS cpuDurMs,
   *
 FROM (
   SELECT
-    COALESCE(EXTRACT_ARG(arg_set_id, 'debug.forced'), FALSE) OR
+    COALESCE(EXTRACT_ARG(arg_set_id, 'debug.forced'), FALSE)
     -- This subquery replaces
     -- metrics.v8.utils.isForcedGarbageCollectionEvent(event)
-    (
+    OR (
       SELECT
         id
       FROM ANCESTOR_SLICE(slice.id) AS ancestor
@@ -78,13 +78,13 @@
     thread.upid || ':' || EXTRACT_ARG(arg_set_id, 'debug.epoch') AS epoch,
     slice.thread_dur AS cpuDurNs,
     slice.*
-  FROM thread_slice as slice
-  INNER JOIN thread_track ON slice.track_id = thread_track.id
-  INNER JOIN thread ON thread_track.utid = thread.id
+  FROM slice
+  JOIN thread_track ON slice.track_id = thread_track.id
+  JOIN thread ON thread_track.utid = thread.id
   WHERE
     slice.dur >= 0 AND (
-    slice.name GLOB "V8.GC*" OR (slice.name GLOB "BlinkGC*" AND NOT forced)
-  )
+      slice.name GLOB "V8.GC*" OR (slice.name GLOB "BlinkGC*" AND NOT forced)
+    )
 );
 
 -- This grabs all the single events for "BlinkGC.*", and restricts to only
@@ -98,7 +98,7 @@
 FROM
   blink_gc_cpu_slice LEFT JOIN
   blink_non_aggregated_gc_event_name AS event_name ON
-      event_name.name = blink_gc_cpu_slice.name
+    event_name.name = blink_gc_cpu_slice.name
 WHERE
   blink_gc_cpu_slice.name GLOB "BlinkGC*" AND NOT forced;
 
@@ -118,7 +118,7 @@
 -- All events that should be summed up to 'blink-gc-mark-roots'.
 DROP VIEW IF EXISTS blink_top_gc_roots_marking_event;
 CREATE VIEW blink_top_gc_roots_marking_event AS
-SELECT * FROM blink_slice WHERE name in (
+SELECT * FROM blink_slice WHERE name IN (
   'BlinkGC.VisitRoots'
 );
 
@@ -126,15 +126,15 @@
 -- 'blink-gc-atomic-pause-mark-transitive-closure'.
 DROP VIEW IF EXISTS blink_gc_atomic_pause_transitive_closure_event;
 CREATE VIEW blink_gc_atomic_pause_transitive_closure_event AS
-SELECT * FROM blink_slice WHERE name in (
+SELECT * FROM blink_slice WHERE name IN (
   'BlinkGC.AtomicPauseMarkTransitiveClosure'
 );
 
 -- All events that should be summed up to 'blink-gc-mark-transitive-closure'.
 DROP VIEW IF EXISTS blink_gc_foreground_marking_transitive_closure_event;
 CREATE VIEW
-      blink_gc_foreground_marking_transitive_closure_event AS
-SELECT * FROM blink_slice WHERE name in (
+blink_gc_foreground_marking_transitive_closure_event AS
+SELECT * FROM blink_slice WHERE name IN (
   'BlinkGC.AtomicPauseMarkTransitiveClosure',
   'BlinkGC.IncrementalMarkingStep',
   'BlinkGC.UnifiedMarkingStep'
@@ -143,7 +143,7 @@
 -- Names of Blink GC foreground marking events in timeline.
 DROP VIEW IF EXISTS blink_top_gc_foreground_marking_event;
 CREATE VIEW blink_top_gc_foreground_marking_event AS
-SELECT * FROM blink_slice WHERE name in (
+SELECT * FROM blink_slice WHERE name IN (
   'BlinkGC.AtomicPauseMarkEpilogue',
   'BlinkGC.AtomicPauseMarkPrologue',
   'BlinkGC.AtomicPauseMarkRoots',
@@ -155,7 +155,7 @@
 -- Names of Blink GC foreground marking events in timeline.
 DROP VIEW IF EXISTS blink_gc_forced_foreground_marking_event;
 CREATE VIEW blink_gc_forced_foreground_marking_event AS
-SELECT * FROM blink_slice WHERE name in (
+SELECT * FROM blink_slice WHERE name IN (
   'BlinkGC.AtomicPauseMarkEpilogue',
   'BlinkGC.AtomicPauseMarkPrologue',
   'BlinkGC.AtomicPauseMarkRoots',
@@ -168,14 +168,14 @@
 -- Names of Blink GC background marking events in timeline.
 DROP VIEW IF EXISTS blink_top_gc_background_marking_event;
 CREATE VIEW blink_top_gc_background_marking_event AS
-SELECT * FROM blink_slice WHERE name in (
+SELECT * FROM blink_slice WHERE name IN (
   'BlinkGC.ConcurrentMarkingStep'
 );
 
 -- Names of Blink GC foreground sweeping events in timeline.
 DROP VIEW IF EXISTS blink_top_gc_foreground_sweeping_event;
 CREATE VIEW blink_top_gc_foreground_sweeping_event AS
-SELECT * FROM blink_slice WHERE name in (
+SELECT * FROM blink_slice WHERE name IN (
   'BlinkGC.CompleteSweep',
   'BlinkGC.LazySweepInIdle',
   'BlinkGC.LazySweepOnAllocation'
@@ -184,16 +184,16 @@
 -- Names of Blink GC background sweeping events in timeline.
 DROP VIEW IF EXISTS blink_top_gc_background_sweeping_event;
 CREATE VIEW blink_top_gc_background_sweeping_event AS
-SELECT * FROM blink_slice WHERE name in (
+SELECT * FROM blink_slice WHERE name IN (
   'BlinkGC.ConcurrentSweepingStep'
 );
 
 -- Names of all Blink Unified GC events in timeline.
 DROP VIEW IF EXISTS blink_top_gc_event;
 CREATE VIEW blink_top_gc_event AS
-SELECT * FROM blink_slice WHERE name in (
+SELECT * FROM blink_slice WHERE name IN (
   SELECT name FROM blink_non_aggregated_gc_event_name
-) OR name in (
+) OR name IN (
   SELECT name FROM blink_gc_atomic_pause_transitive_closure_event
 );
 
@@ -201,7 +201,7 @@
 -- this events need to have an epoch counter in args.epoch.
 DROP VIEW IF EXISTS atomic_pause_event;
 CREATE VIEW atomic_pause_event AS
-SELECT * FROM blink_slice WHERE name in (
+SELECT * FROM blink_slice WHERE name IN (
   'BlinkGC.AtomicPauseMarkEpilogue',
   'BlinkGC.AtomicPauseMarkPrologue',
   'BlinkGC.AtomicPauseMarkRoots',
@@ -221,7 +221,7 @@
   -- metrics.v8.utils.isNotForcedTopGarbageCollectionEvent()
 
   -- These names are found in isTopGarbageCollectionEvent().
-  name in (
+  name IN (
     'V8.GCCompactor',
     'V8.GCFinalizeMC',
     'V8.GCFinalizeMCReduceMemory',
@@ -233,7 +233,7 @@
   ) AND (
     -- This replaces isForcedGarbageCollectionEvent.
     SELECT name FROM ANCESTOR_SLICE(blink_gc_cpu_slice.id) AS ancestor
-    WHERE ancestor.name == 'V8.GCLowMemoryNotification'
+    WHERE ancestor.name = 'V8.GCLowMemoryNotification'
     LIMIT 1
   ) IS NULL
 ) OR (
@@ -245,8 +245,8 @@
     -- This subquery replaces metrics.v8.utils.isGarbageCollectionEvent().
     SELECT name FROM ANCESTOR_SLICE(blink_gc_cpu_slice.id) AS ancestor
     WHERE
-      ancestor.name GLOB "V8.GC*" AND
-      ancestor.name != 'V8.GCLowMemoryNotification'
+      ancestor.name GLOB "V8.GC*"
+      AND ancestor.name != 'V8.GCLowMemoryNotification'
     LIMIT 1
   ) IS NULL
 );
@@ -270,8 +270,8 @@
       RepeatedField(cpuDurPerEpochMs)
     FROM blink_per_epoch_slice
     WHERE
-      blink_non_aggregated_gc_events_new_name =
-          'blink:gc:main_thread:cycle:full:atomic:mark:epilogue'
+      blink_non_aggregated_gc_events_new_name
+      = 'blink:gc:main_thread:cycle:full:atomic:mark:epilogue'
   ),
   'blink_gc_atomic_pause_mark_prologue',
   (
@@ -279,8 +279,8 @@
       RepeatedField(cpuDurMs)
     FROM blink_slice
     WHERE
-      blink_non_aggregated_gc_event_name =
-          'blink-gc-atomic-pause-mark-prologue'
+      blink_non_aggregated_gc_event_name
+      = 'blink-gc-atomic-pause-mark-prologue'
   ),
   'blink_gc_main_thread_cycle_full_atomic_mark_prologue',
   (
@@ -288,8 +288,8 @@
       RepeatedField(cpuDurPerEpochMs)
     FROM blink_per_epoch_slice
     WHERE
-      blink_non_aggregated_gc_events_new_name =
-          'blink:gc:main_thread:cycle:full:atomic:mark:prologue'
+      blink_non_aggregated_gc_events_new_name
+      = 'blink:gc:main_thread:cycle:full:atomic:mark:prologue'
   ),
   'blink_gc_atomic_pause_mark_roots',
   (
@@ -305,8 +305,8 @@
       RepeatedField(cpuDurPerEpochMs)
     FROM blink_per_epoch_slice
     WHERE
-      blink_non_aggregated_gc_events_new_name =
-          'blink:gc:main_thread:cycle:full:atomic:mark:roots'
+      blink_non_aggregated_gc_events_new_name
+      = 'blink:gc:main_thread:cycle:full:atomic:mark:roots'
   ),
   'blink_gc_atomic_pause_sweep_and_compact',
   (
@@ -314,8 +314,8 @@
       RepeatedField(cpuDurMs)
     FROM blink_slice
     WHERE
-      blink_non_aggregated_gc_event_name =
-          'blink-gc-atomic-pause-sweep-and-compact'
+      blink_non_aggregated_gc_event_name
+      = 'blink-gc-atomic-pause-sweep-and-compact'
   ),
   'blink_gc_main_thread_cycle_full_atomic_sweep_compact',
   (
@@ -323,8 +323,8 @@
       RepeatedField(cpuDurPerEpochMs)
     FROM blink_per_epoch_slice
     WHERE
-      blink_non_aggregated_gc_events_new_name =
-          'blink:gc:main_thread:cycle:full:atomic:sweep:compact'
+      blink_non_aggregated_gc_events_new_name
+      = 'blink:gc:main_thread:cycle:full:atomic:sweep:compact'
   ),
   'blink_gc_complete_sweep',
   (
@@ -339,8 +339,8 @@
       RepeatedField(cpuDurPerEpochMs)
     FROM blink_per_epoch_slice
     WHERE
-      blink_non_aggregated_gc_events_new_name =
-          'blink:gc:main_thread:cycle:full:sweep:complete'
+      blink_non_aggregated_gc_events_new_name
+      = 'blink:gc:main_thread:cycle:full:sweep:complete'
   ),
   'blink_gc_incremental_start',
   (
@@ -355,8 +355,8 @@
       RepeatedField(cpuDurPerEpochMs)
     FROM blink_per_epoch_slice
     WHERE
-      blink_non_aggregated_gc_events_new_name =
-          'blink:gc:main_thread:cycle:full:incremental:mark:start'
+      blink_non_aggregated_gc_events_new_name
+      = 'blink:gc:main_thread:cycle:full:incremental:mark:start'
   ),
   'blink_gc_incremental_step',
   (
@@ -371,8 +371,8 @@
       RepeatedField(cpuDurPerEpochMs)
     FROM blink_per_epoch_slice
     WHERE
-      blink_non_aggregated_gc_events_new_name =
-          'blink:gc:main_thread:cycle:full:incremental:mark:step'
+      blink_non_aggregated_gc_events_new_name
+      = 'blink:gc:main_thread:cycle:full:incremental:mark:step'
   ),
   'blink_gc_sweep_allocation',
   (
@@ -387,8 +387,8 @@
       RepeatedField(cpuDurPerEpochMs)
     FROM blink_per_epoch_slice
     WHERE
-      blink_non_aggregated_gc_events_new_name =
-          'blink:gc:main_thread:cycle:full:sweep:on_allocation'
+      blink_non_aggregated_gc_events_new_name
+      = 'blink:gc:main_thread:cycle:full:sweep:on_allocation'
   ),
   'blink_gc_sweep_task_foreground',
   (
@@ -403,8 +403,8 @@
       RepeatedField(cpuDurPerEpochMs)
     FROM blink_per_epoch_slice
     WHERE
-      blink_non_aggregated_gc_events_new_name =
-          'blink:gc:main_thread:cycle:full:sweep:idle'
+      blink_non_aggregated_gc_events_new_name
+      = 'blink:gc:main_thread:cycle:full:sweep:idle'
   ),
   'blink_gc_unified_marking_by_v8',
   (
@@ -419,8 +419,8 @@
       RepeatedField(cpuDurPerEpochMs)
     FROM blink_per_epoch_slice
     WHERE
-      blink_non_aggregated_gc_events_new_name =
-          'unified:gc:main_thread:cycle:full:mark:step'
+      blink_non_aggregated_gc_events_new_name
+      = 'unified:gc:main_thread:cycle:full:mark:step'
   ),
   'blink_gc_atomic_pause',
   (
@@ -432,7 +432,7 @@
   (
     SELECT RepeatedField(val) FROM (
       SELECT
-        SUM(cpuDurMs) as val
+        SUM(cpuDurMs) AS val
       FROM atomic_pause_event
       GROUP BY epoch
     )
@@ -447,7 +447,7 @@
   (
     SELECT RepeatedField(val) FROM (
       SELECT
-        SUM(cpuDurMs) as val
+        SUM(cpuDurMs) AS val
       FROM blink_gc_atomic_pause_transitive_closure_event
       GROUP BY epoch
     )
@@ -462,7 +462,7 @@
   (
     SELECT RepeatedField(val) FROM (
       SELECT
-        SUM(cpuDurMs) as val
+        SUM(cpuDurMs) AS val
       FROM blink_top_gc_event
       GROUP BY epoch
     )
@@ -477,7 +477,7 @@
   (
     SELECT RepeatedField(val) FROM (
       SELECT
-        SUM(cpuDurMs) as val
+        SUM(cpuDurMs) AS val
       FROM blink_top_gc_roots_marking_event
       GROUP BY epoch
     )
@@ -492,7 +492,7 @@
   (
     SELECT RepeatedField(val) FROM (
       SELECT
-        SUM(cpuDurMs) as val
+        SUM(cpuDurMs) AS val
       FROM blink_gc_foreground_marking_transitive_closure_event
       GROUP BY epoch
     )
@@ -507,7 +507,7 @@
   (
     SELECT RepeatedField(val) FROM (
       SELECT
-        SUM(cpuDurMs) as val
+        SUM(cpuDurMs) AS val
       FROM blink_top_gc_foreground_marking_event
       GROUP BY epoch
     )
@@ -522,7 +522,7 @@
   (
     SELECT RepeatedField(val) FROM (
       SELECT
-        SUM(cpuDurMs) as val
+        SUM(cpuDurMs) AS val
       FROM blink_gc_forced_foreground_marking_event
       GROUP BY epoch
     )
@@ -537,7 +537,7 @@
   (
     SELECT RepeatedField(val) FROM (
       SELECT
-        SUM(cpuDurMs) as val
+        SUM(cpuDurMs) AS val
       FROM blink_top_gc_background_marking_event
       GROUP BY epoch
     )
@@ -552,7 +552,7 @@
   (
     SELECT RepeatedField(val) FROM (
       SELECT
-        SUM(cpuDurMs) as val
+        SUM(cpuDurMs) AS val
       FROM blink_top_gc_foreground_sweeping_event
       GROUP BY epoch
     )
@@ -567,7 +567,7 @@
   (
     SELECT RepeatedField(val) FROM (
       SELECT
-        SUM(cpuDurMs) as val
+        SUM(cpuDurMs) AS val
       FROM blink_top_gc_background_sweeping_event
       GROUP BY epoch
     )
diff --git a/src/trace_processor/metrics/sql/experimental/chrome_dropped_frames.sql b/src/trace_processor/metrics/sql/experimental/chrome_dropped_frames.sql
index d36f66e..2e67da6 100644
--- a/src/trace_processor/metrics/sql/experimental/chrome_dropped_frames.sql
+++ b/src/trace_processor/metrics/sql/experimental/chrome_dropped_frames.sql
@@ -18,11 +18,11 @@
 DROP VIEW IF EXISTS dropped_pipeline_reporter_slice;
 CREATE VIEW dropped_pipeline_reporter_slice AS
 SELECT slice.* FROM slice
-INNER JOIN args
-ON slice.arg_set_id = args.arg_set_id
+JOIN args
+  ON slice.arg_set_id = args.arg_set_id
 WHERE
-  slice.name = 'PipelineReporter' AND
-  args.string_value = 'STATE_DROPPED';
+  slice.name = 'PipelineReporter'
+  AND args.string_value = 'STATE_DROPPED';
 
 -- Find the upid of the proccesses where the dropped frames occur.
 DROP VIEW IF EXISTS dropped_frames_with_upid;
@@ -31,8 +31,8 @@
   dropped_pipeline_reporter_slice.ts,
   process_track.upid
 FROM dropped_pipeline_reporter_slice
-INNER JOIN process_track
-ON dropped_pipeline_reporter_slice.track_id = process_track.id;
+JOIN process_track
+  ON dropped_pipeline_reporter_slice.track_id = process_track.id;
 
 -- Find the name and pid of the processes.
 -- If the process name represents a file's pathname, the path part will be
@@ -50,8 +50,8 @@
     '') AS process_name,
   process.pid AS process_id
 FROM dropped_frames_with_upid
-INNER JOIN process
-ON dropped_frames_with_upid.upid = process.upid;
+JOIN process
+  ON dropped_frames_with_upid.upid = process.upid;
 
 -- Create the derived event track for dropped frames.
 -- All tracks generated from chrome_dropped_frames_event are
@@ -70,12 +70,12 @@
   'Dropped Frames' AS group_name
 FROM dropped_frames_with_process_info
 WHERE (SELECT COUNT(DISTINCT process_id)
-       FROM dropped_frames_with_process_info) > 1
+                    FROM dropped_frames_with_process_info) > 1
 GROUP BY ts
 UNION ALL
 SELECT
   'slice' AS track_type,
-  process_name || ' ' || process_id AS track_name,
+  COALESCE(process_name, 'Process') || ' ' || process_id AS track_name,
   ts,
   0 AS dur,
   'Dropped Frame' AS slice_name,
@@ -98,4 +98,4 @@
     FROM dropped_frames_with_process_info
     ORDER BY ts
   )
-);
\ No newline at end of file
+);
diff --git a/src/trace_processor/metrics/sql/experimental/chrome_long_latency.sql b/src/trace_processor/metrics/sql/experimental/chrome_long_latency.sql
index eed5848..ad7eb4d 100644
--- a/src/trace_processor/metrics/sql/experimental/chrome_long_latency.sql
+++ b/src/trace_processor/metrics/sql/experimental/chrome_long_latency.sql
@@ -37,8 +37,8 @@
   long_eventlatency_slice.event_type,
   process_track.upid
 FROM long_eventlatency_slice
-INNER JOIN process_track
-ON long_eventlatency_slice.track_id = process_track.id;
+JOIN process_track
+  ON long_eventlatency_slice.track_id = process_track.id;
 
 -- Find the name and pid of the processes.
 -- Long latency events with the same timestamp and from the same process
@@ -59,8 +59,8 @@
     '') AS process_name,
   process.pid AS process_id
 FROM long_latency_with_upid
-INNER JOIN process
-ON long_latency_with_upid.upid = process.upid
+JOIN process
+  ON long_latency_with_upid.upid = process.upid
 GROUP BY ts, process.pid;
 
 -- Create the derived event track for long latency.
@@ -80,7 +80,7 @@
   'Long Latency' AS group_name
 FROM long_latency_with_process_info
 WHERE (SELECT COUNT(DISTINCT process_id)
-       FROM long_latency_with_process_info) > 1
+                    FROM long_latency_with_process_info) > 1
 GROUP BY ts
 UNION ALL
 SELECT
@@ -109,4 +109,4 @@
     FROM long_latency_with_process_info
     ORDER BY ts
   )
-);
\ No newline at end of file
+);
diff --git a/src/trace_processor/metrics/sql/experimental/frame_times.sql b/src/trace_processor/metrics/sql/experimental/frame_times.sql
index 488963f..1fbbe52 100644
--- a/src/trace_processor/metrics/sql/experimental/frame_times.sql
+++ b/src/trace_processor/metrics/sql/experimental/frame_times.sql
@@ -36,7 +36,7 @@
   SELECT
     ts,
     phase,
-    LEAD(ts) OVER (ORDER BY ts) - ts as dur
+    LEAD(ts) OVER (ORDER BY ts) - ts AS dur
   FROM GestureLegacyEvents
 )
 WHERE phase = 'S';
@@ -107,9 +107,9 @@
 CREATE VIEW FrameSegments AS
 SELECT
   ts,
-  LEAD(ts) OVER wnd - ts as dur,
-  ts as ts_fs,
-  LEAD(ts) OVER wnd - ts as dur_fs,
+  LEAD(ts) OVER wnd - ts AS dur,
+  ts AS ts_fs,
+  LEAD(ts) OVER wnd - ts AS dur_fs,
   exp
 FROM DisplayCompositorPresentationEvents
 WINDOW wnd AS (PARTITION BY exp ORDER BY ts);
@@ -130,8 +130,7 @@
 DROP VIEW IF EXISTS RefreshPeriodAndroid;
 CREATE VIEW RefreshPeriodAndroid AS
 -- Not implemented yet.
-SELECT NULL AS interval_ms
-;
+SELECT NULL AS interval_ms;
 
 DROP VIEW IF EXISTS RefreshPeriodNonAndroid;
 CREATE VIEW RefreshPeriodNonAndroid AS
@@ -181,4 +180,3 @@
   'avg_surface_fps', (SELECT fps FROM AvgSurfaceFps WHERE NOT exp),
   'exp_avg_surface_fps', (SELECT fps FROM AvgSurfaceFps WHERE exp)
 );
-
diff --git a/src/trace_processor/metrics/sql/experimental/media_metric.sql b/src/trace_processor/metrics/sql/experimental/media_metric.sql
index eb2351f..d44ac80 100644
--- a/src/trace_processor/metrics/sql/experimental/media_metric.sql
+++ b/src/trace_processor/metrics/sql/experimental/media_metric.sql
@@ -20,8 +20,8 @@
 CREATE VIEW thread_slice AS
 SELECT s.*, thread.utid, thread.upid
 FROM slice s
-INNER JOIN thread_track ON s.track_id = thread_track.id
-INNER JOIN thread USING(utid);
+JOIN thread_track ON s.track_id = thread_track.id
+JOIN thread USING(utid);
 
 --------------------------------------------------------------------------------
 -- Find all playbacks on renderer main threads.
@@ -33,8 +33,8 @@
   s.ts AS playback_start,
   upid
 FROM slice s
-INNER JOIN thread_track ON s.track_id = thread_track.id
-INNER JOIN thread USING(utid)
+JOIN thread_track ON s.track_id = thread_track.id
+JOIN thread USING(utid)
 WHERE
   s.name = 'WebMediaPlayerImpl::DoLoad'
   AND thread.name = 'CrRendererMain';
@@ -86,8 +86,8 @@
   vs.upid,
   SUM(
     CASE
-    WHEN s.arg_set_id IS NULL THEN 0
-    ELSE EXTRACT_ARG(s.arg_set_id, 'debug.count') END
+      WHEN s.arg_set_id IS NULL THEN 0
+      ELSE EXTRACT_ARG(s.arg_set_id, 'debug.count') END
   ) AS dropped_frame_count
 FROM VideoStart vs
 LEFT JOIN thread_slice s ON
@@ -201,7 +201,7 @@
   slice.ts AS playback_end,
   EXTRACT_ARG(slice.arg_set_id, 'debug.duration') * 1e9 AS duration
 FROM AVStart
-INNER JOIN slice ON slice.id = (
+JOIN slice ON slice.id = (
   SELECT s.id
   FROM thread_slice s
   WHERE
@@ -210,7 +210,7 @@
     AND s.upid = AVStart.upid
   ORDER BY s.ts ASC
   LIMIT 1
-)
+  )
 WHERE NOT EXISTS (
   SELECT 1 FROM SeekStart
   WHERE SeekStart.playback_id = AVStart.playback_id
@@ -227,7 +227,7 @@
   PlaybackStart.upid,
   MAX(EXTRACT_ARG(s.arg_set_id, 'debug.roughness')) AS roughness
 FROM PlaybackStart
-INNER JOIN thread_slice s
+JOIN thread_slice s
 WHERE
   s.name = 'VideoPlaybackRoughness'
   AND EXTRACT_ARG(s.arg_set_id, 'debug.id') = playback_id
@@ -242,7 +242,7 @@
   PlaybackStart.upid,
   MAX(EXTRACT_ARG(s.arg_set_id, 'debug.freezing')) AS freezing
 FROM PlaybackStart
-INNER JOIN thread_slice s
+JOIN thread_slice s
 WHERE
   s.name = 'VideoPlaybackFreezing'
   AND EXTRACT_ARG(s.arg_set_id, 'debug.id') = playback_id
diff --git a/src/trace_processor/metrics/sql/experimental/reported_by_page.sql b/src/trace_processor/metrics/sql/experimental/reported_by_page.sql
index 9936b31..17beae7 100644
--- a/src/trace_processor/metrics/sql/experimental/reported_by_page.sql
+++ b/src/trace_processor/metrics/sql/experimental/reported_by_page.sql
@@ -20,10 +20,10 @@
 
 DROP VIEW IF EXISTS page_reported_events;
 CREATE VIEW page_reported_events AS
-SELECT ts, name, EXTRACT_ARG(arg_set_id, "debug.data.navigationId") as nav_id
+SELECT ts, name, EXTRACT_ARG(arg_set_id, "debug.data.navigationId") AS nav_id
 FROM slice
-WHERE category = 'blink.user_timing' AND
-    (name = 'navigationStart' OR name GLOB 'telemetry:reported_by_page:*')
+WHERE category = 'blink.user_timing'
+  AND (name = 'navigationStart' OR name GLOB 'telemetry:reported_by_page:*')
 ORDER BY nav_id, ts ASC;
 
 --------------------------------------------------------------------------------
@@ -35,17 +35,17 @@
 SELECT p.name, (p.ts - (
     SELECT MAX(ts) FROM page_reported_events
     WHERE
-      nav_id = p.nav_id AND
-      ts < p.ts AND (
+      nav_id = p.nav_id
+      AND ts < p.ts AND (
         -- Viewable/interactive markers measure time from nav start.
-        (p.name GLOB 'telemetry:reported_by_page:*' AND
-         p.name NOT GLOB 'telemetry:reported_by_page:benchmark*' AND
-         name = 'navigationStart') OR
+        (p.name GLOB 'telemetry:reported_by_page:*'
+         AND p.name NOT GLOB 'telemetry:reported_by_page:benchmark*'
+         AND name = 'navigationStart')
         -- Benchmark end markers measure time from the most recent begin marker.
-        (p.name = 'telemetry:reported_by_page:benchmark_end' AND
-         name = 'telemetry:reported_by_page:benchmark_begin')
+        OR (p.name = 'telemetry:reported_by_page:benchmark_end'
+            AND name = 'telemetry:reported_by_page:benchmark_begin')
       ))
-    ) / 1e6 as dur_ms
+) / 1e6 AS dur_ms
 FROM page_reported_events p;
 
 --------------------------------------------------------------------------------
@@ -55,12 +55,12 @@
 CREATE VIEW reported_by_page_output AS
 SELECT ReportedByPage(
   'time_to_viewable', (
-      SELECT RepeatedField(dur_ms) FROM page_reported_durations
-      WHERE name = 'telemetry:reported_by_page:viewable'),
+    SELECT RepeatedField(dur_ms) FROM page_reported_durations
+    WHERE name = 'telemetry:reported_by_page:viewable'),
   'time_to_interactive', (
-      SELECT RepeatedField(dur_ms) FROM page_reported_durations
-      WHERE name = 'telemetry:reported_by_page:interactive'),
+    SELECT RepeatedField(dur_ms) FROM page_reported_durations
+    WHERE name = 'telemetry:reported_by_page:interactive'),
   'benchmark_time', (
-      SELECT RepeatedField(dur_ms) FROM page_reported_durations
-      WHERE name = 'telemetry:reported_by_page:benchmark_end')
+    SELECT RepeatedField(dur_ms) FROM page_reported_durations
+    WHERE name = 'telemetry:reported_by_page:benchmark_end')
 );
diff --git a/src/trace_processor/metrics/sql/trace_metadata.sql b/src/trace_processor/metrics/sql/trace_metadata.sql
index 7a91ed1..c3cdf88 100644
--- a/src/trace_processor/metrics/sql/trace_metadata.sql
+++ b/src/trace_processor/metrics/sql/trace_metadata.sql
@@ -18,11 +18,11 @@
 DROP VIEW IF EXISTS trace_metadata_event;
 CREATE VIEW trace_metadata_event AS
 SELECT
-  'slice' as track_type,
-  'Clock Snapshots' as track_name,
+  'slice' AS track_type,
+  'Clock Snapshots' AS track_name,
   ts,
-  0 as dur,
-  'Snapshot' as slice_name
+  0 AS dur,
+  'Snapshot' AS slice_name
 FROM clock_snapshot
 GROUP BY ts;
 
@@ -42,7 +42,7 @@
     SELECT str_value FROM metadata
     WHERE name = 'unique_session_name'
   ),
- 'trace_size_bytes', (
+  'trace_size_bytes', (
     SELECT int_value FROM metadata
     WHERE name = 'trace_size_bytes'
   ),
@@ -56,6 +56,6 @@
     WHERE name = 'trace_config_pbtxt'
   ),
   'sched_duration_ns', (
-    SELECT MAX(ts) - MIN(ts) from sched
+    SELECT MAX(ts) - MIN(ts) FROM sched
   )
 );
diff --git a/src/trace_processor/metrics/sql/trace_stats.sql b/src/trace_processor/metrics/sql/trace_stats.sql
index 702fc91..61155b7 100644
--- a/src/trace_processor/metrics/sql/trace_stats.sql
+++ b/src/trace_processor/metrics/sql/trace_stats.sql
@@ -33,7 +33,7 @@
         WHEN 'error' THEN 'SEVERITY_ERROR'
         ELSE 'SEVERITY_UNKNOWN'
       END
-    ))
+      ))
     FROM stats ORDER BY name ASC
   )
 );
diff --git a/src/trace_processor/metrics/sql/webview/BUILD.gn b/src/trace_processor/metrics/sql/webview/BUILD.gn
new file mode 100644
index 0000000..8c08b3d
--- /dev/null
+++ b/src/trace_processor/metrics/sql/webview/BUILD.gn
@@ -0,0 +1,22 @@
+# Copyright (C) 2022 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/perfetto_sql.gni")
+
+assert(enable_perfetto_trace_processor_sqlite)
+
+perfetto_sql_source_set("webview") {
+  sources = [ "webview_power_usage.sql" ]
+}
diff --git a/src/trace_processor/metrics/sql/webview/webview_power_usage.sql b/src/trace_processor/metrics/sql/webview/webview_power_usage.sql
index 29dad50..83f969b 100644
--- a/src/trace_processor/metrics/sql/webview/webview_power_usage.sql
+++ b/src/trace_processor/metrics/sql/webview/webview_power_usage.sql
@@ -29,11 +29,11 @@
 -- Slices are only used to calculate the contribution of the browser process,
 -- renderer contribution will be calculated as the sum of all renderer processes' usage.
 CREATE TABLE top_level_slice AS
-  SELECT *
-  FROM slice WHERE
-  depth = 0 AND
-  ((category GLOB '*toplevel*' OR category = 'Java') AND
-  name NOT GLOB '*looper*');
+SELECT *
+FROM slice WHERE
+  depth = 0
+  AND ((category GLOB '*toplevel*' OR category = 'Java')
+    AND name NOT GLOB '*looper*');
 
 DROP TABLE IF EXISTS webview_browser_slices;
 
@@ -42,20 +42,20 @@
 -- as a whole separately.
 -- Slices from Chrome browser processes are also excluded.
 CREATE TABLE webview_browser_slices AS
-  SELECT
-    top_level_slice.ts,
-    top_level_slice.dur,
-    thread_track.utid,
-    process.upid AS upid,
-    extract_arg(process.arg_set_id, 'chrome.host_app_package_name') AS app_name
-  FROM top_level_slice
-  INNER JOIN thread_track
+SELECT
+  top_level_slice.ts,
+  top_level_slice.dur,
+  thread_track.utid,
+  process.upid AS upid,
+  extract_arg(process.arg_set_id, 'chrome.host_app_package_name') AS app_name
+FROM top_level_slice
+JOIN thread_track
   ON top_level_slice.track_id = thread_track.id
-  INNER JOIN process
+JOIN process
   ON thread.upid = process.upid
-  INNER JOIN thread
+JOIN thread
   ON thread_track.utid = thread.utid
-  WHERE process.name NOT GLOB '*SandboxedProcessService*'
+WHERE process.name NOT GLOB '*SandboxedProcessService*'
   AND process.name NOT GLOB '*chrome*'
   AND app_name IS NOT NULL;
 
@@ -74,8 +74,8 @@
 SELECT
   app_name,
   SUM(dur * COALESCE(power_ma, 0) / 1e9) AS power_mas
-  FROM webview_browser_slices_power
-  GROUP BY app_name;
+FROM webview_browser_slices_power
+GROUP BY app_name;
 
 DROP TABLE IF EXISTS webview_renderer_threads;
 
@@ -83,11 +83,11 @@
 CREATE TABLE webview_renderer_threads AS
 SELECT
   thread.utid AS utid,
-  extract_arg(process.arg_set_id, 'chrome.host_app_package_name') as app_name
-  FROM process
-  INNER JOIN thread
+  extract_arg(process.arg_set_id, 'chrome.host_app_package_name') AS app_name
+FROM process
+JOIN thread
   ON thread.upid = process.upid
-  WHERE process.name GLOB '*webview*SandboxedProcessService*'
+WHERE process.name GLOB '*webview*SandboxedProcessService*'
   AND app_name IS NOT NULL;
 
 DROP TABLE IF EXISTS webview_renderer_power_summary;
@@ -95,13 +95,13 @@
 -- Calculate the power usage of all WebView renderer processes for each app
 -- in milliampere-seconds.
 CREATE TABLE webview_renderer_power_summary AS
-  SELECT
-    app_name,
-    SUM(dur * COALESCE(power_ma, 0) / 1e9) AS power_mas
-  FROM power_per_thread
-  INNER JOIN webview_renderer_threads
+SELECT
+  app_name,
+  SUM(dur * COALESCE(power_ma, 0) / 1e9) AS power_mas
+FROM power_per_thread
+JOIN webview_renderer_threads
   ON power_per_thread.utid = webview_renderer_threads.utid
-  GROUP BY app_name;
+GROUP BY app_name;
 
 DROP TABLE IF EXISTS webview_renderer_power_per_core_type;
 
@@ -113,9 +113,9 @@
   core_type_per_cpu.core_type AS core_type,
   SUM(dur * COALESCE(power_ma, 0) / 1e9) AS power_mas
 FROM power_per_thread
-INNER JOIN webview_renderer_threads
+JOIN webview_renderer_threads
   ON power_per_thread.utid = webview_renderer_threads.utid
-INNER JOIN core_type_per_cpu
+JOIN core_type_per_cpu
   ON power_per_thread.cpu = core_type_per_cpu.cpu
 GROUP BY app_name, core_type_per_cpu.core_type;
 
@@ -129,13 +129,13 @@
 -- Excludes all renderer processes and Chrome browser processes.
 CREATE TABLE host_app_threads AS
 SELECT
-    thread.utid AS utid,
-    thread.name AS name,
-    extract_arg(process.arg_set_id, 'chrome.host_app_package_name') as app_name
-  FROM thread
-  JOIN process ON thread.upid = process.upid
-  WHERE thread.upid IN
-    (SELECT DISTINCT(webview_browser_slices.upid) FROM webview_browser_slices)
+  thread.utid AS utid,
+  thread.name AS name,
+  extract_arg(process.arg_set_id, 'chrome.host_app_package_name') AS app_name
+FROM thread
+JOIN process ON thread.upid = process.upid
+WHERE thread.upid IN
+  (SELECT DISTINCT(webview_browser_slices.upid) FROM webview_browser_slices)
   AND process.name NOT GLOB '*SandboxedProcessService*'
   AND process.name NOT GLOB '*chrome*'
   AND app_name IS NOT NULL;
@@ -145,13 +145,13 @@
 -- Calculate the power usage of all WebView (host app+browser) processes for each app
 -- in milliampere-seconds.
 CREATE TABLE host_app_power_summary AS
-  SELECT
-    app_name,
-    SUM(dur * COALESCE(power_ma, 0) / 1e9) AS power_mas
-  FROM power_per_thread
-  INNER JOIN host_app_threads
+SELECT
+  app_name,
+  SUM(dur * COALESCE(power_ma, 0) / 1e9) AS power_mas
+FROM power_per_thread
+JOIN host_app_threads
   ON power_per_thread.utid = host_app_threads.utid
-  GROUP BY app_name;
+GROUP BY app_name;
 
 DROP TABLE IF EXISTS host_app_power_per_core_type;
 
@@ -163,9 +163,9 @@
   core_type_per_cpu.core_type AS core_type,
   SUM(dur * COALESCE(power_ma, 0) / 1e9) AS power_mas
 FROM power_per_thread
-INNER JOIN host_app_threads
+JOIN host_app_threads
   ON power_per_thread.utid = host_app_threads.utid
-INNER JOIN core_type_per_cpu
+JOIN core_type_per_cpu
   ON power_per_thread.cpu = core_type_per_cpu.cpu
 GROUP BY app_name, core_type_per_cpu.core_type;
 
@@ -175,37 +175,37 @@
 CREATE TABLE webview_only_threads AS
 SELECT *
 FROM host_app_threads
-  WHERE name GLOB 'Chrome*' OR name GLOB 'CookieMonster*'
+WHERE name GLOB 'Chrome*' OR name GLOB 'CookieMonster*'
   OR name GLOB 'CompositorTileWorker*' OR name GLOB 'ThreadPool*ground*'
-  OR NAME GLOB 'ThreadPoolService*' OR  name GLOB 'VizCompositorThread*'
+  OR NAME GLOB 'ThreadPoolService*' OR name GLOB 'VizCompositorThread*'
   OR name IN ('AudioThread', 'DedicatedWorker thread', 'GpuMemoryThread',
-  'JavaBridge', 'LevelDBEnv.IDB', 'MemoryInfra', 'NetworkService', 'VizWebView');
+    'JavaBridge', 'LevelDBEnv.IDB', 'MemoryInfra', 'NetworkService', 'VizWebView');
 
 DROP TABLE IF EXISTS webview_only_power_summary;
 
 -- Calculate the power usage of all WebView-specific host app threads
 -- (browser + in-process renderers) for each app in milliampere-seconds.
 CREATE TABLE webview_only_power_summary AS
-  SELECT
-    app_name,
-    SUM(dur * COALESCE(power_ma, 0) / 1e9) AS power_mas
-  FROM power_per_thread
-  INNER JOIN webview_only_threads
+SELECT
+  app_name,
+  SUM(dur * COALESCE(power_ma, 0) / 1e9) AS power_mas
+FROM power_per_thread
+JOIN webview_only_threads
   ON power_per_thread.utid = webview_only_threads.utid
-  GROUP BY app_name;
+GROUP BY app_name;
 
 DROP TABLE IF EXISTS webview_only_power_per_core_type;
 
 -- Calculate the power usage of all WebView-specific host app threads
 -- for each app in milliampere-seconds grouped by core type.
 CREATE TABLE webview_only_power_per_core_type AS
-  SELECT app_name,
+SELECT app_name,
   core_type_per_cpu.core_type AS core_type,
   SUM(dur * COALESCE(power_ma, 0) / 1e9) AS power_mas
 FROM power_per_thread
-INNER JOIN webview_only_threads
+JOIN webview_only_threads
   ON power_per_thread.utid = webview_only_threads.utid
-INNER JOIN core_type_per_cpu
+JOIN core_type_per_cpu
   ON power_per_thread.cpu = core_type_per_cpu.cpu
 GROUP BY app_name, core_type_per_cpu.core_type;
 
@@ -214,63 +214,63 @@
 DROP TABLE IF EXISTS total_app_power_output;
 
 CREATE TABLE total_app_power_output AS
-  SELECT
-    host_app_power_summary.app_name as app_name,
-    host_app_power_summary.power_mas AS total_mas,
-    host_app_power_little_cores_mas.power_mas AS little_cores_mas,
-    host_app_power_big_cores_mas.power_mas AS big_cores_mas,
-    host_app_power_bigger_cores_mas.power_mas AS bigger_cores_mas
-  FROM host_app_power_summary LEFT JOIN host_app_power_per_core_type AS host_app_power_little_cores_mas
+SELECT
+  host_app_power_summary.app_name AS app_name,
+  host_app_power_summary.power_mas AS total_mas,
+  host_app_power_little_cores_mas.power_mas AS little_cores_mas,
+  host_app_power_big_cores_mas.power_mas AS big_cores_mas,
+  host_app_power_bigger_cores_mas.power_mas AS bigger_cores_mas
+FROM host_app_power_summary LEFT JOIN host_app_power_per_core_type AS host_app_power_little_cores_mas
   ON host_app_power_summary.app_name = host_app_power_little_cores_mas.app_name
-  AND host_app_power_little_cores_mas.core_type = 'little'
-  LEFT JOIN host_app_power_per_core_type AS host_app_power_big_cores_mas
+    AND host_app_power_little_cores_mas.core_type = 'little'
+LEFT JOIN host_app_power_per_core_type AS host_app_power_big_cores_mas
   ON host_app_power_summary.app_name = host_app_power_big_cores_mas.app_name
-  AND host_app_power_big_cores_mas.core_type = 'big'
-  LEFT JOIN host_app_power_per_core_type AS host_app_power_bigger_cores_mas
+    AND host_app_power_big_cores_mas.core_type = 'big'
+LEFT JOIN host_app_power_per_core_type AS host_app_power_bigger_cores_mas
   ON host_app_power_summary.app_name = host_app_power_bigger_cores_mas.app_name
-  AND host_app_power_bigger_cores_mas.core_type = 'bigger';
+    AND host_app_power_bigger_cores_mas.core_type = 'bigger';
 
 DROP TABLE IF EXISTS webview_renderer_power_output;
 
 CREATE TABLE webview_renderer_power_output AS
-  SELECT
-    webview_renderer_power_summary.app_name AS app_name,
-    webview_renderer_power_summary.power_mas AS total_mas,
-    webview_renderer_little_power.power_mas AS little_cores_mas,
-    webview_renderer_big_power.power_mas AS big_cores_mas,
-    webview_renderer_bigger_power.power_mas AS bigger_cores_mas
-  FROM webview_renderer_power_summary LEFT JOIN webview_renderer_power_per_core_type AS webview_renderer_little_power
+SELECT
+  webview_renderer_power_summary.app_name AS app_name,
+  webview_renderer_power_summary.power_mas AS total_mas,
+  webview_renderer_little_power.power_mas AS little_cores_mas,
+  webview_renderer_big_power.power_mas AS big_cores_mas,
+  webview_renderer_bigger_power.power_mas AS bigger_cores_mas
+FROM webview_renderer_power_summary LEFT JOIN webview_renderer_power_per_core_type AS webview_renderer_little_power
   ON webview_renderer_power_summary.app_name = webview_renderer_little_power.app_name
-  AND webview_renderer_little_power.core_type = 'little'
-  LEFT JOIN webview_renderer_power_per_core_type AS webview_renderer_big_power
+    AND webview_renderer_little_power.core_type = 'little'
+LEFT JOIN webview_renderer_power_per_core_type AS webview_renderer_big_power
   ON webview_renderer_power_summary.app_name = webview_renderer_big_power.app_name
-  AND webview_renderer_big_power.core_type = 'big'
-  LEFT JOIN webview_renderer_power_per_core_type AS webview_renderer_bigger_power
+    AND webview_renderer_big_power.core_type = 'big'
+LEFT JOIN webview_renderer_power_per_core_type AS webview_renderer_bigger_power
   ON webview_renderer_power_summary.app_name = webview_renderer_bigger_power.app_name
-  AND webview_renderer_bigger_power.core_type = 'bigger';
+    AND webview_renderer_bigger_power.core_type = 'bigger';
 
 DROP TABLE IF EXISTS webview_only_power_output;
 
 CREATE TABLE webview_only_power_output AS
-  SELECT
-    webview_only_power_summary.app_name AS app_name,
-    webview_only_power_summary.power_mas AS total_mas,
-    webview_only_power_little_cores_mas.power_mas AS little_cores_mas,
-    webview_only_power_big_cores_mas.power_mas AS big_cores_mas,
-    webview_only_power_bigger_cores_mas.power_mas AS bigger_cores_mas
-  FROM webview_only_power_summary LEFT JOIN webview_only_power_per_core_type AS webview_only_power_little_cores_mas
+SELECT
+  webview_only_power_summary.app_name AS app_name,
+  webview_only_power_summary.power_mas AS total_mas,
+  webview_only_power_little_cores_mas.power_mas AS little_cores_mas,
+  webview_only_power_big_cores_mas.power_mas AS big_cores_mas,
+  webview_only_power_bigger_cores_mas.power_mas AS bigger_cores_mas
+FROM webview_only_power_summary LEFT JOIN webview_only_power_per_core_type AS webview_only_power_little_cores_mas
   ON webview_only_power_summary.app_name = webview_only_power_little_cores_mas.app_name
-  AND webview_only_power_little_cores_mas.core_type = 'little'
-  LEFT JOIN webview_only_power_per_core_type AS webview_only_power_big_cores_mas
+    AND webview_only_power_little_cores_mas.core_type = 'little'
+LEFT JOIN webview_only_power_per_core_type AS webview_only_power_big_cores_mas
   ON webview_only_power_summary.app_name = webview_only_power_big_cores_mas.app_name
-  AND webview_only_power_big_cores_mas.core_type = 'big'
-  LEFT JOIN webview_only_power_per_core_type AS webview_only_power_bigger_cores_mas
+    AND webview_only_power_big_cores_mas.core_type = 'big'
+LEFT JOIN webview_only_power_per_core_type AS webview_only_power_bigger_cores_mas
   ON webview_only_power_summary.app_name = webview_only_power_bigger_cores_mas.app_name
-  AND webview_only_power_bigger_cores_mas.core_type = 'bigger';
+    AND webview_only_power_bigger_cores_mas.core_type = 'bigger';
 
 DROP TABLE IF EXISTS total_device_power;
 
 -- Calculate the power usage of the device in milliampere-seconds.
 CREATE TABLE total_device_power AS
-  SELECT SUM(dur * COALESCE(power_ma, 0) / 1e9) AS power_mas
-  FROM power_per_thread;
+SELECT SUM(dur * COALESCE(power_ma, 0) / 1e9) AS power_mas
+FROM power_per_thread;
diff --git a/src/trace_processor/prelude/functions/BUILD.gn b/src/trace_processor/prelude/functions/BUILD.gn
new file mode 100644
index 0000000..069c996
--- /dev/null
+++ b/src/trace_processor/prelude/functions/BUILD.gn
@@ -0,0 +1,73 @@
+# Copyright (C) 2022 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")
+
+assert(enable_perfetto_trace_processor_sqlite)
+
+source_set("functions") {
+  sources = [
+    "create_function.cc",
+    "create_function.h",
+    "create_function_internal.cc",
+    "create_function_internal.h",
+    "create_view_function.cc",
+    "create_view_function.h",
+    "import.cc",
+    "import.h",
+    "pprof_functions.cc",
+    "pprof_functions.h",
+    "register_function.cc",
+    "register_function.h",
+    "sqlite3_str_split.cc",
+    "sqlite3_str_split.h",
+    "utils.h",
+    "window_functions.h",
+  ]
+  deps = [
+    "../..:demangle",
+    "../..:export_json",
+    "../..:metatrace",
+    "../../../../gn:default_deps",
+    "../../../../gn:sqlite",
+    "../../../../protos/perfetto/common:zero",
+    "../../../../protos/perfetto/trace/ftrace:zero",
+    "../../../base",
+    "../../containers",
+    "../../db",
+    "../../dynamic",
+    "../../importers/common",
+    "../../importers/ftrace:ftrace_descriptors",
+    "../../sqlite:sqlite_minimal",
+    "../../storage",
+    "../../types",
+    "../../util",
+    "../../util:profile_builder",
+    "../../util:sql_argument",
+    "../../util:stdlib",
+  ]
+}
+
+perfetto_unittest_source_set("unittests") {
+  testonly = true
+  sources = [ "sqlite3_str_split_unittest.cc" ]
+  deps = [
+    ":functions",
+    "../../../../gn:default_deps",
+    "../../../../gn:gtest_and_gmock",
+    "../../../../gn:sqlite",
+    "../../../base",
+    "../../sqlite:sqlite_minimal",
+  ]
+}
diff --git a/src/trace_processor/prelude/functions/create_function.cc b/src/trace_processor/prelude/functions/create_function.cc
new file mode 100644
index 0000000..ee2e733
--- /dev/null
+++ b/src/trace_processor/prelude/functions/create_function.cc
@@ -0,0 +1,283 @@
+/*
+ * 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/prelude/functions/create_function.h"
+
+#include "perfetto/base/status.h"
+#include "perfetto/trace_processor/basic_types.h"
+#include "src/trace_processor/prelude/functions/create_function_internal.h"
+#include "src/trace_processor/sqlite/scoped_db.h"
+#include "src/trace_processor/sqlite/sqlite_utils.h"
+#include "src/trace_processor/tp_metatrace.h"
+#include "src/trace_processor/util/status_macros.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+namespace {
+
+struct CreatedFunction : public SqlFunction {
+  struct Context {
+    sqlite3* db;
+    Prototype prototype;
+    sql_argument::Type return_type;
+    std::string sql;
+    sqlite3_stmt* stmt;
+  };
+
+  static base::Status Run(Context* ctx,
+                          size_t argc,
+                          sqlite3_value** argv,
+                          SqlValue& out,
+                          Destructors&);
+  static base::Status VerifyPostConditions(Context*);
+  static void Cleanup(Context*);
+};
+
+base::Status CreatedFunction::Run(CreatedFunction::Context* ctx,
+                                  size_t argc,
+                                  sqlite3_value** argv,
+                                  SqlValue& out,
+                                  Destructors&) {
+  if (argc != ctx->prototype.arguments.size()) {
+    return base::ErrStatus(
+        "%s: invalid number of args; expected %zu, received %zu",
+        ctx->prototype.function_name.c_str(), ctx->prototype.arguments.size(),
+        argc);
+  }
+
+  // Type check all the arguments.
+  for (size_t i = 0; i < argc; ++i) {
+    sqlite3_value* arg = argv[i];
+    sql_argument::Type type = ctx->prototype.arguments[i].type();
+    base::Status status = sqlite_utils::TypeCheckSqliteValue(
+        arg, sql_argument::TypeToSqlValueType(type),
+        sql_argument::TypeToHumanFriendlyString(type));
+    if (!status.ok()) {
+      return base::ErrStatus("%s[arg=%s]: argument %zu %s",
+                             ctx->prototype.function_name.c_str(),
+                             sqlite3_value_text(arg), i, status.c_message());
+    }
+  }
+
+  PERFETTO_TP_TRACE(
+      metatrace::Category::FUNCTION, "CREATE_FUNCTION",
+      [ctx, argv](metatrace::Record* r) {
+        r->AddArg("Function", ctx->prototype.function_name.c_str());
+        for (uint32_t i = 0; i < ctx->prototype.arguments.size(); ++i) {
+          std::string key = "Arg " + std::to_string(i);
+          const char* value =
+              reinterpret_cast<const char*>(sqlite3_value_text(argv[i]));
+          r->AddArg(base::StringView(key),
+                    value ? base::StringView(value) : base::StringView("NULL"));
+        }
+      });
+
+  // Bind all the arguments to the appropriate places in the function.
+  for (size_t i = 0; i < argc; ++i) {
+    RETURN_IF_ERROR(MaybeBindArgument(ctx->stmt, ctx->prototype.function_name,
+                                      ctx->prototype.arguments[i], argv[i]));
+  }
+
+  int ret = sqlite3_step(ctx->stmt);
+  RETURN_IF_ERROR(
+      SqliteRetToStatus(ctx->db, ctx->prototype.function_name, ret));
+  if (ret == SQLITE_DONE) {
+    // No return value means we just return don't set |out|.
+    return base::OkStatus();
+  }
+
+  PERFETTO_DCHECK(ret == SQLITE_ROW);
+  size_t col_count = static_cast<size_t>(sqlite3_column_count(ctx->stmt));
+  if (col_count != 1) {
+    return base::ErrStatus(
+        "%s: SQL definition should only return one column: returned %zu "
+        "columns",
+        ctx->prototype.function_name.c_str(), col_count);
+  }
+
+  out = sqlite_utils::SqliteValueToSqlValue(sqlite3_column_value(ctx->stmt, 0));
+
+  // If we return a bytes type but have a null pointer, SQLite will convert this
+  // to an SQL null. However, for proto build functions, we actively want to
+  // distinguish between nulls and 0 byte strings. Therefore, change the value
+  // to an empty string.
+  if (out.type == SqlValue::kBytes && out.bytes_value == nullptr) {
+    PERFETTO_DCHECK(out.bytes_count == 0);
+    out.bytes_value = "";
+  }
+  return base::OkStatus();
+}
+
+base::Status CreatedFunction::VerifyPostConditions(Context* ctx) {
+  int ret = sqlite3_step(ctx->stmt);
+  RETURN_IF_ERROR(
+      SqliteRetToStatus(ctx->db, ctx->prototype.function_name, ret));
+  if (ret == SQLITE_ROW) {
+    auto expanded_sql = sqlite_utils::ExpandedSqlForStmt(ctx->stmt);
+    return base::ErrStatus(
+        "%s: multiple values were returned when executing function body. "
+        "Executed SQL was %s",
+        ctx->prototype.function_name.c_str(), expanded_sql.get());
+  }
+  PERFETTO_DCHECK(ret == SQLITE_DONE);
+  return base::OkStatus();
+}
+
+void CreatedFunction::Cleanup(CreatedFunction::Context* ctx) {
+  sqlite3_reset(ctx->stmt);
+  sqlite3_clear_bindings(ctx->stmt);
+}
+
+}  // namespace
+
+size_t CreateFunction::NameAndArgc::Hasher::operator()(
+    const NameAndArgc& s) const noexcept {
+  base::Hasher hash;
+  hash.Update(s.name.data(), s.name.size());
+  hash.Update(s.argc);
+  return static_cast<size_t>(hash.digest());
+}
+
+base::Status CreateFunction::Run(CreateFunction::Context* ctx,
+                                 size_t argc,
+                                 sqlite3_value** argv,
+                                 SqlValue&,
+                                 Destructors&) {
+  if (argc != 3) {
+    return base::ErrStatus(
+        "CREATE_FUNCTION: invalid number of args; expected %u, received %zu",
+        3u, argc);
+  }
+
+  sqlite3_value* prototype_value = argv[0];
+  sqlite3_value* return_type_value = argv[1];
+  sqlite3_value* sql_defn_value = argv[2];
+
+  // Type check all the arguments.
+  {
+    auto type_check = [prototype_value](sqlite3_value* value,
+                                        SqlValue::Type type, const char* desc) {
+      base::Status status = sqlite_utils::TypeCheckSqliteValue(value, type);
+      if (!status.ok()) {
+        return base::ErrStatus("CREATE_FUNCTION[prototype=%s]: %s %s",
+                               sqlite3_value_text(prototype_value), desc,
+                               status.c_message());
+      }
+      return base::OkStatus();
+    };
+
+    RETURN_IF_ERROR(type_check(prototype_value, SqlValue::Type::kString,
+                               "function prototype (first argument)"));
+    RETURN_IF_ERROR(type_check(return_type_value, SqlValue::Type::kString,
+                               "return type (second argument)"));
+    RETURN_IF_ERROR(type_check(sql_defn_value, SqlValue::Type::kString,
+                               "SQL definition (third argument)"));
+  }
+
+  // Extract the arguments from the value wrappers.
+  auto extract_string = [](sqlite3_value* value) -> base::StringView {
+    return reinterpret_cast<const char*>(sqlite3_value_text(value));
+  };
+  base::StringView prototype_str = extract_string(prototype_value);
+  base::StringView return_type_str = extract_string(return_type_value);
+  std::string sql_defn_str = extract_string(sql_defn_value).ToStdString();
+
+  // Parse all the arguments into a more friendly form.
+  Prototype prototype;
+  base::Status status = ParsePrototype(prototype_str, prototype);
+  if (!status.ok()) {
+    return base::ErrStatus("CREATE_FUNCTION[prototype=%s]: %s",
+                           prototype_str.ToStdString().c_str(),
+                           status.c_message());
+  }
+
+  // Parse the return type into a enum format.
+  auto opt_return_type = sql_argument::ParseType(return_type_str);
+  if (!opt_return_type) {
+    return base::ErrStatus(
+        "CREATE_FUNCTION[prototype=%s, return=%s]: unknown return type "
+        "specified",
+        prototype_str.ToStdString().c_str(),
+        return_type_str.ToStdString().c_str());
+  }
+
+  int created_argc = static_cast<int>(prototype.arguments.size());
+  NameAndArgc key{prototype.function_name, created_argc};
+  auto it = ctx->state->find(key);
+  if (it != ctx->state->end()) {
+    // If the function already exists, just verify that the prototype, return
+    // type and SQL matches exactly with what we already had registered. By
+    // doing this, we can avoid the problem plaguing C++ macros where macro
+    // ordering determines which one gets run.
+    auto* created_ctx = static_cast<CreatedFunction::Context*>(
+        it->second.created_functon_context);
+
+    if (created_ctx->prototype != prototype) {
+      return base::ErrStatus(
+          "CREATE_FUNCTION[prototype=%s]: function prototype changed",
+          prototype_str.ToStdString().c_str());
+    }
+
+    if (created_ctx->return_type != *opt_return_type) {
+      return base::ErrStatus(
+          "CREATE_FUNCTION[prototype=%s]: return type changed from %s to %s",
+          prototype_str.ToStdString().c_str(),
+          sql_argument::TypeToHumanFriendlyString(created_ctx->return_type),
+          return_type_str.ToStdString().c_str());
+    }
+
+    if (created_ctx->sql != sql_defn_str) {
+      return base::ErrStatus(
+          "CREATE_FUNCTION[prototype=%s]: function SQL changed from %s to %s",
+          prototype_str.ToStdString().c_str(), created_ctx->sql.c_str(),
+          sql_defn_str.c_str());
+    }
+    return base::OkStatus();
+  }
+
+  // Prepare the SQL definition as a statement using SQLite.
+  ScopedStmt stmt;
+  sqlite3_stmt* stmt_raw = nullptr;
+  int ret = sqlite3_prepare_v2(ctx->db, sql_defn_str.data(),
+                               static_cast<int>(sql_defn_str.size()), &stmt_raw,
+                               nullptr);
+  if (ret != SQLITE_OK) {
+    return base::ErrStatus(
+        "CREATE_FUNCTION[prototype=%s]: SQLite error when preparing "
+        "statement %s",
+        prototype_str.ToStdString().c_str(),
+        sqlite_utils::FormatErrorMessage(
+            stmt_raw, base::StringView(sql_defn_str), ctx->db, ret)
+            .c_message());
+  }
+  stmt.reset(stmt_raw);
+
+  std::unique_ptr<CreatedFunction::Context> created(
+      new CreatedFunction::Context{ctx->db, std::move(prototype),
+                                   *opt_return_type, std::move(sql_defn_str),
+                                   stmt.get()});
+  CreatedFunction::Context* created_ptr = created.get();
+  RETURN_IF_ERROR(RegisterSqlFunction<CreatedFunction>(
+      ctx->db, key.name.c_str(), created_argc, std::move(created)));
+  ctx->state->emplace(key, PerFunctionState{std::move(stmt), created_ptr});
+
+  // CREATE_FUNCTION doesn't have a return value so just don't sent |out|.
+  return base::OkStatus();
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/prelude/functions/create_function.h b/src/trace_processor/prelude/functions/create_function.h
new file mode 100644
index 0000000..258c4bb
--- /dev/null
+++ b/src/trace_processor/prelude/functions/create_function.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 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_PRELUDE_FUNCTIONS_CREATE_FUNCTION_H_
+#define SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_CREATE_FUNCTION_H_
+
+#include <sqlite3.h>
+#include <unordered_map>
+
+#include "src/trace_processor/prelude/functions/register_function.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Implementation of CREATE_FUNCTION SQL function.
+// See https://perfetto.dev/docs/analysis/metrics#metric-helper-functions for
+// usage of this function.
+struct CreateFunction : public SqlFunction {
+  struct PerFunctionState {
+    ScopedStmt stmt;
+    // void* to avoid leaking state.
+    void* created_functon_context;
+  };
+  struct NameAndArgc {
+    std::string name;
+    int argc;
+
+    struct Hasher {
+      std::size_t operator()(const NameAndArgc& s) const noexcept;
+    };
+    bool operator==(const NameAndArgc& other) const {
+      return name == other.name && argc == other.argc;
+    }
+  };
+  using State = std::unordered_map<NameAndArgc,
+                                   CreateFunction::PerFunctionState,
+                                   NameAndArgc::Hasher>;
+
+  struct Context {
+    sqlite3* db;
+    State* state;
+  };
+
+  static constexpr bool kVoidReturn = true;
+
+  static base::Status Run(Context* ctx,
+                          size_t argc,
+                          sqlite3_value** argv,
+                          SqlValue& out,
+                          Destructors&);
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_CREATE_FUNCTION_H_
diff --git a/src/trace_processor/prelude/functions/create_function_internal.cc b/src/trace_processor/prelude/functions/create_function_internal.cc
new file mode 100644
index 0000000..730d89a
--- /dev/null
+++ b/src/trace_processor/prelude/functions/create_function_internal.cc
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2021 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/prelude/functions/create_function_internal.h"
+
+#include "perfetto/base/status.h"
+#include "perfetto/ext/base/string_view.h"
+#include "src/trace_processor/sqlite/sqlite_utils.h"
+#include "src/trace_processor/util/status_macros.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+base::Status ParseFunctionName(base::StringView raw, base::StringView& out) {
+  size_t function_name_end = raw.find('(');
+  if (function_name_end == base::StringView::npos)
+    return base::ErrStatus("unable to find bracket starting argument list");
+
+  base::StringView function_name = raw.substr(0, function_name_end);
+  if (!sql_argument::IsValidName(function_name)) {
+    return base::ErrStatus("function name %s is not alphanumeric",
+                           function_name.ToStdString().c_str());
+  }
+  out = function_name;
+  return base::OkStatus();
+}
+
+base::Status ParsePrototype(base::StringView raw, Prototype& out) {
+  // Examples of function prototypes:
+  // ANDROID_SDK_LEVEL()
+  // STARTUP_SLICE(dur_ns INT)
+  // FIND_NEXT_SLICE_WITH_NAME(ts INT, name STRING)
+
+  base::StringView function_name;
+  RETURN_IF_ERROR(ParseFunctionName(raw, function_name));
+
+  size_t function_name_end = function_name.size();
+  size_t args_start = function_name_end + 1;
+  size_t args_end = raw.find(')', args_start);
+  if (args_end == base::StringView::npos)
+    return base::ErrStatus("unable to find bracket ending argument list");
+
+  base::StringView args_str = raw.substr(args_start, args_end - args_start);
+  RETURN_IF_ERROR(sql_argument::ParseArgumentDefinitions(args_str.ToStdString(),
+                                                         out.arguments));
+
+  out.function_name = function_name.ToStdString();
+  return base::OkStatus();
+}
+
+base::Status SqliteRetToStatus(sqlite3* db,
+                               const std::string& function_name,
+                               int ret) {
+  if (ret != SQLITE_ROW && ret != SQLITE_DONE) {
+    return base::ErrStatus("%s: SQLite error while executing function body: %s",
+                           function_name.c_str(), sqlite3_errmsg(db));
+  }
+  return base::OkStatus();
+}
+
+base::Status MaybeBindArgument(sqlite3_stmt* stmt,
+                               const std::string& function_name,
+                               const sql_argument::ArgumentDefinition& arg,
+                               sqlite3_value* value) {
+  int index = sqlite3_bind_parameter_index(stmt, arg.dollar_name().c_str());
+
+  // If the argument is not in the query, this just means its an unused
+  // argument which we can just ignore.
+  if (index == 0)
+    return base::Status();
+
+  int ret = sqlite3_bind_value(stmt, index, value);
+  if (ret != SQLITE_OK) {
+    return base::ErrStatus(
+        "%s: SQLite error while binding value to argument %s: %s",
+        function_name.c_str(), arg.name().c_str(),
+        sqlite3_errmsg(sqlite3_db_handle(stmt)));
+  }
+  return base::OkStatus();
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/prelude/functions/create_function_internal.h b/src/trace_processor/prelude/functions/create_function_internal.h
new file mode 100644
index 0000000..83c0723
--- /dev/null
+++ b/src/trace_processor/prelude/functions/create_function_internal.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 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_PRELUDE_FUNCTIONS_CREATE_FUNCTION_INTERNAL_H_
+#define SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_CREATE_FUNCTION_INTERNAL_H_
+
+#include <sqlite3.h>
+#include <string>
+
+#include "perfetto/base/status.h"
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/string_view.h"
+#include "perfetto/trace_processor/basic_types.h"
+#include "src/trace_processor/util/sql_argument.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+struct Prototype {
+  std::string function_name;
+  std::vector<sql_argument::ArgumentDefinition> arguments;
+
+  bool operator==(const Prototype& other) const {
+    return function_name == other.function_name && arguments == other.arguments;
+  }
+  bool operator!=(const Prototype& other) const { return !(*this == other); }
+};
+
+base::Status ParseFunctionName(base::StringView raw,
+                               base::StringView& function_name);
+
+base::Status ParsePrototype(base::StringView raw, Prototype& out);
+
+base::Status SqliteRetToStatus(sqlite3* db,
+                               const std::string& function_name,
+                               int ret);
+
+base::Status MaybeBindArgument(sqlite3_stmt*,
+                               const std::string& function_name,
+                               const sql_argument::ArgumentDefinition&,
+                               sqlite3_value*);
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_CREATE_FUNCTION_INTERNAL_H_
diff --git a/src/trace_processor/prelude/functions/create_view_function.cc b/src/trace_processor/prelude/functions/create_view_function.cc
new file mode 100644
index 0000000..e93e8c7
--- /dev/null
+++ b/src/trace_processor/prelude/functions/create_view_function.cc
@@ -0,0 +1,502 @@
+/*
+ * Copyright (C) 2021 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/prelude/functions/create_view_function.h"
+
+#include <numeric>
+
+#include "perfetto/base/status.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "perfetto/ext/base/string_view.h"
+#include "perfetto/trace_processor/basic_types.h"
+#include "src/trace_processor/prelude/functions/create_function_internal.h"
+#include "src/trace_processor/sqlite/scoped_db.h"
+#include "src/trace_processor/sqlite/sqlite_table.h"
+#include "src/trace_processor/sqlite/sqlite_utils.h"
+#include "src/trace_processor/tp_metatrace.h"
+#include "src/trace_processor/util/status_macros.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+namespace {
+
+class CreatedViewFunction : public SqliteTable {
+ public:
+  class Cursor : public SqliteTable::Cursor {
+   public:
+    explicit Cursor(CreatedViewFunction* table);
+    ~Cursor() override;
+
+    int Filter(const QueryConstraints& qc,
+               sqlite3_value**,
+               FilterHistory) override;
+    int Next() override;
+    int Eof() override;
+    int Column(sqlite3_context* context, int N) override;
+
+   private:
+    ScopedStmt scoped_stmt_;
+    sqlite3_stmt* stmt_ = nullptr;
+    CreatedViewFunction* table_ = nullptr;
+    bool is_eof_ = false;
+    int next_call_count_ = 0;
+  };
+
+  CreatedViewFunction(sqlite3*, void*);
+  ~CreatedViewFunction() override;
+
+  base::Status Init(int argc, const char* const* argv, Schema*) override;
+  std::unique_ptr<SqliteTable::Cursor> CreateCursor() override;
+  int BestIndex(const QueryConstraints& qc, BestIndexInfo* info) override;
+
+  static void Register(sqlite3* db) {
+    SqliteTable::Register<CreatedViewFunction>(
+        db, nullptr, "internal_view_function_impl", false, true);
+  }
+
+ private:
+  Schema CreateSchema();
+
+  bool IsReturnValueColumn(size_t i) const {
+    PERFETTO_DCHECK(i < schema().columns().size());
+    return i < return_values_.size();
+  }
+
+  bool IsArgumentColumn(size_t i) const {
+    PERFETTO_DCHECK(i < schema().columns().size());
+    return i >= return_values_.size() &&
+           (i - return_values_.size()) < prototype_.arguments.size();
+  }
+
+  bool IsPrimaryKeyColumn(size_t i) const {
+    PERFETTO_DCHECK(i < schema().columns().size());
+    return i == (return_values_.size() + prototype_.arguments.size());
+  }
+
+  sqlite3* db_ = nullptr;
+
+  Prototype prototype_;
+  std::vector<sql_argument::ArgumentDefinition> return_values_;
+
+  std::string prototype_str_;
+  std::string sql_defn_str_;
+};
+
+CreatedViewFunction::CreatedViewFunction(sqlite3* db, void*) : db_(db) {}
+CreatedViewFunction::~CreatedViewFunction() = default;
+
+base::Status CreatedViewFunction::Init(int argc,
+                                       const char* const* argv,
+                                       Schema* schema) {
+  // The first three args are SQLite ones which we ignore.
+  PERFETTO_CHECK(argc == 6);
+
+  prototype_str_ = argv[3];
+  std::string return_prototype_str = argv[4];
+  sql_defn_str_ = argv[5];
+
+  // SQLite gives us strings with quotes included (i.e. 'string'). Strip these
+  // from the front and back.
+  prototype_str_ = prototype_str_.substr(1, prototype_str_.size() - 2);
+  return_prototype_str =
+      return_prototype_str.substr(1, return_prototype_str.size() - 2);
+  sql_defn_str_ = sql_defn_str_.substr(1, sql_defn_str_.size() - 2);
+
+  // Parse all the arguments into a more friendly form.
+  base::Status status =
+      ParsePrototype(base::StringView(prototype_str_), prototype_);
+  if (!status.ok()) {
+    return base::ErrStatus("CREATE_VIEW_FUNCTION[prototype=%s]: %s",
+                           prototype_str_.c_str(), status.c_message());
+  }
+
+  // Parse the return type into a enum format.
+  status = sql_argument::ParseArgumentDefinitions(return_prototype_str,
+                                                  return_values_);
+  if (!status.ok()) {
+    return base::ErrStatus(
+        "CREATE_VIEW_FUNCTION[prototype=%s, return=%s]: unknown return type "
+        "specified",
+        prototype_str_.c_str(), return_prototype_str.c_str());
+  }
+
+  // Verify that the provided SQL prepares to a statement correctly.
+  ScopedStmt stmt;
+  sqlite3_stmt* raw_stmt = nullptr;
+  int ret = sqlite3_prepare_v2(db_, sql_defn_str_.data(),
+                               static_cast<int>(sql_defn_str_.size()),
+                               &raw_stmt, nullptr);
+  stmt.reset(raw_stmt);
+  if (ret != SQLITE_OK) {
+    return base::ErrStatus(
+        "%s: Failed to prepare SQL statement for function. "
+        "Check the SQL defintion this function for syntax errors.\n%s",
+        prototype_.function_name.c_str(),
+        sqlite_utils::FormatErrorMessage(
+            raw_stmt, base::StringView(sql_defn_str_), db_, ret)
+            .c_message());
+  }
+
+  // Verify that every argument name in the function appears in the
+  // argument list.
+  //
+  // We intentionally loop from 1 to |used_param_count| because SQL
+  // parameters are 1-indexed *not* 0-indexed.
+  int used_param_count = sqlite3_bind_parameter_count(stmt.get());
+  for (int i = 1; i <= used_param_count; ++i) {
+    const char* name = sqlite3_bind_parameter_name(stmt.get(), i);
+
+    if (!name) {
+      return base::ErrStatus(
+          "%s: \"Nameless\" SQL parameters cannot be used in the SQL "
+          "statements of view functions.",
+          prototype_.function_name.c_str());
+    }
+
+    if (!base::StringView(name).StartsWith("$")) {
+      return base::ErrStatus(
+          "%s: invalid parameter name %s used in the SQL definition of "
+          "the view function: all parameters must be prefixed with '$' not ':' "
+          "or '@'.",
+          prototype_.function_name.c_str(), name);
+    }
+
+    auto it =
+        std::find_if(prototype_.arguments.begin(), prototype_.arguments.end(),
+                     [name](const sql_argument::ArgumentDefinition& arg) {
+                       return arg.dollar_name() == name;
+                     });
+    if (it == prototype_.arguments.end()) {
+      return base::ErrStatus(
+          "%s: parameter %s does not appear in the list of arguments in the "
+          "prototype of the view function.",
+          prototype_.function_name.c_str(), name);
+    }
+  }
+
+  // Verify that the prepared statement column count matches the return
+  // count.
+  uint32_t col_count = static_cast<uint32_t>(sqlite3_column_count(stmt.get()));
+  if (col_count != return_values_.size()) {
+    return base::ErrStatus(
+        "%s: number of return values %u does not match SQL statement column "
+        "count %zu.",
+        prototype_.function_name.c_str(), col_count, return_values_.size());
+  }
+
+  // Verify that the return names matches the prepared statment column names.
+  for (uint32_t i = 0; i < col_count; ++i) {
+    const char* name = sqlite3_column_name(stmt.get(), static_cast<int>(i));
+    if (name != return_values_[i].name()) {
+      return base::ErrStatus(
+          "%s: column %s at index %u does not match return value name %s.",
+          prototype_.function_name.c_str(), name, i,
+          return_values_[i].name().c_str());
+    }
+  }
+
+  // Now we've parsed prototype and return values, create the schema.
+  *schema = CreateSchema();
+
+  return base::OkStatus();
+}
+
+SqliteTable::Schema CreatedViewFunction::CreateSchema() {
+  std::vector<Column> columns;
+  for (size_t i = 0; i < return_values_.size(); ++i) {
+    const auto& ret = return_values_[i];
+    columns.push_back(Column(columns.size(), ret.name().ToStdString(),
+                             sql_argument::TypeToSqlValueType(ret.type())));
+  }
+  for (size_t i = 0; i < prototype_.arguments.size(); ++i) {
+    const auto& arg = prototype_.arguments[i];
+
+    // Add the "in_" prefix to every argument param to avoid clashes between the
+    // output and input parameters.
+    columns.push_back(Column(columns.size(), "in_" + arg.name().ToStdString(),
+                             sql_argument::TypeToSqlValueType(arg.type()),
+                             true));
+  }
+
+  std::vector<size_t> primary_keys;
+
+  // Add the "primary key" column. SQLite requires that we provide a column
+  // which is non-null and unique. Unfortunately, we have no restrictions on
+  // the subqueries so we cannot rely on this constriant being held there.
+  // Therefore, we create a "primary key" column which exists purely for SQLite
+  // primary key purposes and is equal to the row number.
+  columns.push_back(
+      Column(columns.size(), "_primary_key", SqlValue::kLong, true));
+  primary_keys.emplace_back(columns.size() - 1);
+
+  return SqliteTable::Schema(std::move(columns), std::move(primary_keys));
+}
+
+std::unique_ptr<SqliteTable::Cursor> CreatedViewFunction::CreateCursor() {
+  return std::unique_ptr<Cursor>(new Cursor(this));
+}
+
+int CreatedViewFunction::BestIndex(const QueryConstraints& qc,
+                                   BestIndexInfo* info) {
+  // Only accept constraint sets where every input parameter has a value.
+  size_t seen_argument_constraints = 0;
+  for (size_t i = 0; i < qc.constraints().size(); ++i) {
+    const auto& cs = qc.constraints()[i];
+    seen_argument_constraints +=
+        IsArgumentColumn(static_cast<size_t>(cs.column));
+  }
+  if (seen_argument_constraints < prototype_.arguments.size())
+    return SQLITE_CONSTRAINT;
+
+  for (size_t i = 0; i < info->sqlite_omit_constraint.size(); ++i) {
+    size_t col = static_cast<size_t>(qc.constraints()[i].column);
+    if (IsArgumentColumn(col)) {
+      info->sqlite_omit_constraint[i] = true;
+    }
+  }
+  return SQLITE_OK;
+}
+
+CreatedViewFunction::Cursor::Cursor(CreatedViewFunction* table)
+    : SqliteTable::Cursor(table), table_(table) {}
+
+CreatedViewFunction::Cursor::~Cursor() = default;
+
+int CreatedViewFunction::Cursor::Filter(const QueryConstraints& qc,
+                                        sqlite3_value** argv,
+                                        FilterHistory) {
+  PERFETTO_TP_TRACE(metatrace::Category::FUNCTION, "CREATE_VIEW_FUNCTION",
+                    [this](metatrace::Record* r) {
+                      r->AddArg("Function",
+                                table_->prototype_.function_name.c_str());
+                    });
+
+  auto col_to_arg_idx = [this](int col) {
+    return static_cast<uint32_t>(col) -
+           static_cast<uint32_t>(table_->return_values_.size());
+  };
+
+  size_t seen_argument_constraints = 0;
+  for (size_t i = 0; i < qc.constraints().size(); ++i) {
+    const auto& cs = qc.constraints()[i];
+
+    // Only consider argument columns (i.e. input parameters) as we're
+    // delegating the rest to SQLite.
+    if (!table_->IsArgumentColumn(static_cast<size_t>(cs.column)))
+      continue;
+
+    // We only support equality constraints as we're expecting "input arguments"
+    // to our "function".
+    if (!sqlite_utils::IsOpEq(cs.op)) {
+      table_->SetErrorMessage(
+          sqlite3_mprintf("%s: non-equality constraint passed",
+                          table_->prototype_.function_name.c_str()));
+      return SQLITE_ERROR;
+    }
+
+    const auto& arg = table_->prototype_.arguments[col_to_arg_idx(cs.column)];
+    base::Status status = sqlite_utils::TypeCheckSqliteValue(
+        argv[i], sql_argument::TypeToSqlValueType(arg.type()),
+        sql_argument::TypeToHumanFriendlyString(arg.type()));
+    if (!status.ok()) {
+      table_->SetErrorMessage(
+          sqlite3_mprintf("%s: argument %s (index %u) %s",
+                          table_->prototype_.function_name.c_str(),
+                          arg.name().c_str(), i, status.c_message()));
+      return SQLITE_ERROR;
+    }
+
+    seen_argument_constraints++;
+  }
+
+  // Verify that we saw one valid constriant for every input argument.
+  if (seen_argument_constraints < table_->prototype_.arguments.size()) {
+    table_->SetErrorMessage(sqlite3_mprintf(
+        "%s: missing value for input argument. Saw %u arguments but expected "
+        "%u",
+        table_->prototype_.function_name.c_str(), seen_argument_constraints,
+        table_->prototype_.arguments.size()));
+    return SQLITE_ERROR;
+  }
+
+  // Prepare the SQL definition as a statement using SQLite.
+  // TODO(lalitm): see if we can reuse this prepared statement rather than
+  // creating it very time.
+  // TODO(lalitm): measure and implement whether it would be a good idea to
+  // forward constraints here when we build the nested query.
+  int ret = sqlite3_prepare_v2(table_->db_, table_->sql_defn_str_.data(),
+                               static_cast<int>(table_->sql_defn_str_.size()),
+                               &stmt_, nullptr);
+  scoped_stmt_.reset(stmt_);
+  PERFETTO_CHECK(ret == SQLITE_OK);
+
+  // Bind all the arguments to the appropriate places in the function.
+  for (size_t i = 0; i < qc.constraints().size(); ++i) {
+    const auto& cs = qc.constraints()[i];
+
+    // Don't deal with any constraints on the output parameters for simplicty.
+    // TODO(lalitm): reconsider this decision to allow more efficient queries:
+    // we would need to wrap the query in a SELECT * FROM (...) WHERE constraint
+    // like we do for SPAN JOIN.
+    if (!table_->IsArgumentColumn(static_cast<size_t>(cs.column)))
+      continue;
+
+    uint32_t index = col_to_arg_idx(cs.column);
+    PERFETTO_DCHECK(index < table_->prototype_.arguments.size());
+
+    const auto& arg = table_->prototype_.arguments[index];
+    auto status = MaybeBindArgument(stmt_, table_->prototype_.function_name,
+                                    arg, argv[i]);
+    if (!status.ok()) {
+      table_->SetErrorMessage(sqlite3_mprintf("%s", status.c_message()));
+      return SQLITE_ERROR;
+    }
+  }
+
+  // Reset the next call count - this is necessary because the same cursor
+  // can be used for multiple filter operations.
+  next_call_count_ = 0;
+  return Next();
+}
+
+int CreatedViewFunction::Cursor::Next() {
+  int ret = sqlite3_step(stmt_);
+  is_eof_ = ret == SQLITE_DONE;
+  next_call_count_++;
+  if (ret != SQLITE_ROW && ret != SQLITE_DONE) {
+    table_->SetErrorMessage(sqlite3_mprintf(
+        "%s: SQLite error while stepping statement: %s",
+        table_->prototype_.function_name.c_str(),
+        sqlite_utils::FormatErrorMessage(stmt_, base::nullopt, table_->db_, ret)
+            .c_message()));
+    return ret;
+  }
+  return SQLITE_OK;
+}
+
+int CreatedViewFunction::Cursor::Eof() {
+  return is_eof_;
+}
+
+int CreatedViewFunction::Cursor::Column(sqlite3_context* ctx, int i) {
+  size_t idx = static_cast<size_t>(i);
+  if (table_->IsReturnValueColumn(idx)) {
+    sqlite3_result_value(ctx, sqlite3_column_value(stmt_, i));
+  } else if (table_->IsArgumentColumn(idx)) {
+    // TODO(lalitm): it may be more appropriate to keep a note of the arguments
+    // which we passed in and return them here. Not doing this to because it
+    // doesn't seem necessary for any useful thing but something which may need
+    // to be changed in the future.
+    sqlite3_result_null(ctx);
+  } else {
+    PERFETTO_DCHECK(table_->IsPrimaryKeyColumn(idx));
+    sqlite3_result_int(ctx, next_call_count_);
+  }
+  return SQLITE_OK;
+}
+
+}  // namespace
+
+base::Status CreateViewFunction::Run(CreateViewFunction::Context* ctx,
+                                     size_t argc,
+                                     sqlite3_value** argv,
+                                     SqlValue&,
+                                     Destructors&) {
+  if (argc != 3) {
+    return base::ErrStatus(
+        "CREATE_VIEW_FUNCTION: invalid number of args; expected %u, received "
+        "%zu",
+        3u, argc);
+  }
+
+  sqlite3_value* prototype_value = argv[0];
+  sqlite3_value* return_prototype_value = argv[1];
+  sqlite3_value* sql_defn_value = argv[2];
+
+  // Type check all the arguments.
+  {
+    auto type_check = [prototype_value](sqlite3_value* value,
+                                        SqlValue::Type type, const char* desc) {
+      base::Status status = sqlite_utils::TypeCheckSqliteValue(value, type);
+      if (!status.ok()) {
+        return base::ErrStatus("CREATE_VIEW_FUNCTION[prototype=%s]: %s %s",
+                               sqlite3_value_text(prototype_value), desc,
+                               status.c_message());
+      }
+      return base::OkStatus();
+    };
+
+    RETURN_IF_ERROR(type_check(prototype_value, SqlValue::Type::kString,
+                               "function prototype (first argument)"));
+    RETURN_IF_ERROR(type_check(return_prototype_value, SqlValue::Type::kString,
+                               "return prototype (second argument)"));
+    RETURN_IF_ERROR(type_check(sql_defn_value, SqlValue::Type::kString,
+                               "SQL definition (third argument)"));
+  }
+
+  // Extract the arguments from the value wrappers.
+  auto extract_string = [](sqlite3_value* value) -> const char* {
+    return reinterpret_cast<const char*>(sqlite3_value_text(value));
+  };
+  const char* prototype_str = extract_string(prototype_value);
+  const char* return_prototype_str = extract_string(return_prototype_value);
+  const char* sql_defn_str = extract_string(sql_defn_value);
+
+  base::StringView function_name;
+  RETURN_IF_ERROR(ParseFunctionName(prototype_str, function_name));
+
+  static constexpr char kSqlTemplate[] = R"""(
+    DROP TABLE IF EXISTS %s;
+
+    CREATE VIRTUAL TABLE %s
+    USING INTERNAL_VIEW_FUNCTION_IMPL('%s', '%s', '%s');
+  )""";
+  std::string function_name_str = function_name.ToStdString();
+
+  ScopedSqliteString errmsg;
+  char* errmsg_raw = nullptr;
+  int ret;
+
+  NullTermStringView sql_defn(sql_defn_str);
+  if (sql_defn.size() < 512) {
+    base::StackString<1024> sql(kSqlTemplate, function_name_str.c_str(),
+                                function_name_str.c_str(), prototype_str,
+                                return_prototype_str, sql_defn_str);
+    ret = sqlite3_exec(ctx->db, sql.c_str(), nullptr, nullptr, &errmsg_raw);
+  } else {
+    std::vector<char> formatted_sql(sql_defn.size() + 1024);
+    base::SprintfTrunc(formatted_sql.data(), formatted_sql.size(), kSqlTemplate,
+                       function_name_str.c_str(), function_name_str.c_str(),
+                       prototype_str, return_prototype_str, sql_defn_str);
+    ret = sqlite3_exec(ctx->db, formatted_sql.data(), nullptr, nullptr,
+                       &errmsg_raw);
+  }
+
+  errmsg.reset(errmsg_raw);
+  if (ret != SQLITE_OK)
+    return base::ErrStatus("%s", errmsg.get());
+
+  // CREATE_VIEW_FUNCTION doesn't have a return value so just don't sent |out|.
+  return base::OkStatus();
+}
+
+void CreateViewFunction::RegisterTable(sqlite3* db) {
+  CreatedViewFunction::Register(db);
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/prelude/functions/create_view_function.h b/src/trace_processor/prelude/functions/create_view_function.h
new file mode 100644
index 0000000..c91817a
--- /dev/null
+++ b/src/trace_processor/prelude/functions/create_view_function.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 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_PRELUDE_FUNCTIONS_CREATE_VIEW_FUNCTION_H_
+#define SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_CREATE_VIEW_FUNCTION_H_
+
+#include <sqlite3.h>
+#include <unordered_map>
+
+#include "src/trace_processor/prelude/functions/register_function.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Implementation of CREATE_VIEW_FUNCTION SQL function.
+// See https://perfetto.dev/docs/analysis/metrics#metric-helper-functions for
+// usage of this function.
+struct CreateViewFunction : public SqlFunction {
+  struct Context {
+    sqlite3* db;
+  };
+
+  static constexpr bool kVoidReturn = true;
+
+  static base::Status Run(Context* ctx,
+                          size_t argc,
+                          sqlite3_value** argv,
+                          SqlValue& out,
+                          Destructors&);
+
+  static void RegisterTable(sqlite3* db);
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_CREATE_VIEW_FUNCTION_H_
diff --git a/src/trace_processor/prelude/functions/import.cc b/src/trace_processor/prelude/functions/import.cc
new file mode 100644
index 0000000..7657969
--- /dev/null
+++ b/src/trace_processor/prelude/functions/import.cc
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2021 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/prelude/functions/import.h"
+
+#include <numeric>
+
+#include "perfetto/base/status.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "perfetto/ext/base/string_view.h"
+#include "perfetto/trace_processor/basic_types.h"
+#include "src/trace_processor/prelude/functions/create_function_internal.h"
+#include "src/trace_processor/sqlite/scoped_db.h"
+#include "src/trace_processor/sqlite/sqlite_table.h"
+#include "src/trace_processor/sqlite/sqlite_utils.h"
+#include "src/trace_processor/tp_metatrace.h"
+#include "src/trace_processor/util/sql_modules.h"
+#include "src/trace_processor/util/status_macros.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+base::Status Import::Run(Import::Context* ctx,
+                         size_t argc,
+                         sqlite3_value** argv,
+                         SqlValue&,
+                         Destructors&) {
+  if (argc != 1) {
+    return base::ErrStatus(
+        "IMPORT: invalid number of args; expected 1, received "
+        "%zu",
+        argc);
+  }
+  sqlite3_value* import_val = argv[0];
+
+  // Type check
+  {
+    base::Status status =
+        sqlite_utils::TypeCheckSqliteValue(import_val, SqlValue::Type::kString);
+    if (!status.ok()) {
+      return base::ErrStatus("IMPORT(%s): %s", sqlite3_value_text(import_val),
+                             status.c_message());
+    }
+  }
+
+  const char* import_key =
+      reinterpret_cast<const char*>(sqlite3_value_text(import_val));
+
+  std::string module_name = sql_modules::GetModuleName(import_key);
+  auto module = ctx->modules->Find(module_name);
+  if (!module)
+    return base::ErrStatus("IMPORT: Unknown module name provided - %s",
+                           import_key);
+
+  auto module_file = module->import_key_to_file.Find(import_key);
+  if (!module_file) {
+    return base::ErrStatus("IMPORT: Unknown filename provided - %s",
+                           import_key);
+  }
+  // IMPORT is noop for already imported files.
+  if (module_file->imported) {
+    return base::OkStatus();
+  }
+
+  auto import_iter = ctx->tp->ExecuteQuery(module_file->sql);
+  if (import_iter.StatementWithOutputCount() > 0)
+    return base::ErrStatus("IMPORT: Imported file returning values.");
+  {
+    auto status = import_iter.Status();
+    if (!status.ok())
+      return base::ErrStatus("SQLite error on IMPORT: %s", status.c_message());
+  }
+
+  module_file->imported = true;
+  return base::OkStatus();
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/prelude/functions/import.h b/src/trace_processor/prelude/functions/import.h
new file mode 100644
index 0000000..9773b5f
--- /dev/null
+++ b/src/trace_processor/prelude/functions/import.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 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_PRELUDE_FUNCTIONS_IMPORT_H_
+#define SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_IMPORT_H_
+
+#include <sqlite3.h>
+#include <string>
+#include <unordered_map>
+
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "perfetto/trace_processor/trace_processor.h"
+#include "src/trace_processor/prelude/functions/register_function.h"
+#include "src/trace_processor/util/sql_modules.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+struct Import : public SqlFunction {
+  struct Context {
+    sqlite3* db;
+    TraceProcessor* tp;
+    base::FlatHashMap<std::string, sql_modules::RegisteredModule>* modules;
+  };
+
+  static constexpr bool kVoidReturn = true;
+
+  static base::Status Run(Context* ctx,
+                          size_t argc,
+                          sqlite3_value** argv,
+                          SqlValue& out,
+                          Destructors&);
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_IMPORT_H_
diff --git a/src/trace_processor/prelude/functions/pprof_functions.cc b/src/trace_processor/prelude/functions/pprof_functions.cc
new file mode 100644
index 0000000..e0e595d
--- /dev/null
+++ b/src/trace_processor/prelude/functions/pprof_functions.cc
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2022 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/prelude/functions/pprof_functions.h"
+
+#include "perfetto/base/logging.h"
+#include "perfetto/base/status.h"
+#include "perfetto/trace_processor/status.h"
+#include "src/trace_processor/sqlite/sqlite_utils.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/profile_builder.h"
+#include "src/trace_processor/util/status_macros.h"
+
+#include <cinttypes>
+#include <cstdint>
+#include <limits>
+#include <utility>
+
+// TODO(carlscab): We currently recreate the GProfileBuilder for every group. We
+// should cache this somewhere maybe even have a helper table that stores all
+// this data.
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+template <typename T, typename... Args>
+std::unique_ptr<T> MakeUnique(Args&&... args) {
+  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+}
+
+void SetSqliteError(sqlite3_context* ctx, const base::Status& status) {
+  PERFETTO_CHECK(!status.ok());
+  sqlite3_result_error(ctx, status.c_message(), -1);
+}
+
+void SetSqliteError(sqlite3_context* ctx,
+                    const std::string& function_name,
+                    const base::Status& status) {
+  SetSqliteError(ctx, base::ErrStatus("%s: %s", function_name.c_str(),
+                                      status.c_message()));
+}
+
+class ProfileFunctionBase {
+ public:
+  static base::Status Register(sqlite3* db,
+                               std::unique_ptr<ProfileFunctionBase> function) {
+    int flags = SQLITE_UTF8 | SQLITE_DETERMINISTIC;
+    // Keep a copy of the name. If registration fails function will be deleted
+    // and thus the name will no longer be available for the error message.
+    std::string function_name = function->name();
+    int n_arg = function->GetArgumentCount();
+    int ret = sqlite3_create_function_v2(db, function_name.c_str(), n_arg,
+                                         flags, function.release(), nullptr,
+                                         Step, Final, Destroy);
+    if (ret != SQLITE_OK) {
+      return base::ErrStatus("Unable to register function with name %s",
+                             function_name.c_str());
+    }
+    return base::OkStatus();
+  }
+
+  virtual ~ProfileFunctionBase() {}
+
+ protected:
+  ProfileFunctionBase(const TraceProcessorContext* tp_context,
+                      std::string name,
+                      bool annotate_callsites)
+      : tp_context_(tp_context),
+        name_(std::move(name)),
+        annotate_callsites_(annotate_callsites) {}
+
+ private:
+  static std::unique_ptr<GProfileBuilder> ReleaseProfileBuilder(
+      sqlite3_context* ctx) {
+    GProfileBuilder** builder =
+        reinterpret_cast<GProfileBuilder**>(sqlite3_aggregate_context(ctx, 0));
+
+    if (!builder) {
+      return nullptr;
+    }
+
+    return std::unique_ptr<GProfileBuilder>(*builder);
+  }
+
+  static void Step(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
+    ProfileFunctionBase* func =
+        reinterpret_cast<ProfileFunctionBase*>(sqlite3_user_data(ctx));
+
+    base::Status status = func->StepImpl(ctx, argc, argv);
+
+    if (!status.ok()) {
+      SetSqliteError(ctx, func->name(), status);
+    }
+  }
+
+  static void Final(sqlite3_context* ctx) {
+    std::unique_ptr<GProfileBuilder> builder = ReleaseProfileBuilder(ctx);
+    if (!builder) {
+      return;
+    }
+
+    // TODO(carlscab): A lot of copies are happening here.
+    std::string profile_proto = builder->Build();
+
+    std::unique_ptr<uint8_t[], base::FreeDeleter> data(
+        static_cast<uint8_t*>(malloc(profile_proto.size())));
+    memcpy(data.get(), profile_proto.data(), profile_proto.size());
+    sqlite3_result_blob(ctx, data.release(),
+                        static_cast<int>(profile_proto.size()), free);
+  }
+
+  static void Destroy(void* p_app) {
+    delete reinterpret_cast<ProfileFunctionBase*>(p_app);
+  }
+
+  static base::Status GetCallsiteId(sqlite3_value* arg, uint32_t& callsite_id) {
+    int64_t value = sqlite3_value_int64(arg);
+
+    if (value < 0 || value > std::numeric_limits<uint32_t>::max()) {
+      return base::ErrStatus("invalid callsite_id value %" PRId64, value);
+    }
+
+    callsite_id = static_cast<uint32_t>(value);
+    return util::OkStatus();
+  }
+
+  virtual base::Status ValidateArguments(int argc,
+                                         sqlite3_value** argv) const = 0;
+  // Will only be called if ValidateArguments returned successfully.
+  virtual base::Status GetSampleTypes(
+      int argc,
+      sqlite3_value** argv,
+      std::vector<GProfileBuilder::ValueType>& sample_types) const = 0;
+
+  // Will only be called if ValidateArguments returned successfully.
+  virtual base::Status AddSample(uint32_t callsite_id,
+                                 int argc,
+                                 sqlite3_value** argv,
+                                 GProfileBuilder& builder) = 0;
+
+  // Returns number of arguments expected by the function. -1 for variable
+  // number of arguments.
+  virtual int GetArgumentCount() const = 0;
+
+  const std::string& name() const { return name_; }
+
+  base::Status StepImpl(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
+    GProfileBuilder** builder = reinterpret_cast<GProfileBuilder**>(
+        sqlite3_aggregate_context(ctx, sizeof(GProfileBuilder*)));
+    if (!builder) {
+      return base::ErrStatus("Failed to allocate aggregate context");
+    }
+
+    if (!*builder) {
+      RETURN_IF_ERROR(ValidateArguments(argc, argv));
+      RETURN_IF_ERROR(ValidateCallsiteIdArgument(argc, argv));
+      RETURN_IF_ERROR(CreateProfileBuilder(argc, argv, builder));
+    }
+
+    // Needs to be initialized to avoid compiler warning. Do it to a most likely
+    // invalid callsite_id.
+    uint32_t callsite_id = std::numeric_limits<uint32_t>::max();
+    RETURN_IF_ERROR(GetCallsiteId(argv[0], callsite_id));
+
+    return AddSample(callsite_id, argc, argv, **builder);
+  }
+
+  base::Status CreateProfileBuilder(int argc,
+                                    sqlite3_value** argv,
+                                    GProfileBuilder** builder) {
+    std::vector<GProfileBuilder::ValueType> sample_types;
+    RETURN_IF_ERROR(GetSampleTypes(argc, argv, sample_types));
+    *builder = new GProfileBuilder(tp_context_, std::move(sample_types),
+                                   annotate_callsites_);
+    return util::OkStatus();
+  }
+
+  base::Status ValidateCallsiteIdArgument(int argc,
+                                          sqlite3_value** argv) const {
+    if (argc < 1) {
+      return base::ErrStatus("missing argument callstack_id");
+    }
+
+    base::Status status =
+        sqlite_utils::TypeCheckSqliteValue(argv[0], SqlValue::kLong);
+    if (!status.ok()) {
+      return base::ErrStatus("argument 1; value %s", status.c_message());
+    }
+
+    return util::OkStatus();
+  }
+
+  const TraceProcessorContext* const tp_context_;
+  const std::string name_;
+  const bool annotate_callsites_;
+};
+
+class PerfProfileFunction : public ProfileFunctionBase {
+ public:
+  PerfProfileFunction(const TraceProcessorContext* context,
+                      std::string name,
+                      bool annotate_callsites)
+      : ProfileFunctionBase(context, name, annotate_callsites) {
+    single_count_value_.Append(1);
+  }
+
+ private:
+  int GetArgumentCount() const override { return 1; }
+
+  base::Status ValidateArguments(int argc, sqlite3_value**) const override {
+    if (GetArgumentCount() != argc) {
+      return base::ErrStatus(
+          "invalid number of args; "
+          "expected %d, received %d",
+          GetArgumentCount(), argc);
+    }
+    return util::OkStatus();
+  }
+
+  base::Status GetSampleTypes(
+      int,
+      sqlite3_value**,
+      std::vector<GProfileBuilder::ValueType>& sample_types) const override {
+    sample_types = {{"samples", "count"}};
+    return util::OkStatus();
+  }
+
+  base::Status AddSample(uint32_t callsite_id,
+                         int,
+                         sqlite3_value**,
+                         GProfileBuilder& builder) override {
+    if (!builder.AddSample(callsite_id, single_count_value_)) {
+      return base::ErrStatus("invalid callsite_id: %" PRIu32, callsite_id);
+    }
+    return util::OkStatus();
+  }
+
+  protozero::PackedVarInt single_count_value_;
+};
+
+class ProfileFunction : public ProfileFunctionBase {
+ public:
+  ProfileFunction(const TraceProcessorContext* context,
+                  std::string name,
+                  bool annotate_callsites)
+      : ProfileFunctionBase(context, name, annotate_callsites) {}
+
+ private:
+  int GetArgumentCount() const override { return -1; }
+
+  base::Status GetSampleTypes(
+      int argc,
+      sqlite3_value** argv,
+      std::vector<GProfileBuilder::ValueType>& sample_types) const override {
+    std::vector<GProfileBuilder::ValueType> tmp;
+
+    PERFETTO_CHECK(argc > 1 && (argc - 1) % 3 == 0);
+
+    for (int i = 1; i < argc; i += 3) {
+      GProfileBuilder::ValueType value_type;
+      value_type.type =
+          reinterpret_cast<const char*>(sqlite3_value_text(argv[i]));
+      value_type.unit =
+          reinterpret_cast<const char*>(sqlite3_value_text(argv[i + 1]));
+      tmp.push_back(std::move(value_type));
+    }
+
+    sample_types = std::move(tmp);
+    return util::OkStatus();
+  }
+
+  base::Status ValidateArguments(int argc,
+                                 sqlite3_value** argv) const override {
+    if (argc == 0) {
+      return base::ErrStatus(
+          "arguments missing; expected callsite_id, type, unit, and value");
+    }
+    if (argc == 1) {
+      return base::ErrStatus(
+          "arguments missing; expected type, unit, and value");
+    }
+
+    for (int i = 1; i < argc;) {
+      base::Status status =
+          sqlite_utils::TypeCheckSqliteValue(argv[i], SqlValue::kString);
+      if (!status.ok()) {
+        return base::ErrStatus("argument %d; type %s", i + 1,
+                               status.c_message());
+      }
+      ++i;
+      if (i == argc) {
+        return base::ErrStatus("arguments missing; expected unit, value");
+      }
+      status = sqlite_utils::TypeCheckSqliteValue(argv[i], SqlValue::kString);
+      if (!status.ok()) {
+        return base::ErrStatus("argument %d; unit %s", i + 1,
+                               status.c_message());
+      }
+      ++i;
+      if (i == argc) {
+        return base::ErrStatus("argument missing; expected value");
+      }
+      status = sqlite_utils::TypeCheckSqliteValue(argv[i], SqlValue::kLong);
+      if (!status.ok()) {
+        return base::ErrStatus("argument %d; value %s", i + 1,
+                               status.c_message());
+      }
+      ++i;
+    }
+    return util::OkStatus();
+  }
+
+  base::Status AddSample(uint32_t callsite_id,
+                         int argc,
+                         sqlite3_value** argv,
+                         GProfileBuilder& builder) override {
+    PERFETTO_CHECK(argc >= 4 && (argc - 1) % 3 == 0);
+
+    protozero::PackedVarInt values;
+
+    for (int i = 3; i < argc; i += 3) {
+      values.Append(sqlite3_value_int64(argv[i]));
+    }
+
+    if (!builder.AddSample(callsite_id, values)) {
+      return base::ErrStatus("invalid callsite_id: %" PRIu32, callsite_id);
+    }
+    return util::OkStatus();
+  }
+};
+
+}  // namespace
+
+base::Status PprofFunctions::Register(sqlite3* db,
+                                      TraceProcessorContext* context) {
+  RETURN_IF_ERROR(PerfProfileFunction::Register(
+      db, MakeUnique<PerfProfileFunction>(
+              context, "EXPERIMENTAL_ANNOTATED_PERF_PROFILE", true)));
+  RETURN_IF_ERROR(PerfProfileFunction::Register(
+      db, MakeUnique<PerfProfileFunction>(context, "EXPERIMENTAL_PERF_PROFILE",
+                                          false)));
+
+  RETURN_IF_ERROR(ProfileFunction::Register(
+      db, MakeUnique<ProfileFunction>(context, "EXPERIMENTAL_ANNOTATED_PROFILE",
+                                      true)));
+  RETURN_IF_ERROR(ProfileFunction::Register(
+      db, MakeUnique<ProfileFunction>(context, "EXPERIMENTAL_PROFILE", false)));
+
+  return util::OkStatus();
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/prelude/functions/pprof_functions.h b/src/trace_processor/prelude/functions/pprof_functions.h
new file mode 100644
index 0000000..f088970
--- /dev/null
+++ b/src/trace_processor/prelude/functions/pprof_functions.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 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_PRELUDE_FUNCTIONS_PPROF_FUNCTIONS_H_
+#define SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_PPROF_FUNCTIONS_H_
+
+#include <sqlite3.h>
+
+#include "perfetto/base/status.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class TraceProcessorContext;
+
+struct PprofFunctions {
+  static base::Status Register(sqlite3* db, TraceProcessorContext* context);
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_PPROF_FUNCTIONS_H_
diff --git a/src/trace_processor/prelude/functions/register_function.cc b/src/trace_processor/prelude/functions/register_function.cc
new file mode 100644
index 0000000..ef41f1d
--- /dev/null
+++ b/src/trace_processor/prelude/functions/register_function.cc
@@ -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.
+ */
+
+#include "src/trace_processor/prelude/functions/register_function.h"
+#include "sqlite3.h"
+#include "src/trace_processor/sqlite/sqlite_utils.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+base::Status SqlFunction::VerifyPostConditions(Context*) {
+  return base::OkStatus();
+}
+
+void SqlFunction::Cleanup(Context*) {}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/prelude/functions/register_function.h b/src/trace_processor/prelude/functions/register_function.h
new file mode 100644
index 0000000..acca3e6
--- /dev/null
+++ b/src/trace_processor/prelude/functions/register_function.h
@@ -0,0 +1,233 @@
+/*
+ * 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_PRELUDE_FUNCTIONS_REGISTER_FUNCTION_H_
+#define SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_REGISTER_FUNCTION_H_
+
+#include <sqlite3.h>
+#include <memory>
+
+#include "src/trace_processor/sqlite/sqlite_utils.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Prototype for a C++ function which can be registered with SQLite.
+//
+// Usage
+//
+// Define a subclass of this struct as follows:
+// struct YourFunction : public SqlFunction {
+//   // Optional if you want a custom context object (i.e. an object
+//   // passed in at registration time which will be passed to Run on
+//   // every invocation)
+//   struct YourContext { /* define context fields here */ };
+//
+//   static base::Status Run(/* see parameters below */) {
+//     /* function body here */
+//   }
+//
+//   static base::Status Cleanup(/* see parameters below */) {
+//     /* function body here */
+//   }
+// }
+//
+// Then, register this function with SQLite using RegisterFunction (see below);
+// you'll likely want to do this in TraceProcessorImpl:
+// RegisterFunction<YourFunction>(/* see arguments below */)
+struct SqlFunction {
+  // The type of the context object which will be passed to the function.
+  // Can be redefined in any sub-classes to override the context.
+  using Context = void;
+
+  // Indicates whether this function is "void" (i.e. doesn't actually want
+  // to return a value). While the function will still return null in SQL
+  // (because SQLite does not actually allow null functions), for accounting
+  // purposes, this null will be ignored when verifying whether this statement
+  // has any output.
+  // Can be redefined in any sub-classes to override it.
+  // If this is set to true, subclasses must not modify |out| or |destructors|.
+  static constexpr bool kVoidReturn = false;
+
+  // Struct which holds destructors for strings/bytes returned from the
+  // function. Passed as an argument to |Run| to allow implementations to
+  // override the destructors.
+  struct Destructors {
+    sqlite3_destructor_type string_destructor = sqlite_utils::kSqliteTransient;
+    sqlite3_destructor_type bytes_destructor = sqlite_utils::kSqliteTransient;
+  };
+
+  // The function which will be exectued with the arguments from SQL.
+  //
+  // Implementations MUST define this function themselves; this function is
+  // declared but *not* defined so linker errors will be thrown if not defined.
+  //
+  // |ctx|:         the context object passed at registration time.
+  // |argc|:        number of arguments.
+  // |argv|:        arguments to the function.
+  // |out|:         the return value of the function.
+  // |destructors|: destructors for string/bytes return values.
+  static base::Status Run(Context* ctx,
+                          size_t argc,
+                          sqlite3_value** argv,
+                          SqlValue& out,
+                          Destructors& destructors);
+
+  // Executed after the result from |Run| is reported to SQLite.
+  // Allows implementations to verify post-conditions without needing to worry
+  // about overwriting return types.
+  //
+  // Implementations do not need to define this function; a default no-op
+  // implementation will be used in this case.
+  static base::Status VerifyPostConditions(Context*);
+
+  // Executed after the result from |Run| is reported to SQLite.
+  // Allows any pending state to be cleaned up post-copy of results by SQLite:
+  // this function will be called even if |Run| or |PostRun| returned errors.
+  //
+  // Implementations do not need to define this function; a default no-op
+  // implementation will be used in this case.
+  static void Cleanup(Context*);
+};
+
+// Registers a C++ function to be runnable from SQL.
+// The format of the function is given by the |SqlFunction|; see the
+// documentaion above.
+//
+// |db|:          sqlite3 database object
+// |name|:        name of the function in SQL
+// |argc|:        number of arguments for this function, -1 if variable
+// |ctx|:         context object for the function (see SqlFunction::Run above);
+//                this object *must* outlive the function so should likely be
+//                either static or scoped to the lifetime of TraceProcessor.
+// |determistic|: whether this function has deterministic output given the
+//                same set of arguments.
+template <typename Function>
+base::Status RegisterSqlFunction(sqlite3* db,
+                                 const char* name,
+                                 int argc,
+                                 typename Function::Context* ctx,
+                                 bool deterministic = true);
+
+// Same as above except allows a unique_ptr to be passed for the context; this
+// allows for SQLite to manage the lifetime of this pointer instead of the
+// essentially static requirement of the context pointer above.
+template <typename Function>
+base::Status RegisterSqlFunction(
+    sqlite3* db,
+    const char* name,
+    int argc,
+    std::unique_ptr<typename Function::Context> ctx,
+    bool deterministic = true);
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+// The rest of this file is just implementation details which we need
+// in the header file because it is templated code. We separate it out
+// like this to keep the API people actually care about easy to read.
+
+namespace perfetto {
+namespace trace_processor {
+
+namespace sqlite_internal {
+
+// RAII type to call Function::Cleanup when destroyed.
+template <typename Function>
+struct ScopedCleanup {
+  typename Function::Context* ctx;
+  ~ScopedCleanup() { Function::Cleanup(ctx); }
+};
+
+template <typename Function>
+void WrapSqlFunction(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
+  using Context = typename Function::Context;
+  Context* ud = static_cast<Context*>(sqlite3_user_data(ctx));
+
+  ScopedCleanup<Function> scoped_cleanup{ud};
+  SqlValue value{};
+  SqlFunction::Destructors destructors{};
+  base::Status status =
+      Function::Run(ud, static_cast<size_t>(argc), argv, value, destructors);
+  if (!status.ok()) {
+    sqlite3_result_error(ctx, status.c_message(), -1);
+    return;
+  }
+
+  if (Function::kVoidReturn) {
+    if (!value.is_null()) {
+      sqlite3_result_error(ctx, "void SQL function returned value", -1);
+      return;
+    }
+
+    // If the function doesn't want to return anything, set the "VOID"
+    // pointer type to a non-null value. Note that because of the weird
+    // way |sqlite3_value_pointer| works, we need to set some value even
+    // if we don't actually read it - just set it to a pointer to an empty
+    // string for this reason.
+    static char kVoidValue[] = "";
+    sqlite3_result_pointer(ctx, kVoidValue, "VOID", nullptr);
+  } else {
+    sqlite_utils::ReportSqlValue(ctx, value, destructors.string_destructor,
+                                 destructors.bytes_destructor);
+  }
+
+  status = Function::VerifyPostConditions(ud);
+  if (!status.ok()) {
+    sqlite3_result_error(ctx, status.c_message(), -1);
+    return;
+  }
+}
+}  // namespace sqlite_internal
+
+template <typename Function>
+base::Status RegisterSqlFunction(sqlite3* db,
+                                 const char* name,
+                                 int argc,
+                                 typename Function::Context* ctx,
+                                 bool deterministic) {
+  int flags = SQLITE_UTF8 | (deterministic ? SQLITE_DETERMINISTIC : 0);
+  int ret = sqlite3_create_function_v2(
+      db, name, static_cast<int>(argc), flags, ctx,
+      sqlite_internal::WrapSqlFunction<Function>, nullptr, nullptr, nullptr);
+  if (ret != SQLITE_OK) {
+    return base::ErrStatus("Unable to register function with name %s", name);
+  }
+  return base::OkStatus();
+}
+
+template <typename Function>
+base::Status RegisterSqlFunction(
+    sqlite3* db,
+    const char* name,
+    int argc,
+    std::unique_ptr<typename Function::Context> user_data,
+    bool deterministic) {
+  int flags = SQLITE_UTF8 | (deterministic ? SQLITE_DETERMINISTIC : 0);
+  int ret = sqlite3_create_function_v2(
+      db, name, static_cast<int>(argc), flags, user_data.release(),
+      sqlite_internal::WrapSqlFunction<Function>, nullptr, nullptr,
+      [](void* ptr) { delete static_cast<typename Function::Context*>(ptr); });
+  if (ret != SQLITE_OK) {
+    return base::ErrStatus("Unable to register function with name %s", name);
+  }
+  return base::OkStatus();
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_REGISTER_FUNCTION_H_
diff --git a/src/trace_processor/prelude/functions/sqlite3_str_split.cc b/src/trace_processor/prelude/functions/sqlite3_str_split.cc
new file mode 100644
index 0000000..57508f3
--- /dev/null
+++ b/src/trace_processor/prelude/functions/sqlite3_str_split.cc
@@ -0,0 +1,85 @@
+/*
+ * 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/prelude/functions/sqlite3_str_split.h"
+
+#include "src/trace_processor/sqlite/sqlite_utils.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+namespace {
+constexpr char kDelimiterError[] =
+    "str_split: delimiter must be a non-empty string";
+constexpr char kSplitFieldIndexError[] =
+    "str_split: field number must be a non-negative integer";
+
+void sqlite_str_split(sqlite3_context* context,
+                      int argc,
+                      sqlite3_value** argv) {
+  PERFETTO_DCHECK(argc == 3);
+  if (sqlite3_value_type(argv[1]) != SQLITE_TEXT) {
+    sqlite3_result_error(context, kDelimiterError, -1);
+    return;
+  }
+  const char* delimiter =
+      reinterpret_cast<const char*>(sqlite3_value_text(argv[1]));
+  const size_t delimiter_len = strlen(delimiter);
+  if (delimiter_len == 0) {
+    sqlite3_result_error(context, kDelimiterError, -1);
+    return;
+  }
+  if (sqlite3_value_type(argv[2]) != SQLITE_INTEGER) {
+    sqlite3_result_error(context, kSplitFieldIndexError, -1);
+    return;
+  }
+  int fld = sqlite3_value_int(argv[2]);
+  if (fld < 0) {
+    sqlite3_result_error(context, kSplitFieldIndexError, -1);
+    return;
+  }
+  if (sqlite3_value_type(argv[0]) != SQLITE_TEXT) {
+    sqlite3_result_null(context);
+    return;
+  }
+  const char* in = reinterpret_cast<const char*>(sqlite3_value_text(argv[0]));
+  const char* next;
+  do {
+    next = strstr(in, delimiter);
+    if (fld == 0) {
+      int size = next != nullptr ? static_cast<int>(next - in)
+                                 : static_cast<int>(strlen(in));
+      sqlite3_result_text(context, in, size, sqlite_utils::kSqliteTransient);
+      return;
+    } else if (next == nullptr) {
+      break;
+    }
+    in = next + delimiter_len;
+    --fld;
+  } while (fld >= 0);
+  sqlite3_result_null(context);
+}
+}  // namespace
+
+void sqlite3_str_split_init(sqlite3* db) {
+  PERFETTO_CHECK(sqlite3_create_function(db, "str_split", 3,
+                                         SQLITE_UTF8 | SQLITE_DETERMINISTIC,
+                                         nullptr, &sqlite_str_split, nullptr,
+                                         nullptr) == SQLITE_OK);
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/prelude/functions/sqlite3_str_split.h b/src/trace_processor/prelude/functions/sqlite3_str_split.h
new file mode 100644
index 0000000..e6a6783
--- /dev/null
+++ b/src/trace_processor/prelude/functions/sqlite3_str_split.h
@@ -0,0 +1,30 @@
+/*
+ * 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_PRELUDE_FUNCTIONS_SQLITE3_STR_SPLIT_H_
+#define SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_SQLITE3_STR_SPLIT_H_
+
+struct sqlite3;
+
+namespace perfetto {
+namespace trace_processor {
+
+void sqlite3_str_split_init(sqlite3* db);
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_SQLITE3_STR_SPLIT_H_
diff --git a/src/trace_processor/prelude/functions/sqlite3_str_split_unittest.cc b/src/trace_processor/prelude/functions/sqlite3_str_split_unittest.cc
new file mode 100644
index 0000000..f5f6b15
--- /dev/null
+++ b/src/trace_processor/prelude/functions/sqlite3_str_split_unittest.cc
@@ -0,0 +1,96 @@
+/*
+ * 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/prelude/functions/sqlite3_str_split.h"
+
+#include <sqlite3.h>
+#include <string>
+
+#include "perfetto/base/logging.h"
+#include "src/trace_processor/sqlite/scoped_db.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+class Sqlite3StrSplitTest : public ::testing::Test {
+ public:
+  Sqlite3StrSplitTest() {
+    sqlite3* db = nullptr;
+    PERFETTO_CHECK(sqlite3_initialize() == SQLITE_OK);
+    PERFETTO_CHECK(sqlite3_open(":memory:", &db) == SQLITE_OK);
+    db_.reset(db);
+    sqlite3_str_split_init(db_.get());
+  }
+
+  const char* SplitStmt(const std::string& str,
+                        const std::string& delim,
+                        int field) {
+    const std::string sql = "SELECT STR_SPLIT(\"" + str + "\", \"" + delim +
+                            "\", " + std::to_string(field) + ");";
+    sqlite3_stmt* stmt = nullptr;
+    PERFETTO_CHECK(sqlite3_prepare_v2(*db_, sql.c_str(),
+                                      static_cast<int>(sql.size()), &stmt,
+                                      nullptr) == SQLITE_OK);
+    stmt_.reset(stmt);
+    PERFETTO_CHECK(sqlite3_step(stmt) == SQLITE_ROW);
+    if (sqlite3_column_type(stmt, 0) == SQLITE_NULL) {
+      return nullptr;
+    }
+    return reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
+  }
+
+ protected:
+  ScopedDb db_;
+  ScopedStmt stmt_;
+};
+
+TEST_F(Sqlite3StrSplitTest, SplitNoDelimiter) {
+  ASSERT_STREQ(SplitStmt("abc", ":", 0), "abc");
+  ASSERT_EQ(SplitStmt("abc", ":", 1), nullptr);
+}
+
+TEST_F(Sqlite3StrSplitTest, SplitSingleCharDelim) {
+  ASSERT_STREQ(SplitStmt("a:bc", ":", 0), "a");
+  ASSERT_STREQ(SplitStmt("a:bc", ":", 1), "bc");
+  ASSERT_EQ(SplitStmt("a:bc", ":", 2), nullptr);
+}
+
+TEST_F(Sqlite3StrSplitTest, SplitInputConsecutiveDelim) {
+  ASSERT_STREQ(SplitStmt("a::b::c", ":", 0), "a");
+  ASSERT_STREQ(SplitStmt("a::b::c", ":", 1), "");
+  ASSERT_STREQ(SplitStmt("a::b::c", ":", 2), "b");
+  ASSERT_STREQ(SplitStmt("a::b::c", ":", 3), "");
+  ASSERT_STREQ(SplitStmt("a::b::c", ":", 4), "c");
+  ASSERT_EQ(SplitStmt("a::b::c", ":", 5), nullptr);
+}
+
+TEST_F(Sqlite3StrSplitTest, SplitStringDelim) {
+  ASSERT_STREQ(SplitStmt("abczzdefzzghi", "zz", 0), "abc");
+  ASSERT_STREQ(SplitStmt("abczzdefzzghi", "zz", 1), "def");
+  ASSERT_STREQ(SplitStmt("abczzdefzzghi", "zz", 2), "ghi");
+}
+
+TEST_F(Sqlite3StrSplitTest, SplitEmptyInput) {
+  ASSERT_STREQ(SplitStmt("", "zz", 0), "");
+  ASSERT_EQ(SplitStmt("", "zz", 1), nullptr);
+  ASSERT_EQ(SplitStmt("", "zz", 1000), nullptr);
+}
+
+}  // namespace
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/prelude/functions/utils.h b/src/trace_processor/prelude/functions/utils.h
new file mode 100644
index 0000000..2fa4189
--- /dev/null
+++ b/src/trace_processor/prelude/functions/utils.h
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2022 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_PRELUDE_FUNCTIONS_UTILS_H_
+#define SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_UTILS_H_
+
+#include <sqlite3.h>
+#include <unordered_map>
+#include "perfetto/ext/base/base64.h"
+#include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/trace_processor/demangle.h"
+#include "protos/perfetto/common/builtin_clock.pbzero.h"
+#include "src/trace_processor/export_json.h"
+#include "src/trace_processor/importers/common/clock_tracker.h"
+#include "src/trace_processor/prelude/functions/create_function_internal.h"
+#include "src/trace_processor/util/status_macros.h"
+
+#include "src/trace_processor/prelude/functions/register_function.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+struct ExportJson : public SqlFunction {
+  using Context = TraceStorage;
+  static base::Status Run(TraceStorage* storage,
+                          size_t /*argc*/,
+                          sqlite3_value** argv,
+                          SqlValue& /*out*/,
+                          Destructors&);
+};
+
+base::Status ExportJson::Run(TraceStorage* storage,
+                             size_t /*argc*/,
+                             sqlite3_value** argv,
+                             SqlValue& /*out*/,
+                             Destructors&) {
+  base::ScopedFstream output;
+  if (sqlite3_value_type(argv[0]) == SQLITE_INTEGER) {
+    // Assume input is an FD.
+    output.reset(fdopen(sqlite3_value_int(argv[0]), "w"));
+    if (!output) {
+      return base::ErrStatus(
+          "EXPORT_JSON: Couldn't open output file from given FD");
+    }
+  } else {
+    const char* filename =
+        reinterpret_cast<const char*>(sqlite3_value_text(argv[0]));
+    output = base::OpenFstream(filename, "w");
+    if (!output) {
+      return base::ErrStatus("EXPORT_JSON: Couldn't open output file");
+    }
+  }
+  return json::ExportJson(storage, output.get());
+}
+
+struct Hash : public SqlFunction {
+  static base::Status Run(void*,
+                          size_t argc,
+                          sqlite3_value** argv,
+                          SqlValue& out,
+                          Destructors&);
+};
+
+base::Status Hash::Run(void*,
+                       size_t argc,
+                       sqlite3_value** argv,
+                       SqlValue& out,
+                       Destructors&) {
+  base::Hasher hash;
+  for (size_t i = 0; i < argc; ++i) {
+    sqlite3_value* value = argv[i];
+    int type = sqlite3_value_type(value);
+    switch (type) {
+      case SQLITE_INTEGER:
+        hash.Update(sqlite3_value_int64(value));
+        break;
+      case SQLITE_TEXT: {
+        const char* ptr =
+            reinterpret_cast<const char*>(sqlite3_value_text(value));
+        hash.Update(ptr, strlen(ptr));
+        break;
+      }
+      default:
+        return base::ErrStatus("HASH: arg %zu has unknown type %d", i, type);
+    }
+  }
+  out = SqlValue::Long(static_cast<int64_t>(hash.digest()));
+  return base::OkStatus();
+}
+
+struct Base64Encode : public SqlFunction {
+  static base::Status Run(void*,
+                          size_t argc,
+                          sqlite3_value** argv,
+                          SqlValue& out,
+                          Destructors&);
+};
+
+base::Status Base64Encode::Run(void*,
+                               size_t argc,
+                               sqlite3_value** argv,
+                               SqlValue& out,
+                               Destructors& destructors) {
+  if (argc != 1)
+    return base::ErrStatus("Unsupported number of arg passed to Base64Encode");
+
+  sqlite3_value* value = argv[0];
+  if (sqlite3_value_type(value) != SQLITE_BLOB)
+    return base::ErrStatus("Base64Encode only supports bytes argument");
+
+  size_t byte_count = static_cast<size_t>(sqlite3_value_bytes(value));
+  std::string res = base::Base64Encode(sqlite3_value_blob(value), byte_count);
+
+  std::unique_ptr<char, base::FreeDeleter> s(
+      static_cast<char*>(malloc(res.size() + 1)));
+  memcpy(s.get(), res.c_str(), res.size() + 1);
+
+  out = SqlValue::String(s.release());
+  destructors.string_destructor = free;
+
+  return base::OkStatus();
+}
+
+struct Demangle : public SqlFunction {
+  static base::Status Run(void*,
+                          size_t argc,
+                          sqlite3_value** argv,
+                          SqlValue& out,
+                          Destructors& destructors);
+};
+
+base::Status Demangle::Run(void*,
+                           size_t argc,
+                           sqlite3_value** argv,
+                           SqlValue& out,
+                           Destructors& destructors) {
+  if (argc != 1)
+    return base::ErrStatus("Unsupported number of arg passed to DEMANGLE");
+  sqlite3_value* value = argv[0];
+  if (sqlite3_value_type(value) == SQLITE_NULL)
+    return base::OkStatus();
+
+  if (sqlite3_value_type(value) != SQLITE_TEXT)
+    return base::ErrStatus("Unsupported type of arg passed to DEMANGLE");
+
+  const char* mangled =
+      reinterpret_cast<const char*>(sqlite3_value_text(value));
+
+  std::unique_ptr<char, base::FreeDeleter> demangled =
+      demangle::Demangle(mangled);
+  if (!demangled)
+    return base::OkStatus();
+
+  destructors.string_destructor = free;
+  out = SqlValue::String(demangled.release());
+  return base::OkStatus();
+}
+
+struct WriteFile : public SqlFunction {
+  using Context = TraceStorage;
+  static base::Status Run(TraceStorage* storage,
+                          size_t,
+                          sqlite3_value** argv,
+                          SqlValue&,
+                          Destructors&);
+};
+
+base::Status WriteFile::Run(TraceStorage*,
+                            size_t argc,
+                            sqlite3_value** argv,
+                            SqlValue& out,
+                            Destructors&) {
+  if (argc != 2) {
+    return base::ErrStatus("WRITE_FILE: expected %d args but got %zu", 2, argc);
+  }
+
+  base::Status status =
+      sqlite_utils::TypeCheckSqliteValue(argv[0], SqlValue::kString);
+  if (!status.ok()) {
+    return base::ErrStatus("WRITE_FILE: argument 1, filename; %s",
+                           status.c_message());
+  }
+
+  status = sqlite_utils::TypeCheckSqliteValue(argv[1], SqlValue::kBytes);
+  if (!status.ok()) {
+    return base::ErrStatus("WRITE_FILE: argument 2, content; %s",
+                           status.c_message());
+  }
+
+  const std::string filename =
+      reinterpret_cast<const char*>(sqlite3_value_text(argv[0]));
+
+  base::ScopedFstream file = base::OpenFstream(filename.c_str(), "wb");
+  if (!file) {
+    return base::ErrStatus("WRITE_FILE: Couldn't open output file %s (%s)",
+                           filename.c_str(), strerror(errno));
+  }
+
+  int int_len = sqlite3_value_bytes(argv[1]);
+  PERFETTO_CHECK(int_len >= 0);
+  size_t len = (static_cast<size_t>(int_len));
+  // Make sure to call last as sqlite3_value_bytes can invalidate pointer
+  // returned.
+  const void* data = sqlite3_value_text(argv[1]);
+  if (fwrite(data, 1, len, file.get()) != len || fflush(file.get()) != 0) {
+    return base::ErrStatus("WRITE_FILE: Failed to write to file %s (%s)",
+                           filename.c_str(), strerror(errno));
+  }
+
+  out = SqlValue::Long(int_len);
+
+  return util::OkStatus();
+}
+
+struct ExtractArg : public SqlFunction {
+  using Context = TraceStorage;
+  static base::Status Run(TraceStorage* storage,
+                          size_t argc,
+                          sqlite3_value** argv,
+                          SqlValue& out,
+                          Destructors& destructors);
+};
+
+base::Status ExtractArg::Run(TraceStorage* storage,
+                             size_t argc,
+                             sqlite3_value** argv,
+                             SqlValue& out,
+                             Destructors& destructors) {
+  if (argc != 2)
+    return base::ErrStatus("EXTRACT_ARG: 2 args required");
+
+  // If the arg set id is null, just return null as the result.
+  if (sqlite3_value_type(argv[0]) == SQLITE_NULL)
+    return base::OkStatus();
+
+  if (sqlite3_value_type(argv[0]) != SQLITE_INTEGER)
+    return base::ErrStatus("EXTRACT_ARG: 1st argument should be arg set id");
+
+  if (sqlite3_value_type(argv[1]) != SQLITE_TEXT)
+    return base::ErrStatus("EXTRACT_ARG: 2nd argument should be key");
+
+  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]));
+
+  base::Optional<Variadic> opt_value;
+  RETURN_IF_ERROR(storage->ExtractArg(arg_set_id, key, &opt_value));
+
+  if (!opt_value)
+    return base::OkStatus();
+
+  // This function always returns static strings (i.e. scoped to lifetime
+  // of the TraceStorage thread pool) so prevent SQLite from making copies.
+  destructors.string_destructor = sqlite_utils::kSqliteStatic;
+
+  switch (opt_value->type) {
+    case Variadic::kNull:
+      return base::OkStatus();
+    case Variadic::kInt:
+      out = SqlValue::Long(opt_value->int_value);
+      return base::OkStatus();
+    case Variadic::kUint:
+      out = SqlValue::Long(static_cast<int64_t>(opt_value->uint_value));
+      return base::OkStatus();
+    case Variadic::kString:
+      out =
+          SqlValue::String(storage->GetString(opt_value->string_value).data());
+      return base::OkStatus();
+    case Variadic::kReal:
+      out = SqlValue::Double(opt_value->real_value);
+      return base::OkStatus();
+    case Variadic::kBool:
+      out = SqlValue::Long(opt_value->bool_value);
+      return base::OkStatus();
+    case Variadic::kPointer:
+      out = SqlValue::Long(static_cast<int64_t>(opt_value->pointer_value));
+      return base::OkStatus();
+    case Variadic::kJson:
+      out = SqlValue::String(storage->GetString(opt_value->json_value).data());
+      return base::OkStatus();
+  }
+  PERFETTO_FATAL("For GCC");
+}
+
+struct AbsTimeStr : public SqlFunction {
+  using Context = ClockTracker;
+  static base::Status Run(ClockTracker* tracker,
+                          size_t argc,
+                          sqlite3_value** argv,
+                          SqlValue& out,
+                          Destructors& destructors);
+};
+
+base::Status AbsTimeStr::Run(ClockTracker* tracker,
+                             size_t argc,
+                             sqlite3_value** argv,
+                             SqlValue& out,
+                             Destructors& destructors) {
+  if (argc != 1) {
+    return base::ErrStatus("ABS_TIME_STR: 1 arg required");
+  }
+
+  // If the timestamp is null, just return null as the result.
+  if (sqlite3_value_type(argv[0]) == SQLITE_NULL) {
+    return base::OkStatus();
+  }
+  if (sqlite3_value_type(argv[0]) != SQLITE_INTEGER) {
+    return base::ErrStatus("ABS_TIME_STR: first argument should be timestamp");
+  }
+
+  int64_t ts = sqlite3_value_int64(argv[0]);
+  base::Optional<std::string> iso8601 = tracker->FromTraceTimeAsISO8601(ts);
+  if (!iso8601.has_value()) {
+    return base::OkStatus();
+  }
+
+  std::unique_ptr<char, base::FreeDeleter> s(
+      static_cast<char*>(malloc(iso8601->size() + 1)));
+  memcpy(s.get(), iso8601->c_str(), iso8601->size() + 1);
+
+  destructors.string_destructor = free;
+  out = SqlValue::String(s.release());
+  return base::OkStatus();
+}
+
+struct ToMonotonic : public SqlFunction {
+  using Context = ClockTracker;
+  static base::Status Run(ClockTracker* tracker,
+                          size_t argc,
+                          sqlite3_value** argv,
+                          SqlValue& out,
+                          Destructors& destructors);
+};
+
+base::Status ToMonotonic::Run(ClockTracker* tracker,
+                              size_t argc,
+                              sqlite3_value** argv,
+                              SqlValue& out,
+                              Destructors&) {
+  if (argc != 1) {
+    return base::ErrStatus("TO_MONOTONIC: 1 arg required");
+  }
+
+  // If the timestamp is null, just return null as the result.
+  if (sqlite3_value_type(argv[0]) == SQLITE_NULL) {
+    return base::OkStatus();
+  }
+  if (sqlite3_value_type(argv[0]) != SQLITE_INTEGER) {
+    return base::ErrStatus("TO_MONOTONIC: first argument should be timestamp");
+  }
+
+  int64_t ts = sqlite3_value_int64(argv[0]);
+  base::Optional<int64_t> monotonic =
+      tracker->FromTraceTime(protos::pbzero::BUILTIN_CLOCK_MONOTONIC, ts);
+
+  if (!monotonic.has_value()) {
+    // This means we'll return NULL
+    return base::OkStatus();
+  }
+
+  out = SqlValue::Long(*monotonic);
+  return base::OkStatus();
+}
+
+struct SourceGeq : public SqlFunction {
+  static base::Status Run(void*,
+                          size_t,
+                          sqlite3_value**,
+                          SqlValue&,
+                          Destructors&) {
+    return base::ErrStatus(
+        "SOURCE_GEQ should not be called from the global scope");
+  }
+};
+
+struct Glob : public SqlFunction {
+  static base::Status Run(void*,
+                          size_t,
+                          sqlite3_value** argv,
+                          SqlValue& out,
+                          Destructors&) {
+    const char* pattern =
+        reinterpret_cast<const char*>(sqlite3_value_text(argv[0]));
+    const char* text =
+        reinterpret_cast<const char*>(sqlite3_value_text(argv[1]));
+    if (pattern && text) {
+      out = SqlValue::Long(sqlite3_strglob(pattern, text) == 0);
+    }
+    return base::OkStatus();
+  }
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_UTILS_H_
diff --git a/src/trace_processor/prelude/functions/window_functions.h b/src/trace_processor/prelude/functions/window_functions.h
new file mode 100644
index 0000000..3a76fce
--- /dev/null
+++ b/src/trace_processor/prelude/functions/window_functions.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2022 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_PRELUDE_FUNCTIONS_WINDOW_FUNCTIONS_H_
+#define SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_WINDOW_FUNCTIONS_H_
+
+#include <sqlite3.h>
+#include <unordered_map>
+#include "perfetto/ext/base/base64.h"
+#include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/trace_processor/demangle.h"
+#include "protos/perfetto/common/builtin_clock.pbzero.h"
+#include "src/trace_processor/export_json.h"
+#include "src/trace_processor/importers/common/clock_tracker.h"
+#include "src/trace_processor/prelude/functions/create_function_internal.h"
+#include "src/trace_processor/util/status_macros.h"
+
+#include "src/trace_processor/prelude/functions/register_function.h"
+
+namespace perfetto {
+namespace trace_processor {
+// Keeps track of the latest non null value and its position withing the
+// window. Every time the window shrinks (`xInverse` is called) the window size
+// is reduced by one and the position of the value moves one back, if it gets
+// out of the window the value is discarded.
+class LastNonNullAggregateContext {
+ public:
+  static LastNonNullAggregateContext* Get(sqlite3_context* ctx) {
+    return reinterpret_cast<LastNonNullAggregateContext*>(
+        sqlite3_aggregate_context(ctx, 0));
+  }
+
+  static LastNonNullAggregateContext* GetOrCreate(sqlite3_context* ctx) {
+    return reinterpret_cast<LastNonNullAggregateContext*>(
+        sqlite3_aggregate_context(ctx, sizeof(LastNonNullAggregateContext)));
+  }
+
+  inline void PopFront() {
+    PERFETTO_CHECK(window_size_ > 0);
+    --window_size_;
+    if (!last_non_null_value_) {
+      return;
+    }
+    if (value_index_ == 0) {
+      sqlite3_value_free(last_non_null_value_);
+      last_non_null_value_ = nullptr;
+      return;
+    }
+    PERFETTO_CHECK(value_index_ > 0);
+    --value_index_;
+  }
+
+  inline void PushBack(sqlite3_value* value) {
+    ++window_size_;
+    if (sqlite3_value_type(value) == SQLITE_NULL) {
+      return;
+    }
+
+    Destroy();
+    last_non_null_value_ = sqlite3_value_dup(value);
+    value_index_ = window_size_ - 1;
+  }
+
+  inline void Destroy() {
+    if (last_non_null_value_) {
+      sqlite3_value_free(last_non_null_value_);
+    }
+  }
+
+  sqlite3_value* last_non_null_value() const { return last_non_null_value_; }
+
+ private:
+  int64_t window_size_;
+  // Index within the window of the last non null value. Only valid if `value`
+  // is set.
+  int64_t value_index_;
+  // Actual value
+  sqlite3_value* last_non_null_value_;
+};
+
+static_assert(std::is_standard_layout<LastNonNullAggregateContext>::value,
+              "Must be able to be initialized by sqlite3_aggregate_context "
+              "(similar to calloc, i.e. no constructor called)");
+static_assert(std::is_trivial<LastNonNullAggregateContext>::value,
+              "Must be able to be destroyed by just calling free (i.e. no "
+              "destructor called)");
+
+inline 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;
+  }
+
+  auto* ptr = LastNonNullAggregateContext::GetOrCreate(ctx);
+  if (!ptr) {
+    sqlite3_result_error(ctx, "LAST_NON_NULL: Failed to allocate context", -1);
+    return;
+  }
+
+  ptr->PushBack(argv[0]);
+}
+
+inline void LastNonNullInverse(sqlite3_context* ctx, int, sqlite3_value**) {
+  auto* ptr = LastNonNullAggregateContext::GetOrCreate(ctx);
+  PERFETTO_CHECK(ptr != nullptr);
+  ptr->PopFront();
+}
+
+inline void LastNonNullValue(sqlite3_context* ctx) {
+  auto* ptr = LastNonNullAggregateContext::GetOrCreate(ctx);
+  if (!ptr || !ptr->last_non_null_value()) {
+    sqlite3_result_null(ctx);
+  } else {
+    sqlite3_result_value(ctx, ptr->last_non_null_value());
+  }
+}
+
+inline void LastNonNullFinal(sqlite3_context* ctx) {
+  auto* ptr = LastNonNullAggregateContext::Get(ctx);
+  if (!ptr || !ptr->last_non_null_value()) {
+    sqlite3_result_null(ctx);
+  } else {
+    sqlite3_result_value(ctx, ptr->last_non_null_value());
+    ptr->Destroy();
+  }
+}
+
+inline void RegisterLastNonNullFunction(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");
+  }
+}
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_PRELUDE_FUNCTIONS_WINDOW_FUNCTIONS_H_
diff --git a/src/trace_processor/prelude/operators/BUILD.gn b/src/trace_processor/prelude/operators/BUILD.gn
new file mode 100644
index 0000000..2a3daa6
--- /dev/null
+++ b/src/trace_processor/prelude/operators/BUILD.gn
@@ -0,0 +1,46 @@
+# Copyright (C) 2022 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")
+
+assert(enable_perfetto_trace_processor_sqlite)
+
+source_set("operators") {
+  sources = [
+    "span_join_operator.cc",
+    "span_join_operator.h",
+    "window_operator.cc",
+    "window_operator.h",
+  ]
+  deps = [
+    "../..:metatrace",
+    "../../../../gn:default_deps",
+    "../../../../gn:sqlite",
+    "../../../../include/perfetto/trace_processor",
+    "../../../base",
+    "../../sqlite",
+    "../../util",
+  ]
+}
+
+perfetto_unittest_source_set("unittests") {
+  testonly = true
+  sources = [ "span_join_operator_unittest.cc" ]
+  deps = [
+    ":operators",
+    "../../../../gn:default_deps",
+    "../../../../gn:gtest_and_gmock",
+    "../../../../gn:sqlite",
+  ]
+}
diff --git a/src/trace_processor/prelude/operators/span_join_operator.cc b/src/trace_processor/prelude/operators/span_join_operator.cc
new file mode 100644
index 0000000..61aa3b6
--- /dev/null
+++ b/src/trace_processor/prelude/operators/span_join_operator.cc
@@ -0,0 +1,894 @@
+/*
+ * 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/prelude/operators/span_join_operator.h"
+
+#include <sqlite3.h>
+#include <string.h>
+
+#include <algorithm>
+#include <set>
+#include <utility>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/string_splitter.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "perfetto/ext/base/string_view.h"
+#include "src/trace_processor/sqlite/sqlite_utils.h"
+#include "src/trace_processor/tp_metatrace.h"
+#include "src/trace_processor/util/status_macros.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+namespace {
+
+constexpr char kTsColumnName[] = "ts";
+constexpr char kDurColumnName[] = "dur";
+
+bool IsRequiredColumn(const std::string& name) {
+  return name == kTsColumnName || name == kDurColumnName;
+}
+
+base::Optional<std::string> HasDuplicateColumns(
+    const std::vector<SqliteTable::Column>& cols) {
+  std::set<std::string> names;
+  for (const auto& col : cols) {
+    if (names.count(col.name()) > 0)
+      return col.name();
+    names.insert(col.name());
+  }
+  return base::nullopt;
+}
+
+std::string OpToString(int op) {
+  switch (op) {
+    case SQLITE_INDEX_CONSTRAINT_EQ:
+      return "=";
+    case SQLITE_INDEX_CONSTRAINT_NE:
+      return "!=";
+    case SQLITE_INDEX_CONSTRAINT_GE:
+      return ">=";
+    case SQLITE_INDEX_CONSTRAINT_GT:
+      return ">";
+    case SQLITE_INDEX_CONSTRAINT_LE:
+      return "<=";
+    case SQLITE_INDEX_CONSTRAINT_LT:
+      return "<";
+    case SQLITE_INDEX_CONSTRAINT_LIKE:
+      return " like ";
+    case SQLITE_INDEX_CONSTRAINT_GLOB:
+      return " glob ";
+    case SQLITE_INDEX_CONSTRAINT_ISNULL:
+      // The "null" will be added below in EscapedSqliteValueAsString.
+      return " is ";
+    case SQLITE_INDEX_CONSTRAINT_ISNOTNULL:
+      // The "null" will be added below in EscapedSqliteValueAsString.
+      return " is not ";
+    default:
+      PERFETTO_FATAL("Operator to string conversion not impemented for %d", op);
+  }
+}
+
+std::string EscapedSqliteValueAsString(sqlite3_value* value) {
+  switch (sqlite3_value_type(value)) {
+    case SQLITE_INTEGER:
+      return std::to_string(sqlite3_value_int64(value));
+    case SQLITE_FLOAT:
+      return std::to_string(sqlite3_value_double(value));
+    case SQLITE_TEXT: {
+      // If str itself contains a single quote, we need to escape it with
+      // another single quote.
+      const char* str =
+          reinterpret_cast<const char*>(sqlite3_value_text(value));
+      return "'" + base::ReplaceAll(str, "'", "''") + "'";
+    }
+    case SQLITE_NULL:
+      return " null";
+    default:
+      PERFETTO_FATAL("Unknown value type %d", sqlite3_value_type(value));
+  }
+}
+
+}  // namespace
+
+SpanJoinOperatorTable::SpanJoinOperatorTable(sqlite3* db, const TraceStorage*)
+    : db_(db) {}
+
+void SpanJoinOperatorTable::RegisterTable(sqlite3* db,
+                                          const TraceStorage* storage) {
+  SqliteTable::Register<SpanJoinOperatorTable>(db, storage, "span_join",
+                                               /* read_write */ false,
+                                               /* requires_args */ true);
+
+  SqliteTable::Register<SpanJoinOperatorTable>(db, storage, "span_left_join",
+                                               /* read_write */ false,
+                                               /* requires_args */ true);
+
+  SqliteTable::Register<SpanJoinOperatorTable>(db, storage, "span_outer_join",
+                                               /* read_write */ false,
+                                               /* requires_args */ true);
+}
+
+util::Status SpanJoinOperatorTable::Init(int argc,
+                                         const char* const* argv,
+                                         Schema* schema) {
+  // argv[0] - argv[2] are SQLite populated fields which are always present.
+  if (argc < 5)
+    return util::Status("SPAN_JOIN: expected at least 2 args");
+
+  TableDescriptor t1_desc;
+  auto status = TableDescriptor::Parse(
+      std::string(reinterpret_cast<const char*>(argv[3])), &t1_desc);
+  if (!status.ok())
+    return status;
+
+  TableDescriptor t2_desc;
+  status = TableDescriptor::Parse(
+      std::string(reinterpret_cast<const char*>(argv[4])), &t2_desc);
+  if (!status.ok())
+    return status;
+
+  // Check that the partition columns match between the two tables.
+  if (t1_desc.partition_col == t2_desc.partition_col) {
+    partitioning_ = t1_desc.IsPartitioned()
+                        ? PartitioningType::kSamePartitioning
+                        : PartitioningType::kNoPartitioning;
+  } else if (t1_desc.IsPartitioned() && t2_desc.IsPartitioned()) {
+    return util::ErrStatus(
+        "SPAN_JOIN: mismatching partitions between the two tables; "
+        "(partition %s in table %s, partition %s in table %s)",
+        t1_desc.partition_col.c_str(), t1_desc.name.c_str(),
+        t2_desc.partition_col.c_str(), t2_desc.name.c_str());
+  } else {
+    partitioning_ = PartitioningType::kMixedPartitioning;
+  }
+
+  bool t1_part_mixed = t1_desc.IsPartitioned() &&
+                       partitioning_ == PartitioningType::kMixedPartitioning;
+  bool t2_part_mixed = t2_desc.IsPartitioned() &&
+                       partitioning_ == PartitioningType::kMixedPartitioning;
+
+  EmitShadowType t1_shadow_type;
+  if (IsOuterJoin()) {
+    if (t1_part_mixed || partitioning_ == PartitioningType::kNoPartitioning) {
+      t1_shadow_type = EmitShadowType::kPresentPartitionOnly;
+    } else {
+      t1_shadow_type = EmitShadowType::kAll;
+    }
+  } else {
+    t1_shadow_type = EmitShadowType::kNone;
+  }
+  status = CreateTableDefinition(t1_desc, t1_shadow_type, &t1_defn_);
+  if (!status.ok())
+    return status;
+
+  EmitShadowType t2_shadow_type;
+  if (IsOuterJoin() || IsLeftJoin()) {
+    if (t2_part_mixed || partitioning_ == PartitioningType::kNoPartitioning) {
+      t2_shadow_type = EmitShadowType::kPresentPartitionOnly;
+    } else {
+      t2_shadow_type = EmitShadowType::kAll;
+    }
+  } else {
+    t2_shadow_type = EmitShadowType::kNone;
+  }
+  status = CreateTableDefinition(t2_desc, t2_shadow_type, &t2_defn_);
+  if (!status.ok())
+    return status;
+
+  std::vector<SqliteTable::Column> cols;
+  // Ensure the shared columns are consistently ordered and are not
+  // present twice in the final schema
+  cols.emplace_back(Column::kTimestamp, kTsColumnName, SqlValue::Type::kLong);
+  cols.emplace_back(Column::kDuration, kDurColumnName, SqlValue::Type::kLong);
+  if (partitioning_ != PartitioningType::kNoPartitioning)
+    cols.emplace_back(Column::kPartition, partition_col(),
+                      SqlValue::Type::kLong);
+
+  CreateSchemaColsForDefn(t1_defn_, &cols);
+  CreateSchemaColsForDefn(t2_defn_, &cols);
+
+  // Check if any column has : in its name. This often happens when SELECT *
+  // is used to create a view with the same column name in two joined tables.
+  for (const auto& col : cols) {
+    if (base::Contains(col.name(), ':')) {
+      return util::ErrStatus("SPAN_JOIN: column %s has illegal character :",
+                             col.name().c_str());
+    }
+  }
+
+  if (auto opt_dupe_col = HasDuplicateColumns(cols)) {
+    return util::ErrStatus(
+        "SPAN_JOIN: column %s present in both tables %s and %s",
+        opt_dupe_col->c_str(), t1_defn_.name().c_str(),
+        t2_defn_.name().c_str());
+  }
+  std::vector<size_t> primary_keys = {Column::kTimestamp};
+  if (partitioning_ != PartitioningType::kNoPartitioning)
+    primary_keys.push_back(Column::kPartition);
+  *schema = Schema(cols, primary_keys);
+
+  return util::OkStatus();
+}
+
+void SpanJoinOperatorTable::CreateSchemaColsForDefn(
+    const TableDefinition& defn,
+    std::vector<SqliteTable::Column>* cols) {
+  for (size_t i = 0; i < defn.columns().size(); i++) {
+    const auto& n = defn.columns()[i].name();
+    if (IsRequiredColumn(n) || n == defn.partition_col())
+      continue;
+
+    ColumnLocator* locator = &global_index_to_column_locator_[cols->size()];
+    locator->defn = &defn;
+    locator->col_index = i;
+
+    cols->emplace_back(cols->size(), n, defn.columns()[i].type());
+  }
+}
+
+std::unique_ptr<SqliteTable::Cursor> SpanJoinOperatorTable::CreateCursor() {
+  return std::unique_ptr<SpanJoinOperatorTable::Cursor>(new Cursor(this, db_));
+}
+
+int SpanJoinOperatorTable::BestIndex(const QueryConstraints& qc,
+                                     BestIndexInfo* info) {
+  // TODO(lalitm): figure out cost estimation.
+  const auto& ob = qc.order_by();
+
+  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);
+  }
+
+  const auto& cs = qc.constraints();
+  for (uint32_t i = 0; i < cs.size(); ++i) {
+    if (cs[i].op == kSourceGeqOpCode) {
+      info->sqlite_omit_constraint[i] = true;
+    }
+  }
+
+  return SQLITE_OK;
+}
+
+int SpanJoinOperatorTable::FindFunction(const char* name,
+                                        FindFunctionFn* fn,
+                                        void**) {
+  if (base::CaseInsensitiveEqual(name, "source_geq")) {
+    *fn = [](sqlite3_context* ctx, int, sqlite3_value**) {
+      sqlite3_result_error(ctx, "Should not be called.", -1);
+    };
+    return kSourceGeqOpCode;
+  }
+  return 0;
+}
+
+std::vector<std::string>
+SpanJoinOperatorTable::ComputeSqlConstraintsForDefinition(
+    const TableDefinition& defn,
+    const QueryConstraints& qc,
+    sqlite3_value** argv) {
+  std::vector<std::string> constraints;
+  for (size_t i = 0; i < qc.constraints().size(); i++) {
+    const auto& cs = qc.constraints()[i];
+    auto col_name = GetNameForGlobalColumnIndex(defn, cs.column);
+    if (col_name.empty())
+      continue;
+
+    // Le constraints can be passed straight to the child tables as they won't
+    // affect the span join computation. Similarily, source_geq constraints
+    // explicitly request that they are passed as geq constraints to the source
+    // tables.
+    if (col_name == kTsColumnName && !sqlite_utils::IsOpLe(cs.op) &&
+        cs.op != kSourceGeqOpCode)
+      continue;
+
+    // Allow SQLite handle any constraints on duration apart from source_geq
+    // constraints.
+    if (col_name == kDurColumnName && cs.op != kSourceGeqOpCode)
+      continue;
+
+    // If we're emitting shadow slices, don't propogate any constraints
+    // on this table as this will break the shadow slice computation.
+    if (defn.ShouldEmitPresentPartitionShadow())
+      continue;
+
+    auto op = OpToString(cs.op == kSourceGeqOpCode ? SQLITE_INDEX_CONSTRAINT_GE
+                                                   : cs.op);
+    auto value = EscapedSqliteValueAsString(argv[i]);
+
+    constraints.emplace_back("`" + col_name + "`" + op + value);
+  }
+  return constraints;
+}
+
+util::Status SpanJoinOperatorTable::CreateTableDefinition(
+    const TableDescriptor& desc,
+    EmitShadowType emit_shadow_type,
+    SpanJoinOperatorTable::TableDefinition* defn) {
+  if (desc.partition_col == kTsColumnName ||
+      desc.partition_col == kDurColumnName) {
+    return util::ErrStatus(
+        "SPAN_JOIN: partition column cannot be any of {ts, dur} for table %s",
+        desc.name.c_str());
+  }
+
+  std::vector<SqliteTable::Column> cols;
+  auto status = sqlite_utils::GetColumnsForTable(db_, desc.name, cols);
+  if (!status.ok()) {
+    return status;
+  }
+
+  uint32_t required_columns_found = 0;
+  uint32_t ts_idx = std::numeric_limits<uint32_t>::max();
+  uint32_t dur_idx = std::numeric_limits<uint32_t>::max();
+  uint32_t partition_idx = std::numeric_limits<uint32_t>::max();
+  for (uint32_t i = 0; i < cols.size(); i++) {
+    auto col = cols[i];
+    if (IsRequiredColumn(col.name())) {
+      ++required_columns_found;
+      if (col.type() != SqlValue::Type::kLong &&
+          col.type() != SqlValue::Type::kNull) {
+        return util::ErrStatus(
+            "SPAN_JOIN: Invalid type for column %s in table %s",
+            col.name().c_str(), desc.name.c_str());
+      }
+    }
+
+    if (col.name() == kTsColumnName) {
+      ts_idx = i;
+    } else if (col.name() == kDurColumnName) {
+      dur_idx = i;
+    } else if (col.name() == desc.partition_col) {
+      partition_idx = i;
+    }
+  }
+  if (required_columns_found != 2) {
+    return util::ErrStatus(
+        "SPAN_JOIN: Missing one of columns {ts, dur} in table %s",
+        desc.name.c_str());
+  } else if (desc.IsPartitioned() && partition_idx >= cols.size()) {
+    return util::ErrStatus("SPAN_JOIN: Missing partition column %s in table %s",
+                           desc.partition_col.c_str(), desc.name.c_str());
+  }
+
+  PERFETTO_DCHECK(ts_idx < cols.size());
+  PERFETTO_DCHECK(dur_idx < cols.size());
+
+  *defn = TableDefinition(desc.name, desc.partition_col, std::move(cols),
+                          emit_shadow_type, ts_idx, dur_idx, partition_idx);
+  return util::OkStatus();
+}
+
+std::string SpanJoinOperatorTable::GetNameForGlobalColumnIndex(
+    const TableDefinition& defn,
+    int global_column) {
+  size_t col_idx = static_cast<size_t>(global_column);
+  if (col_idx == Column::kTimestamp)
+    return kTsColumnName;
+  else if (col_idx == Column::kDuration)
+    return kDurColumnName;
+  else if (col_idx == Column::kPartition &&
+           partitioning_ != PartitioningType::kNoPartitioning)
+    return defn.partition_col().c_str();
+
+  const auto& locator = global_index_to_column_locator_[col_idx];
+  if (locator.defn != &defn)
+    return "";
+  return defn.columns()[locator.col_index].name().c_str();
+}
+
+SpanJoinOperatorTable::Cursor::Cursor(SpanJoinOperatorTable* table, sqlite3* db)
+    : SqliteTable::Cursor(table),
+      t1_(table, &table->t1_defn_, db),
+      t2_(table, &table->t2_defn_, db),
+      table_(table) {}
+
+base::Status SpanJoinOperatorTable::Cursor::FilterInner(
+    const QueryConstraints& qc,
+    sqlite3_value** argv) {
+  PERFETTO_TP_TRACE(metatrace::Category::QUERY, "SPAN_JOIN_XFILTER");
+
+  bool t1_partitioned_mixed =
+      t1_.definition()->IsPartitioned() &&
+      table_->partitioning_ == PartitioningType::kMixedPartitioning;
+  auto t1_eof = table_->IsOuterJoin() && !t1_partitioned_mixed
+                    ? Query::InitialEofBehavior::kTreatAsMissingPartitionShadow
+                    : Query::InitialEofBehavior::kTreatAsEof;
+  RETURN_IF_ERROR(t1_.Initialize(qc, argv, t1_eof));
+
+  bool t2_partitioned_mixed =
+      t2_.definition()->IsPartitioned() &&
+      table_->partitioning_ == PartitioningType::kMixedPartitioning;
+  auto t2_eof =
+      (table_->IsLeftJoin() || table_->IsOuterJoin()) && !t2_partitioned_mixed
+          ? Query::InitialEofBehavior::kTreatAsMissingPartitionShadow
+          : Query::InitialEofBehavior::kTreatAsEof;
+  RETURN_IF_ERROR(t2_.Initialize(qc, argv, t2_eof));
+  return FindOverlappingSpan();
+}
+
+base::Status SpanJoinOperatorTable::Cursor::NextInner() {
+  RETURN_IF_ERROR(next_query_->Next());
+  return FindOverlappingSpan();
+}
+
+bool SpanJoinOperatorTable::Cursor::IsOverlappingSpan() {
+  // If either of the tables are eof, then we cannot possibly have an
+  // overlapping span.
+  if (t1_.IsEof() || t2_.IsEof())
+    return false;
+
+  // One of the tables always needs to have a real span to have a valid
+  // overlapping span.
+  if (!t1_.IsReal() && !t2_.IsReal())
+    return false;
+
+  if (table_->partitioning_ == PartitioningType::kSamePartitioning) {
+    // If both tables are partitioned, then ensure that the partitions overlap.
+    bool partition_in_bounds = (t1_.FirstPartition() >= t2_.FirstPartition() &&
+                                t1_.FirstPartition() <= t2_.LastPartition()) ||
+                               (t2_.FirstPartition() >= t1_.FirstPartition() &&
+                                t2_.FirstPartition() <= t1_.LastPartition());
+    if (!partition_in_bounds)
+      return false;
+  }
+
+  // We consider all slices to be [start, end) - that is the range of
+  // timestamps has an open interval at the start but a closed interval
+  // at the end. (with the exception of dur == -1 which we treat as if
+  // end == start for the purpose of this function).
+  return (t1_.ts() == t2_.ts() && t1_.IsReal() && t2_.IsReal()) ||
+         (t1_.ts() >= t2_.ts() && t1_.ts() < t2_.AdjustedTsEnd()) ||
+         (t2_.ts() >= t1_.ts() && t2_.ts() < t1_.AdjustedTsEnd());
+}
+
+util::Status SpanJoinOperatorTable::Cursor::FindOverlappingSpan() {
+  // We loop until we find a slice which overlaps from the two tables.
+  while (true) {
+    if (table_->partitioning_ == PartitioningType::kMixedPartitioning) {
+      // If we have a mixed partition setup, we need to have special checks
+      // for eof and to reset the unpartitioned cursor every time the partition
+      // changes in the partitioned table.
+      auto* partitioned = t1_.definition()->IsPartitioned() ? &t1_ : &t2_;
+      auto* unpartitioned = t1_.definition()->IsPartitioned() ? &t2_ : &t1_;
+
+      // If the partitioned table reaches eof, then we are really done.
+      if (partitioned->IsEof())
+        break;
+
+      // If the partition has changed from the previous one, reset the cursor
+      // and keep a lot of the new partition.
+      if (last_mixed_partition_ != partitioned->partition()) {
+        util::Status status = unpartitioned->Rewind();
+        if (!status.ok())
+          return status;
+        last_mixed_partition_ = partitioned->partition();
+      }
+    } else if (t1_.IsEof() || t2_.IsEof()) {
+      // For both no partition and same partition cases, either cursor ending
+      // ends the whole span join.
+      break;
+    }
+
+    // Find which slice finishes first.
+    next_query_ = FindEarliestFinishQuery();
+
+    // If the current span is overlapping, just finish there to emit the current
+    // slice.
+    if (IsOverlappingSpan())
+      break;
+
+    // Otherwise, step to the next row.
+    util::Status status = next_query_->Next();
+    if (!status.ok())
+      return status;
+  }
+  return util::OkStatus();
+}
+
+SpanJoinOperatorTable::Query*
+SpanJoinOperatorTable::Cursor::FindEarliestFinishQuery() {
+  int64_t t1_part;
+  int64_t t2_part;
+
+  switch (table_->partitioning_) {
+    case PartitioningType::kMixedPartitioning: {
+      // If either table is EOF, forward the other table to try and make
+      // the partitions not match anymore.
+      if (t1_.IsEof())
+        return &t2_;
+      if (t2_.IsEof())
+        return &t1_;
+
+      // Otherwise, just make the partition equal from both tables.
+      t1_part = last_mixed_partition_;
+      t2_part = last_mixed_partition_;
+      break;
+    }
+    case PartitioningType::kSamePartitioning: {
+      // Get the partition values from the cursor.
+      t1_part = t1_.LastPartition();
+      t2_part = t2_.LastPartition();
+      break;
+    }
+    case PartitioningType::kNoPartitioning: {
+      t1_part = 0;
+      t2_part = 0;
+      break;
+    }
+  }
+
+  // Prefer to forward the earliest cursors based on the following
+  // lexiographical ordering:
+  // 1. partition
+  // 2. end timestamp
+  // 3. whether the slice is real or shadow (shadow < real)
+  bool t1_less = std::make_tuple(t1_part, t1_.AdjustedTsEnd(), t1_.IsReal()) <
+                 std::make_tuple(t2_part, t2_.AdjustedTsEnd(), t2_.IsReal());
+  return t1_less ? &t1_ : &t2_;
+}
+
+int SpanJoinOperatorTable::Cursor::Eof() {
+  return t1_.IsEof() || t2_.IsEof();
+}
+
+int SpanJoinOperatorTable::Cursor::Column(sqlite3_context* context, int N) {
+  PERFETTO_DCHECK(t1_.IsReal() || t2_.IsReal());
+
+  switch (N) {
+    case Column::kTimestamp: {
+      auto max_ts = std::max(t1_.ts(), t2_.ts());
+      sqlite3_result_int64(context, static_cast<sqlite3_int64>(max_ts));
+      break;
+    }
+    case Column::kDuration: {
+      auto max_start = std::max(t1_.ts(), t2_.ts());
+      auto min_end = std::min(t1_.raw_ts_end(), t2_.raw_ts_end());
+      auto dur = min_end - max_start;
+      sqlite3_result_int64(context, static_cast<sqlite3_int64>(dur));
+      break;
+    }
+    case Column::kPartition: {
+      if (table_->partitioning_ != PartitioningType::kNoPartitioning) {
+        int64_t partition;
+        if (table_->partitioning_ == PartitioningType::kMixedPartitioning) {
+          partition = last_mixed_partition_;
+        } else {
+          partition = t1_.IsReal() ? t1_.partition() : t2_.partition();
+        }
+        sqlite3_result_int64(context, static_cast<sqlite3_int64>(partition));
+        break;
+      }
+      [[clang::fallthrough]];
+    }
+    default: {
+      size_t index = static_cast<size_t>(N);
+      const auto& locator = table_->global_index_to_column_locator_[index];
+      if (locator.defn == t1_.definition())
+        t1_.ReportSqliteResult(context, locator.col_index);
+      else
+        t2_.ReportSqliteResult(context, locator.col_index);
+    }
+  }
+  return SQLITE_OK;
+}
+
+SpanJoinOperatorTable::Query::Query(SpanJoinOperatorTable* table,
+                                    const TableDefinition* definition,
+                                    sqlite3* db)
+    : defn_(definition), db_(db), table_(table) {
+  PERFETTO_DCHECK(!defn_->IsPartitioned() ||
+                  defn_->partition_idx() < defn_->columns().size());
+}
+
+SpanJoinOperatorTable::Query::~Query() = default;
+
+util::Status SpanJoinOperatorTable::Query::Initialize(
+    const QueryConstraints& qc,
+    sqlite3_value** argv,
+    InitialEofBehavior eof_behavior) {
+  *this = Query(table_, definition(), db_);
+  sql_query_ = CreateSqlQuery(
+      table_->ComputeSqlConstraintsForDefinition(*defn_, qc, argv));
+  util::Status status = Rewind();
+  if (!status.ok())
+    return status;
+  if (eof_behavior == InitialEofBehavior::kTreatAsMissingPartitionShadow &&
+      IsEof()) {
+    state_ = State::kMissingPartitionShadow;
+  }
+  return status;
+}
+
+util::Status SpanJoinOperatorTable::Query::Next() {
+  RETURN_IF_ERROR(NextSliceState());
+  return FindNextValidSlice();
+}
+
+bool SpanJoinOperatorTable::Query::IsValidSlice() {
+  // Disallow any single partition shadow slices if the definition doesn't allow
+  // them.
+  if (IsPresentPartitionShadow() && !defn_->ShouldEmitPresentPartitionShadow())
+    return false;
+
+  // Disallow any missing partition shadow slices if the definition doesn't
+  // allow them.
+  if (IsMissingPartitionShadow() && !defn_->ShouldEmitMissingPartitionShadow())
+    return false;
+
+  // Disallow any "empty" shadows; these are shadows which either have the same
+  // start and end time or missing-partition shadows which have the same start
+  // and end partition.
+  if (IsEmptyShadow())
+    return false;
+
+  return true;
+}
+
+util::Status SpanJoinOperatorTable::Query::FindNextValidSlice() {
+  // The basic idea of this function is that |NextSliceState()| always emits
+  // all possible slices (including shadows for any gaps inbetween the real
+  // slices) and we filter out the invalid slices (as defined by the table
+  // definition) using |IsValidSlice()|.
+  //
+  // This has proved to be a lot cleaner to implement than trying to choose
+  // when to emit and not emit shadows directly.
+  while (!IsEof() && !IsValidSlice()) {
+    RETURN_IF_ERROR(NextSliceState());
+  }
+  return util::OkStatus();
+}
+
+util::Status SpanJoinOperatorTable::Query::NextSliceState() {
+  switch (state_) {
+    case State::kReal: {
+      // Forward the cursor to figure out where the next slice should be.
+      RETURN_IF_ERROR(CursorNext());
+
+      // Depending on the next slice, we can do two things here:
+      // 1. If the next slice is on the same partition, we can just emit a
+      //    single shadow until the start of the next slice.
+      // 2. If the next slice is on another partition or we hit eof, just emit
+      //    a shadow to the end of the whole partition.
+      bool shadow_to_end = cursor_eof_ || (defn_->IsPartitioned() &&
+                                           partition_ != CursorPartition());
+      state_ = State::kPresentPartitionShadow;
+      ts_ = AdjustedTsEnd();
+      ts_end_ =
+          shadow_to_end ? std::numeric_limits<int64_t>::max() : CursorTs();
+      return util::OkStatus();
+    }
+    case State::kPresentPartitionShadow: {
+      if (ts_end_ == std::numeric_limits<int64_t>::max()) {
+        // If the shadow is to the end of the slice, create a missing partition
+        // shadow to the start of the partition of the next slice or to the max
+        // partition if we hit eof.
+        state_ = State::kMissingPartitionShadow;
+        ts_ = 0;
+        ts_end_ = std::numeric_limits<int64_t>::max();
+
+        missing_partition_start_ = partition_ + 1;
+        missing_partition_end_ = cursor_eof_
+                                     ? std::numeric_limits<int64_t>::max()
+                                     : CursorPartition();
+      } else {
+        // If the shadow is not to the end, we must have another slice on the
+        // current partition.
+        state_ = State::kReal;
+        ts_ = CursorTs();
+        ts_end_ = ts_ + CursorDur();
+
+        PERFETTO_DCHECK(!defn_->IsPartitioned() ||
+                        partition_ == CursorPartition());
+      }
+      return util::OkStatus();
+    }
+    case State::kMissingPartitionShadow: {
+      if (missing_partition_end_ == std::numeric_limits<int64_t>::max()) {
+        PERFETTO_DCHECK(cursor_eof_);
+
+        // If we have a missing partition to the max partition, we must have hit
+        // eof.
+        state_ = State::kEof;
+      } else {
+        PERFETTO_DCHECK(!defn_->IsPartitioned() ||
+                        CursorPartition() == missing_partition_end_);
+
+        // Otherwise, setup a single partition slice on the end partition to the
+        // start of the next slice.
+        state_ = State::kPresentPartitionShadow;
+        ts_ = 0;
+        ts_end_ = CursorTs();
+        partition_ = missing_partition_end_;
+      }
+      return util::OkStatus();
+    }
+    case State::kEof: {
+      PERFETTO_DFATAL("Called Next when EOF");
+      return util::ErrStatus("Called Next when EOF");
+    }
+  }
+  PERFETTO_FATAL("For GCC");
+}
+
+util::Status SpanJoinOperatorTable::Query::Rewind() {
+  sqlite3_stmt* stmt = nullptr;
+  int res =
+      sqlite3_prepare_v2(db_, sql_query_.c_str(),
+                         static_cast<int>(sql_query_.size()), &stmt, nullptr);
+  stmt_.reset(stmt);
+
+  cursor_eof_ = res != SQLITE_OK;
+  if (res != SQLITE_OK)
+    return util::ErrStatus(
+        "%s", sqlite_utils::FormatErrorMessage(
+                  stmt_.get(), base::StringView(sql_query_), db_, res)
+                  .c_message());
+
+  RETURN_IF_ERROR(CursorNext());
+
+  // Setup the first slice as a missing partition shadow from the lowest
+  // partition until the first slice partition. We will handle finding the real
+  // slice in |FindNextValidSlice()|.
+  state_ = State::kMissingPartitionShadow;
+  ts_ = 0;
+  ts_end_ = std::numeric_limits<int64_t>::max();
+  missing_partition_start_ = std::numeric_limits<int64_t>::min();
+
+  if (cursor_eof_) {
+    missing_partition_end_ = std::numeric_limits<int64_t>::max();
+  } else if (defn_->IsPartitioned()) {
+    missing_partition_end_ = CursorPartition();
+  } else {
+    missing_partition_end_ = std::numeric_limits<int64_t>::min();
+  }
+
+  // Actually compute the first valid slice.
+  return FindNextValidSlice();
+}
+
+util::Status SpanJoinOperatorTable::Query::CursorNext() {
+  auto* stmt = stmt_.get();
+  int res;
+  if (defn_->IsPartitioned()) {
+    auto partition_idx = static_cast<int>(defn_->partition_idx());
+    // Fastforward through any rows with null partition keys.
+    int row_type;
+    do {
+      res = sqlite3_step(stmt);
+      row_type = sqlite3_column_type(stmt, partition_idx);
+    } while (res == SQLITE_ROW && row_type == SQLITE_NULL);
+
+    if (res == SQLITE_ROW && row_type != SQLITE_INTEGER) {
+      return util::ErrStatus("SPAN_JOIN: partition is not an int");
+    }
+  } else {
+    res = sqlite3_step(stmt);
+  }
+  cursor_eof_ = res != SQLITE_ROW;
+  return res == SQLITE_ROW || res == SQLITE_DONE
+             ? util::OkStatus()
+             : util::ErrStatus("SPAN_JOIN: %s", sqlite3_errmsg(db_));
+}
+
+std::string SpanJoinOperatorTable::Query::CreateSqlQuery(
+    const std::vector<std::string>& cs) const {
+  std::vector<std::string> col_names;
+  for (const SqliteTable::Column& c : defn_->columns()) {
+    col_names.push_back("`" + c.name() + "`");
+  }
+
+  std::string sql = "SELECT " + base::Join(col_names, ", ");
+  sql += " FROM " + defn_->name();
+  if (!cs.empty()) {
+    sql += " WHERE " + base::Join(cs, " AND ");
+  }
+  sql += " ORDER BY ";
+  sql += defn_->IsPartitioned()
+             ? base::Join({"`" + defn_->partition_col() + "`", "ts"}, ", ")
+             : "ts";
+  sql += ";";
+  PERFETTO_DLOG("%s", sql.c_str());
+  return sql;
+}
+
+void SpanJoinOperatorTable::Query::ReportSqliteResult(sqlite3_context* context,
+                                                      size_t index) {
+  if (state_ != State::kReal) {
+    sqlite3_result_null(context);
+    return;
+  }
+
+  sqlite3_stmt* stmt = stmt_.get();
+  int idx = static_cast<int>(index);
+  switch (sqlite3_column_type(stmt, idx)) {
+    case SQLITE_INTEGER:
+      sqlite3_result_int64(context, sqlite3_column_int64(stmt, idx));
+      break;
+    case SQLITE_FLOAT:
+      sqlite3_result_double(context, sqlite3_column_double(stmt, idx));
+      break;
+    case SQLITE_TEXT: {
+      // TODO(lalitm): note for future optimizations: if we knew the addresses
+      // of the string intern pool, we could check if the string returned here
+      // comes from the pool, and pass it as non-transient.
+      const auto kSqliteTransient =
+          reinterpret_cast<sqlite3_destructor_type>(-1);
+      auto ptr = reinterpret_cast<const char*>(sqlite3_column_text(stmt, idx));
+      sqlite3_result_text(context, ptr, -1, kSqliteTransient);
+      break;
+    }
+  }
+}
+
+SpanJoinOperatorTable::TableDefinition::TableDefinition(
+    std::string name,
+    std::string partition_col,
+    std::vector<SqliteTable::Column> cols,
+    EmitShadowType emit_shadow_type,
+    uint32_t ts_idx,
+    uint32_t dur_idx,
+    uint32_t partition_idx)
+    : emit_shadow_type_(emit_shadow_type),
+      name_(std::move(name)),
+      partition_col_(std::move(partition_col)),
+      cols_(std::move(cols)),
+      ts_idx_(ts_idx),
+      dur_idx_(dur_idx),
+      partition_idx_(partition_idx) {}
+
+util::Status SpanJoinOperatorTable::TableDescriptor::Parse(
+    const std::string& raw_descriptor,
+    SpanJoinOperatorTable::TableDescriptor* descriptor) {
+  // Descriptors have one of the following forms:
+  // table_name [PARTITIONED column_name]
+
+  // Find the table name.
+  base::StringSplitter splitter(raw_descriptor, ' ');
+  if (!splitter.Next())
+    return util::ErrStatus("SPAN_JOIN: Missing table name");
+
+  descriptor->name = splitter.cur_token();
+  if (!splitter.Next())
+    return util::OkStatus();
+
+  if (!base::CaseInsensitiveEqual(splitter.cur_token(), "PARTITIONED"))
+    return util::ErrStatus("SPAN_JOIN: Invalid token");
+
+  if (!splitter.Next())
+    return util::ErrStatus("SPAN_JOIN: Missing partitioning column");
+
+  descriptor->partition_col = splitter.cur_token();
+  return util::OkStatus();
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/prelude/operators/span_join_operator.h b/src/trace_processor/prelude/operators/span_join_operator.h
new file mode 100644
index 0000000..f150a75
--- /dev/null
+++ b/src/trace_processor/prelude/operators/span_join_operator.h
@@ -0,0 +1,459 @@
+/*
+ * 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_PRELUDE_OPERATORS_SPAN_JOIN_OPERATOR_H_
+#define SRC_TRACE_PROCESSOR_PRELUDE_OPERATORS_SPAN_JOIN_OPERATOR_H_
+
+#include <sqlite3.h>
+
+#include <array>
+#include <deque>
+#include <limits>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "perfetto/trace_processor/basic_types.h"
+#include "perfetto/trace_processor/status.h"
+#include "src/trace_processor/sqlite/scoped_db.h"
+#include "src/trace_processor/sqlite/sqlite_table.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// Implements the SPAN JOIN operation between two tables on a particular column.
+//
+// Span:
+// A span is a row with a timestamp and a duration. It is used to model
+// operations which run for a particular *span* of time.
+//
+// We draw spans like so (time on the x-axis):
+// start of span->[ time where opertion is running ]<- end of span
+//
+// Multiple spans can happen in parallel:
+// [      ]
+//    [        ]
+//   [                    ]
+//  [ ]
+//
+// The above for example, models scheduling activity on a 4-core computer for a
+// short period of time.
+//
+// Span join:
+// The span join operation can be thought of as the intersection of span tables.
+// That is, the join table has a span for each pair of spans in the child tables
+// where the spans overlap. Because many spans are possible in parallel, an
+// extra metadata column (labelled the "join column") is used to distinguish
+// between the spanned tables.
+//
+// For a given join key suppose these were the two span tables:
+// Table 1:   [        ]              [      ]         [ ]
+// Table 2:          [      ]            [  ]           [      ]
+// Output :          [ ]                 [  ]           []
+//
+// All other columns apart from timestamp (ts), duration (dur) and the join key
+// are passed through unchanged.
+class SpanJoinOperatorTable : public SqliteTable {
+ public:
+  // Enum indicating whether the queries on the two inner tables should
+  // emit shadows.
+  enum class EmitShadowType {
+    // Used when the table should emit all shadow slices (both present and
+    // missing partition shadows).
+    kAll,
+
+    // Used when the table should only emit shadows for partitions which are
+    // present.
+    kPresentPartitionOnly,
+
+    // Used when the table should emit no shadow slices.
+    kNone,
+  };
+
+  // Contains the definition of the child tables.
+  class TableDefinition {
+   public:
+    TableDefinition() = default;
+
+    TableDefinition(std::string name,
+                    std::string partition_col,
+                    std::vector<SqliteTable::Column> cols,
+                    EmitShadowType emit_shadow_type,
+                    uint32_t ts_idx,
+                    uint32_t dur_idx,
+                    uint32_t partition_idx);
+
+    // Returns whether this table should emit present partition shadow slices.
+    bool ShouldEmitPresentPartitionShadow() const {
+      return emit_shadow_type_ == EmitShadowType::kAll ||
+             emit_shadow_type_ == EmitShadowType::kPresentPartitionOnly;
+    }
+
+    // Returns whether this table should emit missing partition shadow slices.
+    bool ShouldEmitMissingPartitionShadow() const {
+      return emit_shadow_type_ == EmitShadowType::kAll;
+    }
+
+    // Returns whether the table is partitioned.
+    bool IsPartitioned() const { return !partition_col_.empty(); }
+
+    const std::string& name() const { return name_; }
+    const std::string& partition_col() const { return partition_col_; }
+    const std::vector<SqliteTable::Column>& columns() const { return cols_; }
+
+    uint32_t ts_idx() const { return ts_idx_; }
+    uint32_t dur_idx() const { return dur_idx_; }
+    uint32_t partition_idx() const { return partition_idx_; }
+
+   private:
+    EmitShadowType emit_shadow_type_ = EmitShadowType::kNone;
+
+    std::string name_;
+    std::string partition_col_;
+    std::vector<SqliteTable::Column> cols_;
+
+    uint32_t ts_idx_ = std::numeric_limits<uint32_t>::max();
+    uint32_t dur_idx_ = std::numeric_limits<uint32_t>::max();
+    uint32_t partition_idx_ = std::numeric_limits<uint32_t>::max();
+  };
+
+  // Stores information about a single subquery into one of the two child
+  // tables.
+  //
+  // This class is implemented as a state machine which steps from one slice to
+  // the next.
+  class Query {
+   public:
+    // Enum encoding the current state of the query in the state machine.
+    enum class State {
+      // Encodes that the current slice is a real slice (i.e. comes directly
+      // from the cursor).
+      kReal,
+
+      // Encodes that the current slice is on a partition for which there is a
+      // real slice present.
+      kPresentPartitionShadow,
+
+      // Encodes that the current slice is on a paritition(s) for which there is
+      // no real slice for those partition(s).
+      kMissingPartitionShadow,
+
+      // Encodes that this query has reached the end.
+      kEof,
+    };
+
+    Query(SpanJoinOperatorTable*, const TableDefinition*, sqlite3* db);
+    virtual ~Query();
+
+    Query(Query&&) noexcept = default;
+    Query& operator=(Query&&) = default;
+
+    enum class InitialEofBehavior {
+      kTreatAsEof,
+      kTreatAsMissingPartitionShadow
+    };
+
+    // Initializes the query with the given constraints and query parameters.
+    util::Status Initialize(
+        const QueryConstraints& qc,
+        sqlite3_value** argv,
+        InitialEofBehavior eof_behavior = InitialEofBehavior::kTreatAsEof);
+
+    // Forwards the query to the next valid slice.
+    util::Status Next();
+
+    // Rewinds the query to the first valid slice
+    // This is used in the mixed partitioning case where the query with no
+    // partitions is rewound to the start on every new partition.
+    util::Status Rewind();
+
+    // Reports the column at the given index to given context.
+    void ReportSqliteResult(sqlite3_context* context, size_t index);
+
+    // Returns whether the cursor has reached eof.
+    bool IsEof() const { return state_ == State::kEof; }
+
+    // Returns whether the current slice pointed to is a real slice.
+    bool IsReal() const { return state_ == State::kReal; }
+
+    // Returns the first partition this slice covers (for real/single partition
+    // shadows, this is the same as partition()).
+    // This partition encodes a [start, end] (closed at start and at end) range
+    // of partitions which works as the partitions are integers.
+    int64_t FirstPartition() const {
+      PERFETTO_DCHECK(!IsEof());
+      return IsMissingPartitionShadow() ? missing_partition_start_
+                                        : partition();
+    }
+
+    // Returns the last partition this slice covers (for real/single partition
+    // shadows, this is the same as partition()).
+    // This partition encodes a [start, end] (closed at start and at end) range
+    // of partitions which works as the partitions are integers.
+    int64_t LastPartition() const {
+      PERFETTO_DCHECK(!IsEof());
+      return IsMissingPartitionShadow() ? missing_partition_end_ - 1
+                                        : partition();
+    }
+
+    // Returns the end timestamp of this slice adjusted to ensure that -1
+    // duration slices always returns ts.
+    int64_t AdjustedTsEnd() const {
+      PERFETTO_DCHECK(!IsEof());
+      return ts_end_ - ts() == -1 ? ts() : ts_end_;
+    }
+
+    int64_t ts() const {
+      PERFETTO_DCHECK(!IsEof());
+      return ts_;
+    }
+    int64_t partition() const {
+      PERFETTO_DCHECK(!IsEof() && defn_->IsPartitioned());
+      return partition_;
+    }
+
+    int64_t raw_ts_end() const {
+      PERFETTO_DCHECK(!IsEof());
+      return ts_end_;
+    }
+
+    const TableDefinition* definition() const { return defn_; }
+
+   private:
+    Query(Query&) = delete;
+    Query& operator=(const Query&) = delete;
+
+    // Returns whether the current slice pointed to is a valid slice.
+    bool IsValidSlice();
+
+    // Forwards the query to the next valid slice.
+    util::Status FindNextValidSlice();
+
+    // Advances the query state machine by one slice.
+    util::Status NextSliceState();
+
+    // Forwards the cursor to point to the next real slice.
+    util::Status CursorNext();
+
+    // Creates an SQL query from the given set of constraint strings.
+    std::string CreateSqlQuery(const std::vector<std::string>& cs) const;
+
+    // Returns whether the current slice pointed to is a present partition
+    // shadow.
+    bool IsPresentPartitionShadow() const {
+      return state_ == State::kPresentPartitionShadow;
+    }
+
+    // Returns whether the current slice pointed to is a missing partition
+    // shadow.
+    bool IsMissingPartitionShadow() const {
+      return state_ == State::kMissingPartitionShadow;
+    }
+
+    // Returns whether the current slice pointed to is an empty shadow.
+    bool IsEmptyShadow() const {
+      PERFETTO_DCHECK(!IsEof());
+      return (!IsReal() && ts_ == ts_end_) ||
+             (IsMissingPartitionShadow() &&
+              missing_partition_start_ == missing_partition_end_);
+    }
+
+    int64_t CursorTs() const {
+      PERFETTO_DCHECK(!cursor_eof_);
+      auto ts_idx = static_cast<int>(defn_->ts_idx());
+      return sqlite3_column_int64(stmt_.get(), ts_idx);
+    }
+
+    int64_t CursorDur() const {
+      PERFETTO_DCHECK(!cursor_eof_);
+      auto dur_idx = static_cast<int>(defn_->dur_idx());
+      return sqlite3_column_int64(stmt_.get(), dur_idx);
+    }
+
+    int64_t CursorPartition() const {
+      PERFETTO_DCHECK(!cursor_eof_);
+      PERFETTO_DCHECK(defn_->IsPartitioned());
+      auto partition_idx = static_cast<int>(defn_->partition_idx());
+      return sqlite3_column_int64(stmt_.get(), partition_idx);
+    }
+
+    State state_ = State::kMissingPartitionShadow;
+    bool cursor_eof_ = false;
+
+    // Only valid when |state_| != kEof.
+    int64_t ts_ = 0;
+    int64_t ts_end_ = std::numeric_limits<int64_t>::max();
+
+    // Only valid when |state_| == kReal or |state_| == kPresentPartitionShadow.
+    int64_t partition_ = std::numeric_limits<int64_t>::min();
+
+    // Only valid when |state_| == kMissingPartitionShadow.
+    int64_t missing_partition_start_ = 0;
+    int64_t missing_partition_end_ = 0;
+
+    std::string sql_query_;
+    ScopedStmt stmt_;
+
+    const TableDefinition* defn_ = nullptr;
+    sqlite3* db_ = nullptr;
+    SpanJoinOperatorTable* table_ = nullptr;
+  };
+
+  // Base class for a cursor on the span table.
+  class Cursor : public SqliteTable::Cursor {
+   public:
+    Cursor(SpanJoinOperatorTable*, sqlite3* db);
+    ~Cursor() override = default;
+
+    int Filter(const QueryConstraints& qc,
+               sqlite3_value** argv,
+               FilterHistory) override {
+      base::Status status = FilterInner(qc, argv);
+      if (!status.ok()) {
+        table_->SetErrorMessage(sqlite3_mprintf("%s", status.c_message()));
+        return SQLITE_ERROR;
+      }
+      return SQLITE_OK;
+    }
+    int Next() override {
+      base::Status status = NextInner();
+      if (!status.ok()) {
+        table_->SetErrorMessage(sqlite3_mprintf("%s", status.c_message()));
+        return SQLITE_ERROR;
+      }
+      return SQLITE_OK;
+    }
+
+    int Column(sqlite3_context* context, int N) override;
+    int Eof() override;
+
+   private:
+    Cursor(Cursor&) = delete;
+    Cursor& operator=(const Cursor&) = delete;
+
+    Cursor(Cursor&&) noexcept = default;
+    Cursor& operator=(Cursor&&) = default;
+
+    bool IsOverlappingSpan();
+
+    base::Status NextInner();
+    base::Status FilterInner(const QueryConstraints& qc, sqlite3_value** argv);
+    util::Status FindOverlappingSpan();
+
+    Query* FindEarliestFinishQuery();
+
+    Query t1_;
+    Query t2_;
+
+    Query* next_query_ = nullptr;
+
+    // Only valid for kMixedPartition.
+    int64_t last_mixed_partition_ = std::numeric_limits<int64_t>::min();
+
+    SpanJoinOperatorTable* table_;
+  };
+
+  SpanJoinOperatorTable(sqlite3*, const TraceStorage*);
+
+  static void RegisterTable(sqlite3* db, const TraceStorage* storage);
+
+  // Table implementation.
+  util::Status Init(int, const char* const*, SqliteTable::Schema*) override;
+  std::unique_ptr<SqliteTable::Cursor> CreateCursor() override;
+  int BestIndex(const QueryConstraints& qc, BestIndexInfo* info) override;
+  int FindFunction(const char* name, FindFunctionFn* fn, void** args) override;
+
+ private:
+  // Columns of the span operator table.
+  enum Column {
+    kTimestamp = 0,
+    kDuration = 1,
+    kPartition = 2,
+    // All other columns are dynamic depending on the joined tables.
+  };
+
+  // Enum indicating the possible partitionings of the two tables in span join.
+  enum class PartitioningType {
+    // Used when both tables don't have a partition specified.
+    kNoPartitioning = 0,
+
+    // Used when both tables have the same partition specified.
+    kSamePartitioning = 1,
+
+    // Used when one table has a partition and the other table doesn't.
+    kMixedPartitioning = 2
+  };
+
+  // Parsed version of a table descriptor.
+  struct TableDescriptor {
+    static util::Status Parse(const std::string& raw_descriptor,
+                              TableDescriptor* descriptor);
+
+    bool IsPartitioned() const { return !partition_col.empty(); }
+
+    std::string name;
+    std::string partition_col;
+  };
+
+  // Identifier for a column by index in a given table.
+  struct ColumnLocator {
+    const TableDefinition* defn;
+    size_t col_index;
+  };
+
+  bool IsLeftJoin() const {
+    return base::CaseInsensitiveEqual(module_name(), "span_left_join");
+  }
+  bool IsOuterJoin() const {
+    return base::CaseInsensitiveEqual(module_name(), "span_outer_join");
+  }
+
+  const std::string& partition_col() const {
+    return t1_defn_.IsPartitioned() ? t1_defn_.partition_col()
+                                    : t2_defn_.partition_col();
+  }
+
+  util::Status CreateTableDefinition(
+      const TableDescriptor& desc,
+      EmitShadowType emit_shadow_type,
+      SpanJoinOperatorTable::TableDefinition* defn);
+
+  std::vector<std::string> ComputeSqlConstraintsForDefinition(
+      const TableDefinition& defn,
+      const QueryConstraints& qc,
+      sqlite3_value** argv);
+
+  std::string GetNameForGlobalColumnIndex(const TableDefinition& defn,
+                                          int global_column);
+
+  void CreateSchemaColsForDefn(const TableDefinition& defn,
+                               std::vector<SqliteTable::Column>* cols);
+
+  TableDefinition t1_defn_;
+  TableDefinition t2_defn_;
+  PartitioningType partitioning_;
+  base::FlatHashMap<size_t, ColumnLocator> global_index_to_column_locator_;
+
+  sqlite3* const db_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_PRELUDE_OPERATORS_SPAN_JOIN_OPERATOR_H_
diff --git a/src/trace_processor/prelude/operators/span_join_operator_unittest.cc b/src/trace_processor/prelude/operators/span_join_operator_unittest.cc
new file mode 100644
index 0000000..737e828
--- /dev/null
+++ b/src/trace_processor/prelude/operators/span_join_operator_unittest.cc
@@ -0,0 +1,380 @@
+/*
+ * 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/prelude/operators/span_join_operator.h"
+
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+class SpanJoinOperatorTableTest : public ::testing::Test {
+ public:
+  SpanJoinOperatorTableTest() {
+    sqlite3* db = nullptr;
+    PERFETTO_CHECK(sqlite3_initialize() == SQLITE_OK);
+    PERFETTO_CHECK(sqlite3_open(":memory:", &db) == SQLITE_OK);
+    db_.reset(db);
+
+    SpanJoinOperatorTable::RegisterTable(db_.get(), nullptr);
+  }
+
+  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);
+  }
+
+  void RunStatement(const std::string& sql) {
+    PrepareValidStatement(sql);
+    ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_DONE);
+  }
+
+  void AssertNextRow(const std::vector<int64_t> elements) {
+    ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+    for (size_t i = 0; i < elements.size(); ++i) {
+      ASSERT_EQ(sqlite3_column_int64(stmt_.get(), static_cast<int>(i)),
+                elements[i]);
+    }
+  }
+
+ protected:
+  ScopedDb db_;
+  ScopedStmt stmt_;
+};
+
+TEST_F(SpanJoinOperatorTableTest, JoinTwoSpanTables) {
+  RunStatement(
+      "CREATE TEMP TABLE f("
+      "ts BIGINT PRIMARY KEY, "
+      "dur BIGINT, "
+      "cpu UNSIGNED INT"
+      ");");
+  RunStatement(
+      "CREATE TEMP TABLE s("
+      "ts BIGINT PRIMARY KEY, "
+      "dur BIGINT, "
+      "cpu UNSIGNED INT"
+      ");");
+  RunStatement(
+      "CREATE VIRTUAL TABLE sp USING span_join(f PARTITIONED cpu, "
+      "s PARTITIONED cpu);");
+
+  RunStatement("INSERT INTO f VALUES(100, 10, 5);");
+  RunStatement("INSERT INTO f VALUES(110, 50, 5);");
+  RunStatement("INSERT INTO f VALUES(120, 100, 2);");
+  RunStatement("INSERT INTO f VALUES(160, 10, 5);");
+
+  RunStatement("INSERT INTO s VALUES(100, 5, 5);");
+  RunStatement("INSERT INTO s VALUES(105, 100, 5);");
+  RunStatement("INSERT INTO s VALUES(110, 50, 2);");
+  RunStatement("INSERT INTO s VALUES(160, 100, 2);");
+
+  PrepareValidStatement("SELECT * FROM sp");
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 120);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 40);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 2);
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 160);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 60);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 2);
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 100);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 5);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 5);
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 105);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 5);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 5);
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 110);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 50);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 5);
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 160);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 10);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 5);
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_DONE);
+}
+
+TEST_F(SpanJoinOperatorTableTest, NullPartitionKey) {
+  RunStatement(
+      "CREATE TEMP TABLE f("
+      "ts BIGINT PRIMARY KEY, "
+      "dur BIGINT, "
+      "cpu UNSIGNED INT"
+      ");");
+  RunStatement(
+      "CREATE TEMP TABLE s("
+      "ts BIGINT PRIMARY KEY, "
+      "dur BIGINT, "
+      "cpu UNSIGNED INT"
+      ");");
+  RunStatement(
+      "CREATE VIRTUAL TABLE sp USING span_join(f PARTITIONED cpu, "
+      "s PARTITIONED cpu);");
+
+  RunStatement("INSERT INTO f VALUES(30, 20, NULL);");
+  RunStatement("INSERT INTO f VALUES(100, 10, 5);");
+  RunStatement("INSERT INTO f VALUES(110, 50, 5);");
+  RunStatement("INSERT INTO f VALUES(120, 100, 2);");
+  RunStatement("INSERT INTO f VALUES(160, 10, 5);");
+
+  RunStatement("INSERT INTO s VALUES(40, 10, NULL);");
+  RunStatement("INSERT INTO s VALUES(100, 5, 5);");
+  RunStatement("INSERT INTO s VALUES(105, 100, 5);");
+  RunStatement("INSERT INTO s VALUES(110, 50, 2);");
+  RunStatement("INSERT INTO s VALUES(160, 100, 2);");
+
+  PrepareValidStatement("SELECT * FROM sp");
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 120);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 40);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 2);
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 160);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 60);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 2);
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 100);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 5);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 5);
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 105);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 5);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 5);
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 110);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 50);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 5);
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 160);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 10);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 5);
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_DONE);
+}
+
+TEST_F(SpanJoinOperatorTableTest, MixedPartitioning) {
+  RunStatement(
+      "CREATE TEMP TABLE f("
+      "ts BIGINT PRIMARY KEY, "
+      "dur BIGINT, "
+      "upid UNSIGNED INT"
+      ");");
+  RunStatement(
+      "CREATE TEMP TABLE s("
+      "ts BIGINT PRIMARY KEY, "
+      "dur BIGINT, "
+      "s_val BIGINT"
+      ");");
+  RunStatement(
+      "CREATE VIRTUAL TABLE sp USING span_join(f PARTITIONED upid, s);");
+
+  RunStatement("INSERT INTO f VALUES(30, 20, NULL);");
+  RunStatement("INSERT INTO f VALUES(100, 10, 5);");
+  RunStatement("INSERT INTO f VALUES(110, 50, 5);");
+  RunStatement("INSERT INTO f VALUES(120, 100, 2);");
+  RunStatement("INSERT INTO f VALUES(160, 10, 5);");
+  RunStatement("INSERT INTO f VALUES(300, 100, 2);");
+
+  RunStatement("INSERT INTO s VALUES(100, 5, 11111);");
+  RunStatement("INSERT INTO s VALUES(105, 5, 22222);");
+  RunStatement("INSERT INTO s VALUES(110, 60, 33333);");
+  RunStatement("INSERT INTO s VALUES(320, 10, 44444);");
+
+  PrepareValidStatement("SELECT * FROM sp");
+  AssertNextRow({120, 50, 2, 33333});
+  AssertNextRow({320, 10, 2, 44444});
+  AssertNextRow({100, 5, 5, 11111});
+  AssertNextRow({105, 5, 5, 22222});
+  AssertNextRow({110, 50, 5, 33333});
+  AssertNextRow({160, 10, 5, 33333});
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_DONE);
+}
+
+TEST_F(SpanJoinOperatorTableTest, NoPartitioning) {
+  RunStatement(
+      "CREATE TEMP TABLE f("
+      "ts BIGINT PRIMARY KEY, "
+      "dur BIGINT, "
+      "f_val BIGINT"
+      ");");
+  RunStatement(
+      "CREATE TEMP TABLE s("
+      "ts BIGINT PRIMARY KEY, "
+      "dur BIGINT, "
+      "s_val BIGINT"
+      ");");
+  RunStatement("CREATE VIRTUAL TABLE sp USING span_join(f, s);");
+
+  RunStatement("INSERT INTO f VALUES(100, 10, 44444);");
+  RunStatement("INSERT INTO f VALUES(110, 50, 55555);");
+  RunStatement("INSERT INTO f VALUES(160, 10, 44444);");
+
+  RunStatement("INSERT INTO s VALUES(100, 5, 11111);");
+  RunStatement("INSERT INTO s VALUES(105, 5, 22222);");
+  RunStatement("INSERT INTO s VALUES(110, 60, 33333);");
+
+  PrepareValidStatement("SELECT * FROM sp");
+  AssertNextRow({100, 5, 44444, 11111});
+  AssertNextRow({105, 5, 44444, 22222});
+  AssertNextRow({110, 50, 55555, 33333});
+  AssertNextRow({160, 10, 44444, 33333});
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_DONE);
+}
+
+TEST_F(SpanJoinOperatorTableTest, LeftJoinTwoSpanTables) {
+  RunStatement(
+      "CREATE TEMP TABLE f("
+      "ts BIGINT PRIMARY KEY, "
+      "dur BIGINT, "
+      "cpu UNSIGNED INT"
+      ");");
+  RunStatement(
+      "CREATE TEMP TABLE s("
+      "ts BIGINT PRIMARY KEY, "
+      "dur BIGINT, "
+      "tid UNSIGNED INT"
+      ");");
+  RunStatement("CREATE VIRTUAL TABLE sp USING span_left_join(f, s);");
+
+  RunStatement("INSERT INTO f VALUES(100, 10, 0);");
+  RunStatement("INSERT INTO f VALUES(110, 50, 1);");
+
+  RunStatement("INSERT INTO s VALUES(100, 5, 1);");
+  RunStatement("INSERT INTO s VALUES(110, 40, 2);");
+  RunStatement("INSERT INTO s VALUES(150, 50, 3);");
+
+  PrepareValidStatement("SELECT * FROM sp");
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 100);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 5);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 0);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 3), 1);
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 105);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 5);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 0);
+  ASSERT_EQ(sqlite3_column_type(stmt_.get(), 3), SQLITE_NULL);
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 110);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 40);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 1);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 3), 2);
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 150);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 10);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 1);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 3), 3);
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_DONE);
+}
+
+TEST_F(SpanJoinOperatorTableTest, LeftJoinTwoSpanTables_EmptyRight) {
+  RunStatement(
+      "CREATE TEMP TABLE f("
+      "ts BIGINT PRIMARY KEY, "
+      "dur BIGINT, "
+      "cpu UNSIGNED INT"
+      ");");
+  RunStatement(
+      "CREATE TEMP TABLE s("
+      "ts BIGINT PRIMARY KEY, "
+      "dur BIGINT, "
+      "tid UNSIGNED INT"
+      ");");
+  RunStatement("CREATE VIRTUAL TABLE sp USING span_left_join(f, s);");
+
+  RunStatement("INSERT INTO f VALUES(100, 10, 0);");
+  RunStatement("INSERT INTO f VALUES(110, 50, 1);");
+
+  PrepareValidStatement("SELECT * FROM sp");
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 100);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 10);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 0);
+  ASSERT_EQ(sqlite3_column_type(stmt_.get(), 3), SQLITE_NULL);
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 110);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 50);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 1);
+  ASSERT_EQ(sqlite3_column_type(stmt_.get(), 3), SQLITE_NULL);
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_DONE);
+}
+
+TEST_F(SpanJoinOperatorTableTest, CapitalizedLeftJoin) {
+  RunStatement(
+      "CREATE TEMP TABLE f("
+      "ts BIGINT PRIMARY KEY, "
+      "dur BIGINT, "
+      "cpu UNSIGNED INT"
+      ");");
+  RunStatement(
+      "CREATE TEMP TABLE s("
+      "ts BIGINT PRIMARY KEY, "
+      "dur BIGINT, "
+      "tid UNSIGNED INT"
+      ");");
+  RunStatement("CREATE VIRTUAL TABLE sp USING SPAN_LEFT_JOIN(f, s);");
+
+  RunStatement("INSERT INTO f VALUES(100, 10, 0);");
+  RunStatement("INSERT INTO f VALUES(110, 50, 1);");
+
+  PrepareValidStatement("SELECT * FROM sp");
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 100);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 10);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 0);
+  ASSERT_EQ(sqlite3_column_type(stmt_.get(), 3), SQLITE_NULL);
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 110);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 50);
+  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 1);
+  ASSERT_EQ(sqlite3_column_type(stmt_.get(), 3), SQLITE_NULL);
+
+  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_DONE);
+}
+
+}  // namespace
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/prelude/operators/window_operator.cc b/src/trace_processor/prelude/operators/window_operator.cc
new file mode 100644
index 0000000..b308c53
--- /dev/null
+++ b/src/trace_processor/prelude/operators/window_operator.cc
@@ -0,0 +1,188 @@
+/*
+ * 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/prelude/operators/window_operator.h"
+
+#include "src/trace_processor/sqlite/sqlite_utils.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+namespace {
+using namespace sqlite_utils;
+}  // namespace
+
+WindowOperatorTable::WindowOperatorTable(sqlite3*, const TraceStorage*) {}
+
+void WindowOperatorTable::RegisterTable(sqlite3* db,
+                                        const TraceStorage* storage) {
+  SqliteTable::Register<WindowOperatorTable>(db, storage, "window", true);
+}
+
+base::Status WindowOperatorTable::Init(int,
+                                       const char* const*,
+                                       Schema* schema) {
+  const bool kHidden = true;
+  *schema = Schema(
+      {
+          // These are the operator columns:
+          SqliteTable::Column(Column::kRowId, "rowid", SqlValue::Type::kLong,
+                              kHidden),
+          SqliteTable::Column(Column::kQuantum, "quantum",
+                              SqlValue::Type::kLong, kHidden),
+          SqliteTable::Column(Column::kWindowStart, "window_start",
+                              SqlValue::Type::kLong, kHidden),
+          SqliteTable::Column(Column::kWindowDur, "window_dur",
+                              SqlValue::Type::kLong, kHidden),
+          // These are the ouput columns:
+          SqliteTable::Column(Column::kTs, "ts", SqlValue::Type::kLong),
+          SqliteTable::Column(Column::kDuration, "dur", SqlValue::Type::kLong),
+          SqliteTable::Column(Column::kQuantumTs, "quantum_ts",
+                              SqlValue::Type::kLong),
+      },
+      {Column::kRowId});
+  return base::OkStatus();
+}
+
+std::unique_ptr<SqliteTable::Cursor> WindowOperatorTable::CreateCursor() {
+  return std::unique_ptr<SqliteTable::Cursor>(new Cursor(this));
+}
+
+int WindowOperatorTable::BestIndex(const QueryConstraints&, BestIndexInfo*) {
+  return SQLITE_OK;
+}
+
+int WindowOperatorTable::ModifyConstraints(QueryConstraints* qc) {
+  // Remove ordering on timestamp if it is the only ordering as we are already
+  // sorted on TS. This makes span joining significantly faster.
+  const auto& ob = qc->order_by();
+  if (ob.size() == 1 && ob[0].iColumn == Column::kTs && !ob[0].desc) {
+    qc->mutable_order_by()->clear();
+  }
+  return SQLITE_OK;
+}
+
+int WindowOperatorTable::Update(int argc,
+                                sqlite3_value** argv,
+                                sqlite3_int64*) {
+  // We only support updates to ts and dur. Disallow deletes (argc == 1) and
+  // inserts (argv[0] == null).
+  if (argc < 2 || sqlite3_value_type(argv[0]) == SQLITE_NULL)
+    return SQLITE_READONLY;
+
+  int64_t new_quantum = sqlite3_value_int64(argv[3]);
+  int64_t new_start = sqlite3_value_int64(argv[4]);
+  int64_t new_dur = sqlite3_value_int64(argv[5]);
+  if (new_dur == 0) {
+    auto* err = sqlite3_mprintf("Cannot set duration of window table to zero.");
+    SetErrorMessage(err);
+    return SQLITE_ERROR;
+  }
+
+  quantum_ = new_quantum;
+  window_start_ = new_start;
+  window_dur_ = new_dur;
+
+  return SQLITE_OK;
+}
+
+WindowOperatorTable::Cursor::Cursor(WindowOperatorTable* table)
+    : SqliteTable::Cursor(table), table_(table) {}
+
+int WindowOperatorTable::Cursor::Filter(const QueryConstraints& qc,
+                                        sqlite3_value** argv,
+                                        FilterHistory) {
+  *this = Cursor(table_);
+  window_start_ = table_->window_start_;
+  window_end_ = table_->window_start_ + table_->window_dur_;
+  step_size_ = table_->quantum_ == 0 ? table_->window_dur_ : table_->quantum_;
+
+  current_ts_ = window_start_;
+
+  // Set return first if there is a equals constraint on the row id asking to
+  // return the first row.
+  bool return_first = qc.constraints().size() == 1 &&
+                      qc.constraints()[0].column == Column::kRowId &&
+                      IsOpEq(qc.constraints()[0].op) &&
+                      sqlite3_value_int(argv[0]) == 0;
+  if (return_first) {
+    filter_type_ = FilterType::kReturnFirst;
+  } else {
+    filter_type_ = FilterType::kReturnAll;
+  }
+  return SQLITE_OK;
+}
+
+int WindowOperatorTable::Cursor::Column(sqlite3_context* context, int N) {
+  switch (N) {
+    case Column::kQuantum: {
+      sqlite3_result_int64(context,
+                           static_cast<sqlite_int64>(table_->quantum_));
+      break;
+    }
+    case Column::kWindowStart: {
+      sqlite3_result_int64(context,
+                           static_cast<sqlite_int64>(table_->window_start_));
+      break;
+    }
+    case Column::kWindowDur: {
+      sqlite3_result_int(context, static_cast<int>(table_->window_dur_));
+      break;
+    }
+    case Column::kTs: {
+      sqlite3_result_int64(context, static_cast<sqlite_int64>(current_ts_));
+      break;
+    }
+    case Column::kDuration: {
+      sqlite3_result_int64(context, static_cast<sqlite_int64>(step_size_));
+      break;
+    }
+    case Column::kQuantumTs: {
+      sqlite3_result_int64(context, static_cast<sqlite_int64>(quantum_ts_));
+      break;
+    }
+    case Column::kRowId: {
+      sqlite3_result_int64(context, static_cast<sqlite_int64>(row_id_));
+      break;
+    }
+    default: {
+      PERFETTO_FATAL("Unknown column %d", N);
+      break;
+    }
+  }
+  return SQLITE_OK;
+}
+
+int WindowOperatorTable::Cursor::Next() {
+  switch (filter_type_) {
+    case FilterType::kReturnFirst:
+      current_ts_ = window_end_;
+      break;
+    case FilterType::kReturnAll:
+      current_ts_ += step_size_;
+      quantum_ts_++;
+      break;
+  }
+  row_id_++;
+  return SQLITE_OK;
+}
+
+int WindowOperatorTable::Cursor::Eof() {
+  return current_ts_ >= window_end_;
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/prelude/operators/window_operator.h b/src/trace_processor/prelude/operators/window_operator.h
new file mode 100644
index 0000000..d10b81b
--- /dev/null
+++ b/src/trace_processor/prelude/operators/window_operator.h
@@ -0,0 +1,97 @@
+/*
+ * 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_PRELUDE_OPERATORS_WINDOW_OPERATOR_H_
+#define SRC_TRACE_PROCESSOR_PRELUDE_OPERATORS_WINDOW_OPERATOR_H_
+
+#include <limits>
+#include <memory>
+
+#include "src/trace_processor/sqlite/sqlite_table.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class TraceStorage;
+
+class WindowOperatorTable : public SqliteTable {
+ public:
+  enum Column {
+    kRowId = 0,
+    kQuantum = 1,
+    kWindowStart = 2,
+    kWindowDur = 3,
+    kTs = 4,
+    kDuration = 5,
+    kQuantumTs = 6
+  };
+  class Cursor : public SqliteTable::Cursor {
+   public:
+    explicit Cursor(WindowOperatorTable*);
+
+    // Implementation of SqliteTable::Cursor.
+    int Filter(const QueryConstraints& qc,
+               sqlite3_value**,
+               FilterHistory) override;
+    int Next() override;
+    int Eof() override;
+    int Column(sqlite3_context*, int N) override;
+
+   private:
+    // Defines the data to be generated by the table.
+    enum FilterType {
+      // Returns all the spans.
+      kReturnAll = 0,
+      // Only returns the first span of the table. Useful for UPDATE operations.
+      kReturnFirst = 1,
+    };
+
+    int64_t window_start_ = 0;
+    int64_t window_end_ = 0;
+    int64_t step_size_ = 0;
+
+    int64_t current_ts_ = 0;
+    int64_t quantum_ts_ = 0;
+    int64_t row_id_ = 0;
+
+    FilterType filter_type_ = FilterType::kReturnAll;
+
+    WindowOperatorTable* table_ = nullptr;
+  };
+
+  static void RegisterTable(sqlite3* db, const TraceStorage* storage);
+
+  WindowOperatorTable(sqlite3*, const TraceStorage*);
+
+  // Table implementation.
+  base::Status Init(int, const char* const*, Schema* schema) override;
+  std::unique_ptr<SqliteTable::Cursor> CreateCursor() override;
+  int BestIndex(const QueryConstraints&, BestIndexInfo*) override;
+  int ModifyConstraints(QueryConstraints* qc) override;
+  int Update(int, sqlite3_value**, sqlite3_int64*) override;
+
+ private:
+  int64_t quantum_ = 0;
+  int64_t window_start_ = 0;
+
+  // max of int64_t because SQLite technically only supports int64s and not
+  // uint64s.
+  int64_t window_dur_ = std::numeric_limits<int64_t>::max();
+};
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_PRELUDE_OPERATORS_WINDOW_OPERATOR_H_
diff --git a/src/trace_processor/rpc/httpd.cc b/src/trace_processor/rpc/httpd.cc
index f693551..97bee91 100644
--- a/src/trace_processor/rpc/httpd.cc
+++ b/src/trace_processor/rpc/httpd.cc
@@ -210,15 +210,12 @@
     // rpc.Query() call. No further calls will be made once Query() returns.
     auto on_result_chunk = [&](const uint8_t* buf, size_t len, bool has_more) {
       PERFETTO_DLOG("Sending response chunk, len=%zu eof=%d", len, !has_more);
-      char chunk_hdr[32];
-      auto hdr_len = static_cast<size_t>(sprintf(chunk_hdr, "%zx\r\n", len));
-      conn.SendResponseBody(chunk_hdr, hdr_len);
+      base::StackString<32> chunk_hdr("%zx\r\n", len);
+      conn.SendResponseBody(chunk_hdr.c_str(), chunk_hdr.len());
       conn.SendResponseBody(buf, len);
       conn.SendResponseBody("\r\n", 2);
-      if (!has_more) {
-        hdr_len = static_cast<size_t>(sprintf(chunk_hdr, "0\r\n\r\n"));
-        conn.SendResponseBody(chunk_hdr, hdr_len);
-      }
+      if (!has_more)
+        conn.SendResponseBody("0\r\n\r\n", 5);
     };
     trace_processor_rpc_.Query(
         reinterpret_cast<const uint8_t*>(req.body.data()), req.body.size(),
@@ -233,7 +230,8 @@
   }
 
   if (req.uri == "/enable_metatrace") {
-    trace_processor_rpc_.EnableMetatrace();
+    trace_processor_rpc_.EnableMetatrace(
+        reinterpret_cast<const uint8_t*>(req.body.data()), req.body.size());
     return conn.SendResponse("200 OK", headers);
   }
 
diff --git a/src/trace_processor/rpc/query_result_serializer.cc b/src/trace_processor/rpc/query_result_serializer.cc
index d3b9ba2..eec9079 100644
--- a/src/trace_processor/rpc/query_result_serializer.cc
+++ b/src/trace_processor/rpc/query_result_serializer.cc
@@ -194,8 +194,10 @@
   strings = nullptr;
 
   // Write the cells headers (1 byte per cell).
-  batch->AppendBytes(BatchProto::kCellsFieldNumber, cell_types.data(),
-                     cell_idx);
+  if (cell_idx > 0) {
+    batch->AppendBytes(BatchProto::kCellsFieldNumber, cell_types.data(),
+                       cell_idx);
+  }
 
   // Append the |varint_cells|, copying over the packed varint buffer.
   if (varints.size())
@@ -234,7 +236,9 @@
   }  // if (doubles_size > 0)
 
   // Append the blobs.
-  batch->AppendRawProtoBytes(blobs.data(), blobs.size());
+  if (blobs.size() > 0) {
+    batch->AppendRawProtoBytes(blobs.data(), blobs.size());
+  }
 
   // If this is the last batch, write the EOF field.
   if (!batch_full) {
diff --git a/src/trace_processor/rpc/query_result_serializer_unittest.cc b/src/trace_processor/rpc/query_result_serializer_unittest.cc
index 85c882d..6344edc 100644
--- a/src/trace_processor/rpc/query_result_serializer_unittest.cc
+++ b/src/trace_processor/rpc/query_result_serializer_unittest.cc
@@ -408,7 +408,7 @@
   TestDeserializer deser;
   deser.SerializeAndDeserialize(&ser);
   EXPECT_EQ(deser.cells.size(), 0u);
-  EXPECT_EQ(deser.error, "incomplete input (errcode: 1)");
+  EXPECT_EQ(deser.error, "Error: incomplete input (errcode: 1)");
   EXPECT_TRUE(deser.eof_reached);
 }
 
diff --git a/src/trace_processor/rpc/rpc.cc b/src/trace_processor/rpc/rpc.cc
index cef8207..df37e32 100644
--- a/src/trace_processor/rpc/rpc.cc
+++ b/src/trace_processor/rpc/rpc.cc
@@ -91,14 +91,15 @@
 Rpc::Rpc(std::unique_ptr<TraceProcessor> preloaded_instance)
     : trace_processor_(std::move(preloaded_instance)) {
   if (!trace_processor_)
-    ResetTraceProcessor();
+    ResetTraceProcessorInternal(Config());
 }
 
 Rpc::Rpc() : Rpc(nullptr) {}
 Rpc::~Rpc() = default;
 
-void Rpc::ResetTraceProcessor() {
-  trace_processor_ = TraceProcessor::CreateInstance(Config());
+void Rpc::ResetTraceProcessorInternal(const Config& config) {
+  trace_processor_config_ = config;
+  trace_processor_ = TraceProcessor::CreateInstance(config);
   bytes_parsed_ = bytes_last_progress_ = 0;
   t_parse_started_ = base::GetWallTimeNs().count();
   // Deliberately not resetting the RPC channel state (rxbuf_, {tx,rx}_seq_id_).
@@ -125,6 +126,28 @@
   }
 }
 
+namespace {
+
+using ProtoEnum = protos::pbzero::MetatraceCategories;
+TraceProcessor::MetatraceCategories MetatraceCategoriesToPublicEnum(
+    ProtoEnum categories) {
+  switch (categories) {
+    case ProtoEnum::TOPLEVEL:
+      return TraceProcessor::MetatraceCategories::TOPLEVEL;
+    case ProtoEnum::QUERY:
+      return TraceProcessor::MetatraceCategories::QUERY;
+    case ProtoEnum::FUNCTION:
+      return TraceProcessor::MetatraceCategories::FUNCTION;
+    case ProtoEnum::ALL:
+      return TraceProcessor::MetatraceCategories::ALL;
+    case ProtoEnum::NONE:
+      return TraceProcessor::MetatraceCategories::NONE;
+  }
+  return TraceProcessor::MetatraceCategories::NONE;
+}
+
+}  // namespace
+
 // [data, len] here is a tokenized TraceProcessorRpc proto message, without the
 // size header.
 void Rpc::ParseRpcRequest(const uint8_t* data, size_t len) {
@@ -219,7 +242,10 @@
       break;
     }
     case RpcProto::TPM_ENABLE_METATRACE: {
-      trace_processor_->EnableMetatrace();
+      using protos::pbzero::MetatraceCategories;
+      protozero::ConstBytes args = req.enable_metatrace_args();
+      EnableMetatrace(args.data, args.size);
+
       Response resp(tx_seq_id_++, req_type);
       resp.Send(rpc_response_fn_);
       break;
@@ -237,6 +263,13 @@
       resp.Send(rpc_response_fn_);
       break;
     }
+    case RpcProto::TPM_RESET_TRACE_PROCESSOR: {
+      Response resp(tx_seq_id_++, req_type);
+      protozero::ConstBytes args = req.reset_trace_processor_args();
+      ResetTraceProcessor(args.data, args.size);
+      resp.Send(rpc_response_fn_);
+      break;
+    }
     default: {
       // This can legitimately happen if the client is newer. We reply with a
       // generic "unkown request" response, so the client can do feature
@@ -252,10 +285,13 @@
 }
 
 util::Status Rpc::Parse(const uint8_t* data, size_t len) {
+  PERFETTO_TP_TRACE(
+      metatrace::Category::TOPLEVEL, "RPC_PARSE",
+      [&](metatrace::Record* r) { r->AddArg("length", std::to_string(len)); });
   if (eof_) {
     // Reset the trace processor state if another trace has been previously
-    // loaded.
-    ResetTraceProcessor();
+    // loaded. Use the same TraceProcessor Config.
+    ResetTraceProcessorInternal(trace_processor_config_);
   }
 
   eof_ = false;
@@ -272,11 +308,36 @@
 }
 
 void Rpc::NotifyEndOfFile() {
+  PERFETTO_TP_TRACE(metatrace::Category::TOPLEVEL, "RPC_NOTIFY_END_OF_FILE");
+
   trace_processor_->NotifyEndOfFile();
   eof_ = true;
   MaybePrintProgress();
 }
 
+void Rpc::ResetTraceProcessor(const uint8_t* args, size_t len) {
+  protos::pbzero::ResetTraceProcessorArgs::Decoder reset_trace_processor_args(
+      args, len);
+  Config config;
+  if (reset_trace_processor_args.has_drop_track_event_data_before()) {
+    config.drop_track_event_data_before =
+        reset_trace_processor_args.drop_track_event_data_before() ==
+                protos::pbzero::ResetTraceProcessorArgs::
+                    TRACK_EVENT_RANGE_OF_INTEREST
+            ? DropTrackEventDataBefore::kTrackEventRangeOfInterest
+            : DropTrackEventDataBefore::kNoDrop;
+  }
+  if (reset_trace_processor_args.has_ingest_ftrace_in_raw_table()) {
+    config.ingest_ftrace_in_raw_table =
+        reset_trace_processor_args.ingest_ftrace_in_raw_table();
+  }
+  if (reset_trace_processor_args.has_analyze_trace_proto_content()) {
+    config.analyze_trace_proto_content =
+        reset_trace_processor_args.analyze_trace_proto_content();
+  }
+  ResetTraceProcessorInternal(config);
+}
+
 void Rpc::MaybePrintProgress() {
   if (eof_ || bytes_parsed_ - bytes_last_progress_ > kProgressUpdateBytes) {
     bytes_last_progress_ = bytes_parsed_;
@@ -309,8 +370,13 @@
   protos::pbzero::QueryArgs::Decoder query(args, len);
   std::string sql = query.sql_query().ToStdString();
   PERFETTO_DLOG("[RPC] Query < %s", sql.c_str());
-  PERFETTO_TP_TRACE("RPC_QUERY",
-                    [&](metatrace::Record* r) { r->AddArg("SQL", sql); });
+  PERFETTO_TP_TRACE(metatrace::Category::TOPLEVEL, "RPC_QUERY",
+                    [&](metatrace::Record* r) {
+                      r->AddArg("SQL", sql);
+                      if (query.has_tag()) {
+                        r->AddArg("tag", query.tag());
+                      }
+                    });
 
   return trace_processor_->ExecuteQuery(sql.c_str());
 }
@@ -334,12 +400,13 @@
     metric_names.emplace_back(it->as_std_string());
   }
 
-  PERFETTO_TP_TRACE("RPC_COMPUTE_METRIC", [&](metatrace::Record* r) {
-    for (const auto& metric : metric_names) {
-      r->AddArg("Metric", metric);
-      r->AddArg("Format", std::to_string(args.format()));
-    }
-  });
+  PERFETTO_TP_TRACE(metatrace::Category::TOPLEVEL, "RPC_COMPUTE_METRIC",
+                    [&](metatrace::Record* r) {
+                      for (const auto& metric : metric_names) {
+                        r->AddArg("Metric", metric);
+                        r->AddArg("Format", std::to_string(args.format()));
+                      }
+                    });
 
   PERFETTO_DLOG("[RPC] ComputeMetrics(%zu, %s), format=%d", metric_names.size(),
                 metric_names.empty() ? "" : metric_names.front().c_str(),
@@ -371,8 +438,13 @@
   }
 }
 
-void Rpc::EnableMetatrace() {
-  trace_processor_->EnableMetatrace();
+void Rpc::EnableMetatrace(const uint8_t* data, size_t len) {
+  using protos::pbzero::MetatraceCategories;
+  TraceProcessor::MetatraceConfig config;
+  protos::pbzero::EnableMetatraceArgs::Decoder args(data, len);
+  config.categories = MetatraceCategoriesToPublicEnum(
+      static_cast<MetatraceCategories>(args.categories()));
+  trace_processor_->EnableMetatrace(config);
 }
 
 std::vector<uint8_t> Rpc::DisableAndReadMetatrace() {
diff --git a/src/trace_processor/rpc/rpc.h b/src/trace_processor/rpc/rpc.h
index 3aacf88..c2ef645 100644
--- a/src/trace_processor/rpc/rpc.h
+++ b/src/trace_processor/rpc/rpc.h
@@ -24,6 +24,7 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include "perfetto/trace_processor/basic_types.h"
 #include "perfetto/trace_processor/status.h"
 #include "src/protozero/proto_ring_buffer.h"
 
@@ -101,9 +102,10 @@
 
   util::Status Parse(const uint8_t* data, size_t len);
   void NotifyEndOfFile();
+  void ResetTraceProcessor(const uint8_t* args, size_t len);
   std::string GetCurrentTraceName();
   std::vector<uint8_t> ComputeMetric(const uint8_t* data, size_t len);
-  void EnableMetatrace();
+  void EnableMetatrace(const uint8_t* data, size_t len);  // EnableMetatraceArgs
   std::vector<uint8_t> DisableAndReadMetatrace();
   std::vector<uint8_t> GetStatus();
 
@@ -131,7 +133,7 @@
 
  private:
   void ParseRpcRequest(const uint8_t* data, size_t len);
-  void ResetTraceProcessor();
+  void ResetTraceProcessorInternal(const Config& config);
   void MaybePrintProgress();
   Iterator QueryInternal(const uint8_t* args, size_t len);
   void ComputeMetricInternal(const uint8_t* args,
@@ -140,6 +142,7 @@
   void DisableAndReadMetatraceInternal(
       protos::pbzero::DisableAndReadMetatraceResult*);
 
+  Config trace_processor_config_;
   std::unique_ptr<TraceProcessor> trace_processor_;
   RpcResponseFunction rpc_response_fn_;
   protozero::ProtoRingBuffer rxbuf_;
diff --git a/src/trace_processor/sorter/BUILD.gn b/src/trace_processor/sorter/BUILD.gn
new file mode 100644
index 0000000..d1d3069
--- /dev/null
+++ b/src/trace_processor/sorter/BUILD.gn
@@ -0,0 +1,57 @@
+# 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.
+
+import("../../../gn/perfetto.gni")
+import("../../../gn/test.gni")
+
+# Prevent that this file is accidentally included in embedder builds.
+assert(enable_perfetto_trace_processor)
+
+source_set("sorter") {
+  sources = [
+    "trace_sorter.cc",
+    "trace_sorter.h",
+    "trace_sorter_internal.h",
+    "trace_sorter_queue.h",
+  ]
+  deps = [
+    "../../../gn:default_deps",
+    "../../../include/perfetto/trace_processor:storage",
+    "../../base",
+    "../importers/common:parser_types",
+    "../importers/common:trace_parser_hdr",
+    "../importers/fuchsia:fuchsia_record",
+    "../importers/systrace:systrace_line",
+    "../storage",
+    "../types",
+  ]
+}
+
+perfetto_unittest_source_set("unittests") {
+  testonly = true
+  sources = [
+    "trace_sorter_queue_unittest.cc",
+    "trace_sorter_unittest.cc",
+  ]
+  deps = [
+    ":sorter",
+    "../../../gn:default_deps",
+    "../../../gn:gtest_and_gmock",
+    "../../../include/perfetto/trace_processor:storage",
+    "../../base",
+    "../importers/common:parser_types",
+    "../importers/proto:minimal",
+    "../types",
+  ]
+}
diff --git a/src/trace_processor/sorter/trace_sorter.cc b/src/trace_processor/sorter/trace_sorter.cc
new file mode 100644
index 0000000..6ecbb04
--- /dev/null
+++ b/src/trace_processor/sorter/trace_sorter.cc
@@ -0,0 +1,290 @@
+/*
+ * 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 <algorithm>
+#include <memory>
+#include <utility>
+
+#include "src/trace_processor/importers/common/parser_types.h"
+#include "src/trace_processor/importers/fuchsia/fuchsia_record.h"
+#include "src/trace_processor/sorter/trace_sorter.h"
+#include "src/trace_processor/sorter/trace_sorter_queue.h"
+#include "src/trace_processor/storage/trace_storage.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+TraceSorter::TraceSorter(TraceProcessorContext* context,
+                         std::unique_ptr<TraceParser> parser,
+                         SortingMode sorting_mode)
+    : context_(context),
+      parser_(std::move(parser)),
+      sorting_mode_(sorting_mode) {
+  const char* env = getenv("TRACE_PROCESSOR_SORT_ONLY");
+  bypass_next_stage_for_testing_ = env && !strcmp(env, "1");
+  if (bypass_next_stage_for_testing_)
+    PERFETTO_ELOG("TEST MODE: bypassing protobuf parsing stage");
+}
+
+TraceSorter::~TraceSorter() {
+  // If trace processor encountered a fatal error, it's possible for some events
+  // to have been pushed without evicting them by pushing to the next stage. Do
+  // that now.
+  for (auto& queue : queues_) {
+    for (const auto& event : queue.events_) {
+      // Calling this function without using the packet the same
+      // as just calling the destructor for the element.
+      EvictVariadic(event);
+    }
+  }
+}
+
+void TraceSorter::Queue::Sort() {
+  PERFETTO_DCHECK(needs_sorting());
+  PERFETTO_DCHECK(sort_start_idx_ < events_.size());
+
+  // If sort_min_ts_ has been set, it will no long be max_int, and so will be
+  // smaller than max_ts_.
+  PERFETTO_DCHECK(sort_min_ts_ < max_ts_);
+
+  // We know that all events between [0, sort_start_idx_] are sorted. Within
+  // this range, perform a bound search and find the iterator for the min
+  // timestamp that broke the monotonicity. Re-sort from there to the end.
+  auto sort_end = events_.begin() + static_cast<ssize_t>(sort_start_idx_);
+  PERFETTO_DCHECK(std::is_sorted(events_.begin(), sort_end));
+  auto sort_begin = std::lower_bound(events_.begin(), sort_end, sort_min_ts_,
+                                     &TimestampedDescriptor::Compare);
+  std::sort(sort_begin, events_.end());
+  sort_start_idx_ = 0;
+  sort_min_ts_ = 0;
+
+  // At this point |events_| must be fully sorted
+  PERFETTO_DCHECK(std::is_sorted(events_.begin(), events_.end()));
+}
+
+// Removes all the events in |queues_| that are earlier than the given
+// packet index and moves them to the next parser stages, respecting global
+// timestamp order. This function is a "extract min from N sorted queues", with
+// some little cleverness: we know that events tend to be bursty, so events are
+// not going to be randomly distributed on the N |queues_|.
+// Upon each iteration this function finds the first two queues (if any) that
+// have the oldest events, and extracts events from the 1st until hitting the
+// min_ts of the 2nd. Imagine the queues are as follows:
+//
+//  q0           {min_ts: 10  max_ts: 30}
+//  q1    {min_ts:5              max_ts: 35}
+//  q2              {min_ts: 12    max_ts: 40}
+//
+// We know that we can extract all events from q1 until we hit ts=10 without
+// looking at any other queue. After hitting ts=10, we need to re-look to all of
+// them to figure out the next min-event.
+// There are more suitable data structures to do this (e.g. keeping a min-heap
+// to avoid re-scanning all the queues all the times) but doesn't seem worth it.
+// With Android traces (that have 8 CPUs) this function accounts for ~1-3% cpu
+// time in a profiler.
+void TraceSorter::SortAndExtractEventsUntilPacket(uint64_t limit_offset) {
+  constexpr int64_t kTsMax = std::numeric_limits<int64_t>::max();
+  for (;;) {
+    size_t min_queue_idx = 0;  // The index of the queue with the min(ts).
+
+    // The top-2 min(ts) among all queues.
+    // queues_[min_queue_idx].events.timestamp == min_queue_ts[0].
+    int64_t min_queue_ts[2]{kTsMax, kTsMax};
+
+    // This loop identifies the queue which starts with the earliest event and
+    // also remembers the earliest event of the 2nd queue (in min_queue_ts[1]).
+    bool all_queues_empty = true;
+    for (size_t i = 0; i < queues_.size(); i++) {
+      auto& queue = queues_[i];
+      if (queue.events_.empty())
+        continue;
+      all_queues_empty = false;
+
+      PERFETTO_DCHECK(queue.max_ts_ <= append_max_ts_);
+      if (queue.min_ts_ < min_queue_ts[0]) {
+        min_queue_ts[1] = min_queue_ts[0];
+        min_queue_ts[0] = queue.min_ts_;
+        min_queue_idx = i;
+      } else if (queue.min_ts_ < min_queue_ts[1]) {
+        min_queue_ts[1] = queue.min_ts_;
+      }
+    }
+    if (all_queues_empty)
+      break;
+
+    Queue& queue = queues_[min_queue_idx];
+    auto& events = queue.events_;
+    if (queue.needs_sorting())
+      queue.Sort();
+    PERFETTO_DCHECK(queue.min_ts_ == events.front().ts);
+
+    // Now that we identified the min-queue, extract all events from it until
+    // we hit either: (1) the min-ts of the 2nd queue or (2) the packet index
+    // limit, whichever comes first.
+    size_t num_extracted = 0;
+    for (auto& event : events) {
+      if (event.descriptor.offset() >= limit_offset) {
+        break;
+      }
+
+      if (event.ts > min_queue_ts[1]) {
+        // We should never hit this condition on the first extraction as by
+        // the algorithm above (event.ts =) min_queue_ts[0] <= min_queue[1].
+        PERFETTO_DCHECK(num_extracted > 0);
+        break;
+      }
+
+      ++num_extracted;
+      MaybePushAndEvictEvent(min_queue_idx, event);
+    }  // for (event: events)
+
+    // The earliest event cannot be extracted without going past the limit.
+    if (!num_extracted)
+      break;
+
+    // Now remove the entries from the event buffer and update the queue-local
+    // and global time bounds.
+    events.erase_front(num_extracted);
+
+    // After evicting elements we can empty memory in the front of the
+    // queue.
+    variadic_queue_.FreeMemory();
+
+    // Update the queue timestamps to reflect the bounds after extraction.
+    if (events.empty()) {
+      queue.min_ts_ = kTsMax;
+      queue.max_ts_ = 0;
+    } else {
+      queue.min_ts_ = queue.events_.front().ts;
+    }
+  }  // for(;;)
+}
+
+void TraceSorter::EvictVariadic(const TimestampedDescriptor& ts_desc) {
+  switch (ts_desc.descriptor.type()) {
+    case EventType::kTracePacket:
+      EvictTypedVariadic<TracePacketData>(ts_desc);
+      return;
+    case EventType::kTrackEvent:
+      EvictTypedVariadic<TrackEventData>(ts_desc);
+      return;
+    case EventType::kFuchsiaRecord:
+      EvictTypedVariadic<FuchsiaRecord>(ts_desc);
+      return;
+    case EventType::kJsonValue:
+      EvictTypedVariadic<std::string>(ts_desc);
+      return;
+    case EventType::kSystraceLine:
+      EvictTypedVariadic<SystraceLine>(ts_desc);
+      return;
+    case EventType::kInlineSchedSwitch:
+      EvictTypedVariadic<InlineSchedSwitch>(ts_desc);
+      return;
+    case EventType::kInlineSchedWaking:
+      EvictTypedVariadic<InlineSchedWaking>(ts_desc);
+      return;
+    case EventType::kFtraceEvent:
+      EvictTypedVariadic<TracePacketData>(ts_desc);
+      return;
+    case EventType::kInvalid:
+      PERFETTO_FATAL("Invalid event type");
+  }
+  PERFETTO_FATAL("For GCC");
+}
+
+void TraceSorter::ParseTracePacket(const TimestampedDescriptor& ts_desc) {
+  switch (ts_desc.descriptor.type()) {
+    case EventType::kTracePacket:
+      parser_->ParseTracePacket(ts_desc.ts,
+                                EvictTypedVariadic<TracePacketData>(ts_desc));
+      return;
+    case EventType::kTrackEvent:
+      parser_->ParseTrackEvent(ts_desc.ts,
+                               EvictTypedVariadic<TrackEventData>(ts_desc));
+      return;
+    case EventType::kFuchsiaRecord:
+      parser_->ParseFuchsiaRecord(ts_desc.ts,
+                                  EvictTypedVariadic<FuchsiaRecord>(ts_desc));
+      return;
+    case EventType::kJsonValue:
+      parser_->ParseJsonPacket(ts_desc.ts,
+                               EvictTypedVariadic<std::string>(ts_desc));
+      return;
+    case EventType::kSystraceLine:
+      parser_->ParseSystraceLine(ts_desc.ts,
+                                 EvictTypedVariadic<SystraceLine>(ts_desc));
+      return;
+    case EventType::kInlineSchedSwitch:
+    case EventType::kInlineSchedWaking:
+    case EventType::kFtraceEvent:
+    case EventType::kInvalid:
+      PERFETTO_FATAL("Invalid event type");
+  }
+  PERFETTO_FATAL("For GCC");
+}
+
+void TraceSorter::ParseFtracePacket(uint32_t cpu,
+                                    const TimestampedDescriptor& ts_desc) {
+  switch (ts_desc.descriptor.type()) {
+    case EventType::kInlineSchedSwitch:
+      parser_->ParseInlineSchedSwitch(
+          cpu, ts_desc.ts, EvictTypedVariadic<InlineSchedSwitch>(ts_desc));
+      return;
+    case EventType::kInlineSchedWaking:
+      parser_->ParseInlineSchedWaking(
+          cpu, ts_desc.ts, EvictTypedVariadic<InlineSchedWaking>(ts_desc));
+      return;
+    case EventType::kFtraceEvent:
+      parser_->ParseFtraceEvent(cpu, ts_desc.ts,
+                                EvictTypedVariadic<TracePacketData>(ts_desc));
+      return;
+    case EventType::kTrackEvent:
+    case EventType::kSystraceLine:
+    case EventType::kTracePacket:
+    case EventType::kJsonValue:
+    case EventType::kFuchsiaRecord:
+    case EventType::kInvalid:
+      PERFETTO_FATAL("Invalid event type");
+  }
+  PERFETTO_FATAL("For GCC");
+}
+
+void TraceSorter::MaybePushAndEvictEvent(size_t queue_idx,
+                                         const TimestampedDescriptor& ts_desc) {
+  int64_t timestamp = ts_desc.ts;
+  if (timestamp < latest_pushed_event_ts_)
+    context_->storage->IncrementStats(stats::sorter_push_event_out_of_order);
+
+  latest_pushed_event_ts_ = std::max(latest_pushed_event_ts_, timestamp);
+
+  if (PERFETTO_UNLIKELY(bypass_next_stage_for_testing_)) {
+    // In standard run the object would be evicted by Parsing{F}tracePacket.
+    // Without it we need to evict it manually.
+    EvictVariadic(ts_desc);
+    return;
+  }
+
+  if (queue_idx == 0) {
+    ParseTracePacket(ts_desc);
+  } else {
+    // Ftrace queues start at offset 1. So queues_[1] = cpu[0] and so on.
+    uint32_t cpu = static_cast<uint32_t>(queue_idx - 1);
+    ParseFtracePacket(cpu, ts_desc);
+  }
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/sorter/trace_sorter.h b/src/trace_processor/sorter/trace_sorter.h
new file mode 100644
index 0000000..215194d
--- /dev/null
+++ b/src/trace_processor/sorter/trace_sorter.h
@@ -0,0 +1,377 @@
+/*
+ * 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_SORTER_TRACE_SORTER_H_
+#define SRC_TRACE_PROCESSOR_SORTER_TRACE_SORTER_H_
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "perfetto/ext/base/circular_queue.h"
+#include "perfetto/ext/base/utils.h"
+#include "perfetto/trace_processor/basic_types.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
+#include "src/trace_processor/importers/common/parser_types.h"
+#include "src/trace_processor/importers/common/trace_parser.h"
+#include "src/trace_processor/importers/fuchsia/fuchsia_record.h"
+#include "src/trace_processor/importers/systrace/systrace_line.h"
+#include "src/trace_processor/sorter/trace_sorter_queue.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+enum class EventType : uint8_t {
+  kFtraceEvent,
+  kTracePacket,
+  kInlineSchedSwitch,
+  kInlineSchedWaking,
+  kJsonValue,
+  kFuchsiaRecord,
+  kTrackEvent,
+  kSystraceLine,
+  kInvalid,
+  kSize = kInvalid,
+};
+
+namespace trace_sorter_internal {
+class VariadicQueue;
+}  // namespace trace_sorter_internal
+
+// 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
+// order. In order to support streaming use-cases, sorting happens within a
+// window.
+//
+// Events are held in the TraceSorter staging area (events_) until either:
+// 1. We can determine that it's safe to extract events by observing
+//  TracingServiceEvent Flush and ReadBuffer events
+// 2. The trace EOF is reached
+//
+// Incremental extraction
+//
+// Incremental extraction happens by using a combination of flush and read
+// buffer events from the tracing service. Note that incremental extraction
+// is only applicable for write_into_file traces; ring-buffer traces will
+// be sorted fully in-memory implicitly because there is only a single read
+// buffer call at the end.
+//
+// The algorithm for incremental extraction is explained in detail at
+// go/trace-sorting-is-complicated.
+//
+// Sorting algorithm
+//
+// The sorting algorithm is designed around the assumption that:
+// - Most events come from ftrace.
+// - Ftrace events are sorted within each cpu most of the times.
+//
+// Due to this, this class is oprerates as a streaming merge-sort of N+1 queues
+// (N = num cpus + 1 for non-ftrace events). Each queue in turn gets sorted (if
+// necessary) before proceeding with the global merge-sort-extract.
+//
+// When an event is pushed through, it is just appended to the end of one of
+// the N queues. While appending, we keep track of the fact that the queue
+// is still ordered or just lost ordering. When an out-of-order event is
+// detected on a queue we keep track of: (1) the offset within the queue where
+// the chaos begun, (2) the timestamp that broke the ordering.
+//
+// When we decide to extract events from the queues into the next stages of
+// the trace processor, we re-sort the events in the queue. Rather than
+// re-sorting everything all the times, we use the above knowledge to restrict
+// sorting to the (hopefully smaller) tail of the |events_| staging area.
+// At any time, the first partition of |events_| [0 .. sort_start_idx_) is
+// ordered, and the second partition [sort_start_idx_.. end] is not.
+// We use a logarithmic bound search operation to figure out what is the index
+// within the first partition where sorting should start, and sort all events
+// from there to the end.
+class TraceSorter {
+ private:
+  using VariadicQueue = trace_sorter_internal::VariadicQueue;
+
+ public:
+  enum class SortingMode {
+    kDefault,
+    kFullSort,
+  };
+
+  TraceSorter(TraceProcessorContext* context,
+              std::unique_ptr<TraceParser> parser,
+              SortingMode);
+  ~TraceSorter();
+
+  inline void PushTracePacket(int64_t timestamp,
+                              RefPtr<PacketSequenceStateGeneration> state,
+                              TraceBlobView event) {
+    uint32_t offset = variadic_queue_.Append(
+        TracePacketData{std::move(event), std::move(state)});
+    AppendNonFtraceEvent(timestamp, offset, EventType::kTracePacket);
+  }
+
+  inline void PushJsonValue(int64_t timestamp, std::string json_value) {
+    uint32_t offset = variadic_queue_.Append(std::move(json_value));
+    AppendNonFtraceEvent(timestamp, offset, EventType::kJsonValue);
+  }
+
+  inline void PushFuchsiaRecord(int64_t timestamp,
+                                FuchsiaRecord fuchsia_record) {
+    uint32_t offset = variadic_queue_.Append(std::move(fuchsia_record));
+    AppendNonFtraceEvent(timestamp, offset, EventType::kFuchsiaRecord);
+  }
+
+  inline void PushSystraceLine(SystraceLine systrace_line) {
+    auto ts = systrace_line.ts;
+    auto offset = variadic_queue_.Append(std::move(systrace_line));
+    AppendNonFtraceEvent(ts, offset, EventType::kSystraceLine);
+  }
+
+  inline void PushTrackEventPacket(int64_t timestamp,
+                                   TrackEventData track_event) {
+    uint32_t offset = variadic_queue_.Append(std::move(track_event));
+    AppendNonFtraceEvent(timestamp, offset, EventType::kTrackEvent);
+  }
+
+  inline void PushFtraceEvent(uint32_t cpu,
+                              int64_t timestamp,
+                              TraceBlobView event,
+                              RefPtr<PacketSequenceStateGeneration> state) {
+    auto* queue = GetQueue(cpu + 1);
+    uint32_t offset = variadic_queue_.Append(
+        TracePacketData{std::move(event), std::move(state)});
+    queue->Append(TimestampedDescriptor{
+        timestamp, Descriptor(offset, EventType::kFtraceEvent)});
+    UpdateAppendMaxTs(queue);
+  }
+  inline void PushInlineFtraceEvent(uint32_t cpu,
+                                    int64_t timestamp,
+                                    InlineSchedSwitch inline_sched_switch) {
+    // TODO(rsavitski): if a trace has a mix of normal & "compact" events
+    // (being pushed through this function), the ftrace batches will no longer
+    // be fully sorted by timestamp. In such situations, we will have to sort
+    // at the end of the batch. We can do better as both sub-sequences are
+    // sorted however. Consider adding extra queues, or pushing them in a
+    // merge-sort fashion
+    // // instead.
+    auto* queue = GetQueue(cpu + 1);
+    uint32_t offset = variadic_queue_.Append(inline_sched_switch);
+    queue->Append(TimestampedDescriptor{
+        timestamp, Descriptor(offset, EventType::kInlineSchedSwitch)});
+    UpdateAppendMaxTs(queue);
+  }
+  inline void PushInlineFtraceEvent(uint32_t cpu,
+                                    int64_t timestamp,
+                                    InlineSchedWaking inline_sched_waking) {
+    auto* queue = GetQueue(cpu + 1);
+
+    uint32_t offset = variadic_queue_.Append(inline_sched_waking);
+    queue->Append(TimestampedDescriptor{
+        timestamp, Descriptor(offset, EventType::kInlineSchedWaking)});
+    UpdateAppendMaxTs(queue);
+  }
+
+  void ExtractEventsForced() {
+    uint32_t cur_mem_block_offset = variadic_queue_.NextOffset();
+    SortAndExtractEventsUntilPacket(cur_mem_block_offset);
+    queues_.clear();
+
+    offset_for_extraction_ = cur_mem_block_offset;
+    flushes_since_extraction_ = 0;
+  }
+
+  void NotifyFlushEvent() { flushes_since_extraction_++; }
+
+  void NotifyReadBufferEvent() {
+    if (sorting_mode_ == SortingMode::kFullSort ||
+        flushes_since_extraction_ < 2) {
+      return;
+    }
+
+    SortAndExtractEventsUntilPacket(offset_for_extraction_);
+    offset_for_extraction_ = variadic_queue_.NextOffset();
+    flushes_since_extraction_ = 0;
+  }
+
+  int64_t max_timestamp() const { return append_max_ts_; }
+
+ private:
+  // Stores offset and type of metadata.
+  struct Descriptor {
+   public:
+    static constexpr uint8_t kTypeBits = 4;
+    static constexpr uint64_t kTypeMask = (1 << kTypeBits) - 1;
+    static constexpr uint64_t kOffsetShift = kTypeBits;
+    static constexpr uint64_t kMaxType = kTypeMask;
+
+    static_assert(static_cast<uint8_t>(EventType::kSize) <= kTypeMask,
+                  "Too many bits for type");
+
+    Descriptor(uint32_t offset, EventType type)
+        : packed_value_((static_cast<uint64_t>(offset) << kOffsetShift) |
+                        static_cast<uint64_t>(type)) {}
+
+    uint32_t offset() const {
+      return static_cast<uint32_t>(packed_value_ >> kOffsetShift);
+    }
+
+    EventType type() const {
+      return static_cast<EventType>(packed_value_ & kTypeMask);
+    }
+
+   private:
+    uint64_t packed_value_ = 0;
+  };
+
+  struct TimestampedDescriptor {
+    int64_t ts;
+    Descriptor descriptor;
+
+    // For std::lower_bound().
+    static inline bool Compare(const TimestampedDescriptor& x, int64_t ts) {
+      return x.ts < ts;
+    }
+
+    // For std::sort().
+    inline bool operator<(const TimestampedDescriptor& desc) const {
+      return ts < desc.ts ||
+             (ts == desc.ts && descriptor.offset() < desc.descriptor.offset());
+    }
+
+    // For std::sort(). Without this the compiler will fall back on invoking
+    // move operators on temporary objects.
+    friend void swap(TimestampedDescriptor& a, TimestampedDescriptor& b) {
+      // TimestampedDescriptor is 16 bytes + trivially swappable so it can be
+      // done without doing any moving.
+      using AS =
+          typename std::aligned_storage<sizeof(TimestampedDescriptor),
+                                        alignof(TimestampedDescriptor)>::type;
+      using std::swap;
+      swap(reinterpret_cast<AS&>(a), reinterpret_cast<AS&>(b));
+    }
+  };
+
+  static_assert(sizeof(TimestampedDescriptor) == 16,
+                "TimestampeDescriptor cannot grow beyond 16 bytes");
+
+  struct Queue {
+    inline void Append(TimestampedDescriptor ts_desc) {
+      auto ts = ts_desc.ts;
+      events_.emplace_back(std::move(ts_desc));
+      min_ts_ = std::min(min_ts_, ts);
+
+      // Events are often seen in order.
+      if (PERFETTO_LIKELY(ts >= max_ts_)) {
+        max_ts_ = ts;
+      } else {
+        // The event is breaking ordering. The first time it happens, keep
+        // track of which index we are at. We know that everything before that
+        // is sorted (because events were pushed monotonically). Everything
+        // after that index, instead, will need a sorting pass before moving
+        // events to the next pipeline stage.
+        if (sort_start_idx_ == 0) {
+          PERFETTO_DCHECK(events_.size() >= 2);
+          sort_start_idx_ = events_.size() - 1;
+          sort_min_ts_ = ts;
+        } else {
+          sort_min_ts_ = std::min(sort_min_ts_, ts);
+        }
+      }
+
+      PERFETTO_DCHECK(min_ts_ <= max_ts_);
+    }
+
+    bool needs_sorting() const { return sort_start_idx_ != 0; }
+    void Sort();
+
+    base::CircularQueue<TimestampedDescriptor> events_;
+    int64_t min_ts_ = std::numeric_limits<int64_t>::max();
+    int64_t max_ts_ = 0;
+    size_t sort_start_idx_ = 0;
+    int64_t sort_min_ts_ = std::numeric_limits<int64_t>::max();
+  };
+
+  void SortAndExtractEventsUntilPacket(uint64_t limit_packet_idx);
+
+  inline Queue* GetQueue(size_t index) {
+    if (PERFETTO_UNLIKELY(index >= queues_.size()))
+      queues_.resize(index + 1);
+    return &queues_[index];
+  }
+
+  inline void AppendNonFtraceEvent(int64_t ts,
+                                   uint32_t offset,
+                                   EventType type) {
+    Queue* queue = GetQueue(0);
+    queue->Append(TimestampedDescriptor{ts, Descriptor{offset, type}});
+    UpdateAppendMaxTs(queue);
+  }
+
+  inline void UpdateAppendMaxTs(Queue* queue) {
+    append_max_ts_ = std::max(append_max_ts_, queue->max_ts_);
+  }
+
+  void ParseTracePacket(const TimestampedDescriptor& ts_desc);
+  void ParseFtracePacket(uint32_t cpu, const TimestampedDescriptor& ts_desc);
+
+  template <typename T>
+  T EvictTypedVariadic(const TimestampedDescriptor& ts_desc) {
+    return variadic_queue_.Evict<T>(ts_desc.descriptor.offset());
+  }
+
+  void EvictVariadic(const TimestampedDescriptor& ts_desc);
+
+  void MaybePushAndEvictEvent(size_t queue_idx,
+                              const TimestampedDescriptor& ts_desc)
+      PERFETTO_ALWAYS_INLINE;
+
+  TraceProcessorContext* context_;
+  std::unique_ptr<TraceParser> parser_;
+
+  // Whether we should ignore incremental extraction and just wait for
+  // forced extractionn at the end of the trace.
+  SortingMode sorting_mode_ = SortingMode::kDefault;
+
+  // The packet offset until which events should be extracted. Set based
+  // on the packet offset in |OnReadBuffer|.
+  uint32_t offset_for_extraction_ = 0;
+
+  // The number of flushes which have happened since the last incremental
+  // extraction.
+  uint32_t flushes_since_extraction_ = 0;
+
+  // Stores the metadata for each event type in a memory efficient manner.
+  VariadicQueue variadic_queue_;
+
+  // queues_[0] is the general (non-ftrace) queue.
+  // queues_[1] is the ftrace queue for CPU(0).
+  // queues_[x] is the ftrace queue for CPU(x - 1).
+  std::vector<Queue> queues_;
+
+  // max(e.ts for e appended to the sorter)
+  int64_t append_max_ts_ = 0;
+
+  // Used for performance tests. True when setting
+  // TRACE_PROCESSOR_SORT_ONLY=1.
+  bool bypass_next_stage_for_testing_ = false;
+
+  // max(e.ts for e pushed to next stage)
+  int64_t latest_pushed_event_ts_ = std::numeric_limits<int64_t>::min();
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_SORTER_TRACE_SORTER_H_
diff --git a/src/trace_processor/sorter/trace_sorter_internal.h b/src/trace_processor/sorter/trace_sorter_internal.h
new file mode 100644
index 0000000..4caebb0
--- /dev/null
+++ b/src/trace_processor/sorter/trace_sorter_internal.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2022 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_SORTER_TRACE_SORTER_INTERNAL_H_
+#define SRC_TRACE_PROCESSOR_SORTER_TRACE_SORTER_INTERNAL_H_
+
+#include <deque>
+
+#include "perfetto/base/logging.h"
+#include "src/trace_processor/importers/common/parser_types.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace trace_sorter_internal {
+
+// Moves object to the specified pointer and returns the pointer to the space
+// behind it.
+template <typename T>
+char* AppendUnchecked(char* ptr, T value) {
+  PERFETTO_DCHECK(reinterpret_cast<uintptr_t>(ptr) % alignof(T) == 0);
+  new (ptr) T(std::move(value));
+  return ptr + base::AlignUp<8>(sizeof(T));
+}
+
+// Evicts object the the specified pointer, which now points to the space behind
+// it.
+template <typename T>
+T EvictUnchecked(char** ptr) {
+  PERFETTO_DCHECK(reinterpret_cast<uintptr_t>(*ptr) % alignof(T) == 0);
+  T* type_ptr = reinterpret_cast<T*>(*ptr);
+  T out(std::move(*type_ptr));
+  type_ptr->~T();
+  *ptr += base::AlignUp<8>(sizeof(T));
+  return out;
+}
+
+// Stores details of TrackEventData: presence of attributes and the
+// lenght of the array.
+struct TrackEventDataDescriptor {
+ public:
+  static constexpr uint64_t kBitsForCounterValues = 4;
+  static constexpr uint64_t kThreadTimestampMask =
+      1 << (kBitsForCounterValues + 1);
+  static constexpr uint64_t kThreadInstructionCountMask =
+      1 << kBitsForCounterValues;
+
+  TrackEventDataDescriptor(bool has_thread_timestamp,
+                           bool has_thread_instruction_count,
+                           uint64_t number_of_counter_values)
+      : packed_value_(GetPacketValue(has_thread_timestamp,
+                                     has_thread_instruction_count,
+                                     number_of_counter_values)) {
+    PERFETTO_DCHECK(number_of_counter_values <=
+                    TrackEventData::kMaxNumExtraCounters);
+  }
+
+  explicit TrackEventDataDescriptor(const TrackEventData& ted)
+      : TrackEventDataDescriptor(ted.thread_timestamp.has_value(),
+                                 ted.thread_instruction_count.has_value(),
+                                 CountNumberOfCounterValues(ted)) {
+    static_assert(
+        TrackEventData::kMaxNumExtraCounters < (1 << kBitsForCounterValues),
+        "kMaxNumExtraCounters can't be compressed properly");
+  }
+
+  static uint64_t CountNumberOfCounterValues(const TrackEventData& ted) {
+    for (uint32_t i = 0; i < TrackEventData::kMaxNumExtraCounters; ++i) {
+      if (std::equal_to<double>()(ted.extra_counter_values[i], 0)) {
+        return i;
+      }
+    }
+    return TrackEventData::kMaxNumExtraCounters;
+  }
+
+  static uint64_t GetPacketValue(bool has_thread_timestamp,
+                                 bool has_thread_instruction_count,
+                                 uint64_t number_of_counter_values) {
+    return (static_cast<uint64_t>(has_thread_timestamp)
+            << (kBitsForCounterValues + 1)) |
+           (static_cast<uint64_t>(has_thread_instruction_count)
+            << kBitsForCounterValues) |
+           number_of_counter_values;
+  }
+
+  bool HasThreadTimestamp() const {
+    return static_cast<bool>(packed_value_ & kThreadTimestampMask);
+  }
+
+  bool HasThreadInstructionCount() const {
+    return static_cast<bool>(packed_value_ & kThreadInstructionCountMask);
+  }
+
+  uint64_t NumberOfCounterValues() const {
+    return static_cast<uint64_t>(
+        packed_value_ & static_cast<uint64_t>(~(3 << kBitsForCounterValues)));
+  }
+
+  uint64_t AppendedSize() const {
+    return sizeof(TracePacketData) +
+           8l * (/*counter_value*/ 1 + HasThreadTimestamp() +
+                 HasThreadInstructionCount() + NumberOfCounterValues());
+  }
+
+ private:
+  // uint8_t would be enough to hold all of the required data, but we need 8
+  // bytes type for alignment.
+  uint64_t packed_value_ = 0;
+};
+
+// Adds and removes object of the type from queue memory. Can be overriden
+// for more specific functionality related to a type. All child classes
+// should implement the same interface.
+template <typename T>
+class TypedMemoryAccessor {
+ public:
+  static char* Append(char* ptr, T value) {
+    return AppendUnchecked(ptr, std::move(value));
+  }
+  static T Evict(char* ptr) { return EvictUnchecked<T>(&ptr); }
+  static uint64_t AppendSize(const T&) {
+    return static_cast<uint64_t>(sizeof(T));
+  }
+};
+
+// Responsibe for accessing memory in the queue related to TrackEventData.
+// Appends the struct more efficiently by compressing and decompressing some
+// of TrackEventData attributes.
+template <>
+class TypedMemoryAccessor<TrackEventData> {
+ public:
+  static char* Append(char* ptr, TrackEventData ted) {
+    auto ted_desc = TrackEventDataDescriptor(ted);
+    ptr = AppendUnchecked(ptr, ted_desc);
+    ptr = AppendUnchecked(ptr, std::move(ted.trace_packet_data));
+    ptr = AppendUnchecked(ptr, ted.counter_value);
+    if (ted_desc.HasThreadTimestamp()) {
+      ptr = AppendUnchecked(ptr, ted.thread_timestamp.value());
+    }
+    if (ted_desc.HasThreadInstructionCount()) {
+      ptr = AppendUnchecked(ptr, ted.thread_instruction_count.value());
+    }
+    for (uint32_t i = 0; i < ted_desc.NumberOfCounterValues(); i++) {
+      ptr = AppendUnchecked(ptr, ted.extra_counter_values[i]);
+    }
+    return ptr;
+  }
+
+  static TrackEventData Evict(char* ptr) {
+    auto ted_desc = EvictUnchecked<TrackEventDataDescriptor>(&ptr);
+    TrackEventData ted(EvictUnchecked<TracePacketData>(&ptr));
+    ted.counter_value = EvictUnchecked<double>(&ptr);
+    if (ted_desc.HasThreadTimestamp()) {
+      ted.thread_timestamp = EvictUnchecked<int64_t>(&ptr);
+    }
+    if (ted_desc.HasThreadInstructionCount()) {
+      ted.thread_instruction_count = EvictUnchecked<int64_t>(&ptr);
+    }
+    for (uint32_t i = 0; i < ted_desc.NumberOfCounterValues(); i++) {
+      ted.extra_counter_values[i] = EvictUnchecked<double>(&ptr);
+    }
+    return ted;
+  }
+
+  static uint64_t AppendSize(const TrackEventData& value) {
+    return static_cast<uint64_t>(sizeof(TrackEventDataDescriptor)) +
+           TrackEventDataDescriptor(value).AppendedSize();
+  }
+};
+
+}  // namespace trace_sorter_internal
+}  // namespace trace_processor
+}  // namespace perfetto
+#endif  // SRC_TRACE_PROCESSOR_SORTER_TRACE_SORTER_INTERNAL_H_
diff --git a/src/trace_processor/sorter/trace_sorter_queue.h b/src/trace_processor/sorter/trace_sorter_queue.h
new file mode 100644
index 0000000..aabd302
--- /dev/null
+++ b/src/trace_processor/sorter/trace_sorter_queue.h
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2022 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_SORTER_TRACE_SORTER_QUEUE_H_
+#define SRC_TRACE_PROCESSOR_SORTER_TRACE_SORTER_QUEUE_H_
+
+#include <cstddef>
+#include <deque>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/small_vector.h"
+#include "perfetto/ext/base/utils.h"
+#include "src/trace_processor/sorter/trace_sorter_internal.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace trace_sorter_internal {
+
+// 1MB is good tradeoff between having big enough memory blocks so that we don't
+// need to often append and remove blocks for big traces, but small enough to
+// not overuse memory for small traces.
+static constexpr uint32_t kDefaultSize = 1 * 1024 * 1024;  // 1MB
+
+// Used for storing the data for all different packet data types.
+class VariadicQueue {
+ public:
+  VariadicQueue() : VariadicQueue(kDefaultSize) {}
+  ~VariadicQueue() {
+    // These checks verify that we evicted all elements from this queue. This is
+    // important as we need to call the destructor to make sure we're not
+    // leaking memory.
+    FreeMemory();
+    PERFETTO_CHECK(mem_blocks_.size() == 1);
+    PERFETTO_CHECK(mem_blocks_.back().empty());
+  }
+
+  VariadicQueue(const VariadicQueue&) = delete;
+  VariadicQueue& operator=(const VariadicQueue&) = delete;
+
+  VariadicQueue(VariadicQueue&&) = default;
+  VariadicQueue& operator=(VariadicQueue&&) noexcept = default;
+
+  // Moves packet data type to the end of the queue storage.
+  template <typename T>
+  uint32_t Append(T value) {
+    PERFETTO_DCHECK(!mem_blocks_.empty());
+
+    uint64_t size = Block::AppendSize<T>(value);
+    if (PERFETTO_UNLIKELY(!mem_blocks_.back().HasSpace(size))) {
+      mem_blocks_.emplace_back(Block(block_size_));
+    }
+    auto& back_block = mem_blocks_.back();
+    PERFETTO_DCHECK(back_block.HasSpace(size));
+    return GlobalMemOffsetFromLastBlockOffset(
+        back_block.Append(std::move(value)));
+  }
+
+  // Moves object out of queue storage.
+  template <typename T>
+  T Evict(uint32_t global_offset) {
+    uint32_t block = (global_offset / block_size_) - deleted_blocks_;
+    uint32_t block_offset = global_offset % block_size_;
+    return mem_blocks_[block].Evict<T>(block_offset);
+  }
+
+  // Clears the empty front of queue storage.
+  void FreeMemory() {
+    while (mem_blocks_.size() > 1 && mem_blocks_.front().empty()) {
+      mem_blocks_.pop_front();
+      deleted_blocks_++;
+    }
+  }
+
+  // Returns the offset value in which new element can be stored.
+  uint32_t NextOffset() const {
+    PERFETTO_DCHECK(!mem_blocks_.empty());
+    return GlobalMemOffsetFromLastBlockOffset(mem_blocks_.back().offset());
+  }
+
+  static VariadicQueue VariadicQueueForTesting(uint32_t size) {
+    return VariadicQueue(size);
+  }
+
+ private:
+  // Implementation note: this class stores an extra 8 bytes in debug builds to
+  // store the size of the type stored inside.
+  class Block {
+   public:
+    explicit Block(uint32_t block_size)
+        : size_(block_size),
+          storage_(
+              base::AlignedAllocTyped<uint64_t>(size_ / sizeof(uint64_t))) {}
+
+    bool HasSpace(uint64_t size) const { return size <= size_ - offset_; }
+
+    template <typename T>
+    uint32_t Append(T value) {
+      static_assert(alignof(T) <= 8,
+                    "Class must have at most 8 byte alignment");
+      PERFETTO_DCHECK(offset_ % 8 == 0);
+      PERFETTO_DCHECK(HasSpace(AppendSize(value)));
+
+      char* storage_begin_ptr = reinterpret_cast<char*>(storage_.get());
+      char* ptr = storage_begin_ptr + offset_;
+
+#if PERFETTO_DCHECK_IS_ON()
+      ptr = AppendUnchecked(ptr, TypedMemoryAccessor<T>::AppendSize(value));
+#endif
+      ptr = TypedMemoryAccessor<T>::Append(ptr, std::move(value));
+      num_elements_++;
+
+      auto cur_offset = offset_;
+      offset_ = static_cast<uint32_t>(base::AlignUp<8>(static_cast<uint32_t>(
+          ptr - reinterpret_cast<char*>(storage_.get()))));
+      return cur_offset;
+    }
+
+    template <typename T>
+    T Evict(uint32_t offset) {
+      PERFETTO_DCHECK(offset < size_);
+      PERFETTO_DCHECK(offset % 8 == 0);
+
+      char* ptr = reinterpret_cast<char*>(storage_.get()) + offset;
+      uint64_t size = 0;
+#if PERFETTO_DCHECK_IS_ON()
+      size = EvictUnchecked<uint64_t>(&ptr);
+#endif
+      T value = TypedMemoryAccessor<T>::Evict(ptr);
+      PERFETTO_DCHECK(size == TypedMemoryAccessor<T>::AppendSize(value));
+      num_elements_evicted_++;
+      return value;
+    }
+
+    template <typename T>
+    static uint64_t AppendSize(const T& value) {
+#if PERFETTO_DCHECK_IS_ON()
+      // On debug runs for each append of T we also append the sizeof(T) to the
+      // queue for sanity check, which we later evict and compare with object
+      // size. This value needs to be added to general size of an object.
+      return sizeof(uint64_t) + TypedMemoryAccessor<T>::AppendSize(value);
+#else
+      return TypedMemoryAccessor<T>::AppendSize(value);
+#endif
+    }
+
+    uint32_t offset() const { return offset_; }
+    bool empty() const { return num_elements_ == num_elements_evicted_; }
+
+   private:
+    uint32_t size_;
+    uint32_t offset_ = 0;
+
+    uint32_t num_elements_ = 0;
+    uint32_t num_elements_evicted_ = 0;
+
+    base::AlignedUniquePtr<uint64_t> storage_;
+  };
+
+  explicit VariadicQueue(uint32_t block_size) : block_size_(block_size) {
+    mem_blocks_.emplace_back(Block(block_size_));
+  }
+
+  uint32_t GlobalMemOffsetFromLastBlockOffset(uint32_t block_offset) const {
+    return (deleted_blocks_ + static_cast<uint32_t>(mem_blocks_.size()) - 1) *
+               block_size_ +
+           block_offset;
+  }
+
+  std::deque<Block> mem_blocks_;
+
+  uint32_t block_size_ = kDefaultSize;
+  uint32_t deleted_blocks_ = 0;
+};
+
+}  // namespace trace_sorter_internal
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_SORTER_TRACE_SORTER_QUEUE_H_
diff --git a/src/trace_processor/sorter/trace_sorter_queue_unittest.cc b/src/trace_processor/sorter/trace_sorter_queue_unittest.cc
new file mode 100644
index 0000000..a6cd7a4
--- /dev/null
+++ b/src/trace_processor/sorter/trace_sorter_queue_unittest.cc
@@ -0,0 +1,95 @@
+/*
+ * 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/sorter/trace_sorter_queue.h"
+
+#include "src/trace_processor/types/variadic.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace trace_sorter_internal {
+
+#if PERFETTO_DCHECK_IS_ON()
+constexpr uint32_t RESERVED_SIZE_BYTES = 8ul;
+#else
+constexpr uint32_t RESERVED_SIZE_BYTES = 0ul;
+#endif
+
+using ::testing::_;
+
+TEST(VariadicQueueUnittest, AddAndEvict) {
+  VariadicQueue queue =
+      VariadicQueue::VariadicQueueForTesting(8 + RESERVED_SIZE_BYTES);
+  auto offset = queue.Append<int64_t>(10);
+  int64_t evicted_val = queue.Evict<int64_t>(offset);
+  ASSERT_EQ(evicted_val, 10l);
+}
+
+TEST(VariadicQueueUnittest, AddAndEvictFirstElement) {
+  VariadicQueue queue =
+      VariadicQueue::VariadicQueueForTesting(8 + RESERVED_SIZE_BYTES);
+  auto offset1 = queue.Append<int64_t>(10);
+  auto offset2 = queue.Append<int64_t>(20);
+  ASSERT_EQ(queue.Evict<int64_t>(offset1), 10);
+  ASSERT_EQ(queue.Evict<int64_t>(offset2), 20);
+}
+
+TEST(VariadicQueueUnittest, AppendAfterEviction) {
+  VariadicQueue queue =
+      VariadicQueue::VariadicQueueForTesting(8 + RESERVED_SIZE_BYTES);
+  auto offset = queue.Append<int64_t>(10);
+  ASSERT_EQ(queue.Evict<int64_t>(offset), 10);
+  offset = queue.Append<int64_t>(20);
+  ASSERT_EQ(queue.Evict<int64_t>(offset), 20);
+}
+
+TEST(VariadicQueueUnittest, FreeAllMemory) {
+  VariadicQueue queue =
+      VariadicQueue::VariadicQueueForTesting(8 + RESERVED_SIZE_BYTES);
+  auto offset1 = queue.Append<int64_t>(10);
+  auto offset2 = queue.Append<int64_t>(20);
+  ASSERT_EQ(queue.Evict<int64_t>(offset1), 10);
+  ASSERT_EQ(queue.Evict<int64_t>(offset2), 20);
+  queue.FreeMemory();
+}
+
+TEST(VariadicQueueUnittest, FreeMemoryPartially) {
+  VariadicQueue queue =
+      VariadicQueue::VariadicQueueForTesting(8 + RESERVED_SIZE_BYTES);
+  uint32_t offset1 = queue.Append<int64_t>(10);
+  uint32_t offset2 = queue.Append<int64_t>(20);
+  ASSERT_EQ(queue.Evict<int64_t>(offset1), 10);
+  queue.FreeMemory();
+  ASSERT_EQ(queue.Evict<int64_t>(offset2), 20);
+  queue.FreeMemory();
+}
+
+TEST(VariadicQueueUnittest, AppendDifferentSizes) {
+  VariadicQueue queue =
+      VariadicQueue::VariadicQueueForTesting(8 + RESERVED_SIZE_BYTES);
+  uint32_t offset_long_long = queue.Append<int64_t>(10);
+  uint32_t offset_int = queue.Append<int32_t>(20);
+  uint32_t offset_short = queue.Append<int16_t>(30);
+  uint32_t offset_char = queue.Append<char>('s');
+  ASSERT_EQ(queue.Evict<int64_t>(offset_long_long), 10l);
+  ASSERT_EQ(queue.Evict<int32_t>(offset_int), 20);
+  ASSERT_EQ(queue.Evict<int16_t>(offset_short), static_cast<int16_t>(30));
+  ASSERT_EQ(queue.Evict<char>(offset_char), 's');
+}
+
+}  // namespace trace_sorter_internal
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/sorter/trace_sorter_unittest.cc b/src/trace_processor/sorter/trace_sorter_unittest.cc
new file mode 100644
index 0000000..53a1ebe
--- /dev/null
+++ b/src/trace_processor/sorter/trace_sorter_unittest.cc
@@ -0,0 +1,317 @@
+/*
+ * 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/proto/proto_trace_parser.h"
+
+#include <map>
+#include <random>
+#include <vector>
+
+#include "perfetto/trace_processor/basic_types.h"
+#include "perfetto/trace_processor/trace_blob.h"
+#include "src/trace_processor/importers/common/parser_types.h"
+#include "src/trace_processor/importers/proto/packet_sequence_state.h"
+#include "src/trace_processor/sorter/trace_sorter.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::MockFunction;
+using ::testing::NiceMock;
+
+class MockTraceParser : public ProtoTraceParser {
+ public:
+  MockTraceParser(TraceProcessorContext* context) : ProtoTraceParser(context) {}
+
+  MOCK_METHOD4(MOCK_ParseFtracePacket,
+               void(uint32_t cpu,
+                    int64_t timestamp,
+                    const uint8_t* data,
+                    size_t length));
+
+  void ParseFtraceEvent(uint32_t cpu,
+                        int64_t timestamp,
+                        TracePacketData data) override {
+    MOCK_ParseFtracePacket(cpu, timestamp, data.packet.data(),
+                           data.packet.length());
+  }
+
+  MOCK_METHOD3(MOCK_ParseTracePacket,
+               void(int64_t ts, const uint8_t* data, size_t length));
+
+  void ParseTrackEvent(int64_t, TrackEventData) override {}
+
+  void ParseTracePacket(int64_t ts, TracePacketData data) override {
+    TraceBlobView& tbv = data.packet;
+    MOCK_ParseTracePacket(ts, tbv.data(), tbv.length());
+  }
+};
+
+class MockTraceStorage : public TraceStorage {
+ public:
+  MockTraceStorage() : TraceStorage() {}
+
+  MOCK_METHOD1(InternString, StringId(base::StringView view));
+};
+
+class TraceSorterTest : public ::testing::Test {
+ public:
+  TraceSorterTest() : test_buffer_(TraceBlob::Allocate(8)) {
+    storage_ = new NiceMock<MockTraceStorage>();
+    context_.storage.reset(storage_);
+    CreateSorter();
+  }
+
+  void CreateSorter(bool full_sort = true) {
+    std::unique_ptr<MockTraceParser> parser(new MockTraceParser(&context_));
+    parser_ = parser.get();
+    auto sorting_mode = full_sort ? TraceSorter::SortingMode::kFullSort
+                                  : TraceSorter::SortingMode::kDefault;
+    context_.sorter.reset(
+        new TraceSorter(&context_, std::move(parser), sorting_mode));
+  }
+
+ protected:
+  TraceProcessorContext context_;
+  MockTraceParser* parser_;
+  NiceMock<MockTraceStorage>* storage_;
+  TraceBlobView test_buffer_;
+};
+
+TEST_F(TraceSorterTest, TestFtrace) {
+  PacketSequenceState state(&context_);
+  TraceBlobView view = test_buffer_.slice_off(0, 1);
+  EXPECT_CALL(*parser_, MOCK_ParseFtracePacket(0, 1000, view.data(), 1));
+  context_.sorter->PushFtraceEvent(0 /*cpu*/, 1000 /*timestamp*/,
+                                   std::move(view), state.current_generation());
+  context_.sorter->ExtractEventsForced();
+}
+
+TEST_F(TraceSorterTest, TestTracePacket) {
+  PacketSequenceState state(&context_);
+  TraceBlobView view = test_buffer_.slice_off(0, 1);
+  EXPECT_CALL(*parser_, MOCK_ParseTracePacket(1000, view.data(), 1));
+  context_.sorter->PushTracePacket(1000, state.current_generation(),
+                                   std::move(view));
+  context_.sorter->ExtractEventsForced();
+}
+
+TEST_F(TraceSorterTest, Ordering) {
+  PacketSequenceState state(&context_);
+  TraceBlobView view_1 = test_buffer_.slice_off(0, 1);
+  TraceBlobView view_2 = test_buffer_.slice_off(0, 2);
+  TraceBlobView view_3 = test_buffer_.slice_off(0, 3);
+  TraceBlobView view_4 = test_buffer_.slice_off(0, 4);
+
+  InSequence s;
+
+  EXPECT_CALL(*parser_, MOCK_ParseFtracePacket(0, 1000, view_1.data(), 1));
+  EXPECT_CALL(*parser_, MOCK_ParseTracePacket(1001, view_2.data(), 2));
+  EXPECT_CALL(*parser_, MOCK_ParseTracePacket(1100, view_3.data(), 3));
+  EXPECT_CALL(*parser_, MOCK_ParseFtracePacket(2, 1200, view_4.data(), 4));
+
+  context_.sorter->PushFtraceEvent(2 /*cpu*/, 1200 /*timestamp*/,
+                                   std::move(view_4),
+                                   state.current_generation());
+  context_.sorter->PushTracePacket(1001, state.current_generation(),
+                                   std::move(view_2));
+  context_.sorter->PushTracePacket(1100, state.current_generation(),
+                                   std::move(view_3));
+  context_.sorter->PushFtraceEvent(0 /*cpu*/, 1000 /*timestamp*/,
+                                   std::move(view_1),
+                                   state.current_generation());
+  context_.sorter->ExtractEventsForced();
+}
+
+TEST_F(TraceSorterTest, IncrementalExtraction) {
+  CreateSorter(false);
+
+  PacketSequenceState state(&context_);
+
+  TraceBlobView view_1 = test_buffer_.slice_off(0, 1);
+  TraceBlobView view_2 = test_buffer_.slice_off(0, 2);
+  TraceBlobView view_3 = test_buffer_.slice_off(0, 3);
+  TraceBlobView view_4 = test_buffer_.slice_off(0, 4);
+  TraceBlobView view_5 = test_buffer_.slice_off(0, 5);
+
+  // Flush at the start of packet sequence to match behavior of the
+  // service.
+  context_.sorter->NotifyFlushEvent();
+  context_.sorter->PushTracePacket(1200, state.current_generation(),
+                                   std::move(view_2));
+  context_.sorter->PushTracePacket(1100, state.current_generation(),
+                                   std::move(view_1));
+
+  // No data should be exttracted at this point because we haven't
+  // seen two flushes yet.
+  context_.sorter->NotifyReadBufferEvent();
+
+  // Now that we've seen two flushes, we should be ready to start extracting
+  // data on the next OnReadBufer call (after two flushes as usual).
+  context_.sorter->NotifyFlushEvent();
+  context_.sorter->NotifyReadBufferEvent();
+
+  context_.sorter->NotifyFlushEvent();
+  context_.sorter->NotifyFlushEvent();
+  context_.sorter->PushTracePacket(1400, state.current_generation(),
+                                   std::move(view_4));
+  context_.sorter->PushTracePacket(1300, state.current_generation(),
+                                   std::move(view_3));
+
+  // This ReadBuffer call should finally extract until the first OnReadBuffer
+  // call.
+  {
+    InSequence s;
+    EXPECT_CALL(*parser_, MOCK_ParseTracePacket(1100, test_buffer_.data(), 1));
+    EXPECT_CALL(*parser_, MOCK_ParseTracePacket(1200, test_buffer_.data(), 2));
+  }
+  context_.sorter->NotifyReadBufferEvent();
+
+  context_.sorter->NotifyFlushEvent();
+  context_.sorter->PushTracePacket(1500, state.current_generation(),
+                                   std::move(view_5));
+
+  // Nothing should be extracted as we haven't seen the second flush.
+  context_.sorter->NotifyReadBufferEvent();
+
+  // Now we've seen the second flush we should extract the next two packets.
+  context_.sorter->NotifyFlushEvent();
+  {
+    InSequence s;
+    EXPECT_CALL(*parser_, MOCK_ParseTracePacket(1300, test_buffer_.data(), 3));
+    EXPECT_CALL(*parser_, MOCK_ParseTracePacket(1400, test_buffer_.data(), 4));
+  }
+  context_.sorter->NotifyReadBufferEvent();
+
+  // The forced extraction should get the last packet.
+  EXPECT_CALL(*parser_, MOCK_ParseTracePacket(1500, test_buffer_.data(), 5));
+  context_.sorter->ExtractEventsForced();
+}
+
+// Simulate a producer bug where the third packet is emitted
+// out of order. Verify that we track the stats correctly.
+TEST_F(TraceSorterTest, OutOfOrder) {
+  CreateSorter(false);
+
+  PacketSequenceState state(&context_);
+
+  TraceBlobView view_1 = test_buffer_.slice_off(0, 1);
+  TraceBlobView view_2 = test_buffer_.slice_off(0, 2);
+  TraceBlobView view_3 = test_buffer_.slice_off(0, 3);
+  TraceBlobView view_4 = test_buffer_.slice_off(0, 4);
+
+  context_.sorter->NotifyFlushEvent();
+  context_.sorter->NotifyFlushEvent();
+  context_.sorter->PushTracePacket(1200, state.current_generation(),
+                                   std::move(view_2));
+  context_.sorter->PushTracePacket(1100, state.current_generation(),
+                                   std::move(view_1));
+  context_.sorter->NotifyReadBufferEvent();
+
+  // Both of the packets should have been pushed through.
+  context_.sorter->NotifyFlushEvent();
+  context_.sorter->NotifyFlushEvent();
+  {
+    InSequence s;
+    EXPECT_CALL(*parser_, MOCK_ParseTracePacket(1100, test_buffer_.data(), 1));
+    EXPECT_CALL(*parser_, MOCK_ParseTracePacket(1200, test_buffer_.data(), 2));
+  }
+  context_.sorter->NotifyReadBufferEvent();
+
+  // Now, pass the third packet out of order.
+  context_.sorter->NotifyFlushEvent();
+  context_.sorter->NotifyFlushEvent();
+  context_.sorter->PushTracePacket(1150, state.current_generation(),
+                                   std::move(view_3));
+  context_.sorter->NotifyReadBufferEvent();
+
+  // The third packet should still be pushed through.
+  context_.sorter->NotifyFlushEvent();
+  context_.sorter->NotifyFlushEvent();
+  EXPECT_CALL(*parser_, MOCK_ParseTracePacket(1150, test_buffer_.data(), 3));
+  context_.sorter->NotifyReadBufferEvent();
+
+  // But we should also increment the stat that this was out of order.
+  ASSERT_EQ(
+      context_.storage->stats()[stats::sorter_push_event_out_of_order].value,
+      1);
+
+  // Push the fourth packet also out of order but after third.
+  context_.sorter->NotifyFlushEvent();
+  context_.sorter->NotifyFlushEvent();
+  context_.sorter->PushTracePacket(1170, state.current_generation(),
+                                   std::move(view_4));
+  context_.sorter->NotifyReadBufferEvent();
+
+  // The fourt packet should still be pushed through.
+  EXPECT_CALL(*parser_, MOCK_ParseTracePacket(1170, test_buffer_.data(), 4));
+  context_.sorter->ExtractEventsForced();
+
+  // But we should also increment the stat that this was out of order.
+  ASSERT_EQ(
+      context_.storage->stats()[stats::sorter_push_event_out_of_order].value,
+      2);
+}
+
+// Simulates a random stream of ftrace events happening on random CPUs.
+// Tests that the output of the TraceSorter matches the timestamp order
+// (% events happening at the same time on different CPUs).
+TEST_F(TraceSorterTest, MultiQueueSorting) {
+  PacketSequenceState state(&context_);
+  std::minstd_rand0 rnd_engine(0);
+  std::map<int64_t /*ts*/, std::vector<uint32_t /*cpu*/>> expectations;
+
+  EXPECT_CALL(*parser_, MOCK_ParseFtracePacket(_, _, _, _))
+      .WillRepeatedly(Invoke([&expectations](uint32_t cpu, int64_t timestamp,
+                                             const uint8_t*, size_t) {
+        EXPECT_EQ(expectations.begin()->first, timestamp);
+        auto& cpus = expectations.begin()->second;
+        bool cpu_found = false;
+        for (auto it = cpus.begin(); it < cpus.end(); it++) {
+          if (*it != cpu)
+            continue;
+          cpu_found = true;
+          cpus.erase(it);
+          break;
+        }
+        if (cpus.empty())
+          expectations.erase(expectations.begin());
+        EXPECT_TRUE(cpu_found);
+      }));
+
+  for (int i = 0; i < 1000; i++) {
+    int64_t ts = abs(static_cast<int64_t>(rnd_engine()));
+    int num_cpus = rnd_engine() % 3;
+    for (int j = 0; j < num_cpus; j++) {
+      uint32_t cpu = static_cast<uint32_t>(rnd_engine() % 32);
+      expectations[ts].push_back(cpu);
+      context_.sorter->PushFtraceEvent(cpu, ts, TraceBlobView(),
+                                       state.current_generation());
+    }
+  }
+
+  context_.sorter->ExtractEventsForced();
+  EXPECT_TRUE(expectations.empty());
+}
+
+}  // namespace
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/sqlite/BUILD.gn b/src/trace_processor/sqlite/BUILD.gn
index 50dce02..976db0f 100644
--- a/src/trace_processor/sqlite/BUILD.gn
+++ b/src/trace_processor/sqlite/BUILD.gn
@@ -14,99 +14,86 @@
 
 import("../../../gn/test.gni")
 
-if (enable_perfetto_trace_processor_sqlite) {
-  source_set("sqlite") {
-    sources = [
-      "create_function.cc",
-      "create_function.h",
-      "create_function_internal.cc",
-      "create_function_internal.h",
-      "create_view_function.cc",
-      "create_view_function.h",
-      "db_sqlite_table.cc",
-      "db_sqlite_table.h",
-      "query_cache.h",
-      "register_function.cc",
-      "register_function.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_utils.h",
-      "stats_table.cc",
-      "stats_table.h",
-      "window_operator_table.cc",
-      "window_operator_table.h",
-    ]
-    deps = [
-      "..:ftrace_descriptors",
-      "..:metatrace",
-      "../../../gn:default_deps",
-      "../../../gn:sqlite",
-      "../../../protos/perfetto/trace/ftrace:zero",
-      "../../base",
-      "../containers",
-      "../db",
-      "../dynamic",
-      "../importers/common",
-      "../storage",
-      "../types",
-      "../util",
-    ]
-    public_deps = [ ":sqlite_minimal" ]
-  }
+assert(enable_perfetto_trace_processor_sqlite)
 
-  source_set("sqlite_minimal") {
-    sources = [
-      "query_constraints.cc",
-      "query_constraints.h",
-      "scoped_db.h",
-      "sqlite_table.cc",
-      "sqlite_table.h",
-      "sqlite_utils.h",
-    ]
-    deps = [
-      "../../../gn:default_deps",
-      "../../../gn:sqlite",
-      "../../../include/perfetto/trace_processor",
-      "../../base",
-    ]
-  }
+source_set("sqlite") {
+  sources = [
+    "db_sqlite_table.cc",
+    "db_sqlite_table.h",
+    "query_cache.h",
+    "sql_stats_table.cc",
+    "sql_stats_table.h",
+    "sqlite_raw_table.cc",
+    "sqlite_raw_table.h",
+    "sqlite_utils.cc",
+    "sqlite_utils.h",
+    "stats_table.cc",
+    "stats_table.h",
+  ]
+  deps = [
+    "..:metatrace",
+    "../../../gn:default_deps",
+    "../../../gn:sqlite",
+    "../../../protos/perfetto/trace/ftrace:zero",
+    "../../base",
+    "../containers",
+    "../db",
+    "../dynamic",
+    "../importers/common",
+    "../importers/ftrace:ftrace_descriptors",
+    "../storage",
+    "../types",
+    "../util",
+    "../util:profile_builder",
+  ]
+  public_deps = [ ":sqlite_minimal" ]
+}
 
-  perfetto_unittest_source_set("unittests") {
+source_set("sqlite_minimal") {
+  sources = [
+    "query_constraints.cc",
+    "query_constraints.h",
+    "scoped_db.h",
+    "sqlite_table.cc",
+    "sqlite_table.h",
+    "sqlite_utils.h",
+  ]
+  deps = [
+    "..:metatrace",
+    "../../../gn:default_deps",
+    "../../../gn:sqlite",
+    "../../../include/perfetto/trace_processor",
+    "../../base",
+  ]
+}
+
+perfetto_unittest_source_set("unittests") {
+  testonly = true
+  sources = [
+    "db_sqlite_table_unittest.cc",
+    "query_constraints_unittest.cc",
+    "sqlite_utils_unittest.cc",
+  ]
+  deps = [
+    ":sqlite",
+    ":sqlite_minimal",
+    "../../../gn:default_deps",
+    "../../../gn:gtest_and_gmock",
+    "../../../gn:sqlite",
+    "../../base",
+  ]
+}
+
+if (enable_perfetto_benchmarks) {
+  source_set("benchmarks") {
     testonly = true
-    sources = [
-      "db_sqlite_table_unittest.cc",
-      "query_constraints_unittest.cc",
-      "span_join_operator_table_unittest.cc",
-      "sqlite3_str_split_unittest.cc",
-      "sqlite_utils_unittest.cc",
-    ]
     deps = [
       ":sqlite",
-      ":sqlite_minimal",
+      "../../../gn:benchmark",
       "../../../gn:default_deps",
-      "../../../gn:gtest_and_gmock",
       "../../../gn:sqlite",
       "../../base",
     ]
-  }
-
-  if (enable_perfetto_benchmarks) {
-    source_set("benchmarks") {
-      testonly = true
-      deps = [
-        ":sqlite",
-        "../../../gn:benchmark",
-        "../../../gn:default_deps",
-        "../../../gn:sqlite",
-        "../../base",
-      ]
-      sources = [ "sqlite_vtable_benchmark.cc" ]
-    }
+    sources = [ "sqlite_vtable_benchmark.cc" ]
   }
 }
diff --git a/src/trace_processor/sqlite/create_function.cc b/src/trace_processor/sqlite/create_function.cc
deleted file mode 100644
index eee35e7..0000000
--- a/src/trace_processor/sqlite/create_function.cc
+++ /dev/null
@@ -1,276 +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/create_function.h"
-
-#include "perfetto/base/status.h"
-#include "perfetto/trace_processor/basic_types.h"
-#include "src/trace_processor/sqlite/create_function_internal.h"
-#include "src/trace_processor/sqlite/scoped_db.h"
-#include "src/trace_processor/sqlite/sqlite_utils.h"
-#include "src/trace_processor/tp_metatrace.h"
-#include "src/trace_processor/util/status_macros.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-namespace {
-
-struct CreatedFunction : public SqlFunction {
-  struct Context {
-    sqlite3* db;
-    Prototype prototype;
-    SqlValue::Type return_type;
-    std::string sql;
-    sqlite3_stmt* stmt;
-  };
-
-  static base::Status Run(Context* ctx,
-                          size_t argc,
-                          sqlite3_value** argv,
-                          SqlValue& out,
-                          Destructors&);
-  static base::Status VerifyPostConditions(Context*);
-  static void Cleanup(Context*);
-};
-
-base::Status CreatedFunction::Run(CreatedFunction::Context* ctx,
-                                  size_t argc,
-                                  sqlite3_value** argv,
-                                  SqlValue& out,
-                                  Destructors&) {
-  if (argc != ctx->prototype.arguments.size()) {
-    return base::ErrStatus(
-        "%s: invalid number of args; expected %zu, received %zu",
-        ctx->prototype.function_name.c_str(), ctx->prototype.arguments.size(),
-        argc);
-  }
-
-  // Type check all the arguments.
-  for (size_t i = 0; i < argc; ++i) {
-    sqlite3_value* arg = argv[i];
-    base::Status status =
-        TypeCheckSqliteValue(arg, ctx->prototype.arguments[i].type);
-    if (!status.ok()) {
-      return base::ErrStatus("%s[arg=%s]: argument %zu %s",
-                             ctx->prototype.function_name.c_str(),
-                             sqlite3_value_text(arg), i, status.c_message());
-    }
-  }
-
-  PERFETTO_TP_TRACE("CREATE_FUNCTION", [ctx, argv](metatrace::Record* r) {
-    r->AddArg("Function", ctx->prototype.function_name.c_str());
-    for (uint32_t i = 0; i < ctx->prototype.arguments.size(); ++i) {
-      std::string key = "Arg " + std::to_string(i);
-      const char* value =
-          reinterpret_cast<const char*>(sqlite3_value_text(argv[i]));
-      r->AddArg(base::StringView(key),
-                value ? base::StringView(value) : base::StringView("NULL"));
-    }
-  });
-
-  // Bind all the arguments to the appropriate places in the function.
-  for (size_t i = 0; i < argc; ++i) {
-    RETURN_IF_ERROR(MaybeBindArgument(ctx->stmt, ctx->prototype.function_name,
-                                      ctx->prototype.arguments[i], argv[i]));
-  }
-
-  int ret = sqlite3_step(ctx->stmt);
-  RETURN_IF_ERROR(
-      SqliteRetToStatus(ctx->db, ctx->prototype.function_name, ret));
-  if (ret == SQLITE_DONE) {
-    // No return value means we just return don't set |out|.
-    return base::OkStatus();
-  }
-
-  PERFETTO_DCHECK(ret == SQLITE_ROW);
-  size_t col_count = static_cast<size_t>(sqlite3_column_count(ctx->stmt));
-  if (col_count != 1) {
-    return base::ErrStatus(
-        "%s: SQL definition should only return one column: returned %zu "
-        "columns",
-        ctx->prototype.function_name.c_str(), col_count);
-  }
-
-  out = sqlite_utils::SqliteValueToSqlValue(sqlite3_column_value(ctx->stmt, 0));
-
-  // If we return a bytes type but have a null pointer, SQLite will convert this
-  // to an SQL null. However, for proto build functions, we actively want to
-  // distinguish between nulls and 0 byte strings. Therefore, change the value
-  // to an empty string.
-  if (out.type == SqlValue::kBytes && out.bytes_value == nullptr) {
-    PERFETTO_DCHECK(out.bytes_count == 0);
-    out.bytes_value = "";
-  }
-  return base::OkStatus();
-}
-
-base::Status CreatedFunction::VerifyPostConditions(Context* ctx) {
-  int ret = sqlite3_step(ctx->stmt);
-  RETURN_IF_ERROR(
-      SqliteRetToStatus(ctx->db, ctx->prototype.function_name, ret));
-  if (ret == SQLITE_ROW) {
-    return base::ErrStatus(
-        "%s: multiple values were returned when executing function body. "
-        "Executed SQL was %s",
-        ctx->prototype.function_name.c_str(), sqlite3_expanded_sql(ctx->stmt));
-  }
-  PERFETTO_DCHECK(ret == SQLITE_DONE);
-  return base::OkStatus();
-}
-
-void CreatedFunction::Cleanup(CreatedFunction::Context* ctx) {
-  sqlite3_reset(ctx->stmt);
-  sqlite3_clear_bindings(ctx->stmt);
-}
-
-}  // namespace
-
-size_t CreateFunction::NameAndArgc::Hasher::operator()(
-    const NameAndArgc& s) const noexcept {
-  base::Hash hash;
-  hash.Update(s.name.data(), s.name.size());
-  hash.Update(s.argc);
-  return static_cast<size_t>(hash.digest());
-}
-
-base::Status CreateFunction::Run(CreateFunction::Context* ctx,
-                                 size_t argc,
-                                 sqlite3_value** argv,
-                                 SqlValue&,
-                                 Destructors&) {
-  if (argc != 3) {
-    return base::ErrStatus(
-        "CREATE_FUNCTION: invalid number of args; expected %u, received %zu",
-        3u, argc);
-  }
-
-  sqlite3_value* prototype_value = argv[0];
-  sqlite3_value* return_type_value = argv[1];
-  sqlite3_value* sql_defn_value = argv[2];
-
-  // Type check all the arguments.
-  {
-    auto type_check = [prototype_value](sqlite3_value* value,
-                                        SqlValue::Type type, const char* desc) {
-      base::Status status = TypeCheckSqliteValue(value, type);
-      if (!status.ok()) {
-        return base::ErrStatus("CREATE_FUNCTION[prototype=%s]: %s %s",
-                               sqlite3_value_text(prototype_value), desc,
-                               status.c_message());
-      }
-      return base::OkStatus();
-    };
-
-    RETURN_IF_ERROR(type_check(prototype_value, SqlValue::Type::kString,
-                               "function prototype (first argument)"));
-    RETURN_IF_ERROR(type_check(return_type_value, SqlValue::Type::kString,
-                               "return type (second argument)"));
-    RETURN_IF_ERROR(type_check(sql_defn_value, SqlValue::Type::kString,
-                               "SQL definition (third argument)"));
-  }
-
-  // Extract the arguments from the value wrappers.
-  auto extract_string = [](sqlite3_value* value) -> base::StringView {
-    return reinterpret_cast<const char*>(sqlite3_value_text(value));
-  };
-  base::StringView prototype_str = extract_string(prototype_value);
-  base::StringView return_type_str = extract_string(return_type_value);
-  std::string sql_defn_str = extract_string(sql_defn_value).ToStdString();
-
-  // Parse all the arguments into a more friendly form.
-  Prototype prototype;
-  base::Status status = ParsePrototype(prototype_str, prototype);
-  if (!status.ok()) {
-    return base::ErrStatus("CREATE_FUNCTION[prototype=%s]: %s",
-                           prototype_str.ToStdString().c_str(),
-                           status.c_message());
-  }
-
-  // Parse the return type into a enum format.
-  auto opt_return_type = ParseType(return_type_str);
-  if (!opt_return_type) {
-    return base::ErrStatus(
-        "CREATE_FUNCTION[prototype=%s, return=%s]: unknown return type "
-        "specified",
-        prototype_str.ToStdString().c_str(),
-        return_type_str.ToStdString().c_str());
-  }
-  SqlValue::Type return_type = *opt_return_type;
-
-  int created_argc = static_cast<int>(prototype.arguments.size());
-  NameAndArgc key{prototype.function_name, created_argc};
-  auto it = ctx->state->find(key);
-  if (it != ctx->state->end()) {
-    // If the function already exists, just verify that the prototype, return
-    // type and SQL matches exactly with what we already had registered. By
-    // doing this, we can avoid the problem plaguing C++ macros where macro
-    // ordering determines which one gets run.
-    auto* created_ctx = static_cast<CreatedFunction::Context*>(
-        it->second.created_functon_context);
-
-    if (created_ctx->prototype != prototype) {
-      return base::ErrStatus(
-          "CREATE_FUNCTION[prototype=%s]: function prototype changed",
-          prototype_str.ToStdString().c_str());
-    }
-
-    if (created_ctx->return_type != return_type) {
-      return base::ErrStatus(
-          "CREATE_FUNCTION[prototype=%s]: return type changed from %s to %s",
-          prototype_str.ToStdString().c_str(),
-          SqliteTypeToFriendlyString(created_ctx->return_type),
-          return_type_str.ToStdString().c_str());
-    }
-
-    if (created_ctx->sql != sql_defn_str) {
-      return base::ErrStatus(
-          "CREATE_FUNCTION[prototype=%s]: function SQL changed from %s to %s",
-          prototype_str.ToStdString().c_str(), created_ctx->sql.c_str(),
-          sql_defn_str.c_str());
-    }
-    return base::OkStatus();
-  }
-
-  // Prepare the SQL definition as a statement using SQLite.
-  ScopedStmt stmt;
-  sqlite3_stmt* stmt_raw = nullptr;
-  int ret = sqlite3_prepare_v2(ctx->db, sql_defn_str.data(),
-                               static_cast<int>(sql_defn_str.size()), &stmt_raw,
-                               nullptr);
-  if (ret != SQLITE_OK) {
-    return base::ErrStatus(
-        "CREATE_FUNCTION[prototype=%s]: SQLite error when preparing "
-        "statement "
-        "%s",
-        prototype_str.ToStdString().c_str(), sqlite3_errmsg(ctx->db));
-  }
-  stmt.reset(stmt_raw);
-
-  std::unique_ptr<CreatedFunction::Context> created(
-      new CreatedFunction::Context{ctx->db, std::move(prototype), return_type,
-                                   std::move(sql_defn_str), stmt.get()});
-  CreatedFunction::Context* created_ptr = created.get();
-  RETURN_IF_ERROR(RegisterSqlFunction<CreatedFunction>(
-      ctx->db, key.name.c_str(), created_argc, std::move(created)));
-  ctx->state->emplace(key, PerFunctionState{std::move(stmt), created_ptr});
-
-  // CREATE_FUNCTION doesn't have a return value so just don't sent |out|.
-  return base::OkStatus();
-}
-
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/sqlite/create_function.h b/src/trace_processor/sqlite/create_function.h
deleted file mode 100644
index c6778c2..0000000
--- a/src/trace_processor/sqlite/create_function.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2021 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_CREATE_FUNCTION_H_
-#define SRC_TRACE_PROCESSOR_SQLITE_CREATE_FUNCTION_H_
-
-#include <sqlite3.h>
-#include <unordered_map>
-
-#include "src/trace_processor/sqlite/register_function.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-// Implementation of CREATE_FUNCTION SQL function.
-// See https://perfetto.dev/docs/analysis/metrics#metric-helper-functions for
-// usage of this function.
-struct CreateFunction : public SqlFunction {
-  struct PerFunctionState {
-    ScopedStmt stmt;
-    // void* to avoid leaking state.
-    void* created_functon_context;
-  };
-  struct NameAndArgc {
-    std::string name;
-    int argc;
-
-    struct Hasher {
-      std::size_t operator()(const NameAndArgc& s) const noexcept;
-    };
-    bool operator==(const NameAndArgc& other) const {
-      return name == other.name && argc == other.argc;
-    }
-  };
-  using State = std::unordered_map<NameAndArgc,
-                                   CreateFunction::PerFunctionState,
-                                   NameAndArgc::Hasher>;
-
-  struct Context {
-    sqlite3* db;
-    State* state;
-  };
-
-  static base::Status Run(Context* ctx,
-                          size_t argc,
-                          sqlite3_value** argv,
-                          SqlValue& out,
-                          Destructors&);
-};
-
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_SQLITE_CREATE_FUNCTION_H_
diff --git a/src/trace_processor/sqlite/create_function_internal.cc b/src/trace_processor/sqlite/create_function_internal.cc
deleted file mode 100644
index 5748931..0000000
--- a/src/trace_processor/sqlite/create_function_internal.cc
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2021 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/create_function_internal.h"
-
-#include "perfetto/base/status.h"
-#include "perfetto/ext/base/string_view.h"
-#include "src/trace_processor/sqlite/sqlite_utils.h"
-#include "src/trace_processor/util/status_macros.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-bool IsValidName(base::StringView str) {
-  auto pred = [](char c) { return !(isalnum(c) || c == '_'); };
-  return std::find_if(str.begin(), str.end(), pred) == str.end();
-}
-
-base::Optional<SqlValue::Type> ParseType(base::StringView str) {
-  if (str == "INT" || str == "LONG" || str == "BOOL") {
-    return SqlValue::Type::kLong;
-  } else if (str == "DOUBLE" || str == "FLOAT") {
-    return SqlValue::Type::kDouble;
-  } else if (str == "STRING") {
-    return SqlValue::Type::kString;
-  } else if (str == "PROTO" || str == "BYTES") {
-    return SqlValue::Type::kBytes;
-  }
-  return base::nullopt;
-}
-
-const char* SqliteTypeToFriendlyString(SqlValue::Type type) {
-  switch (type) {
-    case SqlValue::Type::kNull:
-      return "NULL";
-    case SqlValue::Type::kLong:
-      return "INT/LONG/BOOL";
-    case SqlValue::Type::kDouble:
-      return "FLOAT/DOUBLE";
-    case SqlValue::Type::kString:
-      return "STRING";
-    case SqlValue::Type::kBytes:
-      return "BYTES/PROTO";
-  }
-  PERFETTO_FATAL("For GCC");
-}
-
-base::Status TypeCheckSqliteValue(sqlite3_value* value,
-                                  SqlValue::Type expected_type) {
-  SqlValue::Type actual_type =
-      sqlite_utils::SqliteTypeToSqlValueType(sqlite3_value_type(value));
-  if (actual_type != SqlValue::Type::kNull && actual_type != expected_type) {
-    return base::ErrStatus(
-        "does not have expected type: expected %s, actual %s",
-        SqliteTypeToFriendlyString(expected_type),
-        SqliteTypeToFriendlyString(actual_type));
-  }
-  return base::OkStatus();
-}
-
-base::Status ParseFunctionName(base::StringView raw, base::StringView& out) {
-  size_t function_name_end = raw.find('(');
-  if (function_name_end == base::StringView::npos)
-    return base::ErrStatus("unable to find bracket starting argument list");
-
-  base::StringView function_name = raw.substr(0, function_name_end);
-  if (!IsValidName(function_name)) {
-    return base::ErrStatus("function name %s is not alphanumeric",
-                           function_name.ToStdString().c_str());
-  }
-  out = function_name;
-  return base::OkStatus();
-}
-
-base::Status ParseArgs(std::string args,
-                       std::vector<Prototype::Argument>& out) {
-  for (const auto& arg : base::SplitString(args, ",")) {
-    const auto& arg_name_and_type = base::SplitString(arg, " ");
-    if (arg_name_and_type.size() != 2) {
-      return base::ErrStatus(
-          "argument %s in function prototype should be of the form `name type`",
-          arg.c_str());
-    }
-
-    const auto& arg_name = arg_name_and_type[0];
-    const auto& arg_type_str = arg_name_and_type[1];
-    if (!IsValidName(base::StringView(arg_name)))
-      return base::ErrStatus("argument %s is not alphanumeric", arg.c_str());
-
-    auto opt_arg_type = ParseType(base::StringView(arg_type_str));
-    if (!opt_arg_type)
-      return base::ErrStatus("unknown argument type in argument %s",
-                             arg.c_str());
-
-    SqlValue::Type arg_type = *opt_arg_type;
-    PERFETTO_DCHECK(arg_type != SqlValue::Type::kNull);
-    out.push_back({arg_name, "$" + arg_name, arg_type});
-  }
-  return base::OkStatus();
-}
-
-base::Status ParsePrototype(base::StringView raw, Prototype& out) {
-  // Examples of function prototypes:
-  // ANDROID_SDK_LEVEL()
-  // STARTUP_SLICE(dur_ns INT)
-  // FIND_NEXT_SLICE_WITH_NAME(ts INT, name STRING)
-
-  base::StringView function_name;
-  RETURN_IF_ERROR(ParseFunctionName(raw, function_name));
-
-  size_t function_name_end = function_name.size();
-  size_t args_start = function_name_end + 1;
-  size_t args_end = raw.find(')', args_start);
-  if (args_end == base::StringView::npos)
-    return base::ErrStatus("unable to find bracket ending argument list");
-
-  base::StringView args_str = raw.substr(args_start, args_end - args_start);
-  RETURN_IF_ERROR(ParseArgs(args_str.ToStdString(), out.arguments));
-
-  out.function_name = function_name.ToStdString();
-  return base::OkStatus();
-}
-
-base::Status SqliteRetToStatus(sqlite3* db,
-                               const std::string& function_name,
-                               int ret) {
-  if (ret != SQLITE_ROW && ret != SQLITE_DONE) {
-    return base::ErrStatus("%s: SQLite error while executing function body: %s",
-                           function_name.c_str(), sqlite3_errmsg(db));
-  }
-  return base::OkStatus();
-}
-
-base::Status MaybeBindArgument(sqlite3_stmt* stmt,
-                               const std::string& function_name,
-                               const Prototype::Argument& arg,
-                               sqlite3_value* value) {
-  int index = sqlite3_bind_parameter_index(stmt, arg.dollar_name.c_str());
-
-  // If the argument is not in the query, this just means its an unused
-  // argument which we can just ignore.
-  if (index == 0)
-    return base::Status();
-
-  int ret = sqlite3_bind_value(stmt, index, value);
-  if (ret != SQLITE_OK) {
-    return base::ErrStatus(
-        "%s: SQLite error while binding value to argument %s: %s",
-        function_name.c_str(), arg.name.c_str(),
-        sqlite3_errmsg(sqlite3_db_handle(stmt)));
-  }
-  return base::OkStatus();
-}
-
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/sqlite/create_function_internal.h b/src/trace_processor/sqlite/create_function_internal.h
deleted file mode 100644
index 7150ade..0000000
--- a/src/trace_processor/sqlite/create_function_internal.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2021 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_CREATE_FUNCTION_INTERNAL_H_
-#define SRC_TRACE_PROCESSOR_SQLITE_CREATE_FUNCTION_INTERNAL_H_
-
-#include <sqlite3.h>
-#include <string>
-
-#include "perfetto/base/status.h"
-#include "perfetto/ext/base/optional.h"
-#include "perfetto/ext/base/string_view.h"
-#include "perfetto/trace_processor/basic_types.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-bool IsValidName(base::StringView str);
-
-base::Optional<SqlValue::Type> ParseType(base::StringView str);
-
-const char* SqliteTypeToFriendlyString(SqlValue::Type type);
-
-base::Status TypeCheckSqliteValue(sqlite3_value* value,
-                                  SqlValue::Type expected_type);
-
-struct Prototype {
-  struct Argument {
-    std::string name;
-    std::string dollar_name;
-    SqlValue::Type type;
-
-    bool operator==(const Argument& other) const {
-      return name == other.name && dollar_name == other.dollar_name &&
-             type == other.type;
-    }
-  };
-  std::string function_name;
-  std::vector<Argument> arguments;
-
-  bool operator==(const Prototype& other) const {
-    return function_name == other.function_name && arguments == other.arguments;
-  }
-  bool operator!=(const Prototype& other) const { return !(*this == other); }
-};
-
-base::Status ParseFunctionName(base::StringView raw,
-                               base::StringView& function_name);
-
-base::Status ParsePrototype(base::StringView raw, Prototype& out);
-
-base::Status ParseArgs(std::string args, std::vector<Prototype::Argument>&);
-
-base::Status SqliteRetToStatus(sqlite3* db,
-                               const std::string& function_name,
-                               int ret);
-
-base::Status MaybeBindArgument(sqlite3_stmt*,
-                               const std::string& function_name,
-                               const Prototype::Argument&,
-                               sqlite3_value*);
-
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_SQLITE_CREATE_FUNCTION_INTERNAL_H_
diff --git a/src/trace_processor/sqlite/create_view_function.cc b/src/trace_processor/sqlite/create_view_function.cc
deleted file mode 100644
index 06367e4..0000000
--- a/src/trace_processor/sqlite/create_view_function.cc
+++ /dev/null
@@ -1,447 +0,0 @@
-/*
- * Copyright (C) 2021 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/create_view_function.h"
-
-#include <numeric>
-
-#include "perfetto/base/status.h"
-#include "perfetto/ext/base/string_utils.h"
-#include "perfetto/ext/base/string_view.h"
-#include "perfetto/trace_processor/basic_types.h"
-#include "src/trace_processor/sqlite/create_function_internal.h"
-#include "src/trace_processor/sqlite/scoped_db.h"
-#include "src/trace_processor/sqlite/sqlite_table.h"
-#include "src/trace_processor/sqlite/sqlite_utils.h"
-#include "src/trace_processor/tp_metatrace.h"
-#include "src/trace_processor/util/status_macros.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-namespace {
-
-class CreatedViewFunction : public SqliteTable {
- public:
-  class Cursor : public SqliteTable::Cursor {
-   public:
-    explicit Cursor(CreatedViewFunction* table);
-    ~Cursor() override;
-
-    int Filter(const QueryConstraints& qc,
-               sqlite3_value**,
-               FilterHistory) override;
-    int Next() override;
-    int Eof() override;
-    int Column(sqlite3_context* context, int N) override;
-
-   private:
-    ScopedStmt scoped_stmt_;
-    sqlite3_stmt* stmt_ = nullptr;
-    CreatedViewFunction* table_ = nullptr;
-    bool is_eof_ = false;
-  };
-
-  CreatedViewFunction(sqlite3*, void*);
-  ~CreatedViewFunction() override;
-
-  base::Status Init(int argc, const char* const* argv, Schema*) override;
-  std::unique_ptr<SqliteTable::Cursor> CreateCursor() override;
-  int BestIndex(const QueryConstraints& qc, BestIndexInfo* info) override;
-
-  static void Register(sqlite3* db) {
-    SqliteTable::Register<CreatedViewFunction>(
-        db, nullptr, "internal_view_function_impl", false, true);
-  }
-
- private:
-  Schema CreateSchema();
-
-  sqlite3* db_ = nullptr;
-
-  Prototype prototype_;
-  std::vector<Prototype::Argument> return_values_;
-
-  std::string prototype_str_;
-  std::string sql_defn_str_;
-};
-
-CreatedViewFunction::CreatedViewFunction(sqlite3* db, void*) : db_(db) {}
-CreatedViewFunction::~CreatedViewFunction() = default;
-
-base::Status CreatedViewFunction::Init(int argc,
-                                       const char* const* argv,
-                                       Schema* schema) {
-  // The first three args are SQLite ones which we ignore.
-  PERFETTO_CHECK(argc == 6);
-
-  prototype_str_ = argv[3];
-  std::string return_prototype_str = argv[4];
-  sql_defn_str_ = argv[5];
-
-  // SQLite gives us strings with quotes included (i.e. 'string'). Strip these
-  // from the front and back.
-  prototype_str_ = prototype_str_.substr(1, prototype_str_.size() - 2);
-  return_prototype_str =
-      return_prototype_str.substr(1, return_prototype_str.size() - 2);
-  sql_defn_str_ = sql_defn_str_.substr(1, sql_defn_str_.size() - 2);
-
-  // Parse all the arguments into a more friendly form.
-  base::Status status =
-      ParsePrototype(base::StringView(prototype_str_), prototype_);
-  if (!status.ok()) {
-    return base::ErrStatus("CREATE_VIEW_FUNCTION[prototype=%s]: %s",
-                           prototype_str_.c_str(), status.c_message());
-  }
-
-  // Parse the return type into a enum format.
-  status = ParseArgs(return_prototype_str, return_values_);
-  if (!status.ok()) {
-    return base::ErrStatus(
-        "CREATE_VIEW_FUNCTION[prototype=%s, return=%s]: unknown return type "
-        "specified",
-        prototype_str_.c_str(), return_prototype_str.c_str());
-  }
-
-  // Verify that the provided SQL prepares to a statement correctly.
-  ScopedStmt stmt;
-  sqlite3_stmt* raw_stmt = nullptr;
-  int ret = sqlite3_prepare_v2(db_, sql_defn_str_.data(),
-                               static_cast<int>(sql_defn_str_.size()),
-                               &raw_stmt, nullptr);
-  stmt.reset(raw_stmt);
-  if (ret != SQLITE_OK) {
-    return base::ErrStatus(
-        "%s: Failed to prepare SQL statement for function. "
-        "Check the SQL defintion this function for syntax errors. "
-        "(SQLite error: %s).",
-        prototype_.function_name.c_str(), sqlite3_errmsg(db_));
-  }
-
-  // Verify that every argument name in the function appears in the
-  // argument list.
-  //
-  // We intentionally loop from 1 to |used_param_count| because SQL
-  // parameters are 1-indexed *not* 0-indexed.
-  int used_param_count = sqlite3_bind_parameter_count(stmt.get());
-  for (int i = 1; i <= used_param_count; ++i) {
-    const char* name = sqlite3_bind_parameter_name(stmt.get(), i);
-
-    if (!name) {
-      return base::ErrStatus(
-          "%s: \"Nameless\" SQL parameters cannot be used in the SQL "
-          "statements of view functions.",
-          prototype_.function_name.c_str());
-    }
-
-    if (!base::StringView(name).StartsWith("$")) {
-      return base::ErrStatus(
-          "%s: invalid parameter name %s used in the SQL definition of "
-          "the view function: all parameters must be prefixed with '$' not ':' "
-          "or '@'.",
-          prototype_.function_name.c_str(), name);
-    }
-
-    auto it =
-        std::find_if(prototype_.arguments.begin(), prototype_.arguments.end(),
-                     [name](const Prototype::Argument& arg) {
-                       return arg.dollar_name == name;
-                     });
-    if (it == prototype_.arguments.end()) {
-      return base::ErrStatus(
-          "%s: parameter %s does not appear in the list of arguments in the "
-          "prototype of the view function.",
-          prototype_.function_name.c_str(), name);
-    }
-  }
-
-  // Verify that the prepared statement column count matches the return
-  // count.
-  uint32_t col_count = static_cast<uint32_t>(sqlite3_column_count(stmt.get()));
-  if (col_count != return_values_.size()) {
-    return base::ErrStatus(
-        "%s: number of return values %u does not match SQL statement column "
-        "count %zu.",
-        prototype_.function_name.c_str(), col_count, return_values_.size());
-  }
-
-  // Verify that the return names matches the prepared statment column names.
-  for (uint32_t i = 0; i < col_count; ++i) {
-    const char* name = sqlite3_column_name(stmt.get(), static_cast<int>(i));
-    if (name != return_values_[i].name) {
-      return base::ErrStatus(
-          "%s: column %s at index %u does not match return value name %s.",
-          prototype_.function_name.c_str(), name, i,
-          return_values_[i].name.c_str());
-    }
-  }
-
-  // Now we've parsed prototype and return values, create the schema.
-  *schema = CreateSchema();
-
-  return base::OkStatus();
-}
-
-SqliteTable::Schema CreatedViewFunction::CreateSchema() {
-  std::vector<Column> columns;
-  for (size_t i = 0; i < return_values_.size(); ++i) {
-    const auto& ret = return_values_[i];
-    columns.push_back(Column(columns.size(), ret.name, ret.type));
-  }
-  for (size_t i = 0; i < prototype_.arguments.size(); ++i) {
-    const auto& arg = prototype_.arguments[i];
-
-    // Add the "in_" prefix to every argument param to avoid clashes between the
-    // output and input parameters.
-    columns.push_back(Column(columns.size(), "in_" + arg.name, arg.type, true));
-  }
-
-  std::vector<size_t> primary_keys(return_values_.size());
-  std::iota(primary_keys.begin(), primary_keys.end(), 0);
-
-  return SqliteTable::Schema(std::move(columns), std::move(primary_keys));
-}
-
-std::unique_ptr<SqliteTable::Cursor> CreatedViewFunction::CreateCursor() {
-  return std::unique_ptr<Cursor>(new Cursor(this));
-}
-
-int CreatedViewFunction::BestIndex(const QueryConstraints& qc,
-                                   BestIndexInfo* info) {
-  // Only accept constraint sets where every input parameter has a value.
-  size_t seen_hidden_constraints = 0;
-  for (size_t i = 0; i < qc.constraints().size(); ++i) {
-    const auto& cs = qc.constraints()[i];
-    if (!schema().columns()[static_cast<size_t>(cs.column)].hidden())
-      continue;
-    seen_hidden_constraints++;
-  }
-  if (seen_hidden_constraints < prototype_.arguments.size())
-    return SQLITE_CONSTRAINT;
-
-  for (size_t i = 0; i < info->sqlite_omit_constraint.size(); ++i) {
-    size_t col = static_cast<size_t>(qc.constraints()[i].column);
-    if (schema().columns()[col].hidden()) {
-      info->sqlite_omit_constraint[i] = true;
-    }
-  }
-  return SQLITE_OK;
-}
-
-CreatedViewFunction::Cursor::Cursor(CreatedViewFunction* table)
-    : SqliteTable::Cursor(table), table_(table) {}
-
-CreatedViewFunction::Cursor::~Cursor() = default;
-
-int CreatedViewFunction::Cursor::Filter(const QueryConstraints& qc,
-                                        sqlite3_value** argv,
-                                        FilterHistory) {
-  PERFETTO_TP_TRACE("CREATE_VIEW_FUNCTION", [this](metatrace::Record* r) {
-    r->AddArg("Function", table_->prototype_.function_name.c_str());
-  });
-
-  auto col_to_arg_idx = [this](int col) {
-    return static_cast<uint32_t>(col) -
-           static_cast<uint32_t>(table_->return_values_.size());
-  };
-
-  size_t seen_hidden_constraints = 0;
-  for (size_t i = 0; i < qc.constraints().size(); ++i) {
-    const auto& cs = qc.constraints()[i];
-
-    // Only consider hidden columns (i.e. input parameters) as we're delegating
-    // the rest to SQLite.
-    if (!table_->schema().columns()[static_cast<size_t>(cs.column)].hidden())
-      continue;
-
-    // We only support equality constraints as we're expecting "input arguments"
-    // to our "function".
-    if (!sqlite_utils::IsOpEq(cs.op)) {
-      table_->SetErrorMessage(
-          sqlite3_mprintf("%s: non-equality constraint passed",
-                          table_->prototype_.function_name.c_str()));
-      return SQLITE_ERROR;
-    }
-
-    const auto& arg = table_->prototype_.arguments[col_to_arg_idx(cs.column)];
-    SqlValue::Type expected_type = arg.type;
-    base::Status status = TypeCheckSqliteValue(argv[i], expected_type);
-    if (!status.ok()) {
-      table_->SetErrorMessage(
-          sqlite3_mprintf("%s: argument %s (index %u) %s",
-                          table_->prototype_.function_name.c_str(),
-                          arg.name.c_str(), i, status.c_message()));
-      return SQLITE_ERROR;
-    }
-
-    seen_hidden_constraints++;
-  }
-
-  // Verify that we saw one valid constriant for every input argument.
-  if (seen_hidden_constraints < table_->prototype_.arguments.size()) {
-    table_->SetErrorMessage(sqlite3_mprintf(
-        "%s: missing value for input argument. Saw %u arguments but expected "
-        "%u",
-        table_->prototype_.function_name.c_str(), seen_hidden_constraints,
-        table_->prototype_.arguments.size()));
-    return SQLITE_ERROR;
-  }
-
-  // Prepare the SQL definition as a statement using SQLite.
-  // TODO(lalitm): see if we can reuse this prepared statement rather than
-  // creating it very time.
-  // TODO(lalitm): measure and implement whether it would be a good idea to
-  // forward constraints here when we build the nested query.
-  int ret = sqlite3_prepare_v2(table_->db_, table_->sql_defn_str_.data(),
-                               static_cast<int>(table_->sql_defn_str_.size()),
-                               &stmt_, nullptr);
-  scoped_stmt_.reset(stmt_);
-  PERFETTO_CHECK(ret == SQLITE_OK);
-
-  // Bind all the arguments to the appropriate places in the function.
-  for (size_t i = 0; i < qc.constraints().size(); ++i) {
-    const auto& cs = qc.constraints()[i];
-
-    // Don't deal with any constraints on the output parameters for simplicty.
-    // TODO(lalitm): reconsider this decision to allow more efficient queries:
-    // we would need to wrap the query in a SELECT * FROM (...) WHERE constraint
-    // like we do for SPAN JOIN.
-    if (!table_->schema().columns()[static_cast<size_t>(cs.column)].hidden())
-      continue;
-
-    uint32_t index = col_to_arg_idx(cs.column);
-    PERFETTO_DCHECK(index < table_->prototype_.arguments.size());
-
-    const auto& arg = table_->prototype_.arguments[index];
-    auto status = MaybeBindArgument(stmt_, table_->prototype_.function_name,
-                                    arg, argv[i]);
-    if (!status.ok()) {
-      table_->SetErrorMessage(sqlite3_mprintf("%s", status.c_message()));
-      return SQLITE_ERROR;
-    }
-  }
-
-  ret = Next();
-  if (ret != SQLITE_OK)
-    return ret;
-
-  return SQLITE_OK;
-}
-
-int CreatedViewFunction::Cursor::Next() {
-  int ret = sqlite3_step(stmt_);
-  is_eof_ = ret == SQLITE_DONE;
-  if (ret != SQLITE_ROW && ret != SQLITE_DONE) {
-    table_->SetErrorMessage(sqlite3_mprintf(
-        "%s: SQLite error while stepping statement: %s",
-        table_->prototype_.function_name.c_str(), sqlite3_errmsg(table_->db_)));
-    return ret;
-  }
-  return SQLITE_OK;
-}
-
-int CreatedViewFunction::Cursor::Eof() {
-  return is_eof_;
-}
-
-int CreatedViewFunction::Cursor::Column(sqlite3_context* ctx, int i) {
-  size_t idx = static_cast<size_t>(i);
-  if (idx < table_->return_values_.size()) {
-    sqlite3_result_value(ctx, sqlite3_column_value(stmt_, i));
-  } else {
-    sqlite3_result_null(ctx);
-  }
-  return SQLITE_OK;
-}
-
-}  // namespace
-
-base::Status CreateViewFunction::Run(CreateViewFunction::Context* ctx,
-                                     size_t argc,
-                                     sqlite3_value** argv,
-                                     SqlValue&,
-                                     Destructors&) {
-  if (argc != 3) {
-    return base::ErrStatus(
-        "CREATE_VIEW_FUNCTION: invalid number of args; expected %u, received "
-        "%zu",
-        3u, argc);
-  }
-
-  sqlite3_value* prototype_value = argv[0];
-  sqlite3_value* return_prototype_value = argv[1];
-  sqlite3_value* sql_defn_value = argv[2];
-
-  // Type check all the arguments.
-  {
-    auto type_check = [prototype_value](sqlite3_value* value,
-                                        SqlValue::Type type, const char* desc) {
-      base::Status status = TypeCheckSqliteValue(value, type);
-      if (!status.ok()) {
-        return base::ErrStatus("CREATE_VIEW_FUNCTION[prototype=%s]: %s %s",
-                               sqlite3_value_text(prototype_value), desc,
-                               status.c_message());
-      }
-      return base::OkStatus();
-    };
-
-    RETURN_IF_ERROR(type_check(prototype_value, SqlValue::Type::kString,
-                               "function prototype (first argument)"));
-    RETURN_IF_ERROR(type_check(return_prototype_value, SqlValue::Type::kString,
-                               "return prototype (second argument)"));
-    RETURN_IF_ERROR(type_check(sql_defn_value, SqlValue::Type::kString,
-                               "SQL definition (third argument)"));
-  }
-
-  // Extract the arguments from the value wrappers.
-  auto extract_string = [](sqlite3_value* value) -> const char* {
-    return reinterpret_cast<const char*>(sqlite3_value_text(value));
-  };
-  const char* prototype_str = extract_string(prototype_value);
-  const char* return_prototype_str = extract_string(return_prototype_value);
-  const char* sql_defn_str = extract_string(sql_defn_value);
-
-  base::StringView function_name;
-  RETURN_IF_ERROR(ParseFunctionName(prototype_str, function_name));
-
-  static constexpr char kSqlTemplate[] = R"""(
-    DROP TABLE IF EXISTS %s;
-
-    CREATE VIRTUAL TABLE %s
-    USING INTERNAL_VIEW_FUNCTION_IMPL('%s', '%s', '%s');
-  )""";
-  std::string function_name_str = function_name.ToStdString();
-  base::StackString<1024> sql(kSqlTemplate, function_name_str.c_str(),
-                              function_name_str.c_str(), prototype_str,
-                              return_prototype_str, sql_defn_str);
-
-  ScopedSqliteString errmsg;
-  char* errmsg_raw = nullptr;
-  int ret = sqlite3_exec(ctx->db, sql.c_str(), nullptr, nullptr, &errmsg_raw);
-  errmsg.reset(errmsg_raw);
-  if (ret != SQLITE_OK)
-    return base::ErrStatus("%s", errmsg.get());
-
-  // CREATE_VIEW_FUNCTION doesn't have a return value so just don't sent |out|.
-  return base::OkStatus();
-}
-
-void CreateViewFunction::RegisterTable(sqlite3* db) {
-  CreatedViewFunction::Register(db);
-}
-
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/sqlite/create_view_function.h b/src/trace_processor/sqlite/create_view_function.h
deleted file mode 100644
index af5615e..0000000
--- a/src/trace_processor/sqlite/create_view_function.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2021 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_CREATE_VIEW_FUNCTION_H_
-#define SRC_TRACE_PROCESSOR_SQLITE_CREATE_VIEW_FUNCTION_H_
-
-#include <sqlite3.h>
-#include <unordered_map>
-
-#include "src/trace_processor/sqlite/register_function.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-// Implementation of CREATE_VIEW_FUNCTION SQL function.
-// See https://perfetto.dev/docs/analysis/metrics#metric-helper-functions for
-// usage of this function.
-struct CreateViewFunction : public SqlFunction {
-  struct Context {
-    sqlite3* db;
-  };
-
-  static base::Status Run(Context* ctx,
-                          size_t argc,
-                          sqlite3_value** argv,
-                          SqlValue& out,
-                          Destructors&);
-
-  static void RegisterTable(sqlite3* db);
-};
-
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_SQLITE_CREATE_VIEW_FUNCTION_H_
diff --git a/src/trace_processor/sqlite/db_sqlite_table.cc b/src/trace_processor/sqlite/db_sqlite_table.cc
index ffc6c80..8919270 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 "perfetto/ext/base/small_vector.h"
 #include "perfetto/ext/base/string_writer.h"
 #include "src/trace_processor/containers/bit_vector.h"
 #include "src/trace_processor/sqlite/query_cache.h"
@@ -47,18 +48,13 @@
       return FilterOp::kIsNull;
     case SQLITE_INDEX_CONSTRAINT_ISNOTNULL:
       return FilterOp::kIsNotNull;
-    case SQLITE_INDEX_CONSTRAINT_LIKE:
     case SQLITE_INDEX_CONSTRAINT_GLOB:
-      return base::nullopt;
-#if SQLITE_VERSION_NUMBER >= 3038000
-    // LIMIT and OFFSET constraints were introduced in 3.38 but we
-    // still build for older versions in most places. We still need
-    // to handle this here as Chrome is very good at staying up to date
-    // with SQLite versions and crashes if we don't have this.
+      return FilterOp::kGlob;
+    case SQLITE_INDEX_CONSTRAINT_LIKE:
+    // TODO(lalitm): start supporting these constraints.
     case SQLITE_INDEX_CONSTRAINT_LIMIT:
     case SQLITE_INDEX_CONSTRAINT_OFFSET:
       return base::nullopt;
-#endif
     default:
       PERFETTO_FATAL("Currently unsupported constraint");
   }
@@ -104,11 +100,35 @@
       });
 }
 
+class SafeStringWriter {
+ public:
+  SafeStringWriter() {}
+  ~SafeStringWriter() {}
+
+  void AppendString(const char* s) {
+    for (const char* c = s; *c; ++c) {
+      buffer_.emplace_back(*c);
+    }
+  }
+
+  void AppendString(const std::string& s) {
+    for (char c : s) {
+      buffer_.emplace_back(c);
+    }
+  }
+
+  base::StringView GetStringView() const {
+    return base::StringView(buffer_.data(), buffer_.size());
+  }
+
+ private:
+  base::SmallVector<char, 2048> buffer_;
+};
+
 }  // namespace
 
 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)) {}
@@ -116,10 +136,9 @@
 
 void DbSqliteTable::RegisterTable(sqlite3* db,
                                   QueryCache* cache,
-                                  Table::Schema schema,
                                   const Table* table,
                                   const std::string& name) {
-  Context context{cache, schema, TableComputation::kStatic, table, nullptr};
+  Context context{cache, TableComputation::kStatic, table, nullptr};
   SqliteTable::Register<DbSqliteTable, Context>(db, std::move(context), name);
 }
 
@@ -127,22 +146,28 @@
     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.
   base::Status status = generator->ValidateConstraints(
       QueryConstraints(std::numeric_limits<uint64_t>::max()));
   bool requires_args = !status.ok();
 
-  Context context{cache, std::move(schema), TableComputation::kDynamic, nullptr,
+  std::string table_name = generator->TableName();
+  Context context{cache, TableComputation::kDynamic, nullptr,
                   std::move(generator)};
-  SqliteTable::Register<DbSqliteTable, Context>(db, std::move(context), name,
-                                                false, requires_args);
+  SqliteTable::Register<DbSqliteTable, Context>(
+      db, std::move(context), table_name, false, requires_args);
 }
 
 base::Status DbSqliteTable::Init(int, const char* const*, Schema* schema) {
+  switch (computation_) {
+    case TableComputation::kStatic:
+      schema_ = static_table_->ComputeSchema();
+      break;
+    case TableComputation::kDynamic:
+      schema_ = generator_->CreateSchema();
+      break;
+  }
   *schema = ComputeSchema(schema_, name().c_str());
   return base::OkStatus();
 }
@@ -474,9 +499,10 @@
       TryCacheCreateSortedTable(qc, history);
       break;
     case TableComputation::kDynamic: {
-      PERFETTO_TP_TRACE("DYNAMIC_TABLE_GENERATE", [this](metatrace::Record* r) {
-        r->AddArg("Table", db_sqlite_table_->name());
-      });
+      PERFETTO_TP_TRACE(metatrace::Category::QUERY, "DYNAMIC_TABLE_GENERATE",
+                        [this](metatrace::Record* r) {
+                          r->AddArg("Table", db_sqlite_table_->name());
+                        });
       // If we have a dynamically created table, regenerate the table based on
       // the new constraints.
       std::unique_ptr<Table> computed_table;
@@ -498,73 +524,77 @@
     }
   }
 
-  PERFETTO_TP_TRACE("DB_TABLE_FILTER_AND_SORT", [this](metatrace::Record* r) {
-    const Table* source = SourceTable();
-    char buffer[2048];
-    for (const Constraint& c : constraints_) {
-      base::StringWriter writer(buffer, sizeof(buffer));
-      writer.AppendString(source->GetColumn(c.col_idx).name());
+  PERFETTO_TP_TRACE(
+      metatrace::Category::QUERY, "DB_TABLE_FILTER_AND_SORT",
+      [this](metatrace::Record* r) {
+        const Table* source = SourceTable();
+        r->AddArg("Table", db_sqlite_table_->name());
+        for (const Constraint& c : constraints_) {
+          SafeStringWriter writer;
+          writer.AppendString(source->GetColumn(c.col_idx).name());
 
-      writer.AppendChar(' ');
-      switch (c.op) {
-        case FilterOp::kEq:
-          writer.AppendString("=");
-          break;
-        case FilterOp::kGe:
-          writer.AppendString(">=");
-          break;
-        case FilterOp::kGt:
-          writer.AppendString(">");
-          break;
-        case FilterOp::kLe:
-          writer.AppendString("<=");
-          break;
-        case FilterOp::kLt:
-          writer.AppendString("<");
-          break;
-        case FilterOp::kNe:
-          writer.AppendString("!=");
-          break;
-        case FilterOp::kIsNull:
-          writer.AppendString("IS");
-          break;
-        case FilterOp::kIsNotNull:
-          writer.AppendString("IS NOT");
-          break;
-      }
-      writer.AppendChar(' ');
+          writer.AppendString(" ");
+          switch (c.op) {
+            case FilterOp::kEq:
+              writer.AppendString("=");
+              break;
+            case FilterOp::kGe:
+              writer.AppendString(">=");
+              break;
+            case FilterOp::kGt:
+              writer.AppendString(">");
+              break;
+            case FilterOp::kLe:
+              writer.AppendString("<=");
+              break;
+            case FilterOp::kLt:
+              writer.AppendString("<");
+              break;
+            case FilterOp::kNe:
+              writer.AppendString("!=");
+              break;
+            case FilterOp::kIsNull:
+              writer.AppendString("IS");
+              break;
+            case FilterOp::kIsNotNull:
+              writer.AppendString("IS NOT");
+              break;
+            case FilterOp::kGlob:
+              writer.AppendString("GLOB");
+              break;
+          }
+          writer.AppendString(" ");
 
-      switch (c.value.type) {
-        case SqlValue::kString:
-          writer.AppendString(c.value.AsString());
-          break;
-        case SqlValue::kBytes:
-          writer.AppendString("<bytes>");
-          break;
-        case SqlValue::kNull:
-          writer.AppendString("<null>");
-          break;
-        case SqlValue::kDouble: {
-          writer.AppendDouble(c.value.AsDouble());
-          break;
+          switch (c.value.type) {
+            case SqlValue::kString:
+              writer.AppendString(c.value.AsString());
+              break;
+            case SqlValue::kBytes:
+              writer.AppendString("<bytes>");
+              break;
+            case SqlValue::kNull:
+              writer.AppendString("<null>");
+              break;
+            case SqlValue::kDouble: {
+              writer.AppendString(std::to_string(c.value.AsDouble()));
+              break;
+            }
+            case SqlValue::kLong: {
+              writer.AppendString(std::to_string(c.value.AsLong()));
+              break;
+            }
+          }
+          r->AddArg("Constraint", writer.GetStringView());
         }
-        case SqlValue::kLong: {
-          writer.AppendInt(c.value.AsLong());
-          break;
-        }
-      }
-      r->AddArg("Table", db_sqlite_table_->name());
-      r->AddArg("Constraint", writer.GetStringView());
-    }
 
-    for (const auto& o : orders_) {
-      base::StringWriter writer(buffer, sizeof(buffer));
-      writer.AppendString(source->GetColumn(o.col_idx).name());
-      if (o.desc)
-        writer.AppendString(" desc");
-      r->AddArg("Order by", writer.GetStringView());
-    }
-  });
+        for (const auto& o : orders_) {
+          SafeStringWriter writer;
+          writer.AppendString(source->GetColumn(o.col_idx).name());
+          if (o.desc)
+            writer.AppendString(" desc");
+          r->AddArg("Order by", writer.GetStringView());
+        }
+      });
 
   // Attempt to filter into a RowMap first - weall figure out whether to apply
   // this to the table or we should use the RowMap directly. Also, if we are
diff --git a/src/trace_processor/sqlite/db_sqlite_table.h b/src/trace_processor/sqlite/db_sqlite_table.h
index 4214bb7..8df67c4 100644
--- a/src/trace_processor/sqlite/db_sqlite_table.h
+++ b/src/trace_processor/sqlite/db_sqlite_table.h
@@ -110,7 +110,6 @@
   };
   struct Context {
     QueryCache* cache;
-    Table::Schema schema;
     TableComputation computation;
 
     // Only valid when computation == TableComputation::kStatic.
@@ -122,7 +121,6 @@
 
   static void RegisterTable(sqlite3* db,
                             QueryCache* cache,
-                            Table::Schema schema,
                             const Table* table,
                             const std::string& name);
 
@@ -158,10 +156,12 @@
 
  private:
   QueryCache* cache_ = nullptr;
-  Table::Schema schema_;
 
   TableComputation computation_ = TableComputation::kStatic;
 
+  // Only valid after Init has completed.
+  Table::Schema schema_;
+
   // Only valid when computation_ == TableComputation::kStatic.
   const Table* static_table_ = nullptr;
 
diff --git a/src/trace_processor/sqlite/register_function.cc b/src/trace_processor/sqlite/register_function.cc
deleted file mode 100644
index 88216bd..0000000
--- a/src/trace_processor/sqlite/register_function.cc
+++ /dev/null
@@ -1,31 +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/register_function.h"
-#include "sqlite3.h"
-#include "src/trace_processor/sqlite/sqlite_utils.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-base::Status SqlFunction::VerifyPostConditions(Context*) {
-  return base::OkStatus();
-}
-
-void SqlFunction::Cleanup(Context*) {}
-
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/sqlite/register_function.h b/src/trace_processor/sqlite/register_function.h
deleted file mode 100644
index 95c24cd..0000000
--- a/src/trace_processor/sqlite/register_function.h
+++ /dev/null
@@ -1,209 +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_REGISTER_FUNCTION_H_
-#define SRC_TRACE_PROCESSOR_SQLITE_REGISTER_FUNCTION_H_
-
-#include <sqlite3.h>
-#include <memory>
-
-#include "src/trace_processor/sqlite/sqlite_utils.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-// Prototype for a C++ function which can be registered with SQLite.
-//
-// Usage
-//
-// Define a subclass of this struct as follows:
-// struct YourFunction : public SqlFunction {
-//   // Optional if you want a custom context object (i.e. an object
-//   // passed in at registration time which will be passed to Run on
-//   // every invocation)
-//   struct YourContext { /* define context fields here */ };
-//
-//   static base::Status Run(/* see parameters below */) {
-//     /* function body here */
-//   }
-//
-//   static base::Status Cleanup(/* see parameters below */) {
-//     /* function body here */
-//   }
-// }
-//
-// Then, register this function with SQLite using RegisterFunction (see below);
-// you'll likely want to do this in TraceProcessorImpl:
-// RegisterFunction<YourFunction>(/* see arguments below */)
-struct SqlFunction {
-  // The type of the context object which will be passed to the function.
-  // Can be redefined in any sub-classes to override the context.
-  using Context = void;
-
-  // Struct which holds destructors for strings/bytes returned from the
-  // function. Passed as an argument to |Run| to allow implementations to
-  // override the destructors.
-  struct Destructors {
-    sqlite3_destructor_type string_destructor = sqlite_utils::kSqliteTransient;
-    sqlite3_destructor_type bytes_destructor = sqlite_utils::kSqliteTransient;
-  };
-
-  // The function which will be exectued with the arguments from SQL.
-  //
-  // Implementations MUST define this function themselves; this function is
-  // declared but *not* defined so linker errors will be thrown if not defined.
-  //
-  // |ctx|:         the context object passed at registration time.
-  // |argc|:        number of arguments.
-  // |argv|:        arguments to the function.
-  // |out|:         the return value of the function.
-  // |destructors|: destructors for string/bytes return values.
-  static base::Status Run(Context* ctx,
-                          size_t argc,
-                          sqlite3_value** argv,
-                          SqlValue& out,
-                          Destructors& destructors);
-
-  // Executed after the result from |Run| is reported to SQLite.
-  // Allows implementations to verify post-conditions without needing to worry
-  // about overwriting return types.
-  //
-  // Implementations do not need to define this function; a default no-op
-  // implementation will be used in this case.
-  static base::Status VerifyPostConditions(Context*);
-
-  // Executed after the result from |Run| is reported to SQLite.
-  // Allows any pending state to be cleaned up post-copy of results by SQLite:
-  // this function will be called even if |Run| or |PostRun| returned errors.
-  //
-  // Implementations do not need to define this function; a default no-op
-  // implementation will be used in this case.
-  static void Cleanup(Context*);
-};
-
-// Registers a C++ function to be runnable from SQL.
-// The format of the function is given by the |SqlFunction|; see the
-// documentaion above.
-//
-// |db|:          sqlite3 database object
-// |name|:        name of the function in SQL
-// |argc|:        number of arguments for this function, -1 if variable
-// |ctx|:         context object for the function (see SqlFunction::Run above);
-//                this object *must* outlive the function so should likely be
-//                either static or scoped to the lifetime of TraceProcessor.
-// |determistic|: whether this function has deterministic output given the
-//                same set of arguments.
-template <typename Function>
-base::Status RegisterSqlFunction(sqlite3* db,
-                                 const char* name,
-                                 int argc,
-                                 typename Function::Context* ctx,
-                                 bool deterministic = true);
-
-// Same as above except allows a unique_ptr to be passed for the context; this
-// allows for SQLite to manage the lifetime of this pointer instead of the
-// essentially static requirement of the context pointer above.
-template <typename Function>
-base::Status RegisterSqlFunction(
-    sqlite3* db,
-    const char* name,
-    int argc,
-    std::unique_ptr<typename Function::Context> ctx,
-    bool deterministic = true);
-
-}  // namespace trace_processor
-}  // namespace perfetto
-
-// The rest of this file is just implementation details which we need
-// in the header file because it is templated code. We separate it out
-// like this to keep the API people actually care about easy to read.
-
-namespace perfetto {
-namespace trace_processor {
-
-namespace sqlite_internal {
-
-// RAII type to call Function::Cleanup when destroyed.
-template <typename Function>
-struct ScopedCleanup {
-  typename Function::Context* ctx;
-  ~ScopedCleanup() { Function::Cleanup(ctx); }
-};
-
-template <typename Function>
-void WrapSqlFunction(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
-  using Context = typename Function::Context;
-  Context* ud = static_cast<Context*>(sqlite3_user_data(ctx));
-
-  ScopedCleanup<Function> scoped_cleanup{ud};
-  SqlValue value{};
-  SqlFunction::Destructors destructors{};
-  base::Status status =
-      Function::Run(ud, static_cast<size_t>(argc), argv, value, destructors);
-  if (!status.ok()) {
-    sqlite3_result_error(ctx, status.c_message(), -1);
-    return;
-  }
-
-  sqlite_utils::ReportSqlValue(ctx, value, destructors.string_destructor,
-                               destructors.bytes_destructor);
-
-  status = Function::VerifyPostConditions(ud);
-  if (!status.ok()) {
-    sqlite3_result_error(ctx, status.c_message(), -1);
-    return;
-  }
-}
-}  // namespace sqlite_internal
-
-template <typename Function>
-base::Status RegisterSqlFunction(sqlite3* db,
-                                 const char* name,
-                                 int argc,
-                                 typename Function::Context* ctx,
-                                 bool deterministic) {
-  int flags = SQLITE_UTF8 | (deterministic ? SQLITE_DETERMINISTIC : 0);
-  int ret = sqlite3_create_function_v2(
-      db, name, static_cast<int>(argc), flags, ctx,
-      sqlite_internal::WrapSqlFunction<Function>, nullptr, nullptr, nullptr);
-  if (ret != SQLITE_OK) {
-    return base::ErrStatus("Unable to register function with name %s", name);
-  }
-  return base::OkStatus();
-}
-
-template <typename Function>
-base::Status RegisterSqlFunction(
-    sqlite3* db,
-    const char* name,
-    int argc,
-    std::unique_ptr<typename Function::Context> user_data,
-    bool deterministic) {
-  int flags = SQLITE_UTF8 | (deterministic ? SQLITE_DETERMINISTIC : 0);
-  int ret = sqlite3_create_function_v2(
-      db, name, static_cast<int>(argc), flags, user_data.release(),
-      sqlite_internal::WrapSqlFunction<Function>, nullptr, nullptr,
-      [](void* ptr) { delete static_cast<typename Function::Context*>(ptr); });
-  if (ret != SQLITE_OK) {
-    return base::ErrStatus("Unable to register function with name %s", name);
-  }
-  return base::OkStatus();
-}
-
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_SQLITE_REGISTER_FUNCTION_H_
diff --git a/src/trace_processor/sqlite/span_join_operator_table.cc b/src/trace_processor/sqlite/span_join_operator_table.cc
deleted file mode 100644
index 48104fe..0000000
--- a/src/trace_processor/sqlite/span_join_operator_table.cc
+++ /dev/null
@@ -1,891 +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/sqlite/span_join_operator_table.h"
-
-#include <sqlite3.h>
-#include <string.h>
-
-#include <algorithm>
-#include <set>
-#include <utility>
-
-#include "perfetto/base/logging.h"
-#include "perfetto/ext/base/string_splitter.h"
-#include "perfetto/ext/base/string_utils.h"
-#include "perfetto/ext/base/string_view.h"
-#include "src/trace_processor/sqlite/sqlite_utils.h"
-#include "src/trace_processor/tp_metatrace.h"
-#include "src/trace_processor/util/status_macros.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-namespace {
-
-constexpr char kTsColumnName[] = "ts";
-constexpr char kDurColumnName[] = "dur";
-
-bool IsRequiredColumn(const std::string& name) {
-  return name == kTsColumnName || name == kDurColumnName;
-}
-
-base::Optional<std::string> HasDuplicateColumns(
-    const std::vector<SqliteTable::Column>& cols) {
-  std::set<std::string> names;
-  for (const auto& col : cols) {
-    if (names.count(col.name()) > 0)
-      return col.name();
-    names.insert(col.name());
-  }
-  return base::nullopt;
-}
-
-std::string OpToString(int op) {
-  switch (op) {
-    case SQLITE_INDEX_CONSTRAINT_EQ:
-      return "=";
-    case SQLITE_INDEX_CONSTRAINT_NE:
-      return "!=";
-    case SQLITE_INDEX_CONSTRAINT_GE:
-      return ">=";
-    case SQLITE_INDEX_CONSTRAINT_GT:
-      return ">";
-    case SQLITE_INDEX_CONSTRAINT_LE:
-      return "<=";
-    case SQLITE_INDEX_CONSTRAINT_LT:
-      return "<";
-    case SQLITE_INDEX_CONSTRAINT_LIKE:
-      return " like ";
-    case SQLITE_INDEX_CONSTRAINT_GLOB:
-      return " glob ";
-    case SQLITE_INDEX_CONSTRAINT_ISNULL:
-      // The "null" will be added below in EscapedSqliteValueAsString.
-      return " is ";
-    case SQLITE_INDEX_CONSTRAINT_ISNOTNULL:
-      // The "null" will be added below in EscapedSqliteValueAsString.
-      return " is not ";
-    default:
-      PERFETTO_FATAL("Operator to string conversion not impemented for %d", op);
-  }
-}
-
-std::string EscapedSqliteValueAsString(sqlite3_value* value) {
-  switch (sqlite3_value_type(value)) {
-    case SQLITE_INTEGER:
-      return std::to_string(sqlite3_value_int64(value));
-    case SQLITE_FLOAT:
-      return std::to_string(sqlite3_value_double(value));
-    case SQLITE_TEXT: {
-      // If str itself contains a single quote, we need to escape it with
-      // another single quote.
-      const char* str =
-          reinterpret_cast<const char*>(sqlite3_value_text(value));
-      return "'" + base::ReplaceAll(str, "'", "''") + "'";
-    }
-    case SQLITE_NULL:
-      return " null";
-    default:
-      PERFETTO_FATAL("Unknown value type %d", sqlite3_value_type(value));
-  }
-}
-
-}  // namespace
-
-SpanJoinOperatorTable::SpanJoinOperatorTable(sqlite3* db, const TraceStorage*)
-    : db_(db) {}
-
-void SpanJoinOperatorTable::RegisterTable(sqlite3* db,
-                                          const TraceStorage* storage) {
-  SqliteTable::Register<SpanJoinOperatorTable>(db, storage, "span_join",
-                                               /* read_write */ false,
-                                               /* requires_args */ true);
-
-  SqliteTable::Register<SpanJoinOperatorTable>(db, storage, "span_left_join",
-                                               /* read_write */ false,
-                                               /* requires_args */ true);
-
-  SqliteTable::Register<SpanJoinOperatorTable>(db, storage, "span_outer_join",
-                                               /* read_write */ false,
-                                               /* requires_args */ true);
-}
-
-util::Status SpanJoinOperatorTable::Init(int argc,
-                                         const char* const* argv,
-                                         Schema* schema) {
-  // argv[0] - argv[2] are SQLite populated fields which are always present.
-  if (argc < 5)
-    return util::Status("SPAN_JOIN: expected at least 2 args");
-
-  TableDescriptor t1_desc;
-  auto status = TableDescriptor::Parse(
-      std::string(reinterpret_cast<const char*>(argv[3])), &t1_desc);
-  if (!status.ok())
-    return status;
-
-  TableDescriptor t2_desc;
-  status = TableDescriptor::Parse(
-      std::string(reinterpret_cast<const char*>(argv[4])), &t2_desc);
-  if (!status.ok())
-    return status;
-
-  // Check that the partition columns match between the two tables.
-  if (t1_desc.partition_col == t2_desc.partition_col) {
-    partitioning_ = t1_desc.IsPartitioned()
-                        ? PartitioningType::kSamePartitioning
-                        : PartitioningType::kNoPartitioning;
-  } else if (t1_desc.IsPartitioned() && t2_desc.IsPartitioned()) {
-    return util::ErrStatus(
-        "SPAN_JOIN: mismatching partitions between the two tables; "
-        "(partition %s in table %s, partition %s in table %s)",
-        t1_desc.partition_col.c_str(), t1_desc.name.c_str(),
-        t2_desc.partition_col.c_str(), t2_desc.name.c_str());
-  } else {
-    partitioning_ = PartitioningType::kMixedPartitioning;
-  }
-
-  bool t1_part_mixed = t1_desc.IsPartitioned() &&
-                       partitioning_ == PartitioningType::kMixedPartitioning;
-  bool t2_part_mixed = t2_desc.IsPartitioned() &&
-                       partitioning_ == PartitioningType::kMixedPartitioning;
-
-  EmitShadowType t1_shadow_type;
-  if (IsOuterJoin()) {
-    if (t1_part_mixed || partitioning_ == PartitioningType::kNoPartitioning) {
-      t1_shadow_type = EmitShadowType::kPresentPartitionOnly;
-    } else {
-      t1_shadow_type = EmitShadowType::kAll;
-    }
-  } else {
-    t1_shadow_type = EmitShadowType::kNone;
-  }
-  status = CreateTableDefinition(t1_desc, t1_shadow_type, &t1_defn_);
-  if (!status.ok())
-    return status;
-
-  EmitShadowType t2_shadow_type;
-  if (IsOuterJoin() || IsLeftJoin()) {
-    if (t2_part_mixed || partitioning_ == PartitioningType::kNoPartitioning) {
-      t2_shadow_type = EmitShadowType::kPresentPartitionOnly;
-    } else {
-      t2_shadow_type = EmitShadowType::kAll;
-    }
-  } else {
-    t2_shadow_type = EmitShadowType::kNone;
-  }
-  status = CreateTableDefinition(t2_desc, t2_shadow_type, &t2_defn_);
-  if (!status.ok())
-    return status;
-
-  std::vector<SqliteTable::Column> cols;
-  // Ensure the shared columns are consistently ordered and are not
-  // present twice in the final schema
-  cols.emplace_back(Column::kTimestamp, kTsColumnName, SqlValue::Type::kLong);
-  cols.emplace_back(Column::kDuration, kDurColumnName, SqlValue::Type::kLong);
-  if (partitioning_ != PartitioningType::kNoPartitioning)
-    cols.emplace_back(Column::kPartition, partition_col(),
-                      SqlValue::Type::kLong);
-
-  CreateSchemaColsForDefn(t1_defn_, &cols);
-  CreateSchemaColsForDefn(t2_defn_, &cols);
-
-  // Check if any column has : in its name. This often happens when SELECT *
-  // is used to create a view with the same column name in two joined tables.
-  for (const auto& col : cols) {
-    if (base::Contains(col.name(), ':')) {
-      return util::ErrStatus("SPAN_JOIN: column %s has illegal character :",
-                             col.name().c_str());
-    }
-  }
-
-  if (auto opt_dupe_col = HasDuplicateColumns(cols)) {
-    return util::ErrStatus(
-        "SPAN_JOIN: column %s present in both tables %s and %s",
-        opt_dupe_col->c_str(), t1_defn_.name().c_str(),
-        t2_defn_.name().c_str());
-  }
-  std::vector<size_t> primary_keys = {Column::kTimestamp};
-  if (partitioning_ != PartitioningType::kNoPartitioning)
-    primary_keys.push_back(Column::kPartition);
-  *schema = Schema(cols, primary_keys);
-
-  return util::OkStatus();
-}
-
-void SpanJoinOperatorTable::CreateSchemaColsForDefn(
-    const TableDefinition& defn,
-    std::vector<SqliteTable::Column>* cols) {
-  for (size_t i = 0; i < defn.columns().size(); i++) {
-    const auto& n = defn.columns()[i].name();
-    if (IsRequiredColumn(n) || n == defn.partition_col())
-      continue;
-
-    ColumnLocator* locator = &global_index_to_column_locator_[cols->size()];
-    locator->defn = &defn;
-    locator->col_index = i;
-
-    cols->emplace_back(cols->size(), n, defn.columns()[i].type());
-  }
-}
-
-std::unique_ptr<SqliteTable::Cursor> SpanJoinOperatorTable::CreateCursor() {
-  return std::unique_ptr<SpanJoinOperatorTable::Cursor>(new Cursor(this, db_));
-}
-
-int SpanJoinOperatorTable::BestIndex(const QueryConstraints& qc,
-                                     BestIndexInfo* info) {
-  // TODO(lalitm): figure out cost estimation.
-  const auto& ob = qc.order_by();
-
-  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);
-  }
-
-  const auto& cs = qc.constraints();
-  for (uint32_t i = 0; i < cs.size(); ++i) {
-    if (cs[i].op == kSourceGeqOpCode) {
-      info->sqlite_omit_constraint[i] = true;
-    }
-  }
-
-  return SQLITE_OK;
-}
-
-int SpanJoinOperatorTable::FindFunction(const char* name,
-                                        FindFunctionFn* fn,
-                                        void**) {
-  if (base::CaseInsensitiveEqual(name, "source_geq")) {
-    *fn = [](sqlite3_context* ctx, int, sqlite3_value**) {
-      sqlite3_result_error(ctx, "Should not be called.", -1);
-    };
-    return kSourceGeqOpCode;
-  }
-  return 0;
-}
-
-std::vector<std::string>
-SpanJoinOperatorTable::ComputeSqlConstraintsForDefinition(
-    const TableDefinition& defn,
-    const QueryConstraints& qc,
-    sqlite3_value** argv) {
-  std::vector<std::string> constraints;
-  for (size_t i = 0; i < qc.constraints().size(); i++) {
-    const auto& cs = qc.constraints()[i];
-    auto col_name = GetNameForGlobalColumnIndex(defn, cs.column);
-    if (col_name.empty())
-      continue;
-
-    // Le constraints can be passed straight to the child tables as they won't
-    // affect the span join computation. Similarily, source_geq constraints
-    // explicitly request that they are passed as geq constraints to the source
-    // tables.
-    if (col_name == kTsColumnName && !sqlite_utils::IsOpLe(cs.op) &&
-        cs.op != kSourceGeqOpCode)
-      continue;
-
-    // Allow SQLite handle any constraints on duration apart from source_geq
-    // constraints.
-    if (col_name == kDurColumnName && cs.op != kSourceGeqOpCode)
-      continue;
-
-    // If we're emitting shadow slices, don't propogate any constraints
-    // on this table as this will break the shadow slice computation.
-    if (defn.ShouldEmitPresentPartitionShadow())
-      continue;
-
-    auto op = OpToString(cs.op == kSourceGeqOpCode ? SQLITE_INDEX_CONSTRAINT_GE
-                                                   : cs.op);
-    auto value = EscapedSqliteValueAsString(argv[i]);
-
-    constraints.emplace_back("`" + col_name + "`" + op + value);
-  }
-  return constraints;
-}
-
-util::Status SpanJoinOperatorTable::CreateTableDefinition(
-    const TableDescriptor& desc,
-    EmitShadowType emit_shadow_type,
-    SpanJoinOperatorTable::TableDefinition* defn) {
-  if (desc.partition_col == kTsColumnName ||
-      desc.partition_col == kDurColumnName) {
-    return util::ErrStatus(
-        "SPAN_JOIN: partition column cannot be any of {ts, dur} for table %s",
-        desc.name.c_str());
-  }
-
-  std::vector<SqliteTable::Column> cols;
-  auto status = sqlite_utils::GetColumnsForTable(db_, desc.name, cols);
-  if (!status.ok()) {
-    return status;
-  }
-
-  uint32_t required_columns_found = 0;
-  uint32_t ts_idx = std::numeric_limits<uint32_t>::max();
-  uint32_t dur_idx = std::numeric_limits<uint32_t>::max();
-  uint32_t partition_idx = std::numeric_limits<uint32_t>::max();
-  for (uint32_t i = 0; i < cols.size(); i++) {
-    auto col = cols[i];
-    if (IsRequiredColumn(col.name())) {
-      ++required_columns_found;
-      if (col.type() != SqlValue::Type::kLong &&
-          col.type() != SqlValue::Type::kNull) {
-        return util::ErrStatus(
-            "SPAN_JOIN: Invalid type for column %s in table %s",
-            col.name().c_str(), desc.name.c_str());
-      }
-    }
-
-    if (col.name() == kTsColumnName) {
-      ts_idx = i;
-    } else if (col.name() == kDurColumnName) {
-      dur_idx = i;
-    } else if (col.name() == desc.partition_col) {
-      partition_idx = i;
-    }
-  }
-  if (required_columns_found != 2) {
-    return util::ErrStatus(
-        "SPAN_JOIN: Missing one of columns {ts, dur} in table %s",
-        desc.name.c_str());
-  } else if (desc.IsPartitioned() && partition_idx >= cols.size()) {
-    return util::ErrStatus("SPAN_JOIN: Missing partition column %s in table %s",
-                           desc.partition_col.c_str(), desc.name.c_str());
-  }
-
-  PERFETTO_DCHECK(ts_idx < cols.size());
-  PERFETTO_DCHECK(dur_idx < cols.size());
-
-  *defn = TableDefinition(desc.name, desc.partition_col, std::move(cols),
-                          emit_shadow_type, ts_idx, dur_idx, partition_idx);
-  return util::OkStatus();
-}
-
-std::string SpanJoinOperatorTable::GetNameForGlobalColumnIndex(
-    const TableDefinition& defn,
-    int global_column) {
-  size_t col_idx = static_cast<size_t>(global_column);
-  if (col_idx == Column::kTimestamp)
-    return kTsColumnName;
-  else if (col_idx == Column::kDuration)
-    return kDurColumnName;
-  else if (col_idx == Column::kPartition &&
-           partitioning_ != PartitioningType::kNoPartitioning)
-    return defn.partition_col().c_str();
-
-  const auto& locator = global_index_to_column_locator_[col_idx];
-  if (locator.defn != &defn)
-    return "";
-  return defn.columns()[locator.col_index].name().c_str();
-}
-
-SpanJoinOperatorTable::Cursor::Cursor(SpanJoinOperatorTable* table, sqlite3* db)
-    : SqliteTable::Cursor(table),
-      t1_(table, &table->t1_defn_, db),
-      t2_(table, &table->t2_defn_, db),
-      table_(table) {}
-
-base::Status SpanJoinOperatorTable::Cursor::FilterInner(
-    const QueryConstraints& qc,
-    sqlite3_value** argv) {
-  PERFETTO_TP_TRACE("SPAN_JOIN_XFILTER");
-
-  bool t1_partitioned_mixed =
-      t1_.definition()->IsPartitioned() &&
-      table_->partitioning_ == PartitioningType::kMixedPartitioning;
-  auto t1_eof = table_->IsOuterJoin() && !t1_partitioned_mixed
-                    ? Query::InitialEofBehavior::kTreatAsMissingPartitionShadow
-                    : Query::InitialEofBehavior::kTreatAsEof;
-  RETURN_IF_ERROR(t1_.Initialize(qc, argv, t1_eof));
-
-  bool t2_partitioned_mixed =
-      t2_.definition()->IsPartitioned() &&
-      table_->partitioning_ == PartitioningType::kMixedPartitioning;
-  auto t2_eof =
-      (table_->IsLeftJoin() || table_->IsOuterJoin()) && !t2_partitioned_mixed
-          ? Query::InitialEofBehavior::kTreatAsMissingPartitionShadow
-          : Query::InitialEofBehavior::kTreatAsEof;
-  RETURN_IF_ERROR(t2_.Initialize(qc, argv, t2_eof));
-  return FindOverlappingSpan();
-}
-
-base::Status SpanJoinOperatorTable::Cursor::NextInner() {
-  RETURN_IF_ERROR(next_query_->Next());
-  return FindOverlappingSpan();
-}
-
-bool SpanJoinOperatorTable::Cursor::IsOverlappingSpan() {
-  // If either of the tables are eof, then we cannot possibly have an
-  // overlapping span.
-  if (t1_.IsEof() || t2_.IsEof())
-    return false;
-
-  // One of the tables always needs to have a real span to have a valid
-  // overlapping span.
-  if (!t1_.IsReal() && !t2_.IsReal())
-    return false;
-
-  if (table_->partitioning_ == PartitioningType::kSamePartitioning) {
-    // If both tables are partitioned, then ensure that the partitions overlap.
-    bool partition_in_bounds = (t1_.FirstPartition() >= t2_.FirstPartition() &&
-                                t1_.FirstPartition() <= t2_.LastPartition()) ||
-                               (t2_.FirstPartition() >= t1_.FirstPartition() &&
-                                t2_.FirstPartition() <= t1_.LastPartition());
-    if (!partition_in_bounds)
-      return false;
-  }
-
-  // We consider all slices to be [start, end) - that is the range of
-  // timestamps has an open interval at the start but a closed interval
-  // at the end. (with the exception of dur == -1 which we treat as if
-  // end == start for the purpose of this function).
-  return (t1_.ts() == t2_.ts() && t1_.IsReal() && t2_.IsReal()) ||
-         (t1_.ts() >= t2_.ts() && t1_.ts() < t2_.AdjustedTsEnd()) ||
-         (t2_.ts() >= t1_.ts() && t2_.ts() < t1_.AdjustedTsEnd());
-}
-
-util::Status SpanJoinOperatorTable::Cursor::FindOverlappingSpan() {
-  // We loop until we find a slice which overlaps from the two tables.
-  while (true) {
-    if (table_->partitioning_ == PartitioningType::kMixedPartitioning) {
-      // If we have a mixed partition setup, we need to have special checks
-      // for eof and to reset the unpartitioned cursor every time the partition
-      // changes in the partitioned table.
-      auto* partitioned = t1_.definition()->IsPartitioned() ? &t1_ : &t2_;
-      auto* unpartitioned = t1_.definition()->IsPartitioned() ? &t2_ : &t1_;
-
-      // If the partitioned table reaches eof, then we are really done.
-      if (partitioned->IsEof())
-        break;
-
-      // If the partition has changed from the previous one, reset the cursor
-      // and keep a lot of the new partition.
-      if (last_mixed_partition_ != partitioned->partition()) {
-        util::Status status = unpartitioned->Rewind();
-        if (!status.ok())
-          return status;
-        last_mixed_partition_ = partitioned->partition();
-      }
-    } else if (t1_.IsEof() || t2_.IsEof()) {
-      // For both no partition and same partition cases, either cursor ending
-      // ends the whole span join.
-      break;
-    }
-
-    // Find which slice finishes first.
-    next_query_ = FindEarliestFinishQuery();
-
-    // If the current span is overlapping, just finish there to emit the current
-    // slice.
-    if (IsOverlappingSpan())
-      break;
-
-    // Otherwise, step to the next row.
-    util::Status status = next_query_->Next();
-    if (!status.ok())
-      return status;
-  }
-  return util::OkStatus();
-}
-
-SpanJoinOperatorTable::Query*
-SpanJoinOperatorTable::Cursor::FindEarliestFinishQuery() {
-  int64_t t1_part;
-  int64_t t2_part;
-
-  switch (table_->partitioning_) {
-    case PartitioningType::kMixedPartitioning: {
-      // If either table is EOF, forward the other table to try and make
-      // the partitions not match anymore.
-      if (t1_.IsEof())
-        return &t2_;
-      if (t2_.IsEof())
-        return &t1_;
-
-      // Otherwise, just make the partition equal from both tables.
-      t1_part = last_mixed_partition_;
-      t2_part = last_mixed_partition_;
-      break;
-    }
-    case PartitioningType::kSamePartitioning: {
-      // Get the partition values from the cursor.
-      t1_part = t1_.LastPartition();
-      t2_part = t2_.LastPartition();
-      break;
-    }
-    case PartitioningType::kNoPartitioning: {
-      t1_part = 0;
-      t2_part = 0;
-      break;
-    }
-  }
-
-  // Prefer to forward the earliest cursors based on the following
-  // lexiographical ordering:
-  // 1. partition
-  // 2. end timestamp
-  // 3. whether the slice is real or shadow (shadow < real)
-  bool t1_less = std::make_tuple(t1_part, t1_.AdjustedTsEnd(), t1_.IsReal()) <
-                 std::make_tuple(t2_part, t2_.AdjustedTsEnd(), t2_.IsReal());
-  return t1_less ? &t1_ : &t2_;
-}
-
-int SpanJoinOperatorTable::Cursor::Eof() {
-  return t1_.IsEof() || t2_.IsEof();
-}
-
-int SpanJoinOperatorTable::Cursor::Column(sqlite3_context* context, int N) {
-  PERFETTO_DCHECK(t1_.IsReal() || t2_.IsReal());
-
-  switch (N) {
-    case Column::kTimestamp: {
-      auto max_ts = std::max(t1_.ts(), t2_.ts());
-      sqlite3_result_int64(context, static_cast<sqlite3_int64>(max_ts));
-      break;
-    }
-    case Column::kDuration: {
-      auto max_start = std::max(t1_.ts(), t2_.ts());
-      auto min_end = std::min(t1_.raw_ts_end(), t2_.raw_ts_end());
-      auto dur = min_end - max_start;
-      sqlite3_result_int64(context, static_cast<sqlite3_int64>(dur));
-      break;
-    }
-    case Column::kPartition: {
-      if (table_->partitioning_ != PartitioningType::kNoPartitioning) {
-        int64_t partition;
-        if (table_->partitioning_ == PartitioningType::kMixedPartitioning) {
-          partition = last_mixed_partition_;
-        } else {
-          partition = t1_.IsReal() ? t1_.partition() : t2_.partition();
-        }
-        sqlite3_result_int64(context, static_cast<sqlite3_int64>(partition));
-        break;
-      }
-      [[clang::fallthrough]];
-    }
-    default: {
-      size_t index = static_cast<size_t>(N);
-      const auto& locator = table_->global_index_to_column_locator_[index];
-      if (locator.defn == t1_.definition())
-        t1_.ReportSqliteResult(context, locator.col_index);
-      else
-        t2_.ReportSqliteResult(context, locator.col_index);
-    }
-  }
-  return SQLITE_OK;
-}
-
-SpanJoinOperatorTable::Query::Query(SpanJoinOperatorTable* table,
-                                    const TableDefinition* definition,
-                                    sqlite3* db)
-    : defn_(definition), db_(db), table_(table) {
-  PERFETTO_DCHECK(!defn_->IsPartitioned() ||
-                  defn_->partition_idx() < defn_->columns().size());
-}
-
-SpanJoinOperatorTable::Query::~Query() = default;
-
-util::Status SpanJoinOperatorTable::Query::Initialize(
-    const QueryConstraints& qc,
-    sqlite3_value** argv,
-    InitialEofBehavior eof_behavior) {
-  *this = Query(table_, definition(), db_);
-  sql_query_ = CreateSqlQuery(
-      table_->ComputeSqlConstraintsForDefinition(*defn_, qc, argv));
-  util::Status status = Rewind();
-  if (!status.ok())
-    return status;
-  if (eof_behavior == InitialEofBehavior::kTreatAsMissingPartitionShadow &&
-      IsEof()) {
-    state_ = State::kMissingPartitionShadow;
-  }
-  return status;
-}
-
-util::Status SpanJoinOperatorTable::Query::Next() {
-  RETURN_IF_ERROR(NextSliceState());
-  return FindNextValidSlice();
-}
-
-bool SpanJoinOperatorTable::Query::IsValidSlice() {
-  // Disallow any single partition shadow slices if the definition doesn't allow
-  // them.
-  if (IsPresentPartitionShadow() && !defn_->ShouldEmitPresentPartitionShadow())
-    return false;
-
-  // Disallow any missing partition shadow slices if the definition doesn't
-  // allow them.
-  if (IsMissingPartitionShadow() && !defn_->ShouldEmitMissingPartitionShadow())
-    return false;
-
-  // Disallow any "empty" shadows; these are shadows which either have the same
-  // start and end time or missing-partition shadows which have the same start
-  // and end partition.
-  if (IsEmptyShadow())
-    return false;
-
-  return true;
-}
-
-util::Status SpanJoinOperatorTable::Query::FindNextValidSlice() {
-  // The basic idea of this function is that |NextSliceState()| always emits
-  // all possible slices (including shadows for any gaps inbetween the real
-  // slices) and we filter out the invalid slices (as defined by the table
-  // definition) using |IsValidSlice()|.
-  //
-  // This has proved to be a lot cleaner to implement than trying to choose
-  // when to emit and not emit shadows directly.
-  while (!IsEof() && !IsValidSlice()) {
-    RETURN_IF_ERROR(NextSliceState());
-  }
-  return util::OkStatus();
-}
-
-util::Status SpanJoinOperatorTable::Query::NextSliceState() {
-  switch (state_) {
-    case State::kReal: {
-      // Forward the cursor to figure out where the next slice should be.
-      RETURN_IF_ERROR(CursorNext());
-
-      // Depending on the next slice, we can do two things here:
-      // 1. If the next slice is on the same partition, we can just emit a
-      //    single shadow until the start of the next slice.
-      // 2. If the next slice is on another partition or we hit eof, just emit
-      //    a shadow to the end of the whole partition.
-      bool shadow_to_end = cursor_eof_ || (defn_->IsPartitioned() &&
-                                           partition_ != CursorPartition());
-      state_ = State::kPresentPartitionShadow;
-      ts_ = AdjustedTsEnd();
-      ts_end_ =
-          shadow_to_end ? std::numeric_limits<int64_t>::max() : CursorTs();
-      return util::OkStatus();
-    }
-    case State::kPresentPartitionShadow: {
-      if (ts_end_ == std::numeric_limits<int64_t>::max()) {
-        // If the shadow is to the end of the slice, create a missing partition
-        // shadow to the start of the partition of the next slice or to the max
-        // partition if we hit eof.
-        state_ = State::kMissingPartitionShadow;
-        ts_ = 0;
-        ts_end_ = std::numeric_limits<int64_t>::max();
-
-        missing_partition_start_ = partition_ + 1;
-        missing_partition_end_ = cursor_eof_
-                                     ? std::numeric_limits<int64_t>::max()
-                                     : CursorPartition();
-      } else {
-        // If the shadow is not to the end, we must have another slice on the
-        // current partition.
-        state_ = State::kReal;
-        ts_ = CursorTs();
-        ts_end_ = ts_ + CursorDur();
-
-        PERFETTO_DCHECK(!defn_->IsPartitioned() ||
-                        partition_ == CursorPartition());
-      }
-      return util::OkStatus();
-    }
-    case State::kMissingPartitionShadow: {
-      if (missing_partition_end_ == std::numeric_limits<int64_t>::max()) {
-        PERFETTO_DCHECK(cursor_eof_);
-
-        // If we have a missing partition to the max partition, we must have hit
-        // eof.
-        state_ = State::kEof;
-      } else {
-        PERFETTO_DCHECK(!defn_->IsPartitioned() ||
-                        CursorPartition() == missing_partition_end_);
-
-        // Otherwise, setup a single partition slice on the end partition to the
-        // start of the next slice.
-        state_ = State::kPresentPartitionShadow;
-        ts_ = 0;
-        ts_end_ = CursorTs();
-        partition_ = missing_partition_end_;
-      }
-      return util::OkStatus();
-    }
-    case State::kEof: {
-      PERFETTO_DFATAL("Called Next when EOF");
-      return util::ErrStatus("Called Next when EOF");
-    }
-  }
-  PERFETTO_FATAL("For GCC");
-}
-
-util::Status SpanJoinOperatorTable::Query::Rewind() {
-  sqlite3_stmt* stmt = nullptr;
-  int res =
-      sqlite3_prepare_v2(db_, sql_query_.c_str(),
-                         static_cast<int>(sql_query_.size()), &stmt, nullptr);
-  stmt_.reset(stmt);
-
-  cursor_eof_ = res != SQLITE_OK;
-  if (res != SQLITE_OK)
-    return util::ErrStatus("%s", sqlite3_errmsg(db_));
-
-  RETURN_IF_ERROR(CursorNext());
-
-  // Setup the first slice as a missing partition shadow from the lowest
-  // partition until the first slice partition. We will handle finding the real
-  // slice in |FindNextValidSlice()|.
-  state_ = State::kMissingPartitionShadow;
-  ts_ = 0;
-  ts_end_ = std::numeric_limits<int64_t>::max();
-  missing_partition_start_ = std::numeric_limits<int64_t>::min();
-
-  if (cursor_eof_) {
-    missing_partition_end_ = std::numeric_limits<int64_t>::max();
-  } else if (defn_->IsPartitioned()) {
-    missing_partition_end_ = CursorPartition();
-  } else {
-    missing_partition_end_ = std::numeric_limits<int64_t>::min();
-  }
-
-  // Actually compute the first valid slice.
-  return FindNextValidSlice();
-}
-
-util::Status SpanJoinOperatorTable::Query::CursorNext() {
-  auto* stmt = stmt_.get();
-  int res;
-  if (defn_->IsPartitioned()) {
-    auto partition_idx = static_cast<int>(defn_->partition_idx());
-    // Fastforward through any rows with null partition keys.
-    int row_type;
-    do {
-      res = sqlite3_step(stmt);
-      row_type = sqlite3_column_type(stmt, partition_idx);
-    } while (res == SQLITE_ROW && row_type == SQLITE_NULL);
-
-    if (res == SQLITE_ROW && row_type != SQLITE_INTEGER) {
-      return util::ErrStatus("SPAN_JOIN: partition is not an int");
-    }
-  } else {
-    res = sqlite3_step(stmt);
-  }
-  cursor_eof_ = res != SQLITE_ROW;
-  return res == SQLITE_ROW || res == SQLITE_DONE
-             ? util::OkStatus()
-             : util::ErrStatus("SPAN_JOIN: %s", sqlite3_errmsg(db_));
-}
-
-std::string SpanJoinOperatorTable::Query::CreateSqlQuery(
-    const std::vector<std::string>& cs) const {
-  std::vector<std::string> col_names;
-  for (const SqliteTable::Column& c : defn_->columns()) {
-    col_names.push_back("`" + c.name() + "`");
-  }
-
-  std::string sql = "SELECT " + base::Join(col_names, ", ");
-  sql += " FROM " + defn_->name();
-  if (!cs.empty()) {
-    sql += " WHERE " + base::Join(cs, " AND ");
-  }
-  sql += " ORDER BY ";
-  sql += defn_->IsPartitioned()
-             ? base::Join({"`" + defn_->partition_col() + "`", "ts"}, ", ")
-             : "ts";
-  sql += ";";
-  PERFETTO_DLOG("%s", sql.c_str());
-  return sql;
-}
-
-void SpanJoinOperatorTable::Query::ReportSqliteResult(sqlite3_context* context,
-                                                      size_t index) {
-  if (state_ != State::kReal) {
-    sqlite3_result_null(context);
-    return;
-  }
-
-  sqlite3_stmt* stmt = stmt_.get();
-  int idx = static_cast<int>(index);
-  switch (sqlite3_column_type(stmt, idx)) {
-    case SQLITE_INTEGER:
-      sqlite3_result_int64(context, sqlite3_column_int64(stmt, idx));
-      break;
-    case SQLITE_FLOAT:
-      sqlite3_result_double(context, sqlite3_column_double(stmt, idx));
-      break;
-    case SQLITE_TEXT: {
-      // TODO(lalitm): note for future optimizations: if we knew the addresses
-      // of the string intern pool, we could check if the string returned here
-      // comes from the pool, and pass it as non-transient.
-      const auto kSqliteTransient =
-          reinterpret_cast<sqlite3_destructor_type>(-1);
-      auto ptr = reinterpret_cast<const char*>(sqlite3_column_text(stmt, idx));
-      sqlite3_result_text(context, ptr, -1, kSqliteTransient);
-      break;
-    }
-  }
-}
-
-SpanJoinOperatorTable::TableDefinition::TableDefinition(
-    std::string name,
-    std::string partition_col,
-    std::vector<SqliteTable::Column> cols,
-    EmitShadowType emit_shadow_type,
-    uint32_t ts_idx,
-    uint32_t dur_idx,
-    uint32_t partition_idx)
-    : emit_shadow_type_(emit_shadow_type),
-      name_(std::move(name)),
-      partition_col_(std::move(partition_col)),
-      cols_(std::move(cols)),
-      ts_idx_(ts_idx),
-      dur_idx_(dur_idx),
-      partition_idx_(partition_idx) {}
-
-util::Status SpanJoinOperatorTable::TableDescriptor::Parse(
-    const std::string& raw_descriptor,
-    SpanJoinOperatorTable::TableDescriptor* descriptor) {
-  // Descriptors have one of the following forms:
-  // table_name [PARTITIONED column_name]
-
-  // Find the table name.
-  base::StringSplitter splitter(raw_descriptor, ' ');
-  if (!splitter.Next())
-    return util::ErrStatus("SPAN_JOIN: Missing table name");
-
-  descriptor->name = splitter.cur_token();
-  if (!splitter.Next())
-    return util::OkStatus();
-
-  if (!base::CaseInsensitiveEqual(splitter.cur_token(), "PARTITIONED"))
-    return util::ErrStatus("SPAN_JOIN: Invalid token");
-
-  if (!splitter.Next())
-    return util::ErrStatus("SPAN_JOIN: Missing partitioning column");
-
-  descriptor->partition_col = splitter.cur_token();
-  return util::OkStatus();
-}
-
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/sqlite/span_join_operator_table.h b/src/trace_processor/sqlite/span_join_operator_table.h
deleted file mode 100644
index 03272bb..0000000
--- a/src/trace_processor/sqlite/span_join_operator_table.h
+++ /dev/null
@@ -1,456 +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_SQLITE_SPAN_JOIN_OPERATOR_TABLE_H_
-#define SRC_TRACE_PROCESSOR_SQLITE_SPAN_JOIN_OPERATOR_TABLE_H_
-
-#include <sqlite3.h>
-
-#include <array>
-#include <deque>
-#include <limits>
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "perfetto/ext/base/flat_hash_map.h"
-#include "perfetto/trace_processor/basic_types.h"
-#include "perfetto/trace_processor/status.h"
-#include "src/trace_processor/sqlite/scoped_db.h"
-#include "src/trace_processor/sqlite/sqlite_table.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-// Implements the SPAN JOIN operation between two tables on a particular column.
-//
-// Span:
-// A span is a row with a timestamp and a duration. It is used to model
-// operations which run for a particular *span* of time.
-//
-// We draw spans like so (time on the x-axis):
-// start of span->[ time where opertion is running ]<- end of span
-//
-// Multiple spans can happen in parallel:
-// [      ]
-//    [        ]
-//   [                    ]
-//  [ ]
-//
-// The above for example, models scheduling activity on a 4-core computer for a
-// short period of time.
-//
-// Span join:
-// The span join operation can be thought of as the intersection of span tables.
-// That is, the join table has a span for each pair of spans in the child tables
-// where the spans overlap. Because many spans are possible in parallel, an
-// extra metadata column (labelled the "join column") is used to distinguish
-// between the spanned tables.
-//
-// For a given join key suppose these were the two span tables:
-// Table 1:   [        ]              [      ]         [ ]
-// Table 2:          [      ]            [  ]           [      ]
-// Output :          [ ]                 [  ]           []
-//
-// All other columns apart from timestamp (ts), duration (dur) and the join key
-// are passed through unchanged.
-class SpanJoinOperatorTable : public SqliteTable {
- public:
-  static constexpr int kSourceGeqOpCode = SQLITE_INDEX_CONSTRAINT_FUNCTION + 1;
-
-  // Enum indicating whether the queries on the two inner tables should
-  // emit shadows.
-  enum class EmitShadowType {
-    // Used when the table should emit all shadow slices (both present and
-    // missing partition shadows).
-    kAll,
-
-    // Used when the table should only emit shadows for partitions which are
-    // present.
-    kPresentPartitionOnly,
-
-    // Used when the table should emit no shadow slices.
-    kNone,
-  };
-
-  // Contains the definition of the child tables.
-  class TableDefinition {
-   public:
-    TableDefinition() = default;
-
-    TableDefinition(std::string name,
-                    std::string partition_col,
-                    std::vector<SqliteTable::Column> cols,
-                    EmitShadowType emit_shadow_type,
-                    uint32_t ts_idx,
-                    uint32_t dur_idx,
-                    uint32_t partition_idx);
-
-    // Returns whether this table should emit present partition shadow slices.
-    bool ShouldEmitPresentPartitionShadow() const {
-      return emit_shadow_type_ == EmitShadowType::kAll ||
-             emit_shadow_type_ == EmitShadowType::kPresentPartitionOnly;
-    }
-
-    // Returns whether this table should emit missing partition shadow slices.
-    bool ShouldEmitMissingPartitionShadow() const {
-      return emit_shadow_type_ == EmitShadowType::kAll;
-    }
-
-    // Returns whether the table is partitioned.
-    bool IsPartitioned() const { return !partition_col_.empty(); }
-
-    const std::string& name() const { return name_; }
-    const std::string& partition_col() const { return partition_col_; }
-    const std::vector<SqliteTable::Column>& columns() const { return cols_; }
-
-    uint32_t ts_idx() const { return ts_idx_; }
-    uint32_t dur_idx() const { return dur_idx_; }
-    uint32_t partition_idx() const { return partition_idx_; }
-
-   private:
-    EmitShadowType emit_shadow_type_ = EmitShadowType::kNone;
-
-    std::string name_;
-    std::string partition_col_;
-    std::vector<SqliteTable::Column> cols_;
-
-    uint32_t ts_idx_ = std::numeric_limits<uint32_t>::max();
-    uint32_t dur_idx_ = std::numeric_limits<uint32_t>::max();
-    uint32_t partition_idx_ = std::numeric_limits<uint32_t>::max();
-  };
-
-  // Stores information about a single subquery into one of the two child
-  // tables.
-  //
-  // This class is implemented as a state machine which steps from one slice to
-  // the next.
-  class Query {
-   public:
-    // Enum encoding the current state of the query in the state machine.
-    enum class State {
-      // Encodes that the current slice is a real slice (i.e. comes directly
-      // from the cursor).
-      kReal,
-
-      // Encodes that the current slice is on a partition for which there is a
-      // real slice present.
-      kPresentPartitionShadow,
-
-      // Encodes that the current slice is on a paritition(s) for which there is
-      // no real slice for those partition(s).
-      kMissingPartitionShadow,
-
-      // Encodes that this query has reached the end.
-      kEof,
-    };
-
-    Query(SpanJoinOperatorTable*, const TableDefinition*, sqlite3* db);
-    virtual ~Query();
-
-    Query(Query&&) noexcept = default;
-    Query& operator=(Query&&) = default;
-
-    enum class InitialEofBehavior {
-      kTreatAsEof,
-      kTreatAsMissingPartitionShadow
-    };
-
-    // Initializes the query with the given constraints and query parameters.
-    util::Status Initialize(
-        const QueryConstraints& qc,
-        sqlite3_value** argv,
-        InitialEofBehavior eof_behavior = InitialEofBehavior::kTreatAsEof);
-
-    // Forwards the query to the next valid slice.
-    util::Status Next();
-
-    // Rewinds the query to the first valid slice
-    // This is used in the mixed partitioning case where the query with no
-    // partitions is rewound to the start on every new partition.
-    util::Status Rewind();
-
-    // Reports the column at the given index to given context.
-    void ReportSqliteResult(sqlite3_context* context, size_t index);
-
-    // Returns whether the cursor has reached eof.
-    bool IsEof() const { return state_ == State::kEof; }
-
-    // Returns whether the current slice pointed to is a real slice.
-    bool IsReal() const { return state_ == State::kReal; }
-
-    // Returns the first partition this slice covers (for real/single partition
-    // shadows, this is the same as partition()).
-    // This partition encodes a [start, end] (closed at start and at end) range
-    // of partitions which works as the partitions are integers.
-    int64_t FirstPartition() const {
-      PERFETTO_DCHECK(!IsEof());
-      return IsMissingPartitionShadow() ? missing_partition_start_
-                                        : partition();
-    }
-
-    // Returns the last partition this slice covers (for real/single partition
-    // shadows, this is the same as partition()).
-    // This partition encodes a [start, end] (closed at start and at end) range
-    // of partitions which works as the partitions are integers.
-    int64_t LastPartition() const {
-      PERFETTO_DCHECK(!IsEof());
-      return IsMissingPartitionShadow() ? missing_partition_end_ - 1
-                                        : partition();
-    }
-
-    // Returns the end timestamp of this slice adjusted to ensure that -1
-    // duration slices always returns ts.
-    int64_t AdjustedTsEnd() const {
-      PERFETTO_DCHECK(!IsEof());
-      return ts_end_ - ts() == -1 ? ts() : ts_end_;
-    }
-
-    int64_t ts() const {
-      PERFETTO_DCHECK(!IsEof());
-      return ts_;
-    }
-    int64_t partition() const {
-      PERFETTO_DCHECK(!IsEof() && defn_->IsPartitioned());
-      return partition_;
-    }
-
-    int64_t raw_ts_end() const {
-      PERFETTO_DCHECK(!IsEof());
-      return ts_end_;
-    }
-
-    const TableDefinition* definition() const { return defn_; }
-
-   private:
-    Query(Query&) = delete;
-    Query& operator=(const Query&) = delete;
-
-    // Returns whether the current slice pointed to is a valid slice.
-    bool IsValidSlice();
-
-    // Forwards the query to the next valid slice.
-    util::Status FindNextValidSlice();
-
-    // Advances the query state machine by one slice.
-    util::Status NextSliceState();
-
-    // Forwards the cursor to point to the next real slice.
-    util::Status CursorNext();
-
-    // Creates an SQL query from the given set of constraint strings.
-    std::string CreateSqlQuery(const std::vector<std::string>& cs) const;
-
-    // Returns whether the current slice pointed to is a present partition
-    // shadow.
-    bool IsPresentPartitionShadow() const {
-      return state_ == State::kPresentPartitionShadow;
-    }
-
-    // Returns whether the current slice pointed to is a missing partition
-    // shadow.
-    bool IsMissingPartitionShadow() const {
-      return state_ == State::kMissingPartitionShadow;
-    }
-
-    // Returns whether the current slice pointed to is an empty shadow.
-    bool IsEmptyShadow() const {
-      PERFETTO_DCHECK(!IsEof());
-      return (!IsReal() && ts_ == ts_end_) ||
-             (IsMissingPartitionShadow() &&
-              missing_partition_start_ == missing_partition_end_);
-    }
-
-    int64_t CursorTs() const {
-      PERFETTO_DCHECK(!cursor_eof_);
-      auto ts_idx = static_cast<int>(defn_->ts_idx());
-      return sqlite3_column_int64(stmt_.get(), ts_idx);
-    }
-
-    int64_t CursorDur() const {
-      PERFETTO_DCHECK(!cursor_eof_);
-      auto dur_idx = static_cast<int>(defn_->dur_idx());
-      return sqlite3_column_int64(stmt_.get(), dur_idx);
-    }
-
-    int64_t CursorPartition() const {
-      PERFETTO_DCHECK(!cursor_eof_);
-      PERFETTO_DCHECK(defn_->IsPartitioned());
-      auto partition_idx = static_cast<int>(defn_->partition_idx());
-      return sqlite3_column_int64(stmt_.get(), partition_idx);
-    }
-
-    State state_ = State::kMissingPartitionShadow;
-    bool cursor_eof_ = false;
-
-    // Only valid when |state_| != kEof.
-    int64_t ts_ = 0;
-    int64_t ts_end_ = std::numeric_limits<int64_t>::max();
-
-    // Only valid when |state_| == kReal or |state_| == kPresentPartitionShadow.
-    int64_t partition_ = std::numeric_limits<int64_t>::min();
-
-    // Only valid when |state_| == kMissingPartitionShadow.
-    int64_t missing_partition_start_ = 0;
-    int64_t missing_partition_end_ = 0;
-
-    std::string sql_query_;
-    ScopedStmt stmt_;
-
-    const TableDefinition* defn_ = nullptr;
-    sqlite3* db_ = nullptr;
-    SpanJoinOperatorTable* table_ = nullptr;
-  };
-
-  // Base class for a cursor on the span table.
-  class Cursor : public SqliteTable::Cursor {
-   public:
-    Cursor(SpanJoinOperatorTable*, sqlite3* db);
-    ~Cursor() override = default;
-
-    int Filter(const QueryConstraints& qc,
-               sqlite3_value** argv,
-               FilterHistory) override {
-      base::Status status = FilterInner(qc, argv);
-      if (!status.ok()) {
-        table_->SetErrorMessage(sqlite3_mprintf("%s", status.c_message()));
-        return SQLITE_ERROR;
-      }
-      return SQLITE_OK;
-    }
-    int Next() override {
-      base::Status status = NextInner();
-      if (!status.ok()) {
-        table_->SetErrorMessage(sqlite3_mprintf("%s", status.c_message()));
-        return SQLITE_ERROR;
-      }
-      return SQLITE_OK;
-    }
-
-    int Column(sqlite3_context* context, int N) override;
-    int Eof() override;
-
-   private:
-    Cursor(Cursor&) = delete;
-    Cursor& operator=(const Cursor&) = delete;
-
-    Cursor(Cursor&&) noexcept = default;
-    Cursor& operator=(Cursor&&) = default;
-
-    bool IsOverlappingSpan();
-
-    base::Status NextInner();
-    base::Status FilterInner(const QueryConstraints& qc, sqlite3_value** argv);
-    util::Status FindOverlappingSpan();
-
-    Query* FindEarliestFinishQuery();
-
-    Query t1_;
-    Query t2_;
-
-    Query* next_query_ = nullptr;
-
-    // Only valid for kMixedPartition.
-    int64_t last_mixed_partition_ = std::numeric_limits<int64_t>::min();
-
-    SpanJoinOperatorTable* table_;
-  };
-
-  SpanJoinOperatorTable(sqlite3*, const TraceStorage*);
-
-  static void RegisterTable(sqlite3* db, const TraceStorage* storage);
-
-  // Table implementation.
-  util::Status Init(int, const char* const*, SqliteTable::Schema*) override;
-  std::unique_ptr<SqliteTable::Cursor> CreateCursor() override;
-  int BestIndex(const QueryConstraints& qc, BestIndexInfo* info) override;
-  int FindFunction(const char* name, FindFunctionFn* fn, void** args) override;
-
- private:
-  // Columns of the span operator table.
-  enum Column {
-    kTimestamp = 0,
-    kDuration = 1,
-    kPartition = 2,
-    // All other columns are dynamic depending on the joined tables.
-  };
-
-  // Enum indicating the possible partitionings of the two tables in span join.
-  enum class PartitioningType {
-    // Used when both tables don't have a partition specified.
-    kNoPartitioning = 0,
-
-    // Used when both tables have the same partition specified.
-    kSamePartitioning = 1,
-
-    // Used when one table has a partition and the other table doesn't.
-    kMixedPartitioning = 2
-  };
-
-  // Parsed version of a table descriptor.
-  struct TableDescriptor {
-    static util::Status Parse(const std::string& raw_descriptor,
-                              TableDescriptor* descriptor);
-
-    bool IsPartitioned() const { return !partition_col.empty(); }
-
-    std::string name;
-    std::string partition_col;
-  };
-
-  // Identifier for a column by index in a given table.
-  struct ColumnLocator {
-    const TableDefinition* defn;
-    size_t col_index;
-  };
-
-  bool IsLeftJoin() const { return name() == "span_left_join"; }
-  bool IsOuterJoin() const { return name() == "span_outer_join"; }
-
-  const std::string& partition_col() const {
-    return t1_defn_.IsPartitioned() ? t1_defn_.partition_col()
-                                    : t2_defn_.partition_col();
-  }
-
-  util::Status CreateTableDefinition(
-      const TableDescriptor& desc,
-      EmitShadowType emit_shadow_type,
-      SpanJoinOperatorTable::TableDefinition* defn);
-
-  std::vector<std::string> ComputeSqlConstraintsForDefinition(
-      const TableDefinition& defn,
-      const QueryConstraints& qc,
-      sqlite3_value** argv);
-
-  std::string GetNameForGlobalColumnIndex(const TableDefinition& defn,
-                                          int global_column);
-
-  void CreateSchemaColsForDefn(const TableDefinition& defn,
-                               std::vector<SqliteTable::Column>* cols);
-
-  TableDefinition t1_defn_;
-  TableDefinition t2_defn_;
-  PartitioningType partitioning_;
-  base::FlatHashMap<size_t, ColumnLocator> global_index_to_column_locator_;
-
-  sqlite3* const db_;
-};
-
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_SQLITE_SPAN_JOIN_OPERATOR_TABLE_H_
diff --git a/src/trace_processor/sqlite/span_join_operator_table_unittest.cc b/src/trace_processor/sqlite/span_join_operator_table_unittest.cc
deleted file mode 100644
index 4c28397..0000000
--- a/src/trace_processor/sqlite/span_join_operator_table_unittest.cc
+++ /dev/null
@@ -1,345 +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/sqlite/span_join_operator_table.h"
-
-#include "test/gtest_and_gmock.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace {
-
-class SpanJoinOperatorTableTest : public ::testing::Test {
- public:
-  SpanJoinOperatorTableTest() {
-    sqlite3* db = nullptr;
-    PERFETTO_CHECK(sqlite3_initialize() == SQLITE_OK);
-    PERFETTO_CHECK(sqlite3_open(":memory:", &db) == SQLITE_OK);
-    db_.reset(db);
-
-    SpanJoinOperatorTable::RegisterTable(db_.get(), nullptr);
-  }
-
-  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);
-  }
-
-  void RunStatement(const std::string& sql) {
-    PrepareValidStatement(sql);
-    ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_DONE);
-  }
-
-  void AssertNextRow(const std::vector<int64_t> elements) {
-    ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
-    for (size_t i = 0; i < elements.size(); ++i) {
-      ASSERT_EQ(sqlite3_column_int64(stmt_.get(), static_cast<int>(i)),
-                elements[i]);
-    }
-  }
-
- protected:
-  ScopedDb db_;
-  ScopedStmt stmt_;
-};
-
-TEST_F(SpanJoinOperatorTableTest, JoinTwoSpanTables) {
-  RunStatement(
-      "CREATE TEMP TABLE f("
-      "ts BIG INT PRIMARY KEY, "
-      "dur BIG INT, "
-      "cpu UNSIGNED INT"
-      ");");
-  RunStatement(
-      "CREATE TEMP TABLE s("
-      "ts BIG INT PRIMARY KEY, "
-      "dur BIG INT, "
-      "cpu UNSIGNED INT"
-      ");");
-  RunStatement(
-      "CREATE VIRTUAL TABLE sp USING span_join(f PARTITIONED cpu, "
-      "s PARTITIONED cpu);");
-
-  RunStatement("INSERT INTO f VALUES(100, 10, 5);");
-  RunStatement("INSERT INTO f VALUES(110, 50, 5);");
-  RunStatement("INSERT INTO f VALUES(120, 100, 2);");
-  RunStatement("INSERT INTO f VALUES(160, 10, 5);");
-
-  RunStatement("INSERT INTO s VALUES(100, 5, 5);");
-  RunStatement("INSERT INTO s VALUES(105, 100, 5);");
-  RunStatement("INSERT INTO s VALUES(110, 50, 2);");
-  RunStatement("INSERT INTO s VALUES(160, 100, 2);");
-
-  PrepareValidStatement("SELECT * FROM sp");
-
-  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 120);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 40);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 2);
-
-  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 160);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 60);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 2);
-
-  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 100);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 5);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 5);
-
-  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 105);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 5);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 5);
-
-  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 110);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 50);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 5);
-
-  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 160);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 10);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 5);
-
-  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_DONE);
-}
-
-TEST_F(SpanJoinOperatorTableTest, NullPartitionKey) {
-  RunStatement(
-      "CREATE TEMP TABLE f("
-      "ts BIG INT PRIMARY KEY, "
-      "dur BIG INT, "
-      "cpu UNSIGNED INT"
-      ");");
-  RunStatement(
-      "CREATE TEMP TABLE s("
-      "ts BIG INT PRIMARY KEY, "
-      "dur BIG INT, "
-      "cpu UNSIGNED INT"
-      ");");
-  RunStatement(
-      "CREATE VIRTUAL TABLE sp USING span_join(f PARTITIONED cpu, "
-      "s PARTITIONED cpu);");
-
-  RunStatement("INSERT INTO f VALUES(30, 20, NULL);");
-  RunStatement("INSERT INTO f VALUES(100, 10, 5);");
-  RunStatement("INSERT INTO f VALUES(110, 50, 5);");
-  RunStatement("INSERT INTO f VALUES(120, 100, 2);");
-  RunStatement("INSERT INTO f VALUES(160, 10, 5);");
-
-  RunStatement("INSERT INTO s VALUES(40, 10, NULL);");
-  RunStatement("INSERT INTO s VALUES(100, 5, 5);");
-  RunStatement("INSERT INTO s VALUES(105, 100, 5);");
-  RunStatement("INSERT INTO s VALUES(110, 50, 2);");
-  RunStatement("INSERT INTO s VALUES(160, 100, 2);");
-
-  PrepareValidStatement("SELECT * FROM sp");
-
-  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 120);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 40);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 2);
-
-  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 160);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 60);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 2);
-
-  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 100);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 5);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 5);
-
-  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 105);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 5);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 5);
-
-  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 110);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 50);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 5);
-
-  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 160);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 10);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 5);
-
-  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_DONE);
-}
-
-TEST_F(SpanJoinOperatorTableTest, MixedPartitioning) {
-  RunStatement(
-      "CREATE TEMP TABLE f("
-      "ts BIG INT PRIMARY KEY, "
-      "dur BIG INT, "
-      "upid UNSIGNED INT"
-      ");");
-  RunStatement(
-      "CREATE TEMP TABLE s("
-      "ts BIG INT PRIMARY KEY, "
-      "dur BIG INT, "
-      "s_val BIG INT"
-      ");");
-  RunStatement(
-      "CREATE VIRTUAL TABLE sp USING span_join(f PARTITIONED upid, s);");
-
-  RunStatement("INSERT INTO f VALUES(30, 20, NULL);");
-  RunStatement("INSERT INTO f VALUES(100, 10, 5);");
-  RunStatement("INSERT INTO f VALUES(110, 50, 5);");
-  RunStatement("INSERT INTO f VALUES(120, 100, 2);");
-  RunStatement("INSERT INTO f VALUES(160, 10, 5);");
-  RunStatement("INSERT INTO f VALUES(300, 100, 2);");
-
-  RunStatement("INSERT INTO s VALUES(100, 5, 11111);");
-  RunStatement("INSERT INTO s VALUES(105, 5, 22222);");
-  RunStatement("INSERT INTO s VALUES(110, 60, 33333);");
-  RunStatement("INSERT INTO s VALUES(320, 10, 44444);");
-
-  PrepareValidStatement("SELECT * FROM sp");
-  AssertNextRow({120, 50, 2, 33333});
-  AssertNextRow({320, 10, 2, 44444});
-  AssertNextRow({100, 5, 5, 11111});
-  AssertNextRow({105, 5, 5, 22222});
-  AssertNextRow({110, 50, 5, 33333});
-  AssertNextRow({160, 10, 5, 33333});
-  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_DONE);
-}
-
-TEST_F(SpanJoinOperatorTableTest, NoPartitioning) {
-  RunStatement(
-      "CREATE TEMP TABLE f("
-      "ts BIG INT PRIMARY KEY, "
-      "dur BIG INT, "
-      "f_val BIG INT"
-      ");");
-  RunStatement(
-      "CREATE TEMP TABLE s("
-      "ts BIG INT PRIMARY KEY, "
-      "dur BIG INT, "
-      "s_val BIG INT"
-      ");");
-  RunStatement("CREATE VIRTUAL TABLE sp USING span_join(f, s);");
-
-  RunStatement("INSERT INTO f VALUES(100, 10, 44444);");
-  RunStatement("INSERT INTO f VALUES(110, 50, 55555);");
-  RunStatement("INSERT INTO f VALUES(160, 10, 44444);");
-
-  RunStatement("INSERT INTO s VALUES(100, 5, 11111);");
-  RunStatement("INSERT INTO s VALUES(105, 5, 22222);");
-  RunStatement("INSERT INTO s VALUES(110, 60, 33333);");
-
-  PrepareValidStatement("SELECT * FROM sp");
-  AssertNextRow({100, 5, 44444, 11111});
-  AssertNextRow({105, 5, 44444, 22222});
-  AssertNextRow({110, 50, 55555, 33333});
-  AssertNextRow({160, 10, 44444, 33333});
-  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_DONE);
-}
-
-TEST_F(SpanJoinOperatorTableTest, LeftJoinTwoSpanTables) {
-  RunStatement(
-      "CREATE TEMP TABLE f("
-      "ts BIG INT PRIMARY KEY, "
-      "dur BIG INT, "
-      "cpu UNSIGNED INT"
-      ");");
-  RunStatement(
-      "CREATE TEMP TABLE s("
-      "ts BIG INT PRIMARY KEY, "
-      "dur BIG INT, "
-      "tid UNSIGNED INT"
-      ");");
-  RunStatement("CREATE VIRTUAL TABLE sp USING span_left_join(f, s);");
-
-  RunStatement("INSERT INTO f VALUES(100, 10, 0);");
-  RunStatement("INSERT INTO f VALUES(110, 50, 1);");
-
-  RunStatement("INSERT INTO s VALUES(100, 5, 1);");
-  RunStatement("INSERT INTO s VALUES(110, 40, 2);");
-  RunStatement("INSERT INTO s VALUES(150, 50, 3);");
-
-  PrepareValidStatement("SELECT * FROM sp");
-
-  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 100);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 5);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 0);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 3), 1);
-
-  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 105);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 5);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 0);
-  ASSERT_EQ(sqlite3_column_type(stmt_.get(), 3), SQLITE_NULL);
-
-  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 110);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 40);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 1);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 3), 2);
-
-  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 150);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 10);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 1);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 3), 3);
-
-  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_DONE);
-}
-
-TEST_F(SpanJoinOperatorTableTest, LeftJoinTwoSpanTables_EmptyRight) {
-  RunStatement(
-      "CREATE TEMP TABLE f("
-      "ts BIG INT PRIMARY KEY, "
-      "dur BIG INT, "
-      "cpu UNSIGNED INT"
-      ");");
-  RunStatement(
-      "CREATE TEMP TABLE s("
-      "ts BIG INT PRIMARY KEY, "
-      "dur BIG INT, "
-      "tid UNSIGNED INT"
-      ");");
-  RunStatement("CREATE VIRTUAL TABLE sp USING span_left_join(f, s);");
-
-  RunStatement("INSERT INTO f VALUES(100, 10, 0);");
-  RunStatement("INSERT INTO f VALUES(110, 50, 1);");
-
-  PrepareValidStatement("SELECT * FROM sp");
-
-  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 100);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 10);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 0);
-  ASSERT_EQ(sqlite3_column_type(stmt_.get(), 3), SQLITE_NULL);
-
-  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_ROW);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 0), 110);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 1), 50);
-  ASSERT_EQ(sqlite3_column_int64(stmt_.get(), 2), 1);
-  ASSERT_EQ(sqlite3_column_type(stmt_.get(), 3), SQLITE_NULL);
-
-  ASSERT_EQ(sqlite3_step(stmt_.get()), SQLITE_DONE);
-}
-
-}  // namespace
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/sqlite/sqlite3_str_split.cc b/src/trace_processor/sqlite/sqlite3_str_split.cc
deleted file mode 100644
index 7907e9b..0000000
--- a/src/trace_processor/sqlite/sqlite3_str_split.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.
- */
-
-#include "src/trace_processor/sqlite/sqlite3_str_split.h"
-
-#include "src/trace_processor/sqlite/sqlite_utils.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-namespace {
-constexpr char kDelimiterError[] =
-    "str_split: delimiter must be a non-empty string";
-constexpr char kSplitFieldIndexError[] =
-    "str_split: field number must be a non-negative integer";
-
-void sqlite_str_split(sqlite3_context* context,
-                      int argc,
-                      sqlite3_value** argv) {
-  PERFETTO_DCHECK(argc == 3);
-  if (sqlite3_value_type(argv[1]) != SQLITE_TEXT) {
-    sqlite3_result_error(context, kDelimiterError, -1);
-    return;
-  }
-  const char* delimiter =
-      reinterpret_cast<const char*>(sqlite3_value_text(argv[1]));
-  const size_t delimiter_len = strlen(delimiter);
-  if (delimiter_len == 0) {
-    sqlite3_result_error(context, kDelimiterError, -1);
-    return;
-  }
-  if (sqlite3_value_type(argv[2]) != SQLITE_INTEGER) {
-    sqlite3_result_error(context, kSplitFieldIndexError, -1);
-    return;
-  }
-  int fld = sqlite3_value_int(argv[2]);
-  if (fld < 0) {
-    sqlite3_result_error(context, kSplitFieldIndexError, -1);
-    return;
-  }
-  if (sqlite3_value_type(argv[0]) != SQLITE_TEXT) {
-    sqlite3_result_null(context);
-    return;
-  }
-  const char* in = reinterpret_cast<const char*>(sqlite3_value_text(argv[0]));
-  const char* next;
-  do {
-    next = strstr(in, delimiter);
-    if (fld == 0) {
-      int size = next != nullptr ? static_cast<int>(next - in)
-                                 : static_cast<int>(strlen(in));
-      sqlite3_result_text(context, in, size, sqlite_utils::kSqliteTransient);
-      return;
-    } else if (next == nullptr) {
-      break;
-    }
-    in = next + delimiter_len;
-    --fld;
-  } while (fld >= 0);
-  sqlite3_result_null(context);
-}
-}  // namespace
-
-void sqlite3_str_split_init(sqlite3* db) {
-  PERFETTO_CHECK(sqlite3_create_function(db, "str_split", 3,
-                                         SQLITE_UTF8 | SQLITE_DETERMINISTIC,
-                                         nullptr, &sqlite_str_split, nullptr,
-                                         nullptr) == SQLITE_OK);
-}
-
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/sqlite/sqlite3_str_split.h b/src/trace_processor/sqlite/sqlite3_str_split.h
deleted file mode 100644
index c1aa7cf..0000000
--- a/src/trace_processor/sqlite/sqlite3_str_split.h
+++ /dev/null
@@ -1,30 +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_SQLITE3_STR_SPLIT_H_
-#define SRC_TRACE_PROCESSOR_SQLITE_SQLITE3_STR_SPLIT_H_
-
-struct sqlite3;
-
-namespace perfetto {
-namespace trace_processor {
-
-void sqlite3_str_split_init(sqlite3* db);
-
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_SQLITE_SQLITE3_STR_SPLIT_H_
diff --git a/src/trace_processor/sqlite/sqlite3_str_split_unittest.cc b/src/trace_processor/sqlite/sqlite3_str_split_unittest.cc
deleted file mode 100644
index 35e0003..0000000
--- a/src/trace_processor/sqlite/sqlite3_str_split_unittest.cc
+++ /dev/null
@@ -1,96 +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/sqlite3_str_split.h"
-
-#include <sqlite3.h>
-#include <string>
-
-#include "perfetto/base/logging.h"
-#include "src/trace_processor/sqlite/scoped_db.h"
-#include "test/gtest_and_gmock.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace {
-
-class Sqlite3StrSplitTest : public ::testing::Test {
- public:
-  Sqlite3StrSplitTest() {
-    sqlite3* db = nullptr;
-    PERFETTO_CHECK(sqlite3_initialize() == SQLITE_OK);
-    PERFETTO_CHECK(sqlite3_open(":memory:", &db) == SQLITE_OK);
-    db_.reset(db);
-    sqlite3_str_split_init(db_.get());
-  }
-
-  const char* SplitStmt(const std::string& str,
-                        const std::string& delim,
-                        int field) {
-    const std::string sql = "SELECT STR_SPLIT(\"" + str + "\", \"" + delim +
-                            "\", " + std::to_string(field) + ");";
-    sqlite3_stmt* stmt = nullptr;
-    PERFETTO_CHECK(sqlite3_prepare_v2(*db_, sql.c_str(),
-                                      static_cast<int>(sql.size()), &stmt,
-                                      nullptr) == SQLITE_OK);
-    stmt_.reset(stmt);
-    PERFETTO_CHECK(sqlite3_step(stmt) == SQLITE_ROW);
-    if (sqlite3_column_type(stmt, 0) == SQLITE_NULL) {
-      return nullptr;
-    }
-    return reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
-  }
-
- protected:
-  ScopedDb db_;
-  ScopedStmt stmt_;
-};
-
-TEST_F(Sqlite3StrSplitTest, SplitNoDelimiter) {
-  ASSERT_STREQ(SplitStmt("abc", ":", 0), "abc");
-  ASSERT_EQ(SplitStmt("abc", ":", 1), nullptr);
-}
-
-TEST_F(Sqlite3StrSplitTest, SplitSingleCharDelim) {
-  ASSERT_STREQ(SplitStmt("a:bc", ":", 0), "a");
-  ASSERT_STREQ(SplitStmt("a:bc", ":", 1), "bc");
-  ASSERT_EQ(SplitStmt("a:bc", ":", 2), nullptr);
-}
-
-TEST_F(Sqlite3StrSplitTest, SplitInputConsecutiveDelim) {
-  ASSERT_STREQ(SplitStmt("a::b::c", ":", 0), "a");
-  ASSERT_STREQ(SplitStmt("a::b::c", ":", 1), "");
-  ASSERT_STREQ(SplitStmt("a::b::c", ":", 2), "b");
-  ASSERT_STREQ(SplitStmt("a::b::c", ":", 3), "");
-  ASSERT_STREQ(SplitStmt("a::b::c", ":", 4), "c");
-  ASSERT_EQ(SplitStmt("a::b::c", ":", 5), nullptr);
-}
-
-TEST_F(Sqlite3StrSplitTest, SplitStringDelim) {
-  ASSERT_STREQ(SplitStmt("abczzdefzzghi", "zz", 0), "abc");
-  ASSERT_STREQ(SplitStmt("abczzdefzzghi", "zz", 1), "def");
-  ASSERT_STREQ(SplitStmt("abczzdefzzghi", "zz", 2), "ghi");
-}
-
-TEST_F(Sqlite3StrSplitTest, SplitEmptyInput) {
-  ASSERT_STREQ(SplitStmt("", "zz", 0), "");
-  ASSERT_EQ(SplitStmt("", "zz", 1), nullptr);
-  ASSERT_EQ(SplitStmt("", "zz", 1000), nullptr);
-}
-
-}  // namespace
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/sqlite/sqlite_raw_table.cc b/src/trace_processor/sqlite/sqlite_raw_table.cc
index a42d83b..f6f0945 100644
--- a/src/trace_processor/sqlite/sqlite_raw_table.cc
+++ b/src/trace_processor/sqlite/sqlite_raw_table.cc
@@ -37,6 +37,7 @@
 #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
 #include "protos/perfetto/trace/ftrace/g2d.pbzero.h"
 #include "protos/perfetto/trace/ftrace/irq.pbzero.h"
+#include "protos/perfetto/trace/ftrace/mdss.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"
@@ -204,10 +205,12 @@
     WriteArgForField(SS::kPrevStateFieldNumber, [this](const Variadic& value) {
       PERFETTO_DCHECK(value.type == Variadic::Type::kInt);
       auto state = static_cast<uint16_t>(value.int_value);
-      auto kernel_version =
+      base::Optional<VersionNumber> kernel_version =
           SystemInfoTracker::GetOrCreate(context_)->GetKernelVersion();
       writer_->AppendString(
-          ftrace_utils::TaskState(state, kernel_version).ToString('|').data());
+          ftrace_utils::TaskState::FromRawPrevState(state, kernel_version)
+              .ToString('|')
+              .data());
     });
     writer_->AppendLiteral(" ==>");
     WriteArgForField(SS::kNextCommFieldNumber, DVW());
@@ -430,6 +433,18 @@
     });
     writer_->AppendString("]");
     return;
+  } else if (event_name_ == "tracing_mark_write") {
+    using TMW = protos::pbzero::TracingMarkWriteFtraceEvent;
+    WriteValueForField(TMW::kTraceBeginFieldNumber,
+                       [this](const Variadic& value) {
+                         PERFETTO_DCHECK(value.type == Variadic::Type::kUint);
+                         writer_->AppendChar(value.uint_value ? 'B' : 'E');
+                       });
+    writer_->AppendString("|");
+    WriteValueForField(TMW::kPidFieldNumber, DVW());
+    writer_->AppendString("|");
+    WriteValueForField(TMW::kTraceNameFieldNumber, DVW());
+    return;
   } else if (event_name_ == "dpu_tracing_mark_write") {
     using TMW = protos::pbzero::DpuTracingMarkWriteFtraceEvent;
     WriteValueForField(TMW::kTypeFieldNumber, [this](const Variadic& value) {
@@ -524,10 +539,9 @@
 }  // namespace
 
 SqliteRawTable::SqliteRawTable(sqlite3* db, Context context)
-    : DbSqliteTable(
-          db,
-          {context.cache, tables::RawTable::Schema(), TableComputation::kStatic,
-           &context.context->storage->raw_table(), nullptr}),
+    : DbSqliteTable(db,
+                    {context.cache, TableComputation::kStatic,
+                     &context.context->storage->raw_table(), nullptr}),
       serializer_(context.context) {
   auto fn = [](sqlite3_context* ctx, int argc, sqlite3_value** argv) {
     auto* thiz = static_cast<SqliteRawTable*>(sqlite3_user_data(ctx));
diff --git a/src/trace_processor/sqlite/sqlite_table.cc b/src/trace_processor/sqlite/sqlite_table.cc
index a12f1e6..a0c5ce9 100644
--- a/src/trace_processor/sqlite/sqlite_table.cc
+++ b/src/trace_processor/sqlite/sqlite_table.cc
@@ -22,18 +22,19 @@
 #include <map>
 
 #include "perfetto/base/logging.h"
+#include "src/trace_processor/tp_metatrace.h"
 
 namespace perfetto {
 namespace trace_processor {
 
 namespace {
 
-std::string TypeToString(SqlValue::Type type) {
+std::string TypeToSqlString(SqlValue::Type type) {
   switch (type) {
     case SqlValue::Type::kString:
       return "TEXT";
     case SqlValue::Type::kLong:
-      return "BIG INT";
+      return "BIGINT";
     case SqlValue::Type::kDouble:
       return "DOUBLE";
     case SqlValue::Type::kBytes:
@@ -44,7 +45,7 @@
   PERFETTO_FATAL("Not reached");  // For gcc
 }
 
-std::string OpToString(int op) {
+std::string OpToDebugString(int op) {
   switch (op) {
     case SQLITE_INDEX_CONSTRAINT_EQ:
       return "=";
@@ -66,11 +67,47 @@
       return "is not null";
     case SQLITE_INDEX_CONSTRAINT_GLOB:
       return "glob";
+    case SQLITE_INDEX_CONSTRAINT_LIMIT:
+      return "limit";
+    case SQLITE_INDEX_CONSTRAINT_OFFSET:
+      return "offset";
+    case SqliteTable::CustomFilterOpcode::kSourceGeqOpCode:
+      return "source_geq";
     default:
       PERFETTO_FATAL("Operator to string conversion not impemented for %d", op);
   }
 }
 
+void ConstraintsToString(const QueryConstraints& qc,
+                         const SqliteTable::Schema& schema,
+                         std::string& out) {
+  bool is_first = true;
+  for (const auto& cs : qc.constraints()) {
+    if (!is_first) {
+      out.append(",");
+    }
+    out.append(schema.columns()[static_cast<size_t>(cs.column)].name());
+    out.append(" ");
+    out.append(OpToDebugString(cs.op));
+    is_first = false;
+  }
+}
+
+void OrderByToString(const QueryConstraints& qc,
+                     const SqliteTable::Schema& schema,
+                     std::string& out) {
+  bool is_first = true;
+  for (const auto& ob : qc.order_by()) {
+    if (!is_first) {
+      out.append(",");
+    }
+    out.append(schema.columns()[static_cast<size_t>(ob.iColumn)].name());
+    out.append(" ");
+    out.append(std::to_string(ob.desc));
+    is_first = false;
+  }
+}
+
 std::string QcDebugStr(const QueryConstraints& qc,
                        const SqliteTable::Schema& schema) {
   std::string str_result;
@@ -79,24 +116,14 @@
   str_result.append("C");
   str_result.append(std::to_string(qc.constraints().size()));
   str_result.append(",");
-  for (const auto& cs : qc.constraints()) {
-    str_result.append(schema.columns()[static_cast<size_t>(cs.column)].name());
-    str_result.append(" ");
-    str_result.append(OpToString(cs.op));
-    str_result.append(",");
-  }
-  str_result.back() = ';';
+  ConstraintsToString(qc, schema, str_result);
+  str_result.append(";");
 
   str_result.append("O");
   str_result.append(std::to_string(qc.order_by().size()));
   str_result.append(",");
-  for (const auto& ob : qc.order_by()) {
-    str_result.append(schema.columns()[static_cast<size_t>(ob.iColumn)].name());
-    str_result.append(" ");
-    str_result.append(std::to_string(ob.desc));
-    str_result.append(",");
-  }
-  str_result.back() = ';';
+  OrderByToString(qc, schema, str_result);
+  str_result.append(";");
 
   str_result.append("U");
   str_result.append(std::to_string(qc.cols_used()));
@@ -104,6 +131,20 @@
   return str_result;
 }
 
+void WriteQueryConstraintsToMetatrace(metatrace::Record* r,
+                                      const QueryConstraints& qc,
+                                      const SqliteTable::Schema& schema) {
+  r->AddArg("constraint_count", std::to_string(qc.constraints().size()));
+  std::string constraints;
+  ConstraintsToString(qc, schema, constraints);
+  r->AddArg("constraints", constraints);
+  r->AddArg("order_by_count", std::to_string(qc.order_by().size()));
+  std::string order_by;
+  OrderByToString(qc, schema, order_by);
+  r->AddArg("order_by", order_by);
+  r->AddArg("columns_used", std::to_string(qc.cols_used()));
+}
+
 }  // namespace
 
 // static
@@ -167,6 +208,17 @@
     u.argvIndex = static_cast<int>(i) + 1;
   }
 
+  PERFETTO_TP_TRACE(
+      metatrace::Category::QUERY, "SQLITE_TABLE_BEST_INDEX",
+      [&](metatrace::Record* r) {
+        r->AddArg("name", name_);
+        WriteQueryConstraintsToMetatrace(r, qc, schema());
+        r->AddArg("order_by_consumed", std::to_string(idx->orderByConsumed));
+        r->AddArg("estimated_cost", std::to_string(idx->estimatedCost));
+        r->AddArg("estimated_rows",
+                  std::to_string(static_cast<int64_t>(idx->estimatedRows)));
+      });
+
   auto out_qc_str = qc.ToNewSqlite3String();
   if (SqliteTable::debug) {
     PERFETTO_LOG(
@@ -203,6 +255,15 @@
     cache_hit = false;
   }
 
+  PERFETTO_TP_TRACE(metatrace::Category::QUERY, "SQLITE_TABLE_READ_CONSTRAINTS",
+                    [&](metatrace::Record* r) {
+                      r->AddArg("cache_hit", std::to_string(cache_hit));
+                      r->AddArg("name", name_);
+                      WriteQueryConstraintsToMetatrace(r, qc_cache_, schema_);
+                      r->AddArg("raw_constraints", idxStr);
+                      r->AddArg("argc", std::to_string(argc));
+                    });
+
   // Logging this every ReadConstraints just leads to log spam on joins making
   // it unusable. Instead, only print this out when we miss the cache (which
   // happens precisely when the constraint set from SQLite changes.)
@@ -252,7 +313,7 @@
     stmt += " " + col.name();
 
     if (col.type() != SqlValue::Type::kNull) {
-      stmt += " " + TypeToString(col.type());
+      stmt += " " + TypeToSqlString(col.type());
     } else if (std::find(primary_keys_.begin(), primary_keys_.end(), i) !=
                primary_keys_.end()) {
       PERFETTO_FATAL("Unknown type for primary key column %s",
diff --git a/src/trace_processor/sqlite/sqlite_table.h b/src/trace_processor/sqlite/sqlite_table.h
index a8c5a9b..2d9ed50 100644
--- a/src/trace_processor/sqlite/sqlite_table.h
+++ b/src/trace_processor/sqlite/sqlite_table.h
@@ -45,6 +45,13 @@
   using Factory =
       std::function<std::unique_ptr<SqliteTable>(sqlite3*, Context)>;
 
+  // Custom opcodes used by subclasses of SqliteTable.
+  // Stored here as we need a central repository of opcodes to prevent clashes
+  // between different sub-classes.
+  enum CustomFilterOpcode {
+    kSourceGeqOpCode = SQLITE_INDEX_CONSTRAINT_FUNCTION + 1,
+  };
+
   // Describes a column of this table.
   class Column {
    public:
@@ -182,7 +189,6 @@
   struct TableDescriptor {
     SqliteTable::Factory<Context> factory;
     Context context;
-    std::string name;
     sqlite3_module module = {};
   };
 
@@ -197,7 +203,7 @@
   template <typename TTable, typename Context = const TraceStorage*>
   static void Register(sqlite3* db,
                        Context ctx,
-                       const std::string& table_name,
+                       const std::string& module_name,
                        bool read_write = false,
                        bool requires_args = false) {
     using TCursor = typename TTable::Cursor;
@@ -206,7 +212,6 @@
         new TableDescriptor<Context>());
     desc->context = std::move(ctx);
     desc->factory = GetFactory<TTable, Context>();
-    desc->name = table_name;
     sqlite3_module* module = &desc->module;
     memset(module, 0, sizeof(*module));
 
@@ -215,7 +220,16 @@
                         char** pzErr) {
       auto* xdesc = static_cast<TableDescriptor<Context>*>(arg);
       auto table = xdesc->factory(xdb, std::move(xdesc->context));
-      table->name_ = xdesc->name;
+
+      // SQLite guarantees that argv[0] will be the "module" name: this is the
+      // same as |table_name| passed to the Register function.
+      table->module_name_ = argv[0];
+
+      // SQLite guarantees that argv[2] contains the name of the table: for
+      // non-arg taking tables, this will be the same as |table_name| but for
+      // arg-taking tables, this will be the table name as defined by the user
+      // in the CREATE VIRTUAL TABLE call.
+      table->name_ = argv[2];
 
       Schema schema;
       base::Status status = table->Init(argc, argv, &schema);
@@ -291,7 +305,7 @@
     }
 
     int res = sqlite3_create_module_v2(
-        db, table_name.c_str(), module, desc.release(),
+        db, module_name.c_str(), module, desc.release(),
         [](void* arg) { delete static_cast<TableDescriptor<Context>*>(arg); });
     PERFETTO_CHECK(res == SQLITE_OK);
 
@@ -300,8 +314,9 @@
     // that virtual tables requiring arguments aren't registered because they
     // can't be automatically instantiated for exporting.
     if (!requires_args) {
-      char* insert_sql = sqlite3_mprintf(
-          "INSERT INTO perfetto_tables(name) VALUES('%q')", table_name.c_str());
+      char* insert_sql =
+          sqlite3_mprintf("INSERT INTO perfetto_tables(name) VALUES('%q')",
+                          module_name.c_str());
       char* error = nullptr;
       sqlite3_exec(db, insert_sql, nullptr, nullptr, &error);
       sqlite3_free(insert_sql);
@@ -331,6 +346,7 @@
   }
 
   const Schema& schema() const { return schema_; }
+  const std::string& module_name() const { return module_name_; }
   const std::string& name() const { return name_; }
 
  private:
@@ -350,7 +366,17 @@
   SqliteTable(const SqliteTable&) = delete;
   SqliteTable& operator=(const SqliteTable&) = delete;
 
+  // This name of the table. For tables created using CREATE VIRTUAL TABLE, this
+  // will be the name of the table specified by the query. For automatically
+  // created tables, this will be the same as the module name passed to
+  // RegisterTable.
   std::string name_;
+
+  // The module name is the name passed to RegisterTable. This is differs from
+  // the table name (|name_|) where the table was created using CREATE VIRTUAL
+  // TABLE.
+  std::string module_name_;
+
   Schema schema_;
 
   QueryConstraints qc_cache_;
diff --git a/src/trace_processor/sqlite/sqlite_utils.cc b/src/trace_processor/sqlite/sqlite_utils.cc
new file mode 100644
index 0000000..006e4f9
--- /dev/null
+++ b/src/trace_processor/sqlite/sqlite_utils.cc
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2022 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_utils.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace sqlite_utils {
+
+std::wstring SqliteValueToWString(sqlite3_value* value) {
+  PERFETTO_CHECK(sqlite3_value_type(value) == SQLITE_TEXT);
+  int len = sqlite3_value_bytes16(value);
+  PERFETTO_CHECK(len >= 0);
+  size_t count = static_cast<size_t>(len) / sizeof(wchar_t);
+  return std::wstring(
+      reinterpret_cast<const wchar_t*>(sqlite3_value_text16(value)), count);
+}
+
+base::Status GetColumnsForTable(sqlite3* db,
+                                const std::string& raw_table_name,
+                                std::vector<SqliteTable::Column>& columns) {
+  PERFETTO_DCHECK(columns.empty());
+  char sql[1024];
+  const char kRawSql[] = "SELECT name, type from pragma_table_info(\"%s\")";
+
+  // Support names which are table valued functions with arguments.
+  std::string table_name = raw_table_name.substr(0, raw_table_name.find('('));
+  size_t n = base::SprintfTrunc(sql, sizeof(sql), kRawSql, table_name.c_str());
+  PERFETTO_DCHECK(n > 0);
+
+  sqlite3_stmt* raw_stmt = nullptr;
+  int err =
+      sqlite3_prepare_v2(db, sql, static_cast<int>(n), &raw_stmt, nullptr);
+  if (err != SQLITE_OK) {
+    return base::ErrStatus("Preparing database failed");
+  }
+  ScopedStmt stmt(raw_stmt);
+  PERFETTO_DCHECK(sqlite3_column_count(*stmt) == 2);
+
+  for (;;) {
+    err = sqlite3_step(raw_stmt);
+    if (err == SQLITE_DONE)
+      break;
+    if (err != SQLITE_ROW) {
+      return base::ErrStatus("Querying schema of table %s failed",
+                             raw_table_name.c_str());
+    }
+
+    const char* name =
+        reinterpret_cast<const char*>(sqlite3_column_text(*stmt, 0));
+    const char* raw_type =
+        reinterpret_cast<const char*>(sqlite3_column_text(*stmt, 1));
+    if (!name || !raw_type || !*name) {
+      return base::ErrStatus("Schema for %s has invalid column values",
+                             raw_table_name.c_str());
+    }
+
+    SqlValue::Type type;
+    if (base::CaseInsensitiveEqual(raw_type, "STRING") ||
+        base::CaseInsensitiveEqual(raw_type, "TEXT")) {
+      type = SqlValue::Type::kString;
+    } else if (base::CaseInsensitiveEqual(raw_type, "DOUBLE")) {
+      type = SqlValue::Type::kDouble;
+    } else if (base::CaseInsensitiveEqual(raw_type, "BIG INT") ||
+               base::CaseInsensitiveEqual(raw_type, "BIGINT") ||
+               base::CaseInsensitiveEqual(raw_type, "UNSIGNED INT") ||
+               base::CaseInsensitiveEqual(raw_type, "INT") ||
+               base::CaseInsensitiveEqual(raw_type, "BOOLEAN") ||
+               base::CaseInsensitiveEqual(raw_type, "INTEGER")) {
+      type = SqlValue::Type::kLong;
+    } else if (!*raw_type) {
+      PERFETTO_DLOG("Unknown column type for %s %s", raw_table_name.c_str(),
+                    name);
+      type = SqlValue::Type::kNull;
+    } else {
+      return base::ErrStatus("Unknown column type '%s' on table %s", raw_type,
+                             raw_table_name.c_str());
+    }
+    columns.emplace_back(columns.size(), name, type);
+  }
+
+  // Catch mis-spelt table names.
+  //
+  // A SELECT on pragma_table_info() returns no rows if the
+  // table that was queried is not present.
+  if (columns.empty()) {
+    return base::ErrStatus("Unknown table or view name '%s'",
+                           raw_table_name.c_str());
+  }
+
+  return base::OkStatus();
+}
+
+const char* SqliteTypeToFriendlyString(SqlValue::Type type) {
+  switch (type) {
+    case SqlValue::Type::kNull:
+      return "NULL";
+    case SqlValue::Type::kLong:
+      return "BOOL/INT/UINT/LONG";
+    case SqlValue::Type::kDouble:
+      return "FLOAT/DOUBLE";
+    case SqlValue::Type::kString:
+      return "STRING";
+    case SqlValue::Type::kBytes:
+      return "BYTES/PROTO";
+  }
+  PERFETTO_FATAL("For GCC");
+}
+
+base::Status TypeCheckSqliteValue(sqlite3_value* value,
+                                  SqlValue::Type expected_type) {
+  return TypeCheckSqliteValue(value, expected_type,
+                              SqliteTypeToFriendlyString(expected_type));
+}
+
+base::Status TypeCheckSqliteValue(sqlite3_value* value,
+                                  SqlValue::Type expected_type,
+                                  const char* expected_type_str) {
+  SqlValue::Type actual_type =
+      sqlite_utils::SqliteTypeToSqlValueType(sqlite3_value_type(value));
+  if (actual_type != SqlValue::Type::kNull && actual_type != expected_type) {
+    return base::ErrStatus(
+        "does not have expected type: expected %s, actual %s",
+        expected_type_str, SqliteTypeToFriendlyString(actual_type));
+  }
+  return base::OkStatus();
+}
+
+template <typename T>
+base::Status ExtractFromSqlValueInt(const SqlValue& value,
+                                    base::Optional<T>& out) {
+  if (value.is_null()) {
+    out = base::nullopt;
+    return base::OkStatus();
+  }
+  if (value.type != SqlValue::kLong) {
+    return base::ErrStatus(
+        "value has type %s which does not match the expected type %s",
+        SqliteTypeToFriendlyString(value.type),
+        SqliteTypeToFriendlyString(SqlValue::kLong));
+  }
+
+  int64_t res = value.AsLong();
+  if (res > std::numeric_limits<T>::max() ||
+      res < std::numeric_limits<T>::min()) {
+    return base::ErrStatus("value %ld does not fit inside the range [%ld, %ld]",
+                           static_cast<long>(res),
+                           static_cast<long>(std::numeric_limits<T>::min()),
+                           static_cast<long>(std::numeric_limits<T>::max()));
+  }
+  out = static_cast<T>(res);
+  return base::OkStatus();
+}
+
+base::Status ExtractFromSqlValue(const SqlValue& value,
+                                 base::Optional<int64_t>& out) {
+  return ExtractFromSqlValueInt(value, out);
+}
+base::Status ExtractFromSqlValue(const SqlValue& value,
+                                 base::Optional<int32_t>& out) {
+  return ExtractFromSqlValueInt(value, out);
+}
+base::Status ExtractFromSqlValue(const SqlValue& value,
+                                 base::Optional<uint32_t>& out) {
+  return ExtractFromSqlValueInt(value, out);
+}
+base::Status ExtractFromSqlValue(const SqlValue& value,
+                                 base::Optional<double>& out) {
+  if (value.is_null()) {
+    out = base::nullopt;
+    return base::OkStatus();
+  }
+  if (value.type != SqlValue::kDouble) {
+    return base::ErrStatus(
+        "value has type %s which does not match the expected type %s",
+        SqliteTypeToFriendlyString(value.type),
+        SqliteTypeToFriendlyString(SqlValue::kDouble));
+  }
+  out = value.AsDouble();
+  return base::OkStatus();
+}
+base::Status ExtractFromSqlValue(const SqlValue& value,
+                                 base::Optional<const char*>& out) {
+  if (value.is_null()) {
+    out = base::nullopt;
+    return base::OkStatus();
+  }
+  if (value.type != SqlValue::kString) {
+    return base::ErrStatus(
+        "value has type %s which does not match the expected type %s",
+        SqliteTypeToFriendlyString(value.type),
+        SqliteTypeToFriendlyString(SqlValue::kString));
+  }
+  out = value.AsString();
+  return base::OkStatus();
+}
+
+}  // namespace sqlite_utils
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/sqlite/sqlite_utils.h b/src/trace_processor/sqlite/sqlite_utils.h
index adafe5b..f09a258 100644
--- a/src/trace_processor/sqlite/sqlite_utils.h
+++ b/src/trace_processor/sqlite/sqlite_utils.h
@@ -19,9 +19,14 @@
 
 #include <math.h>
 #include <sqlite3.h>
+#include <cstddef>
+#include <cstring>
 
+#include "perfetto/base/logging.h"
 #include "perfetto/base/status.h"
+#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_utils.h"
+#include "perfetto/ext/base/string_view.h"
 #include "perfetto/trace_processor/basic_types.h"
 #include "src/trace_processor/sqlite/scoped_db.h"
 #include "src/trace_processor/sqlite/sqlite_table.h"
@@ -132,6 +137,47 @@
   }
 }
 
+inline ScopedSqliteString ExpandedSqlForStmt(sqlite3_stmt* stmt) {
+  return ScopedSqliteString(sqlite3_expanded_sql(stmt));
+}
+
+inline base::Status FormatErrorMessage(base::StringView sql,
+                                       sqlite3* db,
+                                       int error_code) {
+  uint32_t offset = static_cast<uint32_t>(sqlite3_error_offset(db));
+
+  auto error_opt = FindLineWithOffset(sql, offset);
+
+  if (!error_opt.has_value()) {
+    return base::ErrStatus("Error: %s (errcode: %d)", sqlite3_errmsg(db),
+                           error_code);
+  }
+
+  auto error = error_opt.value();
+
+  return base::ErrStatus(
+      "Error in line:%u, col: %u.\n"
+      "%s\n"
+      "%s^\n"
+      "%s (errcode: %d)",
+      error.line_num, error.line_offset + 1, error.line.ToStdString().c_str(),
+      std::string(error.line_offset, ' ').c_str(), sqlite3_errmsg(db),
+      error_code);
+}
+
+inline base::Status FormatErrorMessage(sqlite3_stmt* stmt,
+                                       base::Optional<base::StringView> sql,
+                                       sqlite3* db,
+                                       int error_code) {
+  if (stmt) {
+    auto expanded_sql = ExpandedSqlForStmt(stmt);
+    PERFETTO_CHECK(expanded_sql);
+    return FormatErrorMessage(expanded_sql.get(), db, error_code);
+  }
+  PERFETTO_CHECK(sql.has_value());
+  return FormatErrorMessage(sql.value(), db, error_code);
+}
+
 inline base::Status PrepareStmt(sqlite3* db,
                                 const char* sql,
                                 ScopedStmt* stmt,
@@ -140,7 +186,7 @@
   int err = sqlite3_prepare_v2(db, sql, -1, &raw_stmt, tail);
   stmt->reset(raw_stmt);
   if (err != SQLITE_OK)
-    return base::ErrStatus("%s (errcode: %d)", sqlite3_errmsg(db), err);
+    return base::ErrStatus("%s", FormatErrorMessage(sql, db, err).c_message());
   return base::OkStatus();
 }
 
@@ -158,76 +204,52 @@
   for (err = sqlite3_step(stmt); err == SQLITE_ROW; err = sqlite3_step(stmt)) {
   }
   if (err != SQLITE_DONE) {
-    return base::ErrStatus("%s (errcode: %d)",
-                           sqlite3_errmsg(sqlite3_db_handle(stmt)), err);
+    auto db = sqlite3_db_handle(stmt);
+    return base::ErrStatus(
+        "%s", FormatErrorMessage(stmt, base::nullopt, db, err).c_message());
   }
   return base::OkStatus();
 }
 
-inline base::Status GetColumnsForTable(
-    sqlite3* db,
-    const std::string& raw_table_name,
-    std::vector<SqliteTable::Column>& columns) {
-  PERFETTO_DCHECK(columns.empty());
-  char sql[1024];
-  const char kRawSql[] = "SELECT name, type from pragma_table_info(\"%s\")";
+// Exracts the given type from the SqlValue if |value| can fit
+// in the provided optional. Note that SqlValue::kNull will always
+// succeed and cause base::nullopt to be set.
+//
+// Returns base::ErrStatus if the type does not match or does not
+// fit in the width of the provided optional type (i.e. int64 value
+// not fitting in int32 optional).
+base::Status ExtractFromSqlValue(const SqlValue& value,
+                                 base::Optional<int64_t>&);
+base::Status ExtractFromSqlValue(const SqlValue& value,
+                                 base::Optional<int32_t>&);
+base::Status ExtractFromSqlValue(const SqlValue& value,
+                                 base::Optional<uint32_t>&);
+base::Status ExtractFromSqlValue(const SqlValue& value,
+                                 base::Optional<double>&);
+base::Status ExtractFromSqlValue(const SqlValue& value,
+                                 base::Optional<const char*>&);
 
-  // Support names which are table valued functions with arguments.
-  std::string table_name = raw_table_name.substr(0, raw_table_name.find('('));
-  size_t n = base::SprintfTrunc(sql, sizeof(sql), kRawSql, table_name.c_str());
-  PERFETTO_DCHECK(n > 0);
+// Returns the column names for the table named by |raw_table_name|.
+base::Status GetColumnsForTable(sqlite3* db,
+                                const std::string& raw_table_name,
+                                std::vector<SqliteTable::Column>& columns);
 
-  sqlite3_stmt* raw_stmt = nullptr;
-  int err =
-      sqlite3_prepare_v2(db, sql, static_cast<int>(n), &raw_stmt, nullptr);
-  if (err != SQLITE_OK) {
-    return base::ErrStatus("Preparing database failed");
-  }
-  ScopedStmt stmt(raw_stmt);
-  PERFETTO_DCHECK(sqlite3_column_count(*stmt) == 2);
+// Reads a `SQLITE_TEXT` value and returns it as a wstring (UTF-16) in the
+// default byte order. `value` must be a `SQLITE_TEXT`.
+std::wstring SqliteValueToWString(sqlite3_value* value);
 
-  for (;;) {
-    err = sqlite3_step(raw_stmt);
-    if (err == SQLITE_DONE)
-      break;
-    if (err != SQLITE_ROW) {
-      return base::ErrStatus("Querying schema of table %s failed",
-                             raw_table_name.c_str());
-    }
+// Given an SqlValue::Type, converts it to a human-readable string.
+// This should really only be used for debugging messages.
+const char* SqliteTypeToFriendlyString(SqlValue::Type type);
 
-    const char* name =
-        reinterpret_cast<const char*>(sqlite3_column_text(*stmt, 0));
-    const char* raw_type =
-        reinterpret_cast<const char*>(sqlite3_column_text(*stmt, 1));
-    if (!name || !raw_type || !*name) {
-      return base::ErrStatus("Schema for %s has invalid column values",
-                             raw_table_name.c_str());
-    }
-
-    SqlValue::Type type;
-    if (base::CaseInsensitiveEqual(raw_type, "STRING") ||
-        base::CaseInsensitiveEqual(raw_type, "TEXT")) {
-      type = SqlValue::Type::kString;
-    } else if (base::CaseInsensitiveEqual(raw_type, "DOUBLE")) {
-      type = SqlValue::Type::kDouble;
-    } else if (base::CaseInsensitiveEqual(raw_type, "BIG INT") ||
-               base::CaseInsensitiveEqual(raw_type, "UNSIGNED INT") ||
-               base::CaseInsensitiveEqual(raw_type, "INT") ||
-               base::CaseInsensitiveEqual(raw_type, "BOOLEAN") ||
-               base::CaseInsensitiveEqual(raw_type, "INTEGER")) {
-      type = SqlValue::Type::kLong;
-    } else if (!*raw_type) {
-      PERFETTO_DLOG("Unknown column type for %s %s", raw_table_name.c_str(),
-                    name);
-      type = SqlValue::Type::kNull;
-    } else {
-      return base::ErrStatus("Unknown column type '%s' on table %s", raw_type,
-                             raw_table_name.c_str());
-    }
-    columns.emplace_back(columns.size(), name, type);
-  }
-  return base::OkStatus();
-}
+// Verifies if |value| has the type represented by |expected_type|.
+// Returns base::OkStatus if it does or an base::ErrStatus with an
+// appropriate error mesage (incorporating |expected_type_str| if specified).
+base::Status TypeCheckSqliteValue(sqlite3_value* value,
+                                  SqlValue::Type expected_type);
+base::Status TypeCheckSqliteValue(sqlite3_value* value,
+                                  SqlValue::Type expected_type,
+                                  const char* expected_type_str);
 
 }  // namespace sqlite_utils
 }  // namespace trace_processor
diff --git a/src/trace_processor/sqlite/sqlite_utils_unittest.cc b/src/trace_processor/sqlite/sqlite_utils_unittest.cc
index 1d1de13..b52df66 100644
--- a/src/trace_processor/sqlite/sqlite_utils_unittest.cc
+++ b/src/trace_processor/sqlite/sqlite_utils_unittest.cc
@@ -20,6 +20,8 @@
 
 namespace perfetto {
 namespace trace_processor {
+namespace sqlite_utils {
+
 namespace {
 
 class GetColumnsForTableTest : public ::testing::Test {
@@ -66,6 +68,116 @@
   ASSERT_FALSE(status.ok());
 }
 
+TEST_F(GetColumnsForTableTest, UnknownTableName) {
+  std::vector<SqliteTable::Column> columns;
+  auto status = sqlite_utils::GetColumnsForTable(*db_, "unknowntable", columns);
+  ASSERT_FALSE(status.ok());
+}
+
+TEST(SqliteUtilsTest, ExtractFromSqlValueInt32) {
+  base::Optional<int32_t> int32;
+
+  static constexpr int64_t kMin = std::numeric_limits<int32_t>::min();
+  static constexpr int64_t kMax = std::numeric_limits<int32_t>::max();
+
+  ASSERT_TRUE(ExtractFromSqlValue(SqlValue::Long(1234), int32).ok());
+  ASSERT_EQ(*int32, 1234);
+
+  ASSERT_TRUE(ExtractFromSqlValue(SqlValue::Long(kMin), int32).ok());
+  ASSERT_EQ(*int32, kMin);
+
+  ASSERT_TRUE(ExtractFromSqlValue(SqlValue::Long(kMax), int32).ok());
+  ASSERT_EQ(*int32, kMax);
+
+  ASSERT_TRUE(ExtractFromSqlValue(SqlValue(), int32).ok());
+  ASSERT_FALSE(int32.has_value());
+
+  ASSERT_FALSE(ExtractFromSqlValue(SqlValue::Long(kMax + 1), int32).ok());
+  ASSERT_FALSE(ExtractFromSqlValue(SqlValue::Double(1.0), int32).ok());
+  ASSERT_FALSE(ExtractFromSqlValue(SqlValue::String("foo"), int32).ok());
+}
+
+TEST(SqliteUtilsTest, ExtractFromSqlValueUint32) {
+  base::Optional<uint32_t> uint32;
+
+  static constexpr int64_t kMin = std::numeric_limits<uint32_t>::min();
+  static constexpr int64_t kMax = std::numeric_limits<uint32_t>::max();
+
+  ASSERT_TRUE(ExtractFromSqlValue(SqlValue::Long(1234), uint32).ok());
+  ASSERT_EQ(*uint32, 1234u);
+
+  ASSERT_TRUE(ExtractFromSqlValue(SqlValue::Long(kMin), uint32).ok());
+  ASSERT_EQ(*uint32, kMin);
+
+  ASSERT_TRUE(ExtractFromSqlValue(SqlValue::Long(kMax), uint32).ok());
+  ASSERT_EQ(*uint32, kMax);
+
+  ASSERT_TRUE(ExtractFromSqlValue(SqlValue(), uint32).ok());
+  ASSERT_FALSE(uint32.has_value());
+
+  ASSERT_FALSE(ExtractFromSqlValue(SqlValue::Long(kMax + 1), uint32).ok());
+  ASSERT_FALSE(ExtractFromSqlValue(SqlValue::Double(1.0), uint32).ok());
+  ASSERT_FALSE(ExtractFromSqlValue(SqlValue::String("foo"), uint32).ok());
+}
+
+TEST(SqliteUtilsTest, ExtractFromSqlValueInt64) {
+  base::Optional<int64_t> int64;
+
+  static constexpr int64_t kMin = std::numeric_limits<int64_t>::min();
+  static constexpr int64_t kMax = std::numeric_limits<int64_t>::max();
+
+  ASSERT_TRUE(ExtractFromSqlValue(SqlValue::Long(1234), int64).ok());
+  ASSERT_EQ(*int64, 1234);
+
+  ASSERT_TRUE(ExtractFromSqlValue(SqlValue::Long(kMin), int64).ok());
+  ASSERT_EQ(*int64, kMin);
+
+  ASSERT_TRUE(ExtractFromSqlValue(SqlValue::Long(kMax), int64).ok());
+  ASSERT_EQ(*int64, kMax);
+
+  ASSERT_TRUE(ExtractFromSqlValue(SqlValue(), int64).ok());
+  ASSERT_FALSE(int64.has_value());
+
+  ASSERT_FALSE(ExtractFromSqlValue(SqlValue::Double(1.0), int64).ok());
+  ASSERT_FALSE(ExtractFromSqlValue(SqlValue::String("foo"), int64).ok());
+}
+
+TEST(SqliteUtilsTest, ExtractFromSqlValueDouble) {
+  base::Optional<double> doub;
+
+  static constexpr double kMin = std::numeric_limits<double>::min();
+  static constexpr double kMax = std::numeric_limits<double>::max();
+
+  ASSERT_TRUE(ExtractFromSqlValue(SqlValue::Double(1234.1), doub).ok());
+  ASSERT_EQ(*doub, 1234.1);
+
+  ASSERT_TRUE(ExtractFromSqlValue(SqlValue::Double(kMin), doub).ok());
+  ASSERT_EQ(*doub, kMin);
+
+  ASSERT_TRUE(ExtractFromSqlValue(SqlValue::Double(kMax), doub).ok());
+  ASSERT_EQ(*doub, kMax);
+
+  ASSERT_TRUE(ExtractFromSqlValue(SqlValue(), doub).ok());
+  ASSERT_FALSE(doub.has_value());
+
+  ASSERT_FALSE(ExtractFromSqlValue(SqlValue::Long(1234), doub).ok());
+  ASSERT_FALSE(ExtractFromSqlValue(SqlValue::String("foo"), doub).ok());
+}
+
+TEST(SqliteUtilsTest, ExtractFromSqlValueString) {
+  base::Optional<const char*> string;
+
+  ASSERT_TRUE(ExtractFromSqlValue(SqlValue::String("foo"), string).ok());
+  ASSERT_STREQ(*string, "foo");
+
+  ASSERT_TRUE(ExtractFromSqlValue(SqlValue(), string).ok());
+  ASSERT_FALSE(string.has_value());
+
+  ASSERT_FALSE(ExtractFromSqlValue(SqlValue::Long(1234), string).ok());
+  ASSERT_FALSE(ExtractFromSqlValue(SqlValue::Double(123.1), string).ok());
+}
+
 }  // namespace
+}  // namespace sqlite_utils
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/sqlite/sqlite_vtable_benchmark.cc b/src/trace_processor/sqlite/sqlite_vtable_benchmark.cc
index 30e6c7d..f231abf 100644
--- a/src/trace_processor/sqlite/sqlite_vtable_benchmark.cc
+++ b/src/trace_processor/sqlite/sqlite_vtable_benchmark.cc
@@ -144,7 +144,7 @@
     auto& _context = *static_cast<VtabContext*>(aux);
     std::string sql = "CREATE TABLE x(";
     for (size_t col = 0; col < _context.num_cols; col++)
-      sql += "c" + std::to_string(col) + " BIG INT,";
+      sql += "c" + std::to_string(col) + " BIGINT,";
     sql[sql.size() - 1] = ')';
     int res = sqlite3_declare_vtab(xdb, sql.c_str());
     PERFETTO_CHECK(res == SQLITE_OK);
diff --git a/src/trace_processor/sqlite/window_operator_table.cc b/src/trace_processor/sqlite/window_operator_table.cc
deleted file mode 100644
index c1a9631..0000000
--- a/src/trace_processor/sqlite/window_operator_table.cc
+++ /dev/null
@@ -1,188 +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/sqlite/window_operator_table.h"
-
-#include "src/trace_processor/sqlite/sqlite_utils.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-namespace {
-using namespace sqlite_utils;
-}  // namespace
-
-WindowOperatorTable::WindowOperatorTable(sqlite3*, const TraceStorage*) {}
-
-void WindowOperatorTable::RegisterTable(sqlite3* db,
-                                        const TraceStorage* storage) {
-  SqliteTable::Register<WindowOperatorTable>(db, storage, "window", true);
-}
-
-base::Status WindowOperatorTable::Init(int,
-                                       const char* const*,
-                                       Schema* schema) {
-  const bool kHidden = true;
-  *schema = Schema(
-      {
-          // These are the operator columns:
-          SqliteTable::Column(Column::kRowId, "rowid", SqlValue::Type::kLong,
-                              kHidden),
-          SqliteTable::Column(Column::kQuantum, "quantum",
-                              SqlValue::Type::kLong, kHidden),
-          SqliteTable::Column(Column::kWindowStart, "window_start",
-                              SqlValue::Type::kLong, kHidden),
-          SqliteTable::Column(Column::kWindowDur, "window_dur",
-                              SqlValue::Type::kLong, kHidden),
-          // These are the ouput columns:
-          SqliteTable::Column(Column::kTs, "ts", SqlValue::Type::kLong),
-          SqliteTable::Column(Column::kDuration, "dur", SqlValue::Type::kLong),
-          SqliteTable::Column(Column::kQuantumTs, "quantum_ts",
-                              SqlValue::Type::kLong),
-      },
-      {Column::kRowId});
-  return base::OkStatus();
-}
-
-std::unique_ptr<SqliteTable::Cursor> WindowOperatorTable::CreateCursor() {
-  return std::unique_ptr<SqliteTable::Cursor>(new Cursor(this));
-}
-
-int WindowOperatorTable::BestIndex(const QueryConstraints&, BestIndexInfo*) {
-  return SQLITE_OK;
-}
-
-int WindowOperatorTable::ModifyConstraints(QueryConstraints* qc) {
-  // Remove ordering on timestamp if it is the only ordering as we are already
-  // sorted on TS. This makes span joining significantly faster.
-  const auto& ob = qc->order_by();
-  if (ob.size() == 1 && ob[0].iColumn == Column::kTs && !ob[0].desc) {
-    qc->mutable_order_by()->clear();
-  }
-  return SQLITE_OK;
-}
-
-int WindowOperatorTable::Update(int argc,
-                                sqlite3_value** argv,
-                                sqlite3_int64*) {
-  // We only support updates to ts and dur. Disallow deletes (argc == 1) and
-  // inserts (argv[0] == null).
-  if (argc < 2 || sqlite3_value_type(argv[0]) == SQLITE_NULL)
-    return SQLITE_READONLY;
-
-  int64_t new_quantum = sqlite3_value_int64(argv[3]);
-  int64_t new_start = sqlite3_value_int64(argv[4]);
-  int64_t new_dur = sqlite3_value_int64(argv[5]);
-  if (new_dur == 0) {
-    auto* err = sqlite3_mprintf("Cannot set duration of window table to zero.");
-    SetErrorMessage(err);
-    return SQLITE_ERROR;
-  }
-
-  quantum_ = new_quantum;
-  window_start_ = new_start;
-  window_dur_ = new_dur;
-
-  return SQLITE_OK;
-}
-
-WindowOperatorTable::Cursor::Cursor(WindowOperatorTable* table)
-    : SqliteTable::Cursor(table), table_(table) {}
-
-int WindowOperatorTable::Cursor::Filter(const QueryConstraints& qc,
-                                        sqlite3_value** argv,
-                                        FilterHistory) {
-  *this = Cursor(table_);
-  window_start_ = table_->window_start_;
-  window_end_ = table_->window_start_ + table_->window_dur_;
-  step_size_ = table_->quantum_ == 0 ? table_->window_dur_ : table_->quantum_;
-
-  current_ts_ = window_start_;
-
-  // Set return first if there is a equals constraint on the row id asking to
-  // return the first row.
-  bool return_first = qc.constraints().size() == 1 &&
-                      qc.constraints()[0].column == Column::kRowId &&
-                      IsOpEq(qc.constraints()[0].op) &&
-                      sqlite3_value_int(argv[0]) == 0;
-  if (return_first) {
-    filter_type_ = FilterType::kReturnFirst;
-  } else {
-    filter_type_ = FilterType::kReturnAll;
-  }
-  return SQLITE_OK;
-}
-
-int WindowOperatorTable::Cursor::Column(sqlite3_context* context, int N) {
-  switch (N) {
-    case Column::kQuantum: {
-      sqlite3_result_int64(context,
-                           static_cast<sqlite_int64>(table_->quantum_));
-      break;
-    }
-    case Column::kWindowStart: {
-      sqlite3_result_int64(context,
-                           static_cast<sqlite_int64>(table_->window_start_));
-      break;
-    }
-    case Column::kWindowDur: {
-      sqlite3_result_int(context, static_cast<int>(table_->window_dur_));
-      break;
-    }
-    case Column::kTs: {
-      sqlite3_result_int64(context, static_cast<sqlite_int64>(current_ts_));
-      break;
-    }
-    case Column::kDuration: {
-      sqlite3_result_int64(context, static_cast<sqlite_int64>(step_size_));
-      break;
-    }
-    case Column::kQuantumTs: {
-      sqlite3_result_int64(context, static_cast<sqlite_int64>(quantum_ts_));
-      break;
-    }
-    case Column::kRowId: {
-      sqlite3_result_int64(context, static_cast<sqlite_int64>(row_id_));
-      break;
-    }
-    default: {
-      PERFETTO_FATAL("Unknown column %d", N);
-      break;
-    }
-  }
-  return SQLITE_OK;
-}
-
-int WindowOperatorTable::Cursor::Next() {
-  switch (filter_type_) {
-    case FilterType::kReturnFirst:
-      current_ts_ = window_end_;
-      break;
-    case FilterType::kReturnAll:
-      current_ts_ += step_size_;
-      quantum_ts_++;
-      break;
-  }
-  row_id_++;
-  return SQLITE_OK;
-}
-
-int WindowOperatorTable::Cursor::Eof() {
-  return current_ts_ >= window_end_;
-}
-
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/sqlite/window_operator_table.h b/src/trace_processor/sqlite/window_operator_table.h
deleted file mode 100644
index 5bd8e99..0000000
--- a/src/trace_processor/sqlite/window_operator_table.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_SQLITE_WINDOW_OPERATOR_TABLE_H_
-#define SRC_TRACE_PROCESSOR_SQLITE_WINDOW_OPERATOR_TABLE_H_
-
-#include <limits>
-#include <memory>
-
-#include "src/trace_processor/sqlite/sqlite_table.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-class TraceStorage;
-
-class WindowOperatorTable : public SqliteTable {
- public:
-  enum Column {
-    kRowId = 0,
-    kQuantum = 1,
-    kWindowStart = 2,
-    kWindowDur = 3,
-    kTs = 4,
-    kDuration = 5,
-    kQuantumTs = 6
-  };
-  class Cursor : public SqliteTable::Cursor {
-   public:
-    Cursor(WindowOperatorTable*);
-
-    // Implementation of SqliteTable::Cursor.
-    int Filter(const QueryConstraints& qc,
-               sqlite3_value**,
-               FilterHistory) override;
-    int Next() override;
-    int Eof() override;
-    int Column(sqlite3_context*, int N) override;
-
-   private:
-    // Defines the data to be generated by the table.
-    enum FilterType {
-      // Returns all the spans.
-      kReturnAll = 0,
-      // Only returns the first span of the table. Useful for UPDATE operations.
-      kReturnFirst = 1,
-    };
-
-    int64_t window_start_ = 0;
-    int64_t window_end_ = 0;
-    int64_t step_size_ = 0;
-
-    int64_t current_ts_ = 0;
-    int64_t quantum_ts_ = 0;
-    int64_t row_id_ = 0;
-
-    FilterType filter_type_ = FilterType::kReturnAll;
-
-    WindowOperatorTable* table_ = nullptr;
-  };
-
-  static void RegisterTable(sqlite3* db, const TraceStorage* storage);
-
-  WindowOperatorTable(sqlite3*, const TraceStorage*);
-
-  // Table implementation.
-  base::Status Init(int, const char* const*, Schema* schema) override;
-  std::unique_ptr<SqliteTable::Cursor> CreateCursor() override;
-  int BestIndex(const QueryConstraints&, BestIndexInfo*) override;
-  int ModifyConstraints(QueryConstraints* qc) override;
-  int Update(int, sqlite3_value**, sqlite3_int64*) override;
-
- private:
-  int64_t quantum_ = 0;
-  int64_t window_start_ = 0;
-
-  // max of int64_t because SQLite technically only supports int64s and not
-  // uint64s.
-  int64_t window_dur_ = std::numeric_limits<int64_t>::max();
-};
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_SQLITE_WINDOW_OPERATOR_TABLE_H_
diff --git a/src/trace_processor/stdlib/BUILD.gn b/src/trace_processor/stdlib/BUILD.gn
new file mode 100644
index 0000000..b91a7d3
--- /dev/null
+++ b/src/trace_processor/stdlib/BUILD.gn
@@ -0,0 +1,30 @@
+# Copyright (C) 2022 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/perfetto_sql.gni")
+
+assert(enable_perfetto_trace_processor_sqlite)
+
+perfetto_amalgamated_sql_header("gen_amalgamated_stdlib") {
+  deps = [
+    "android",
+    "chrome:chrome_sql",
+    "common",
+    "experimental",
+  ]
+  generated_header = "amalgamated_stdlib.h"
+  namespace = "stdlib"
+  generate_docs = true
+}
diff --git a/src/trace_processor/stdlib/android/BUILD.gn b/src/trace_processor/stdlib/android/BUILD.gn
new file mode 100644
index 0000000..ea6659e
--- /dev/null
+++ b/src/trace_processor/stdlib/android/BUILD.gn
@@ -0,0 +1,24 @@
+# Copyright (C) 2022 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_sql.gni")
+
+perfetto_sql_source_set("android") {
+  deps = [ "startup" ]
+  sources = [
+    "battery.sql",
+    "binder.sql",
+    "process_metadata.sql",
+  ]
+}
diff --git a/src/trace_processor/stdlib/android/battery.sql b/src/trace_processor/stdlib/android/battery.sql
new file mode 100644
index 0000000..ee025e4
--- /dev/null
+++ b/src/trace_processor/stdlib/android/battery.sql
@@ -0,0 +1,60 @@
+--
+-- Copyright 2022 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.
+
+-- Battery charge at timestamp.
+--
+-- @column ts                  Timestamp.
+-- @column current_avg_ua      Current average micro ampers.
+-- @column capacity_percent    Current capacity percentage.
+-- @column charge_uah          Current charge in micro ampers.
+-- @column current_ua          Current micro ampers.
+CREATE VIEW android_battery_charge AS
+SELECT
+  all_ts.ts,
+  current_avg_ua,
+  capacity_percent,
+  charge_uah,
+  current_ua
+FROM (
+  SELECT DISTINCT(ts) AS ts
+  FROM counter c
+  JOIN counter_track t ON c.track_id = t.id
+  WHERE name GLOB 'batt.*'
+) AS all_ts
+LEFT JOIN (
+  SELECT ts, value AS current_avg_ua
+  FROM counter c
+  JOIN counter_track t ON c.track_id = t.id
+  WHERE name = 'batt.current.avg_ua'
+) USING(ts)
+LEFT JOIN (
+  SELECT ts, value AS capacity_percent
+  FROM counter c
+  JOIN counter_track t ON c.track_id = t.id
+  WHERE name = 'batt.capacity_pct'
+) USING(ts)
+LEFT JOIN (
+  SELECT ts, value AS charge_uah
+  FROM counter c
+  JOIN counter_track t ON c.track_id = t.id
+  WHERE name = 'batt.charge_uah'
+) USING(ts)
+LEFT JOIN (
+  SELECT ts, value AS current_ua
+  FROM counter c
+  JOIN counter_track t ON c.track_id = t.id
+  WHERE name = 'batt.current_ua'
+) USING(ts)
+ORDER BY ts;
diff --git a/src/trace_processor/stdlib/android/binder.sql b/src/trace_processor/stdlib/android/binder.sql
new file mode 100644
index 0000000..d62d7d8
--- /dev/null
+++ b/src/trace_processor/stdlib/android/binder.sql
@@ -0,0 +1,37 @@
+--
+-- Copyright 2022 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.
+--
+
+-- Count Binder transactions per process.
+--
+-- @column process_name  Name of the process that started the binder transaction.
+-- @column pid           PID of the process that started the binder transaction.
+-- @column slice_name    Name of the slice with binder transaction.
+-- @column event_count   Number of binder transactions in process in slice.
+CREATE VIEW android_binder_metrics_by_process AS
+SELECT
+  process.name AS process_name,
+  process.pid AS pid,
+  slice.name AS slice_name,
+  COUNT(*) AS event_count
+FROM slice
+JOIN thread_track ON slice.track_id = thread_track.id
+JOIN thread ON thread.utid = thread_track.utid
+JOIN process ON thread.upid = process.upid
+WHERE
+  slice.name GLOB 'binder*'
+GROUP BY
+  process_name,
+  slice_name;
diff --git a/src/trace_processor/stdlib/android/process_metadata.sql b/src/trace_processor/stdlib/android/process_metadata.sql
new file mode 100644
index 0000000..a593f69
--- /dev/null
+++ b/src/trace_processor/stdlib/android/process_metadata.sql
@@ -0,0 +1,60 @@
+--
+-- 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.
+--
+
+-- Count packages by package UID.
+CREATE TABLE internal_uid_package_count AS
+SELECT uid, COUNT(1) AS cnt
+FROM package_list
+GROUP BY 1;
+
+-- Data about packages running on the process.
+--
+-- @column upid         Process upid.
+-- @column process_name Process name.
+-- @column package_name Name of the packages running in this process.
+-- @column version_code Package version code.
+-- @column debuggable   Whether package is debuggable.
+CREATE TABLE android_process_metadata AS
+SELECT
+  process.upid,
+  -- workaround for b/169226092: the bug has been fixed it Android T, but
+  -- we support ingesting traces from older Android versions.
+  CASE
+    -- cmdline gets rewritten after fork, if these are still there we must
+    -- have seen a racy capture.
+    WHEN length(process.name) = 15 AND (
+      process.cmdline IN ('zygote', 'zygote64', '<pre-initialized>')
+      OR process.cmdline GLOB '*' || process.name)
+      THEN process.cmdline
+    ELSE process.name
+  END AS process_name,
+  process.android_appid AS uid,
+  CASE WHEN internal_uid_package_count.cnt > 1 THEN TRUE ELSE NULL END AS shared_uid,
+  plist.package_name,
+  plist.version_code,
+  plist.debuggable
+FROM process
+LEFT JOIN internal_uid_package_count ON process.android_appid = internal_uid_package_count.uid
+LEFT JOIN package_list plist
+  ON (
+    process.android_appid = plist.uid
+    AND internal_uid_package_count.uid = plist.uid
+    AND (
+      -- unique match
+      internal_uid_package_count.cnt = 1
+      -- or process name starts with the package name
+      OR process.name GLOB plist.package_name || '*')
+  );
\ No newline at end of file
diff --git a/src/trace_processor/stdlib/android/startup/BUILD.gn b/src/trace_processor/stdlib/android/startup/BUILD.gn
new file mode 100644
index 0000000..7e8cb44
--- /dev/null
+++ b/src/trace_processor/stdlib/android/startup/BUILD.gn
@@ -0,0 +1,24 @@
+# Copyright (C) 2022 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_sql.gni")
+
+perfetto_sql_source_set("startup") {
+  sources = [
+    "internal_startups_maxsdk28.sql",
+    "internal_startups_minsdk29.sql",
+    "internal_startups_minsdk33.sql",
+    "startups.sql",
+  ]
+}
diff --git a/src/trace_processor/stdlib/android/startup/internal_startups_maxsdk28.sql b/src/trace_processor/stdlib/android/startup/internal_startups_maxsdk28.sql
new file mode 100644
index 0000000..2ca73f0
--- /dev/null
+++ b/src/trace_processor/stdlib/android/startup/internal_startups_maxsdk28.sql
@@ -0,0 +1,28 @@
+--
+-- 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.
+
+
+INSERT INTO internal_all_startups
+SELECT
+  "maxsdk28",
+  ROW_NUMBER() OVER(ORDER BY ts) AS startup_id,
+  le.ts,
+  le.ts_end AS ts_end,
+  le.ts_end - le.ts AS dur,
+  package_name AS package,
+  NULL AS startup_type
+FROM internal_startup_events le
+ORDER BY ts;
+
diff --git a/src/trace_processor/stdlib/android/startup/internal_startups_minsdk29.sql b/src/trace_processor/stdlib/android/startup/internal_startups_minsdk29.sql
new file mode 100644
index 0000000..72e054d
--- /dev/null
+++ b/src/trace_processor/stdlib/android/startup/internal_startups_minsdk29.sql
@@ -0,0 +1,68 @@
+--
+-- 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.
+
+-- Marks the beginning of the trace and is equivalent to when the statsd startup
+-- logging begins.
+CREATE VIEW internal_activity_intent_received AS
+SELECT ts FROM slice
+WHERE name = 'MetricsLogger:launchObserverNotifyIntentStarted';
+
+-- We partition the trace into spans based on posted activity intents.
+-- We will refine these progressively in the next steps to only encompass
+-- activity starts.
+CREATE TABLE internal_activity_intent_recv_spans AS
+SELECT
+  ROW_NUMBER()
+  OVER(ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS startup_id,
+  ts,
+  LEAD(ts, 1, (SELECT end_ts FROM trace_bounds)) OVER(ORDER BY ts) - ts AS dur
+FROM internal_activity_intent_received
+ORDER BY ts;
+
+-- Filter activity_intent_recv_spans, keeping only the ones that triggered
+-- a startup.
+CREATE VIEW internal_startup_partitions AS
+SELECT * FROM internal_activity_intent_recv_spans AS spans
+WHERE 1 = (
+  SELECT COUNT(1)
+  FROM internal_startup_events
+  WHERE internal_startup_events.ts BETWEEN spans.ts AND spans.ts + spans.dur);
+
+-- Successful activity startup. The end of the 'launching' event is not related
+-- to whether it actually succeeded or not.
+CREATE VIEW internal_activity_intent_startup_successful AS
+SELECT ts FROM slice
+WHERE name = 'MetricsLogger:launchObserverNotifyActivityLaunchFinished';
+
+-- Use the starting event package name. The finish event package name
+-- is not reliable in the case of failed startups.
+INSERT INTO internal_all_startups
+SELECT
+  "minsdk29",
+  lpart.startup_id,
+  lpart.ts,
+  le.ts_end,
+  le.ts_end - lpart.ts AS dur,
+  package_name AS package,
+  NULL AS startup_type
+FROM internal_startup_partitions AS lpart
+JOIN internal_startup_events le ON
+  (le.ts BETWEEN lpart.ts AND lpart.ts + lpart.dur)
+  AND (le.ts_end BETWEEN lpart.ts AND lpart.ts + lpart.dur)
+WHERE (
+  SELECT COUNT(1)
+  FROM internal_activity_intent_startup_successful AS successful
+  WHERE successful.ts BETWEEN lpart.ts AND lpart.ts + lpart.dur
+) > 0;
diff --git a/src/trace_processor/stdlib/android/startup/internal_startups_minsdk33.sql b/src/trace_processor/stdlib/android/startup/internal_startups_minsdk33.sql
new file mode 100644
index 0000000..206867e
--- /dev/null
+++ b/src/trace_processor/stdlib/android/startup/internal_startups_minsdk33.sql
@@ -0,0 +1,63 @@
+--
+-- 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.
+
+
+CREATE VIEW internal_startup_async_events AS
+SELECT
+  ts,
+  dur,
+  SUBSTR(name, 19) AS startup_id
+FROM slice
+WHERE
+  name GLOB 'launchingActivity#*'
+  AND dur != 0
+  AND INSTR(name, ':') = 0;
+
+CREATE VIEW internal_startup_complete_events AS
+SELECT
+  STR_SPLIT(completed, ':', 0) AS startup_id,
+  STR_SPLIT(completed, ':', 2) AS package_name,
+  CASE
+    WHEN STR_SPLIT(completed, ':', 1) = 'completed-hot' THEN 'hot'
+    WHEN STR_SPLIT(completed, ':', 1) = 'completed-warm' THEN 'warm'
+    WHEN STR_SPLIT(completed, ':', 1) = 'completed-cold' THEN 'cold'
+    ELSE NULL
+  END AS startup_type,
+  MIN(ts)
+FROM (
+  SELECT ts, SUBSTR(name, 19) AS completed
+  FROM slice
+  WHERE
+    dur = 0
+    -- Originally completed was unqualified, but at some point we introduced
+    -- the startup type as well
+    AND name GLOB 'launchingActivity#*:completed*:*'
+)
+GROUP BY 1, 2, 3;
+
+INSERT INTO internal_all_startups
+SELECT
+  "minsdk33",
+  startup_id,
+  ts,
+  ts + dur AS ts_end,
+  dur,
+  package_name,
+  startup_type
+FROM internal_startup_async_events
+JOIN internal_startup_complete_events USING (startup_id);
+
+
+
diff --git a/src/trace_processor/stdlib/android/startup/startups.sql b/src/trace_processor/stdlib/android/startup/startups.sql
new file mode 100644
index 0000000..644d122
--- /dev/null
+++ b/src/trace_processor/stdlib/android/startup/startups.sql
@@ -0,0 +1,278 @@
+--
+-- 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 IMPORT('common.slices');
+SELECT IMPORT('android.process_metadata');
+
+-- All activity startup events.
+CREATE TABLE internal_startup_events AS
+SELECT
+  ts,
+  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
+  s.name GLOB 'launching: *'
+  AND (process.name IS NULL OR process.name = 'system_server');
+
+-- Gather all startup data. Populate by different sdks.
+CREATE TABLE internal_all_startups(
+  sdk STRING,
+  startup_id INTEGER BIGINT,
+  ts BIGINT,
+  ts_end BIGINT,
+  dur BIGINT,
+  package STRING,
+  startup_type STRING
+);
+
+SELECT IMPORT('android.startup.internal_startups_maxsdk28');
+SELECT IMPORT('android.startup.internal_startups_minsdk29');
+SELECT IMPORT('android.startup.internal_startups_minsdk33');
+
+-- All activity startups in the trace by startup id.
+-- Populated by different scripts depending on the platform version/contents.
+--
+-- @column id           Startup id.
+-- @column ts           Timestamp of startup start.
+-- @column ts_end       Timestamp of startup end.
+-- @column dur          Startup duration.
+-- @column package      Package name.
+-- @column startup_type Startup type.
+CREATE TABLE android_startups AS
+SELECT startup_id, ts, ts_end, dur, package, startup_type FROM
+internal_all_startups WHERE ( CASE
+  WHEN SLICE_COUNT('launchingActivity#*:*') > 0
+    THEN sdk = "minsdk33"
+  WHEN SLICE_COUNT('MetricsLogger:*') > 0
+    THEN sdk = "minsdk29"
+  ELSE sdk = "maxsdk28"
+  END);
+
+--
+-- Create startup processes
+--
+
+-- Create a table containing only the slices which are necessary for determining
+-- whether a startup happened.
+CREATE TABLE internal_startup_indicator_slices AS
+SELECT ts, name, track_id
+FROM slice
+WHERE name IN ('bindApplication', 'activityStart', 'activityResume');
+
+SELECT CREATE_FUNCTION(
+  'INTERNAL_STARTUP_INDICATOR_SLICE_COUNT(start_ts LONG, end_ts LONG, utid INT, name STRING)',
+  'INT',
+  '
+    SELECT COUNT(1)
+    FROM thread_track t
+    JOIN internal_startup_indicator_slices s ON s.track_id = t.id
+    WHERE
+      t.utid = $utid AND
+      s.ts >= $start_ts AND
+      s.ts < $end_ts AND
+      s.name = $name
+  '
+);
+
+-- Maps a startup to the set of processes that handled the activity start.
+--
+-- The vast majority of cases should be a single process. However it is
+-- possible that the process dies during the activity startup and is respawned.
+--
+-- @column startup_id   Startup id.
+-- @column upid         Upid of process on which activity started.
+-- @column startup_type Type of the startup.
+CREATE TABLE android_startup_processes AS
+-- This is intentionally a materialized query. For some reason, if we don't
+-- materialize, we end up with a query which is an order of magnitude slower :(
+WITH startup_with_type AS MATERIALIZED (
+  SELECT
+    startup_id,
+    upid,
+    CASE
+      -- type parsed from platform event takes precedence if available
+      WHEN startup_type IS NOT NULL THEN startup_type
+      WHEN bind_app > 0 AND a_start > 0 AND a_resume > 0 THEN 'cold'
+      WHEN a_start > 0 AND a_resume > 0 THEN 'warm'
+      WHEN a_resume > 0 THEN 'hot'
+      ELSE NULL
+    END AS startup_type
+  FROM (
+    SELECT
+      l.startup_id,
+      l.startup_type,
+      p.upid,
+      INTERNAL_STARTUP_INDICATOR_SLICE_COUNT(l.ts, l.ts_end, t.utid, 'bindApplication') AS bind_app,
+      INTERNAL_STARTUP_INDICATOR_SLICE_COUNT(l.ts, l.ts_end, t.utid, 'activityStart') AS a_start,
+      INTERNAL_STARTUP_INDICATOR_SLICE_COUNT(l.ts, l.ts_end, t.utid, 'activityResume') AS a_resume
+    FROM android_startups l
+    JOIN android_process_metadata p ON (
+      l.package = p.package_name
+      -- If the package list data source was not enabled in the trace, nothing
+      -- will match the above constraint so also match any process whose name
+      -- is a prefix of the package name.
+      OR (
+        (SELECT COUNT(1) = 0 FROM package_list)
+        AND p.process_name GLOB l.package || '*'
+      )
+      )
+    JOIN thread t ON (p.upid = t.upid AND t.is_main_thread)
+  )
+)
+SELECT *
+FROM startup_with_type
+WHERE startup_type IS NOT NULL;
+
+
+-- Maps a startup to the set of threads on processes that handled the
+-- activity start.
+--
+-- @column startup_id     Startup id.
+-- @column ts             Timestamp of start.
+-- @column dur            Duration of startup.
+-- @column upid           Upid of process involved in startup.
+-- @column utid           Utid of the thread.
+-- @column thread_name    Name of the thread.
+-- @column is_main_thread Thread is a main thread.
+CREATE VIEW android_startup_threads AS
+SELECT
+  startups.startup_id,
+  startups.ts,
+  startups.dur,
+  android_startup_processes.upid,
+  thread.utid,
+  thread.name AS thread_name,
+  thread.is_main_thread AS is_main_thread
+FROM android_startups startups
+JOIN android_startup_processes USING (startup_id)
+JOIN thread USING (upid);
+
+---
+--- Functions
+---
+
+-- All the slices for all startups in trace.
+--
+-- Generally, this view should not be used. Instead, use one of the view functions related
+-- to the startup slices which are created from this table.
+--
+-- @column startup_ts     Timestamp of startup.
+-- @column startup_ts_end Timestamp of startup end.
+-- @column startup_id     Startup id.
+-- @column utid           UTID of thread with slice.
+-- @column thread_name    Name of thread.
+-- @column is_main_thread Whether it is main thread.
+-- @column arg_set_id     Arg set id.
+-- @column slice_id       Slice id.
+-- @column slice_name     Name of slice.
+-- @column slice_ts       Timestamp of slice start.
+-- @column slice_dur      Slice duration.
+CREATE VIEW android_thread_slices_for_all_startups AS
+SELECT
+  st.ts AS startup_ts,
+  st.ts + st.dur AS startup_ts_end,
+  st.startup_id,
+  st.utid,
+  st.thread_name,
+  st.is_main_thread,
+  slice.arg_set_id,
+  slice.id as slice_id,
+  slice.name AS slice_name,
+  slice.ts AS slice_ts,
+  slice.dur AS slice_dur
+FROM android_startup_threads st
+JOIN thread_track USING (utid)
+JOIN slice ON (slice.track_id = thread_track.id)
+WHERE slice.ts BETWEEN st.ts AND st.ts + st.dur;
+
+-- Given a startup id and GLOB for a slice name, returns matching slices with data.
+--
+-- @arg startup_id INT    Startup id.
+-- @arg slice_name STRING Glob of the slice.
+-- @column slice_name     Name of the slice.
+-- @column slice_ts       Timestamp of start of the slice.
+-- @column slice_dur      Duration of the slice.
+-- @column thread_name    Name of the thread with the slice.
+-- @column arg_set_id     Arg set id.
+SELECT CREATE_VIEW_FUNCTION(
+  'ANDROID_SLICES_FOR_STARTUP_AND_SLICE_NAME(startup_id INT, slice_name STRING)',
+  'slice_name STRING, slice_ts INT, slice_dur INT, thread_name STRING, arg_set_id INT',
+  '
+    SELECT slice_name, slice_ts, slice_dur, thread_name, arg_set_id
+    FROM android_thread_slices_for_all_startups
+    WHERE startup_id = $startup_id AND slice_name GLOB $slice_name
+  '
+);
+
+-- Returns binder transaction slices for a given startup id with duration over threshold.
+--
+-- @arg startup_id INT    Startup id.
+-- @arg threshold DOUBLE  Only return slices with duration over threshold.
+-- @column id             Slice id.
+-- @column slice_dur      Slice duration.
+-- @column thread_name    Name of the thread with slice.
+-- @column process        Name of the process with slice.
+-- @column arg_set_id     Arg set id.
+-- @column is_main_thread Whether is main thread.
+SELECT CREATE_VIEW_FUNCTION(
+  'ANDROID_BINDER_TRANSACTION_SLICES_FOR_STARTUP(startup_id INT, threshold DOUBLE)',
+  'id INT, slice_dur INT, thread_name STRING, process STRING, arg_set_id INT, is_main_thread BOOL',
+  '
+    SELECT slice_id as id, slice_dur, thread_name, process.name as process, s.arg_set_id, is_main_thread
+    FROM android_thread_slices_for_all_startups s
+    JOIN process ON (
+      EXTRACT_ARG(s.arg_set_id, "destination process") = process.pid
+    )
+    WHERE startup_id = $startup_id AND slice_name GLOB "binder transaction" AND slice_dur > $threshold
+  '
+);
+
+-- Returns duration of startup for slice name.
+--
+-- Sums duration of all slices of startup with provided name.
+--
+-- @arg startup_id LONG   Startup id.
+-- @arg slice_name STRING Slice name.
+-- @ret INT               Sum of duration.
+SELECT CREATE_FUNCTION(
+  'ANDROID_SUM_DUR_FOR_STARTUP_AND_SLICE(startup_id LONG, slice_name STRING)',
+  'INT',
+  '
+    SELECT SUM(slice_dur)
+    FROM android_thread_slices_for_all_startups
+    WHERE startup_id = $startup_id AND slice_name GLOB $slice_name
+  '
+);
+
+-- Returns duration of startup for slice name on main thread.
+--
+-- Sums duration of all slices of startup with provided name only on main thread.
+--
+-- @arg startup_id LONG   Startup id.
+-- @arg slice_name STRING Slice name.
+-- @ret INT               Sum of duration.
+SELECT CREATE_FUNCTION(
+  'ANDROID_SUM_DUR_ON_MAIN_THREAD_FOR_STARTUP_AND_SLICE(startup_id LONG, slice_name STRING)',
+  'INT',
+  '
+    SELECT SUM(slice_dur)
+    FROM android_thread_slices_for_all_startups
+    WHERE startup_id = $startup_id AND slice_name GLOB $slice_name AND is_main_thread
+  '
+);
\ No newline at end of file
diff --git a/src/trace_processor/stdlib/chrome/BUILD.gn b/src/trace_processor/stdlib/chrome/BUILD.gn
new file mode 100644
index 0000000..b3ea475
--- /dev/null
+++ b/src/trace_processor/stdlib/chrome/BUILD.gn
@@ -0,0 +1,19 @@
+# Copyright (C) 2022 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_sql.gni")
+
+perfetto_sql_source_set("chrome_sql") {
+  sources = [ "cpu_powerups.sql" ]
+}
diff --git a/src/trace_processor/stdlib/chrome/cpu_powerups.sql b/src/trace_processor/stdlib/chrome/cpu_powerups.sql
new file mode 100644
index 0000000..fcf3767
--- /dev/null
+++ b/src/trace_processor/stdlib/chrome/cpu_powerups.sql
@@ -0,0 +1,176 @@
+-- Copyright 2022 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.
+
+-- Find causes for CPUs powering up.
+--
+-- The scripts below analyse traces with the following tracing options
+-- enabled:
+--
+--  - Linux kernel:
+---    "power/*", "sched/*", "task/*",
+--  - Chromium:
+--      "toplevel", "toplevel.flow".
+
+-- Noteworthy tables:
+--
+--   chrome_cpu_power_first_toplevel_slice_after_powerup :: The top-level
+--      slices that ran after a CPU power-up.
+
+-- The CPU power transitions in the trace.
+--
+-- @column ts            The timestamp at the start of the slice.
+-- @column dur           The duration of the slice.
+-- @column cpu           The CPU on which the transition occurred
+-- @column power_state   The power state that the CPU was in at time 'ts' for
+--                       duration 'dur'.
+-- @column previous_power_state The power state that the CPU was previously in.
+-- @column powerup_id    A unique ID for the CPU power-up.
+--
+-- Power states are encoded as non-negative integers, with zero representing
+-- full-power operation and positive values representing increasingly deep
+-- sleep states.
+--
+-- On ARM systems, power state 1 represents the WFI (Wait For Interrupt) sleep
+-- state that the CPU enters while idle.
+CREATE VIEW chrome_cpu_power_slice AS
+  WITH cpu_power_states AS (
+    SELECT
+      c.id AS id,
+      cct.cpu AS cpu,
+      c.ts,
+      -- Encode the 'value' field as a power state.
+      CAST((CASE c.value WHEN 4294967295 THEN 0 ELSE c.value + 1 END)
+        AS INT) AS power_state
+    FROM counter AS c
+    JOIN cpu_counter_track AS cct
+      ON c.track_id = cct.id
+    WHERE cct.name = 'cpuidle'
+  )
+  SELECT *
+  FROM (
+    SELECT
+      ts,
+      LEAD(ts) OVER (PARTITION BY cpu ORDER BY ts ASC) - ts
+        AS dur,
+      cpu,
+      power_state,
+      LAG(power_state) OVER (PARTITION BY cpu ORDER BY ts ASC)
+        AS previous_power_state,
+      id AS powerup_id
+    FROM cpu_power_states
+  )
+  WHERE dur IS NOT NULL
+    AND previous_power_state IS NOT NULL
+    AND power_state = 0                      -- Track full-power states.
+    AND power_state != previous_power_state  -- Skip missing spans.
+    ORDER BY ts ASC;
+
+-- We do not want scheduler slices with utid = 0 (the 'swapper' kernel thread).
+CREATE VIEW internal_cpu_power_valid_sched_slice AS
+  SELECT *
+  FROM sched_slice
+  WHERE utid != 0;
+
+-- Join scheduler slices with the spans with CPU power slices.
+--
+-- There multiple scheduler slices could fall into one CPU power slice.
+--
+---  CPU Power:
+--   |----------------------------|....................|---------|
+--   A       <cpu active>         B     <cpu idling>   C         D
+
+--   Scheduler slices on that CPU:
+--     |-----T1-----| |....T2....|                      |---T3--|
+--     E            F G          H                      I       J
+--
+-- Here threads T1 and T2 executed in CPU power slice [A,B].  The
+-- time between F and G represents time between threads in the kernel.
+CREATE VIRTUAL TABLE internal_cpu_power_and_sched_slice
+USING
+  SPAN_JOIN(chrome_cpu_power_slice PARTITIONED cpu,
+            internal_cpu_power_valid_sched_slice PARTITIONED cpu);
+
+-- The Linux scheduler slices that executed immediately after a
+-- CPU power up.
+--
+-- @column ts          The timestamp at the start of the slice.
+-- @column dur         The duration of the slice.
+-- @column cpu         The cpu on which the slice executed.
+-- @column sched_id    Id for the sched_slice table.
+-- @column utid        Unique id for the thread that ran within the slice.
+-- @column previous_power_state   The CPU's power state before this slice.
+CREATE TABLE chrome_cpu_power_first_sched_slice_after_powerup AS
+  SELECT
+    ts,
+    dur,
+    cpu,
+    id AS sched_id,
+    utid,
+    previous_power_state,
+    powerup_id
+  FROM internal_cpu_power_and_sched_slice
+  WHERE power_state = 0     -- Power-ups only.
+  GROUP BY cpu, powerup_id
+  HAVING ts = MIN(ts)       -- There will only be one MIN sched slice
+                            -- per CPU power up.
+  ORDER BY ts ASC;
+
+-- A view joining thread tracks and top-level slices.
+--
+-- This view is intended to be intersected by time with the scheduler
+-- slices scheduled after a CPU power up.
+--
+--   utid      Thread unique id.
+--   slice_id  The slice_id for the top-level slice.
+--   ts        Starting timestamp for the slice.
+--   dur       The duration for the slice.
+CREATE VIEW internal_cpu_power_thread_and_toplevel_slice AS
+  SELECT
+    t.utid AS utid,
+    s.id AS slice_id,
+    s.ts,
+    s.dur
+  FROM slice AS s
+  JOIN thread_track AS t
+    ON s.track_id = t.id
+  WHERE s.depth = 0   -- Top-level slices only.
+  ORDER BY ts ASC;
+
+-- A table holding the slices that executed within the scheduler
+-- slice that ran on a CPU immediately after power-up.
+--
+-- @column  ts        Timestamp of the resulting slice
+-- @column dur        Duration of the slice.
+-- @column cpu        The CPU the sched slice ran on.
+-- @column utid       Unique thread id for the slice.
+-- @column sched_id   'id' field from the sched_slice table.
+-- @column type       From the sched_slice table, always 'sched_slice'.
+-- @column end_state  The ending state for the sched_slice
+-- @column priority   The kernel thread priority
+-- @column slice_id   Id of the top-level slice for this (sched) slice.
+CREATE VIRTUAL TABLE chrome_cpu_power_post_powerup_slice
+USING
+  SPAN_JOIN(chrome_cpu_power_first_sched_slice_after_powerup PARTITIONED utid,
+            internal_cpu_power_thread_and_toplevel_slice PARTITIONED utid);
+
+-- The first top-level slice that ran after a CPU power-up.
+--
+-- @column slice_id              ID of the slice in the slice table.
+-- @column previous_power_state  The power state of the CPU prior to power-up.
+CREATE VIEW chrome_cpu_power_first_toplevel_slice_after_powerup AS
+  SELECT slice_id, previous_power_state
+  FROM chrome_cpu_power_post_powerup_slice
+  GROUP BY cpu, powerup_id
+  HAVING ts = MIN(ts)
+  ORDER BY ts ASC;
diff --git a/src/trace_processor/stdlib/common/BUILD.gn b/src/trace_processor/stdlib/common/BUILD.gn
new file mode 100644
index 0000000..8147bcf
--- /dev/null
+++ b/src/trace_processor/stdlib/common/BUILD.gn
@@ -0,0 +1,25 @@
+# Copyright (C) 2022 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_sql.gni")
+
+perfetto_sql_source_set("common") {
+  sources = [
+    "counters.sql",
+    "metadata.sql",
+    "percentiles.sql",
+    "slices.sql",
+    "timestamps.sql",
+  ]
+}
diff --git a/src/trace_processor/stdlib/common/counters.sql b/src/trace_processor/stdlib/common/counters.sql
new file mode 100644
index 0000000..22fb72d
--- /dev/null
+++ b/src/trace_processor/stdlib/common/counters.sql
@@ -0,0 +1,110 @@
+--
+-- Copyright 2023 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 IMPORT('common.timestamps');
+
+-- Timestamp of first counter value in a counter.
+--
+-- @arg counter_track_id  INT Id of a counter track with a counter.
+-- @ret LONG                  Timestamp of first counter value. Null if doesn't exist. 
+SELECT CREATE_FUNCTION(
+  'EARLIEST_TIMESTAMP_FOR_COUNTER_TRACK(counter_track_id INT)',
+  'LONG',
+  'SELECT MIN(ts) FROM counter WHERE counter.track_id = $counter_track_id;'
+);
+
+
+-- Counter values with details of counter track with calculated duration of each counter value.
+-- Duration is calculated as time from counter to the next counter.
+--
+-- @arg counter_track_id INT   Id of track counter track.
+-- @column ts                  Timestamp of the counter value.
+-- @column dur                 Duration of the counter value.
+-- @column value               Counter value.
+-- @column track_id            If of the counter track.
+-- @column track_name          Name of the counter track.
+-- @column track_arg_set_id    Counter track set id.
+-- @column arg_set_id          Counter arg set id.
+SELECT CREATE_VIEW_FUNCTION(
+  'COUNTER_WITH_DUR_FOR_TRACK(counter_track_id INT)',
+  '
+    ts LONG,
+    dur LONG,
+    value DOUBLE,
+    track_id INT,
+    track_name STRING,
+    track_arg_set_id INT,
+    arg_set_id INT
+  ',
+  '
+    SELECT
+        ts,
+        LEAD(ts, 1, TRACE_END()) OVER(ORDER BY ts) - ts AS dur,
+        value,
+        track.id AS track_id,
+        track.name AS track_name,
+        track.source_arg_set_id AS track_arg_set_id,
+        counter.arg_set_id AS arg_set_id
+    FROM counter
+    JOIN counter_track track ON track.id = counter.track_id
+    WHERE track.id = $counter_track_id
+  '
+);
+
+-- COUNTER_WITH_DUR_FOR_TRACK but in a specified time.
+-- Does calculation over the table ends - creates an artificial counter value at
+-- the start if needed and chops the duration of the last timestamps in range.
+--
+-- @arg counter_track_id INT   Id of track counter track.
+-- @arg start_ts LONG          Timestamp of the timerange start.
+-- Can be earlier than the first counter value.
+-- @arg end_ts LONG            Timestamp of the timerange end.
+-- @column ts                  Timestamp of the counter value.
+-- @column dur                 Duration of the counter value.
+-- @column value               Counter value.
+-- @column track_id            If of the counter track.
+-- @column track_name          Name of the counter track.
+-- @column track_arg_set_id    Counter track set id.
+-- @column arg_set_id          Counter arg set id.
+SELECT CREATE_VIEW_FUNCTION(
+  'COUNTER_FOR_TIME_RANGE(counter_track_id INT, start_ts LONG, end_ts LONG)',
+  '
+    ts LONG,
+    dur LONG,
+    value DOUBLE,
+    track_id INT,
+    track_name STRING,
+    track_arg_set_id INT,
+    arg_set_id INT
+  ',
+  '
+  SELECT
+    IIF(ts < $start_ts, $start_ts, ts) AS ts,
+    IIF(
+      ts < $start_ts,
+      dur - ($start_ts - ts),
+      IIF(ts + dur > $end_ts, $end_ts - ts, dur)) AS dur,
+    value,
+    track_id,
+    track_name,
+    track_arg_set_id,
+    arg_set_id
+  FROM COUNTER_WITH_DUR_FOR_TRACK($counter_track_id)
+  WHERE TRUE
+    AND ts + dur >= $start_ts
+    AND ts < $end_ts
+  ORDER BY ts ASC;
+'
+);
\ No newline at end of file
diff --git a/src/trace_processor/stdlib/common/metadata.sql b/src/trace_processor/stdlib/common/metadata.sql
new file mode 100644
index 0000000..117af1e
--- /dev/null
+++ b/src/trace_processor/stdlib/common/metadata.sql
@@ -0,0 +1,25 @@
+--
+-- Copyright 2022 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.
+
+-- Extracts an int value with the given name from the metadata table.
+--
+-- @arg name STRING The name of the metadata entry.
+-- @ret LONG int_value for the given name. NULL if there's no such entry.
+SELECT
+    CREATE_FUNCTION(
+        'EXTRACT_INT_METADATA(name STRING)',
+        'LONG',
+        'SELECT int_value FROM metadata WHERE name = ($name)'
+    );
\ No newline at end of file
diff --git a/src/trace_processor/stdlib/common/percentiles.sql b/src/trace_processor/stdlib/common/percentiles.sql
new file mode 100644
index 0000000..74bd271
--- /dev/null
+++ b/src/trace_processor/stdlib/common/percentiles.sql
@@ -0,0 +1,113 @@
+--
+-- Copyright 2023 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 IMPORT('common.counters');
+SELECT IMPORT('common.timestamps');
+
+SELECT CREATE_VIEW_FUNCTION(
+    'INTERNAL_NUMBER_GENERATOR(to INT)',
+    'num INT',
+    'WITH NUMS AS
+        (SELECT 1 num UNION SELECT num + 1
+        from NUMS
+        WHERE num < $to)
+    SELECT num FROM NUMS;'
+);
+
+--
+-- Get durations for percentile
+--
+
+-- All percentiles (range 1-100) for counter track ID in a given time range.
+--
+-- Percentiles are calculated by:
+-- 1. Dividing the sum of duration in time range for each value in the counter
+-- by duration of the counter in range. This gives us `percentile_for)value` (DOUBLE).
+-- 2. Fetching each percentile by taking floor of each `percentile_for_value`, grouping by
+-- resulting `percentile` and MIN from value for each grouping. As we are rounding down,
+-- taking MIN assures most reliable data.
+-- 3. Filling the possible gaps in percentiles by getting the minimal value from higher
+-- percentiles for each gap.
+--
+-- @arg counter_track_id INT Id of the counter track.
+-- @arg start_ts LONG        Timestamp of start of time range.
+-- @arg end_ts LONG          Timestamp of end of time range.
+-- @column percentile        All of the numbers from 1 to 100.
+-- @column value             Value for the percentile.
+SELECT CREATE_VIEW_FUNCTION(
+    'COUNTER_PERCENTILES_FOR_TIME_RANGE(counter_track_id INT, start_ts LONG, end_ts LONG)',
+    'percentile INT, value DOUBLE',
+    'WITH percentiles_for_value AS (
+        SELECT
+            value,
+            (CAST(SUM(dur) OVER(ORDER BY value ASC) AS DOUBLE) /
+                ($end_ts - MAX($start_ts, EARLIEST_TIMESTAMP_FOR_COUNTER_TRACK($counter_track_id)))) * 100
+            AS percentile_for_value
+        FROM COUNTER_FOR_TIME_RANGE($counter_track_id, $start_ts, $end_ts)
+        ORDER BY value ASC
+    ),
+    with_gaps AS (
+        SELECT
+            CAST(percentile_for_value AS INT) AS percentile,
+            MIN(value) AS value
+        FROM percentiles_for_value
+        GROUP BY percentile
+        ORDER BY percentile ASC)
+    SELECT
+        num AS percentile,
+        IFNULL(value, MIN(value) OVER (ORDER BY percentile DESC)) AS value
+    FROM INTERNAL_NUMBER_GENERATOR(100) AS nums
+    LEFT JOIN with_gaps ON with_gaps.percentile = nums.num
+    ORDER BY percentile DESC
+    '
+);
+
+-- All percentiles (range 1-100) for counter track ID.
+--
+-- @arg counter_track_id INT Id of the counter track.
+-- @column percentile        All of the numbers from 1 to 100.
+-- @column value             Value for the percentile.
+SELECT CREATE_VIEW_FUNCTION(
+    'COUNTER_PERCENTILES_FOR_TRACK(counter_track_id INT)',
+    'percentile INT, value DOUBLE',
+    'SELECT * FROM COUNTER_PERCENTILES_FOR_TIME_RANGE($counter_track_id, TRACE_START(), TRACE_END());'
+);
+
+-- Value for specific percentile (range 1-100) for counter track ID in time range.
+--
+-- @arg counter_track_id INT Id of the counter track.
+-- @arg percentile INT       Any of the numbers from 1 to 100.
+-- @arg start_ts LONG        Timestamp of start of time range.
+-- @arg end_ts LONG          Timestamp of end of time range.
+-- @ret DOUBLE               Value for the percentile.
+SELECT CREATE_FUNCTION(
+    'COUNTER_TRACK_PERCENTILE_FOR_TIME(counter_track_id INT, percentile INT, start_ts LONG, end_ts LONG)',
+    'DOUBLE',
+    'SELECT value
+    FROM COUNTER_PERCENTILES_FOR_TIME_RANGE($counter_track_id, $start_ts, $end_ts)
+    WHERE percentile = $percentile;'
+);
+
+-- Value for specific percentile (range 1-100) for counter track ID.
+--
+-- @arg counter_track_id INT Id of the counter track.
+-- @arg percentile INT       Any of the numbers from 1 to 100.
+-- @ret DOUBLE               Value for the percentile.
+SELECT CREATE_FUNCTION(
+    'COUNTER_TRACK_PERCENTILE(counter_track_id INT, percentile INT)',
+    'DOUBLE',
+    'SELECT COUNTER_TRACK_PERCENTILE_FOR_TIME($counter_track_id, $percentile, TRACE_START(), TRACE_END());'
+);
+
diff --git a/src/trace_processor/stdlib/common/slices.sql b/src/trace_processor/stdlib/common/slices.sql
new file mode 100644
index 0000000..ab59ea6
--- /dev/null
+++ b/src/trace_processor/stdlib/common/slices.sql
@@ -0,0 +1,476 @@
+--
+-- Copyright 2022 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.
+
+--
+-- Thread slices view functions
+--
+
+-- All thread slices with data about thread, thread track and process.
+-- Where possible, use available view functions which filter this view.
+--
+-- @column slice_id           Id of slice.
+-- @column slice_name         Name of slice.
+-- @column ts                 Timestamp of slice start.
+-- @column dur                Duration of slice.
+-- @column slice_depth        Depth of slice.
+-- @column arg_set_id         Slice arg set id.
+-- @column thread_track_id    Id of thread track.
+-- @column thread_track_name  Name of thread track.
+-- @column utid               Utid of thread with slice.
+-- @column thread_name        Name of thread with slice.
+-- @column upid               Upid of process with slice.
+-- @column process_name       Name of process with slice.
+CREATE VIEW all_thread_slices AS
+SELECT
+  slice.id AS slice_id,
+  slice.name AS slice_name,
+  ts,
+  dur,
+  slice.depth AS slice_depth,
+  slice.arg_set_id,
+  slice.track_id AS thread_track_id,
+  thread_track.name AS thread_track_name,
+  utid,
+  thread.name AS thread_name,
+  upid,
+  process.name AS process_name
+FROM slice
+JOIN thread_track ON slice.track_id = thread_track.id
+JOIN thread using (utid)
+LEFT JOIN process using (upid);
+
+-- Detailed thread slices data with process, thread and track for thread with provided utid.
+--
+-- @arg utid INT              Utid of thread.
+-- @column slice_id           Id of slice.
+-- @column slice_name         Name of slice.
+-- @column ts                 Timestamp of slice start.
+-- @column dur                Duration of slice.
+-- @column slice_depth        Depth of slice.
+-- @column arg_set_id         Slice arg set id.
+-- @column thread_track_id    Id of thread track.
+-- @column thread_track_name  Name of thread track.
+-- @column utid               Utid of thread with slice.
+-- @column thread_name        Name of thread with slice.
+-- @column upid               Upid of process with slice.
+-- @column process_name       Name of process with slice.
+SELECT CREATE_VIEW_FUNCTION(
+'THREAD_SLICES_FOR_UTID(utid INT)',
+  '
+    slice_id INT,
+    slice_name STRING,
+    ts LONG,
+    dur LONG,
+    slice_depth INT,
+    arg_set_id INT,
+    thread_track_id INT,
+    thread_track_name STRING,
+    utid INT,
+    thread_name STRING,
+    upid INT,
+    process_name STRING
+  ',
+'
+  SELECT * FROM all_thread_slices
+  WHERE utid = $utid;
+');
+
+-- Detailed thread slices data with process, thread and track for process with provided upid.
+--
+-- @arg upid INT              Upid of process.
+-- @column slice_id           Id of slice.
+-- @column slice_name         Name of slice.
+-- @column ts                 Timestamp of slice start.
+-- @column dur                Duration of slice.
+-- @column slice_depth        Depth of slice.
+-- @column arg_set_id         Slice arg set id.
+-- @column thread_track_id    Id of thread track.
+-- @column thread_track_name  Name of thread track.
+-- @column utid               Utid of thread with slice.
+-- @column thread_name        Name of thread with slice.
+-- @column upid               Upid of process with slice.
+-- @column process_name       Name of process with slice.
+SELECT CREATE_VIEW_FUNCTION(
+  'THREAD_SLICES_FOR_UPID(upid INT)',
+  '
+    slice_id INT,
+    slice_name STRING,
+    ts LONG,
+    dur LONG,
+    slice_depth INT,
+    arg_set_id INT,
+    thread_track_id INT,
+    thread_track_name STRING,
+    utid INT,
+    thread_name STRING,
+    upid INT,
+    process_name STRING
+  ',
+  '
+    SELECT * FROM all_thread_slices
+    WHERE upid = $upid;
+  '
+);
+
+-- Detailed thread slices data with process, thread and track for track id.
+--
+-- @arg thread_track_id INT   Id of thread_track.
+-- @column slice_id           Id of slice.
+-- @column slice_name         Name of slice.
+-- @column ts                 Timestamp of slice start.
+-- @column dur                Duration of slice.
+-- @column slice_depth        Depth of slice.
+-- @column arg_set_id         Slice arg set id.
+-- @column thread_track_id    Id of thread track.
+-- @column thread_track_name  Name of thread track.
+-- @column utid               Utid of thread with slice.
+-- @column thread_name        Name of thread with slice.
+-- @column upid               Upid of process with slice.
+-- @column process_name       Name of process with slice.
+SELECT CREATE_VIEW_FUNCTION(
+  'THREAD_SLICES_FOR_THREAD_TRACK_ID(thread_track_id INT)',
+  '
+    slice_id INT,
+    slice_name STRING,
+    ts LONG,
+    dur LONG,
+    slice_depth INT,
+    arg_set_id INT,
+    thread_track_id INT,
+    thread_track_name STRING,
+    utid INT,
+    thread_name STRING,
+    upid INT,
+    process_name STRING
+  ',
+  '
+    SELECT * FROM all_thread_slices
+    WHERE thread_track_id = $thread_track_id;
+  '
+);
+
+
+-- Detailed thread slices data with process, thread and track for specified slice name.
+-- Searches for slice name with GLOB.
+--
+-- @arg glob_name STRING      String name to glob.
+-- @column slice_id           Id of slice.
+-- @column slice_name         Name of slice.
+-- @column ts                 Timestamp of slice start.
+-- @column dur                Duration of slice.
+-- @column slice_depth        Depth of slice.
+-- @column arg_set_id         Slice arg set id.
+-- @column thread_track_id    Id of thread track.
+-- @column thread_track_name  Name of thread track.
+-- @column utid               Utid of thread with slice.
+-- @column thread_name        Name of thread with slice.
+-- @column upid               Upid of process with slice.
+-- @column process_name       Name of process with slice.
+SELECT CREATE_VIEW_FUNCTION(
+  'THREAD_SLICES_FOR_SLICE_NAME(glob_name STRING)',
+  '
+    slice_id INT,
+    slice_name STRING,
+    ts LONG,
+    dur LONG,
+    slice_depth INT,
+    arg_set_id INT,
+    thread_track_id INT,
+    thread_track_name STRING,
+    utid INT,
+    thread_name STRING,
+    upid INT,
+    process_name STRING
+  ',
+  '
+    SELECT * FROM all_thread_slices
+    WHERE slice_name GLOB $glob_name;
+  ');
+
+-- Detailed thread slices data with process, thread and track for specified thread name.
+-- Searches for thread name with GLOB.
+--
+-- @arg glob_name STRING      Thread name to glob.
+-- @column slice_id           Id of slice.
+-- @column slice_name         Name of slice.
+-- @column ts                 Timestamp of slice start.
+-- @column dur                Duration of slice.
+-- @column slice_depth        Depth of slice.
+-- @column arg_set_id         Slice arg set id.
+-- @column thread_track_id    Id of thread track.
+-- @column thread_track_name  Name of thread track.
+-- @column utid               Utid of thread with slice.
+-- @column thread_name        Name of thread with slice.
+-- @column upid               Upid of process with slice.
+-- @column process_name       Name of process with slice.
+SELECT CREATE_VIEW_FUNCTION(
+  'THREAD_SLICES_FOR_THREAD_NAME(glob_name STRING)',
+  '
+    slice_id INT,
+    slice_name STRING,
+    ts LONG,
+    dur LONG,
+    slice_depth INT,
+    arg_set_id INT,
+    thread_track_id INT,
+    thread_track_name STRING,
+    utid INT,
+    thread_name STRING,
+    upid INT,
+    process_name STRING
+  ',
+  '
+    SELECT * FROM all_thread_slices
+    WHERE thread_name GLOB $glob_name;
+  ');
+
+-- Detailed thread slices data with process, thread and track for specified process name.
+-- Searches for process name with GLOB.
+--
+-- @arg glob_name STRING      Process name to glob.
+-- @column slice_id           Id of slice.
+-- @column slice_name         Name of slice.
+-- @column ts                 Timestamp of slice start.
+-- @column dur                Duration of slice.
+-- @column slice_depth        Depth of slice.
+-- @column arg_set_id         Slice arg set id.
+-- @column thread_track_id    Id of thread track.
+-- @column thread_track_name  Name of thread track.
+-- @column utid               Utid of thread with slice.
+-- @column thread_name        Name of thread with slice.
+-- @column upid               Upid of process with slice.
+-- @column process_name       Name of process with slice.
+SELECT CREATE_VIEW_FUNCTION(
+  'THREAD_SLICES_FOR_PROCESS_NAME(glob_name STRING)',
+  '
+    slice_id INT,
+    slice_name STRING,
+    ts LONG,
+    dur LONG,
+    slice_depth INT,
+    arg_set_id INT,
+    thread_track_id INT,
+    thread_track_name STRING,
+    utid INT,
+    thread_name STRING,
+    upid INT,
+    process_name STRING
+  ',
+  '
+    SELECT * FROM all_thread_slices
+    WHERE process_name GLOB $glob_name;
+  ');
+
+--
+-- Process slices view functions
+--
+
+-- All process slices with data about process track and process.
+-- Where possible, use available view functions which filter this view.
+--
+-- @column slice_id           Id of slice.
+-- @column slice_name         Name of slice.
+-- @column ts                 Timestamp of slice start.
+-- @column dur                Duration of slice.
+-- @column slice_depth        Depth of slice.
+-- @column arg_set_id         Slice arg set id.
+-- @column process_track_id   Id of process track.
+-- @column process_track_name Name of process track.
+-- @column upid               Upid of process with slice.
+-- @column process_name       Name of process with slice.
+CREATE VIEW all_process_slices AS
+SELECT
+  slice.id AS slice_id,
+  slice.name AS slice_name,
+  ts,
+  dur,
+  slice.depth AS slice_depth,
+  slice.arg_set_id,
+  process_track.id AS process_track_id,
+  process_track.name AS process_track_name,
+  upid,
+  process.name AS process_name
+FROM slice
+JOIN process_track ON slice.track_id = process_track.id
+JOIN process using (upid);
+
+-- Detailed process slices data for process and track with provided upid.
+--
+-- @arg upid INT              Upid of process.
+-- @column slice_id           Id of slice.
+-- @column slice_name         Name of slice.
+-- @column ts                 Timestamp of slice start.
+-- @column dur                Duration of slice.
+-- @column slice_depth        Depth of slice.
+-- @column arg_set_id         Slice arg set id.
+-- @column process_track_id   Id of process track.
+-- @column process_track_name Name of process track.
+-- @column upid               Upid of process with slice.
+-- @column process_name       Name of process with slice.
+SELECT CREATE_VIEW_FUNCTION(
+  'PROCESS_SLICES_FOR_UPID(upid INT)',
+  '
+    slice_id INT,
+    slice_name STRING,
+    ts LONG,
+    dur LONG,
+    slice_depth INT,
+    arg_set_id INT,
+    process_track_id INT,
+    process_track_name INT,
+    upid INT,
+    process_name STRING
+  ',
+  '
+    SELECT * FROM all_process_slices
+    WHERE upid = $upid;
+  '
+);
+
+-- Detailed process slices data for process and track with provided process_track_id.
+--
+-- @arg process_track_id INT  Id of process track.
+-- @column slice_id           Id of slice.
+-- @column slice_name         Name of slice.
+-- @column ts                 Timestamp of slice start.
+-- @column dur                Duration of slice.
+-- @column slice_depth        Depth of slice.
+-- @column arg_set_id         Slice arg set id.
+-- @column process_track_id   Id of process track.
+-- @column process_track_name Name of process track.
+-- @column upid               Upid of process with slice.
+-- @column process_name       Name of process with slice.
+SELECT CREATE_VIEW_FUNCTION(
+  'PROCESS_SLICES_FOR_PROCESS_TRACK_ID(process_track_id INT)',
+  '
+    slice_id INT,
+    slice_name STRING,
+    ts LONG,
+    dur LONG,
+    slice_depth INT,
+    arg_set_id INT,
+    process_track_id INT,
+    process_track_name INT,
+    upid INT,
+    process_name STRING
+  ',
+  '
+    SELECT * FROM all_process_slices
+    WHERE process_track_id = $process_track_id;
+  '
+);
+
+
+-- Detailed process slices data for specified slice name.
+-- Searches for slice name with GLOB.
+--
+-- @arg glob_name STRING      String name to glob.
+-- @column slice_id           Id of slice.
+-- @column slice_name         Name of slice.
+-- @column ts                 Timestamp of slice start.
+-- @column dur                Duration of slice.
+-- @column slice_depth        Depth of slice.
+-- @column arg_set_id         Slice arg set id.
+-- @column process_track_id   Id of process track.
+-- @column process_track_name Name of process track.
+-- @column upid               Upid of process with slice.
+-- @column process_name       Name of process with slice.
+SELECT CREATE_VIEW_FUNCTION(
+  'PROCESS_SLICES_FOR_SLICE_NAME(glob_name STRING)',
+  '
+    slice_id INT,
+    slice_name STRING,
+    ts LONG,
+    dur LONG,
+    slice_depth INT,
+    arg_set_id INT,
+    process_track_id INT,
+    process_track_name INT,
+    upid INT,
+    process_name STRING
+  ',
+  '
+    SELECT * FROM all_process_slices
+    WHERE slice_name GLOB $glob_name;
+  '
+);
+
+-- Detailed process slices data for specified process name.
+-- Searches for process name with GLOB.
+--
+-- @arg glob_name STRING      Process name to glob.
+-- @column slice_id           Id of slice.
+-- @column slice_name         Name of slice.
+-- @column ts                 Timestamp of slice start.
+-- @column dur                Duration of slice.
+-- @column slice_depth        Depth of slice.
+-- @column arg_set_id         Slice arg set id.
+-- @column process_track_id   Id of process track.
+-- @column process_track_name Name of process track.
+-- @column upid               Upid of process with slice.
+-- @column process_name       Name of process with slice.
+SELECT CREATE_VIEW_FUNCTION(
+  'PROCESS_SLICES_FOR_PROCESS_NAME(glob_name STRING)',
+  '
+    slice_id INT,
+    slice_name STRING,
+    ts LONG,
+    dur LONG,
+    slice_depth INT,
+    arg_set_id INT,
+    process_track_id INT,
+    process_track_name INT,
+    upid INT,
+    process_name STRING
+  ',
+  '
+    SELECT * FROM all_process_slices
+    WHERE process_name GLOB $glob_name;
+  '
+);
+
+
+--
+-- Other functions
+--
+
+-- Checks if slice has an ancestor with provided name.
+--
+-- @arg id INT              Id of the slice to check parents of.
+-- @arg parent_name STRING  Name of potential ancestor slice.
+-- @ret BOOL                Whether `parent_name` is a name of an ancestor slice.
+SELECT
+  CREATE_FUNCTION(
+    'HAS_PARENT_SLICE_WITH_NAME(id INT, parent_name STRING)',
+    'BOOL',
+    '
+    SELECT EXISTS(
+      SELECT 1
+      FROM ancestor_slice($id)
+      WHERE name = $parent_name
+      LIMIT 1
+    );
+  '
+);
+
+-- Count slices with specified name.
+--
+-- @arg slice_glob STRING Name of the slices to counted.
+-- @ret INT               Number of slices with the name.
+SELECT CREATE_FUNCTION(
+  'SLICE_COUNT(slice_glob STRING)',
+  'INT',
+  'SELECT COUNT(1) FROM slice WHERE name GLOB $slice_glob;'
+);
diff --git a/src/trace_processor/stdlib/common/timestamps.sql b/src/trace_processor/stdlib/common/timestamps.sql
new file mode 100644
index 0000000..c1088b8
--- /dev/null
+++ b/src/trace_processor/stdlib/common/timestamps.sql
@@ -0,0 +1,58 @@
+--
+-- Copyright 2022 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.
+
+--
+-- Trace bounds
+--
+
+-- Fetch start of the trace.
+-- @ret LONG  Start of the trace in nanoseconds.
+SELECT CREATE_FUNCTION(
+    'TRACE_START()',
+    'LONG',
+    'SELECT start_ts FROM trace_bounds;'
+);
+
+-- Fetch end of the trace.
+-- @ret LONG  End of the trace in nanoseconds.
+SELECT CREATE_FUNCTION(
+    'TRACE_END()',
+    'LONG',
+    'SELECT end_ts FROM trace_bounds;'
+);
+
+-- Fetch duration of the trace.
+-- @ret LONG  Duration of the trace in nanoseconds.
+SELECT CREATE_FUNCTION(
+    'TRACE_DUR()',
+    'LONG',
+    'SELECT TRACE_END() - TRACE_START();'
+);
+
+-- Checks whether two spans are overlapping.
+--
+-- @arg ts1 LONG      Start of first span.
+-- @arg ts_end1 LONG  End of first span.
+-- @arg ts2 LONG      Start of second span.
+-- @arg ts_end2 LONG  End of second span.
+-- @ret BOOL          Whether two spans are overlapping.
+SELECT CREATE_FUNCTION(
+  'IS_SPANS_OVERLAPPING(ts1 LONG, ts_end1 LONG, ts2 LONG, ts_end2 LONG)',
+  'BOOL',
+  '
+    SELECT (IIF($ts1 < $ts2, $ts2, $ts1)
+      < IIF($ts_end1 < $ts_end2, $ts_end1, $ts_end2))
+  '
+);
\ No newline at end of file
diff --git a/src/trace_processor/stdlib/experimental/BUILD.gn b/src/trace_processor/stdlib/experimental/BUILD.gn
new file mode 100644
index 0000000..7c2f5f1
--- /dev/null
+++ b/src/trace_processor/stdlib/experimental/BUILD.gn
@@ -0,0 +1,19 @@
+# Copyright (C) 2022 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_sql.gni")
+
+perfetto_sql_source_set("experimental") {
+  sources = [ "android_broadcast.sql" ]
+}
diff --git a/src/trace_processor/stdlib/experimental/android_broadcast.sql b/src/trace_processor/stdlib/experimental/android_broadcast.sql
new file mode 100644
index 0000000..c3ce4bc
--- /dev/null
+++ b/src/trace_processor/stdlib/experimental/android_broadcast.sql
@@ -0,0 +1,48 @@
+--
+-- Copyright 2022 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.
+--
+
+-- Provides a list of broadcast names and processes they were sent to by the
+-- system_server process on U+ devices.
+--
+-- @column type          The name of the broadcast type which was sent.
+-- @column process_name  The process name the broadcast was sent to.
+-- @column queue_name    The name of the broacast queue the broadcast was
+--                       dispatched from.
+CREATE VIEW experimental_android_broadcasts_minsdk_u AS
+WITH
+broadcast_queues AS (
+  SELECT process_track.id, process_track.name AS queue_name
+  FROM process_track
+  JOIN process USING (upid)
+  WHERE
+    process_track.name GLOB 'BroadcastQueue.mRunning*'
+    AND process.name = 'system_server'
+),
+broadcast_process_running AS (
+  SELECT ts, dur, str_split(slice.name, '/', 0) AS process_name, queue_name
+  FROM slice
+  JOIN broadcast_queues ON broadcast_queues.id = slice.track_id
+  WHERE slice.name GLOB '* running'
+)
+SELECT str_split(slice.name, '/', 0) AS type, process_name, queue_name
+FROM broadcast_process_running
+JOIN broadcast_queues USING (queue_name)
+JOIN slice ON (
+  broadcast_process_running.ts < slice.ts
+  AND slice.ts < broadcast_process_running.ts + broadcast_process_running.dur
+  AND slice.track_id = broadcast_queues.id
+  )
+WHERE slice.name GLOB '* scheduled';
diff --git a/src/trace_processor/storage/metadata.h b/src/trace_processor/storage/metadata.h
index 70e5e60..de2e651 100644
--- a/src/trace_processor/storage/metadata.h
+++ b/src/trace_processor/storage/metadata.h
@@ -41,7 +41,8 @@
   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(ftrace_setup_errors,               KeyType::kSingle,  Variadic::kString), \
+  F(ftrace_setup_errors,               KeyType::kMulti,   Variadic::kString), \
+  F(range_of_interest_start_us,        KeyType::kSingle,  Variadic::kInt),    \
   F(statsd_triggering_subscription_id, KeyType::kSingle,  Variadic::kInt),    \
   F(system_machine,                    KeyType::kSingle,  Variadic::kString), \
   F(system_name,                       KeyType::kSingle,  Variadic::kString), \
@@ -49,6 +50,7 @@
   F(system_version,                    KeyType::kSingle,  Variadic::kString), \
   F(trace_config_pbtxt,                KeyType::kSingle,  Variadic::kString), \
   F(trace_size_bytes,                  KeyType::kSingle,  Variadic::kInt),    \
+  F(trace_type,                        KeyType::kSingle,  Variadic::kString), \
   F(trace_uuid,                        KeyType::kSingle,  Variadic::kString), \
   F(tracing_disabled_ns,               KeyType::kSingle,  Variadic::kInt),    \
   F(tracing_started_ns,                KeyType::kSingle,  Variadic::kInt),    \
diff --git a/src/trace_processor/storage/stats.h b/src/trace_processor/storage/stats.h
index 4435236..430b1ab 100644
--- a/src/trace_processor/storage/stats.h
+++ b/src/trace_processor/storage/stats.h
@@ -33,6 +33,9 @@
   F(android_log_num_total,              kSingle,  kInfo,     kTrace,    ""),   \
   F(counter_events_out_of_order,        kSingle,  kError,    kAnalysis, ""),   \
   F(deobfuscate_location_parse_error,   kSingle,  kError,    kTrace,    ""),   \
+  F(energy_breakdown_missing_values,    kSingle,  kError,    kAnalysis, ""),   \
+  F(energy_descriptor_invalid,          kSingle,  kError,    kAnalysis, ""),   \
+  F(energy_uid_breakdown_missing_values,kSingle,  kError,    kAnalysis, ""),   \
   F(frame_timeline_event_parser_errors, kSingle,  kInfo,     kAnalysis, ""),   \
   F(ftrace_bundle_tokenizer_errors,     kSingle,  kError,    kAnalysis, ""),   \
   F(ftrace_cpu_bytes_read_begin,        kIndexed, kInfo,     kTrace,    ""),   \
@@ -131,6 +134,12 @@
   F(traced_total_buffers,               kSingle,  kInfo,     kTrace,    ""),   \
   F(traced_tracing_sessions,            kSingle,  kInfo,     kTrace,    ""),   \
   F(track_event_parser_errors,          kSingle,  kInfo,     kAnalysis, ""),   \
+  F(track_event_dropped_packets_outside_of_range_of_interest,                  \
+                                        kSingle,  kInfo,     kAnalysis,        \
+      "The number of TrackEvent packets dropped by trace processor due to "    \
+      "being outside of the range of interest. This happens if a trace has a " \
+      "TrackEventRangeOfInterest packet, and track event dropping is "         \
+      "enabled."),                                                             \
   F(track_event_tokenizer_errors,       kSingle,  kInfo,     kAnalysis, ""),   \
   F(track_event_thread_invalid_end,     kSingle,  kError,    kTrace,           \
       "The end event for a thread track does not match a track event "         \
@@ -202,6 +211,10 @@
        "the file name is not found or no permission to access the file"),      \
   F(compact_sched_has_parse_errors,     kSingle,  kError,    kTrace,    ""),   \
   F(misplaced_end_event,                kSingle,  kDataLoss, kAnalysis, ""),   \
+  F(truncated_sys_write_duration,       kSingle,  kDataLoss,  kAnalysis,       \
+      "Count of sys_write slices that have a truncated duration to resolve "   \
+      "nesting incompatibilities with atrace slices. Real durations "          \
+      "can be recovered via the |raw| table."),                                \
   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, ""),   \
diff --git a/src/trace_processor/storage/trace_storage.h b/src/trace_processor/storage/trace_storage.h
index 3368f4d..325f3b0 100644
--- a/src/trace_processor/storage/trace_storage.h
+++ b/src/trace_processor/storage/trace_storage.h
@@ -43,6 +43,7 @@
 #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/trace_proto_tables.h"
 #include "src/trace_processor/tables/track_tables.h"
 #include "src/trace_processor/types/variadic.h"
 #include "src/trace_processor/views/slice_views.h"
@@ -329,6 +330,13 @@
   const tables::ProcessTable& process_table() const { return process_table_; }
   tables::ProcessTable* mutable_process_table() { return &process_table_; }
 
+  const tables::FiledescriptorTable& filedescriptor_table() const {
+    return filedescriptor_table_;
+  }
+  tables::FiledescriptorTable* mutable_filedescriptor_table() {
+    return &filedescriptor_table_;
+  }
+
   const tables::TrackTable& track_table() const { return track_table_; }
   tables::TrackTable* mutable_track_table() { return &track_table_; }
 
@@ -360,6 +368,29 @@
     return &gpu_counter_track_table_;
   }
 
+  const tables::EnergyCounterTrackTable& energy_counter_track_table() const {
+    return energy_counter_track_table_;
+  }
+  tables::EnergyCounterTrackTable* mutable_energy_counter_track_table() {
+    return &energy_counter_track_table_;
+  }
+
+  const tables::UidCounterTrackTable& uid_counter_track_table() const {
+    return uid_counter_track_table_;
+  }
+  tables::UidCounterTrackTable* mutable_uid_counter_track_table() {
+    return &uid_counter_track_table_;
+  }
+
+  const tables::EnergyPerUidCounterTrackTable&
+  energy_per_uid_counter_track_table() const {
+    return energy_per_uid_counter_track_table_;
+  }
+  tables::EnergyPerUidCounterTrackTable*
+  mutable_energy_per_uid_counter_track_table() {
+    return &energy_per_uid_counter_track_table_;
+  }
+
   const tables::IrqCounterTrackTable& irq_counter_track_table() const {
     return irq_counter_track_table_;
   }
@@ -429,13 +460,6 @@
   const tables::FlowTable& flow_table() const { return flow_table_; }
   tables::FlowTable* mutable_flow_table() { return &flow_table_; }
 
-  const tables::ThreadSliceTable& thread_slice_table() const {
-    return thread_slice_table_;
-  }
-  tables::ThreadSliceTable* mutable_thread_slice_table() {
-    return &thread_slice_table_;
-  }
-
   const VirtualTrackSlices& virtual_track_slices() const {
     return virtual_track_slices_;
   }
@@ -461,6 +485,14 @@
     return &android_log_table_;
   }
 
+  const tables::AndroidDumpstateTable& android_dumpstate_table() const {
+    return android_dumpstate_table_;
+  }
+
+  tables::AndroidDumpstateTable* mutable_android_dumpstate_table() {
+    return &android_dumpstate_table_;
+  }
+
   const StatsMap& stats() const { return stats_; }
 
   const tables::MetadataTable& metadata_table() const {
@@ -660,6 +692,32 @@
     return &actual_frame_timeline_slice_table_;
   }
 
+  const tables::ExperimentalProtoPathTable& experimental_proto_path_table()
+      const {
+    return experimental_proto_path_table_;
+  }
+  tables::ExperimentalProtoPathTable* mutable_experimental_proto_path_table() {
+    return &experimental_proto_path_table_;
+  }
+
+  const tables::ExperimentalProtoContentTable&
+  experimental_proto_content_table() const {
+    return experimental_proto_content_table_;
+  }
+  tables::ExperimentalProtoContentTable*
+  mutable_experimental_proto_content_table() {
+    return &experimental_proto_content_table_;
+  }
+
+  const tables::ExpMissingChromeProcTable&
+  experimental_missing_chrome_processes_table() const {
+    return experimental_missing_chrome_processes_table_;
+  }
+  tables::ExpMissingChromeProcTable*
+  mutable_experimental_missing_chrome_processes_table() {
+    return &experimental_missing_chrome_processes_table_;
+  }
+
   const views::ThreadSliceView& thread_slice_view() const {
     return thread_slice_view_;
   }
@@ -790,6 +848,12 @@
       &string_pool_, &counter_track_table_};
   tables::GpuCounterTrackTable gpu_counter_track_table_{&string_pool_,
                                                         &counter_track_table_};
+  tables::EnergyCounterTrackTable energy_counter_track_table_{
+      &string_pool_, &counter_track_table_};
+  tables::UidCounterTrackTable uid_counter_track_table_{&string_pool_,
+                                                        &counter_track_table_};
+  tables::EnergyPerUidCounterTrackTable energy_per_uid_counter_track_table_{
+      &string_pool_, &uid_counter_track_table_};
   tables::GpuCounterGroupTable gpu_counter_group_table_{&string_pool_, nullptr};
   tables::PerfCounterTrackTable perf_counter_track_table_{
       &string_pool_, &counter_track_table_};
@@ -798,8 +862,9 @@
   tables::ArgTable arg_table_{&string_pool_, nullptr};
 
   // Information about all the threads and processes in the trace.
-  tables::ThreadTable thread_table_{&string_pool_, nullptr};
-  tables::ProcessTable process_table_{&string_pool_, nullptr};
+  tables::ThreadTable thread_table_{&string_pool_};
+  tables::ProcessTable process_table_{&string_pool_};
+  tables::FiledescriptorTable filedescriptor_table_{&string_pool_, nullptr};
 
   // Slices coming from userspace events (e.g. Chromium TRACE_EVENT macros).
   tables::SliceTable slice_table_{&string_pool_, nullptr};
@@ -810,9 +875,6 @@
   // Slices from CPU scheduling data.
   tables::SchedSliceTable sched_slice_table_{&string_pool_, nullptr};
 
-  // Additional attributes for threads slices (sub-type of NestableSlices).
-  tables::ThreadSliceTable thread_slice_table_{&string_pool_, &slice_table_};
-
   // Additional attributes for virtual track slices (sub-type of
   // NestableSlices).
   VirtualTrackSlices virtual_track_slices_;
@@ -837,7 +899,10 @@
 
   tables::CpuFreqTable cpu_freq_table_{&string_pool_, nullptr};
 
-  tables::AndroidLogTable android_log_table_{&string_pool_, nullptr};
+  tables::AndroidLogTable android_log_table_{&string_pool_};
+
+  tables::AndroidDumpstateTable android_dumpstate_table_{&string_pool_,
+                                                         nullptr};
 
   tables::StackProfileMappingTable stack_profile_mapping_table_{&string_pool_,
                                                                 nullptr};
@@ -884,6 +949,14 @@
   tables::ActualFrameTimelineSliceTable actual_frame_timeline_slice_table_{
       &string_pool_, &slice_table_};
 
+  tables::ExperimentalProtoPathTable experimental_proto_path_table_{
+      &string_pool_, nullptr};
+  tables::ExperimentalProtoContentTable experimental_proto_content_table_{
+      &string_pool_, nullptr};
+
+  tables::ExpMissingChromeProcTable
+      experimental_missing_chrome_processes_table_{&string_pool_, nullptr};
+
   views::ThreadSliceView thread_slice_view_{&slice_table_, &thread_track_table_,
                                             &thread_table_};
 
diff --git a/src/trace_processor/tables/BUILD.gn b/src/trace_processor/tables/BUILD.gn
index 0e10681..dfa7781 100644
--- a/src/trace_processor/tables/BUILD.gn
+++ b/src/trace_processor/tables/BUILD.gn
@@ -12,8 +12,17 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("../../../gn/perfetto_tp_tables.gni")
 import("../../../gn/test.gni")
 
+perfetto_tp_tables("tables_python") {
+  sources = [
+    "android_tables.py",
+    "metadata_tables.py",
+  ]
+  generate_docs = true
+}
+
 source_set("tables") {
   sources = [
     "android_tables.h",
@@ -26,19 +35,29 @@
     "profiler_tables.h",
     "slice_tables.h",
     "table_destructors.cc",
+    "trace_proto_tables.h",
     "track_tables.h",
   ]
   deps = [
+    ":tables_python",
     "../../../gn:default_deps",
     "../../../include/perfetto/ext/base",
     "../db",
   ]
 }
 
+perfetto_tp_tables("py_tables_unittest") {
+  sources = [ "py_tables_unittest.py" ]
+}
+
 source_set("unittests") {
   testonly = true
-  sources = [ "macros_unittest.cc" ]
+  sources = [
+    "macros_unittest.cc",
+    "py_tables_unittest.cc",
+  ]
   deps = [
+    ":py_tables_unittest",
     ":tables",
     "../../../gn:default_deps",
     "../../../gn:gtest_and_gmock",
diff --git a/src/trace_processor/tables/android_tables.h b/src/trace_processor/tables/android_tables.h
index 4c46358..1f46c16 100644
--- a/src/trace_processor/tables/android_tables.h
+++ b/src/trace_processor/tables/android_tables.h
@@ -17,34 +17,13 @@
 #ifndef SRC_TRACE_PROCESSOR_TABLES_ANDROID_TABLES_H_
 #define SRC_TRACE_PROCESSOR_TABLES_ANDROID_TABLES_H_
 
+#include "src/trace_processor/tables/android_tables_py.h"
 #include "src/trace_processor/tables/macros.h"
 
 namespace perfetto {
 namespace trace_processor {
 namespace tables {
 
-// Log entries from Android logcat.
-//
-// NOTE: this table is not sorted by timestamp. This is why we omit the
-// sorted flag on the ts column.
-//
-// @param ts timestamp of log entry.
-// @param utid thread writing the log entry {@joinable thread.utid}.
-// @param prio priority of the log. 3=DEBUG, 4=INFO, 5=WARN, 6=ERROR.
-// @param tag tag of the log entry.
-// @param msg content of the log entry.
-// @tablegroup Events
-#define PERFETTO_TP_ANDROID_LOG_TABLE_DEF(NAME, PARENT, C) \
-  NAME(AndroidLogTable, "android_logs")                    \
-  PERFETTO_TP_ROOT_TABLE(PARENT, C)                        \
-  C(int64_t, ts)                                           \
-  C(uint32_t, utid)                                        \
-  C(uint32_t, prio)                                        \
-  C(base::Optional<StringPool::Id>, tag)                   \
-  C(StringPool::Id, msg)
-
-PERFETTO_TP_TABLE(PERFETTO_TP_ANDROID_LOG_TABLE_DEF);
-
 // A table presenting all game modes and interventions
 // of games installed on the system.
 // This is generated by the game_mode_intervention data-source.
@@ -88,6 +67,22 @@
 
 PERFETTO_TP_TABLE(PERFETTO_TP_ANDROID_GAME_INTERVENTION_LIST_DEF);
 
+// Dumpsys entries from Android dumpstate.
+//
+// @param section name of the dumpstate section.
+// @param service name of the dumpsys service. Only present when
+// dumpstate=="dumpsys", NULL otherwise.
+// @param line line-by-line contents of the section/service, one row per line.
+// @tablegroup Events
+#define PERFETTO_TP_ANDROID_DUMPSTATE_TABLE_DEF(NAME, PARENT, C) \
+  NAME(AndroidDumpstateTable, "android_dumpstate")               \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)                              \
+  C(base::Optional<StringPool::Id>, section)                     \
+  C(base::Optional<StringPool::Id>, service)                     \
+  C(StringPool::Id, line)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_ANDROID_DUMPSTATE_TABLE_DEF);
+
 }  // namespace tables
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/tables/android_tables.py b/src/trace_processor/tables/android_tables.py
new file mode 100644
index 0000000..dc1b96f
--- /dev/null
+++ b/src/trace_processor/tables/android_tables.py
@@ -0,0 +1,55 @@
+# Copyright (C) 2022 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.
+"""Contains tables for relevant for Android."""
+
+from python.generators.trace_processor_table.public import Column as C
+from python.generators.trace_processor_table.public import CppInt64
+from python.generators.trace_processor_table.public import CppOptional
+from python.generators.trace_processor_table.public import CppString
+from python.generators.trace_processor_table.public import Table
+from python.generators.trace_processor_table.public import TableDoc
+from python.generators.trace_processor_table.public import CppTableId
+from python.generators.trace_processor_table.public import CppUint32
+from src.trace_processor.tables.metadata_tables import THREAD_TABLE
+
+ANDROID_LOG_TABLE = Table(
+    class_name="AndroidLogTable",
+    sql_name="android_logs",
+    columns=[
+        C("ts", CppInt64()),
+        C("utid", CppTableId(THREAD_TABLE)),
+        C("prio", CppUint32()),
+        C("tag", CppOptional(CppString())),
+        C("msg", CppString()),
+    ],
+    tabledoc=TableDoc(
+        doc='''
+          Log entries from Android logcat.
+
+          NOTE: this table is not sorted by timestamp. This is why we omit the
+          sorted flag on the ts column.
+        ''',
+        group='Events',
+        columns={
+            'ts': 'Timestamp of log entry.',
+            'utid': 'Thread writing the log entry.',
+            'prio': 'Priority of the log. 3=DEBUG, 4=INFO, 5=WARN, 6=ERROR.',
+            'tag': 'Tag of the log entry.',
+            'msg': 'Content of the log entry.'
+        }))
+
+# Keep this list sorted.
+ALL_TABLES = [
+    ANDROID_LOG_TABLE,
+]
diff --git a/src/trace_processor/tables/macros_internal.h b/src/trace_processor/tables/macros_internal.h
index 35930ca..ae69411 100644
--- a/src/trace_processor/tables/macros_internal.h
+++ b/src/trace_processor/tables/macros_internal.h
@@ -803,7 +803,7 @@
               RowNumber(row_number)};                                         \
     }                                                                         \
                                                                               \
-    static Table::Schema Schema() {                                           \
+    static Table::Schema ComputeStaticSchema() {                              \
       Table::Schema schema;                                                   \
       schema.columns.emplace_back(Table::Schema::Column{                      \
           "id", SqlValue::Type::kLong, true, true, false, false});            \
diff --git a/src/trace_processor/tables/macros_unittest.cc b/src/trace_processor/tables/macros_unittest.cc
index 211d283..9844a9d 100644
--- a/src/trace_processor/tables/macros_unittest.cc
+++ b/src/trace_processor/tables/macros_unittest.cc
@@ -463,7 +463,7 @@
 
 TEST_F(TableMacrosUnittest, ChildDoesntInheritArgsSetFlag) {
   ASSERT_FALSE(args_child_.arg_set_id().IsSetId());
-  ASSERT_FALSE(TestArgsChildTable::Schema()
+  ASSERT_FALSE(TestArgsChildTable::ComputeStaticSchema()
                    .columns[args_child_.arg_set_id().index_in_table()]
                    .is_set_id);
 }
diff --git a/src/trace_processor/tables/metadata_tables.h b/src/trace_processor/tables/metadata_tables.h
index 2654ee4..76768ed 100644
--- a/src/trace_processor/tables/metadata_tables.h
+++ b/src/trace_processor/tables/metadata_tables.h
@@ -18,6 +18,7 @@
 #define SRC_TRACE_PROCESSOR_TABLES_METADATA_TABLES_H_
 
 #include "src/trace_processor/tables/macros.h"
+#include "src/trace_processor/tables/metadata_tables_py.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -59,86 +60,40 @@
 
 PERFETTO_TP_TABLE(PERFETTO_TP_METADATA_TABLE_DEF);
 
-// Contains information of threads seen during the trace
+// Contains information of filedescriptors collected during the trace
 //
-// @name thread
-// @param utid             {uint32_t} Unique thread id. This is != the OS tid.
-//                         This is a monotonic number associated to each thread.
-//                         The OS thread id (tid) cannot be used as primary key
-//                         because tids and pids are recycled by most kernels.
-// @param tid              The OS id for this thread. Note: this is *not*
-//                         unique over the lifetime of the trace so cannot be
-//                         used as a primary key. Use |utid| instead.
-// @param name             The name of the thread. Can be populated from many
-//                         sources (e.g. ftrace, /proc scraping, track event
-//                         etc).
-// @param start_ts         The start timestamp of this thread (if known). Is
-//                         null in most cases unless a thread creation event is
-//                         enabled (e.g. task_newtask ftrace event on
-//                         Linux/Android).
-// @param end_ts           The end timestamp of this thread (if known). Is
-//                         null in most cases unless a thread destruction event
-//                         is enabled (e.g. sched_process_free ftrace event on
-//                         Linux/Android).
-// @param upid             {@joinable process.upid} The process hosting this
-//                         thread.
-// @param is_main_thread   Boolean indicating if this thread is the main thread
-//                         in the process.
-#define PERFETTO_TP_THREAD_TABLE_DEF(NAME, PARENT, C) \
-  NAME(ThreadTable, "internal_thread")                \
-  PERFETTO_TP_ROOT_TABLE(PARENT, C)                   \
-  C(uint32_t, tid)                                    \
-  C(base::Optional<StringPool::Id>, name)             \
-  C(base::Optional<int64_t>, start_ts)                \
-  C(base::Optional<int64_t>, end_ts)                  \
-  C(base::Optional<uint32_t>, upid)                   \
-  C(base::Optional<uint32_t>, is_main_thread)
-
-PERFETTO_TP_TABLE(PERFETTO_TP_THREAD_TABLE_DEF);
-
-// Contains information of processes seen during the trace
-//
-// @name process
-// @param upid            {uint32_t} Unique process id. This is != the OS pid.
-//                        This is a monotonic number associated to each process.
-//                        The OS process id (pid) cannot be used as primary key
-//                        because tids and pids are recycled by most kernels.
-// @param pid             The OS id for this process. Note: this is *not*
+// @name filedescriptor
+// @param ufd             {int64_t} Unique fd. This is != the OS fd.
+//                        This is a monotonic number associated to each
+//                        filedescriptor. The OS assigned fd cannot be used as
+//                        primary key because fds are recycled by most kernels.
+// @param fd              The OS id for this process. Note: this is *not*
 //                        unique over the lifetime of the trace so cannot be
-//                        used as a primary key. Use |upid| instead.
-// @param name            The name of the process. Can be populated from many
-//                        sources (e.g. ftrace, /proc scraping, track event
-//                        etc).
-// @param start_ts        The start timestamp of this process (if known). Is
-//                        null in most cases unless a process creation event is
-//                        enabled (e.g. task_newtask ftrace event on
-//                        Linux/Android).
-// @param end_ts          The end timestamp of this process (if known). Is
-//                        null in most cases unless a process destruction event
-//                        is enabled (e.g. sched_process_free ftrace event on
-//                        Linux/Android).
-// @param parent_upid     {@joinable process.upid} The upid of the process which
-//                        caused this process to be spawned.
-// @param uid             {@joinable package_list.uid} The Unix user id of the
-//                        process.
-// @param android_appid   Android appid of this process.
-// @param cmdline         /proc/cmdline for this process.
-// @param arg_set_id      {@joinable args.arg_set_id} Extra args for this
-//                        process.
-#define PERFETTO_TP_PROCESS_TABLE_DEF(NAME, PARENT, C) \
-  NAME(ProcessTable, "internal_process")               \
-  PERFETTO_TP_ROOT_TABLE(PARENT, C)                    \
-  C(uint32_t, pid)                                     \
-  C(base::Optional<StringPool::Id>, name)              \
-  C(base::Optional<int64_t>, start_ts)                 \
-  C(base::Optional<int64_t>, end_ts)                   \
-  C(base::Optional<uint32_t>, parent_upid)             \
-  C(base::Optional<uint32_t>, uid)                     \
-  C(base::Optional<uint32_t>, android_appid)           \
-  C(base::Optional<StringPool::Id>, cmdline)           \
-  C(uint32_t, arg_set_id)
+//                        used as a primary key. Use |ufd| instead.
+// @param ts              The timestamp for when the fd was collected.
+// @param upid            {@joinable process.upid} The upid of the process which
+//                        opened the filedescriptor.
+// @param path            The path to the file or device backing the fd
+//                        In case this was a socket the path will be the port
+//                        number.
+#define PERFETTO_TP_FILEDESCRIPTOR_TABLE_DEF(NAME, PARENT, C) \
+  NAME(FiledescriptorTable, "filedescriptor")                 \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)                           \
+  C(int64_t, fd)                                              \
+  C(base::Optional<int64_t>, ts)                              \
+  C(base::Optional<uint32_t>, upid)                           \
+  C(base::Optional<StringPool::Id>, path)
 
-PERFETTO_TP_TABLE(PERFETTO_TP_PROCESS_TABLE_DEF);
+PERFETTO_TP_TABLE(PERFETTO_TP_FILEDESCRIPTOR_TABLE_DEF);
+
+// Experimental table, subject to arbitrary breaking changes.
+#define PERFETTO_TP_EXP_MISSING_CHROME_PROC_TABLE_DEF(NAME, PARENT, C)     \
+  NAME(ExpMissingChromeProcTable, "experimental_missing_chrome_processes") \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)                                        \
+  C(uint32_t, upid)                                                        \
+  C(base::Optional<int64_t>, reliable_from)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_EXP_MISSING_CHROME_PROC_TABLE_DEF);
 
 // Contains information of processes seen during the trace
 //
diff --git a/src/trace_processor/tables/metadata_tables.py b/src/trace_processor/tables/metadata_tables.py
new file mode 100644
index 0000000..18f0324
--- /dev/null
+++ b/src/trace_processor/tables/metadata_tables.py
@@ -0,0 +1,159 @@
+# Copyright (C) 2022 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.
+"""Contains metadata tables for a wide range of usecases."""
+
+from python.generators.trace_processor_table.public import Alias
+from python.generators.trace_processor_table.public import Column as C
+from python.generators.trace_processor_table.public import ColumnDoc
+from python.generators.trace_processor_table.public import CppInt64
+from python.generators.trace_processor_table.public import CppOptional
+from python.generators.trace_processor_table.public import CppString
+from python.generators.trace_processor_table.public import Table
+from python.generators.trace_processor_table.public import TableDoc
+from python.generators.trace_processor_table.public import CppTableId
+from python.generators.trace_processor_table.public import CppUint32
+from python.generators.trace_processor_table.public import CppSelfTableId
+from python.generators.trace_processor_table.public import WrappingSqlView
+
+PROCESS_TABLE = Table(
+    class_name='ProcessTable',
+    sql_name='internal_process',
+    columns=[
+        C('upid', Alias(underlying_column='id')),
+        C('pid', CppUint32()),
+        C('name', CppOptional(CppString())),
+        C('start_ts', CppOptional(CppInt64())),
+        C('end_ts', CppOptional(CppInt64())),
+        C('parent_upid', CppOptional(CppSelfTableId())),
+        C('uid', CppOptional(CppUint32())),
+        C('android_appid', CppOptional(CppUint32())),
+        C('cmdline', CppOptional(CppString())),
+        C('arg_set_id', CppUint32()),
+    ],
+    wrapping_sql_view=WrappingSqlView(view_name='process',),
+    tabledoc=TableDoc(
+        doc='Contains information of processes seen during the trace',
+        group='Misc',
+        skip_id_and_type=True,
+        columns={
+            'upid':
+                '''
+                   Unique process id. This is != the OS pid. This is a
+                   monotonic number associated to each process. The OS process
+                   id (pid) cannot be used as primary key because tids and pids
+                   are recycled by most kernels.
+                ''',
+            'pid':
+                '''
+                  The OS id for this process. Note: this is *not* unique
+                  over the lifetime of the trace so cannot be used as a
+                  primary key. Use |upid| instead.
+                ''',
+            'name':
+                '''
+                  The name of the process. Can be populated from many sources
+                  (e.g. ftrace, /proc scraping, track event etc).
+                ''',
+            'start_ts':
+                '''
+                  The start timestamp of this process (if known). Is null
+                  in most cases unless a process creation event is enabled
+                  (e.g. task_newtask ftrace event on Linux/Android).
+                ''',
+            'end_ts':
+                '''
+                  The end timestamp of this process (if known). Is null in
+                  most cases unless a process destruction event is enabled
+                  (e.g. sched_process_free ftrace event on Linux/Android).
+                ''',
+            'parent_upid':
+                '''
+                  The upid of the process which caused this process to be
+                  spawned.
+                ''',
+            'uid':
+                ColumnDoc(
+                    'The Unix user id of the process.',
+                    joinable='package_list.uid'),
+            'android_appid':
+                'Android appid of this process.',
+            'cmdline':
+                '/proc/cmdline for this process.',
+            'arg_set_id':
+                ColumnDoc(
+                    'Extra args for this process.', joinable='args.arg_set_id'),
+        }))
+
+THREAD_TABLE = Table(
+    class_name='ThreadTable',
+    sql_name='internal_thread',
+    columns=[
+        C('utid', Alias(underlying_column='id')),
+        C('tid', CppUint32()),
+        C('name', CppOptional(CppString())),
+        C('start_ts', CppOptional(CppInt64())),
+        C('end_ts', CppOptional(CppInt64())),
+        C('upid', CppOptional(CppTableId(PROCESS_TABLE))),
+        C('is_main_thread', CppOptional(CppUint32())),
+    ],
+    wrapping_sql_view=WrappingSqlView(view_name='thread',),
+    tabledoc=TableDoc(
+        doc='Contains information of threads seen during the trace',
+        group='Misc',
+        skip_id_and_type=True,
+        columns={
+            'utid':
+                '''
+                  Unique thread id. This is != the OS tid. This is a monotonic
+                  number associated to each thread. The OS thread id (tid)
+                  cannot be used as primary key because tids and pids are
+                  recycled by most kernels.
+                ''',
+            'tid':
+                '''
+                  The OS id for this thread. Note: this is *not* unique over the
+                  lifetime of the trace so cannot be used as a primary key. Use
+                  |utid| instead.
+                ''',
+            'name':
+                '''
+                  The name of the thread. Can be populated from many sources
+                  (e.g. ftrace, /proc scraping, track event etc).
+                ''',
+            'start_ts':
+                '''
+                  The start timestamp of this thread (if known). Is null in most
+                  cases unless a thread creation event is enabled (e.g.
+                  task_newtask ftrace event on Linux/Android).
+                ''',
+            'end_ts':
+                '''
+                  The end timestamp of this thread (if known). Is null in most
+                  cases unless a thread destruction event is enabled (e.g.
+                  sched_process_free ftrace event on Linux/Android).
+                ''',
+            'upid':
+                'The process hosting this thread.',
+            'is_main_thread':
+                '''
+                  Boolean indicating if this thread is the main thread
+                  in the process.
+                '''
+        }))
+
+# Keep this list sorted.
+ALL_TABLES = [
+    THREAD_TABLE,
+    PROCESS_TABLE,
+]
diff --git a/src/trace_processor/tables/py_tables_unittest.cc b/src/trace_processor/tables/py_tables_unittest.cc
new file mode 100644
index 0000000..8966159
--- /dev/null
+++ b/src/trace_processor/tables/py_tables_unittest.cc
@@ -0,0 +1,101 @@
+/*
+ * 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/tables/py_tables_unittest_py.h"
+
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace tables {
+
+TestEventTable::~TestEventTable() = default;
+TestArgsTable::~TestArgsTable() = default;
+
+namespace {
+
+class PyTablesUnittest : public ::testing::Test {
+ protected:
+  StringPool pool_;
+
+  TestEventTable event_{&pool_};
+  TestArgsTable args_{&pool_};
+};
+
+TEST_F(PyTablesUnittest, EventTableProprties) {
+  ASSERT_STREQ(TestEventTable::Name(), "event");
+
+  ASSERT_EQ(TestEventTable::ColumnIndex::id, 0u);
+  ASSERT_EQ(TestEventTable::ColumnIndex::type, 1u);
+  ASSERT_EQ(TestEventTable::ColumnIndex::ts, 2u);
+  ASSERT_EQ(TestEventTable::ColumnIndex::arg_set_id, 3u);
+
+  ASSERT_EQ(TestEventTable::ColumnFlag::ts,
+            Column::Flag::kSorted | Column::Flag::kNonNull);
+  ASSERT_EQ(TestEventTable::ColumnFlag::arg_set_id, Column::Flag::kNonNull);
+}
+
+TEST_F(PyTablesUnittest, ArgsTableProprties) {
+  ASSERT_STREQ(TestArgsTable::Name(), "args");
+
+  ASSERT_EQ(TestArgsTable::ColumnIndex::id, 0u);
+  ASSERT_EQ(TestArgsTable::ColumnIndex::type, 1u);
+  ASSERT_EQ(TestArgsTable::ColumnIndex::arg_set_id, 2u);
+
+  ASSERT_EQ(TestArgsTable::ColumnFlag::arg_set_id, Column::Flag::kSorted |
+                                                       Column::Flag::kSetId |
+                                                       Column::Flag::kNonNull);
+}
+
+TEST_F(PyTablesUnittest, InsertEvent) {
+  event_.Insert(TestEventTable::Row(100, 0));
+
+  ASSERT_EQ(event_.type().GetString(0).ToStdString(), "event");
+  ASSERT_EQ(event_.ts()[0], 100);
+  ASSERT_EQ(event_.arg_set_id()[0], 0u);
+}
+
+TEST_F(PyTablesUnittest, InsertEventSpecifyCols) {
+  TestEventTable::Row row;
+  row.ts = 100;
+  row.arg_set_id = 0;
+  event_.Insert(row);
+
+  ASSERT_EQ(event_.type().GetString(0).ToStdString(), "event");
+  ASSERT_EQ(event_.ts()[0], 100);
+  ASSERT_EQ(event_.arg_set_id()[0], 0u);
+}
+
+TEST_F(PyTablesUnittest, MutableColumn) {
+  event_.Insert(TestEventTable::Row(100, 0));
+
+  ASSERT_EQ((*event_.mutable_ts())[0], 100);
+  ASSERT_EQ((*event_.mutable_arg_set_id())[0], 0u);
+}
+
+TEST_F(PyTablesUnittest, ShrinkToFit) {
+  event_.Insert(TestEventTable::Row(100, 0));
+  event_.ShrinkToFit();
+
+  // Unfortunately given the loose restrictions on shrink_to_fit provided by the
+  // standard library, we cannot really assert anything. Just call the method to
+  // ensure it doesn't cause crashes.
+}
+
+}  // namespace
+}  // namespace tables
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/tables/py_tables_unittest.py b/src/trace_processor/tables/py_tables_unittest.py
new file mode 100644
index 0000000..d167137
--- /dev/null
+++ b/src/trace_processor/tables/py_tables_unittest.py
@@ -0,0 +1,46 @@
+# Copyright (C) 2022 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.
+"""Contains tables for unittesting."""
+
+from python.generators.trace_processor_table.public import Column as C
+from python.generators.trace_processor_table.public import ColumnFlag
+from python.generators.trace_processor_table.public import CppInt64
+from python.generators.trace_processor_table.public import Table
+from python.generators.trace_processor_table.public import TableDoc
+from python.generators.trace_processor_table.public import CppUint32
+
+EVENT_TABLE = Table(
+    class_name="TestEventTable",
+    sql_name="event",
+    columns=[
+        C("ts", CppInt64(), flags=ColumnFlag.SORTED),
+        C("arg_set_id", CppUint32()),
+    ],
+    tabledoc=TableDoc(doc='', group='', columns={}))
+
+ARGS_TABLE = Table(
+    class_name="TestArgsTable",
+    sql_name="args",
+    columns=[
+        C("arg_set_id",
+          CppUint32(),
+          flags=ColumnFlag.SET_ID | ColumnFlag.SORTED),
+    ],
+    tabledoc=TableDoc(doc='', group='', columns={}))
+
+# Keep this list sorted.
+ALL_TABLES = [
+    ARGS_TABLE,
+    EVENT_TABLE,
+]
diff --git a/src/trace_processor/tables/slice_tables.h b/src/trace_processor/tables/slice_tables.h
index 724c58e..f67a4f0 100644
--- a/src/trace_processor/tables/slice_tables.h
+++ b/src/trace_processor/tables/slice_tables.h
@@ -29,26 +29,49 @@
 // @param ts timestamp of the start of the slice (in nanoseconds)
 // @param dur duration of the slice (in nanoseconds)
 // @param arg_set_id {@joinable args.arg_set_id}
-#define PERFETTO_TP_SLICE_TABLE_DEF(NAME, PARENT, C) \
-  NAME(SliceTable, "internal_slice")                 \
-  PERFETTO_TP_ROOT_TABLE(PARENT, C)                  \
-  C(int64_t, ts, Column::Flag::kSorted)              \
-  C(int64_t, dur)                                    \
-  C(TrackTable::Id, track_id)                        \
-  C(base::Optional<StringPool::Id>, category)        \
-  C(base::Optional<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)
+// @param thread_instruction_count The value of the CPU instruction counter at
+// the start of the slice.
+// @param thread_instruction_delta The change in value from
+// @param thread_instruction_count to the end of the slice.
+#define PERFETTO_TP_SLICE_TABLE_DEF(NAME, PARENT, C)   \
+  NAME(SliceTable, "internal_slice")                   \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)                    \
+  C(int64_t, ts, Column::Flag::kSorted)                \
+  C(int64_t, dur)                                      \
+  C(TrackTable::Id, track_id)                          \
+  C(base::Optional<StringPool::Id>, category)          \
+  C(base::Optional<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)                              \
+  C(base::Optional<int64_t>, thread_ts)                \
+  C(base::Optional<int64_t>, thread_dur)               \
+  C(base::Optional<int64_t>, thread_instruction_count) \
+  C(base::Optional<int64_t>, thread_instruction_delta)
 
 PERFETTO_TP_TABLE(PERFETTO_TP_SLICE_TABLE_DEF);
 
+// @name sched_slice
+//   This table holds slices with kernel thread scheduling information.
+//   These slices are collected when the Linux "ftrace" data source is
+//   used with the "sched/switch" and "sched/wakeup*" events enabled.
 // @tablegroup Events
-// @param ts timestamp of the start of the slice (in nanoseconds)
-// @param dur duration of the slice (in nanoseconds)
-// @param utid {@joinable thread.utid}
+// @param id The row id for the table row.
+// @param type This field always contains the string 'sched_slice'.
+// @param ts The timestamp at the start of the slice (in nanoseconds).
+// @param dur The duration of the slice (in nanoseconds).
+// @param utid The thread's unique id in the trace. {@joinable thread.utid}.
+// @param cpu The CPU that the slice executed on.
+// @param end_state A string representing the scheduling state of the
+//   kernel thread at the end of the slice.  The individual characters in
+//   the string mean the following: R (runnable), S (awaiting a wakeup),
+//   D (in an uninterruptible sleep), T (suspended), t (being traced),
+//   X (exiting), P (parked), W (waking), I (idle), N (not contributing
+//   to the load average), K (wakeable on fatal signals) and
+//   Z (zombie, awaiting cleanup).
+// @param priority The kernel priority that the thread ran at.
 #define PERFETTO_TP_SCHED_SLICE_TABLE_DEF(NAME, PARENT, C) \
   NAME(SchedSliceTable, "sched_slice")                     \
   PERFETTO_TP_ROOT_TABLE(PARENT, C)                        \
@@ -107,15 +130,6 @@
 
 PERFETTO_TP_TABLE(PERFETTO_TP_GRAPHICS_FRAME_SLICES_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);
-
 #define PERFETTO_TP_EXPECTED_FRAME_TIMELINE_SLICES_DEF(NAME, PARENT, C)  \
   NAME(ExpectedFrameTimelineSliceTable, "expected_frame_timeline_slice") \
   PARENT(PERFETTO_TP_SLICE_TABLE_DEF, C)                                 \
@@ -142,20 +156,6 @@
 
 PERFETTO_TP_TABLE(PERFETTO_TP_ACTUAL_FRAME_TIMELINE_SLICES_DEF);
 
-// @param thread_instruction_count The value of the CPU instruction counter at
-// the start of the slice.
-// @param thread_instruction_delta The change in value from
-// @param thread_instruction_count to the end of the slice.
-#define PERFETTO_TP_THREAD_SLICE_DEF(NAME, PARENT, C)  \
-  NAME(ThreadSliceTable, "thread_slice")               \
-  PARENT(PERFETTO_TP_SLICE_TABLE_DEF, C)               \
-  C(base::Optional<int64_t>, thread_ts)                \
-  C(base::Optional<int64_t>, thread_dur)               \
-  C(base::Optional<int64_t>, thread_instruction_count) \
-  C(base::Optional<int64_t>, thread_instruction_delta)
-
-PERFETTO_TP_TABLE(PERFETTO_TP_THREAD_SLICE_DEF);
-
 #define PERFETTO_TP_EXPERIMENTAL_FLAT_SLICE_TABLE_DEF(NAME, PARENT, C) \
   NAME(ExperimentalFlatSliceTable, "experimental_flat_slice")          \
   PERFETTO_TP_ROOT_TABLE(PARENT, C)                                    \
diff --git a/src/trace_processor/tables/table_destructors.cc b/src/trace_processor/tables/table_destructors.cc
index c84fb9b..48d192e 100644
--- a/src/trace_processor/tables/table_destructors.cc
+++ b/src/trace_processor/tables/table_destructors.cc
@@ -21,6 +21,7 @@
 #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/trace_proto_tables.h"
 #include "src/trace_processor/tables/track_tables.h"
 
 namespace perfetto {
@@ -32,6 +33,8 @@
 
 namespace tables {
 // android_tables.h
+AndroidDumpstateTable::~AndroidDumpstateTable() = default;
+AndroidGameInterventionListTable::~AndroidGameInterventionListTable() = default;
 AndroidLogTable::~AndroidLogTable() = default;
 
 // counter_tables.h
@@ -40,11 +43,13 @@
 // metadata_tables.h
 RawTable::~RawTable() = default;
 ArgTable::~ArgTable() = default;
+ExpMissingChromeProcTable::~ExpMissingChromeProcTable() = default;
 MetadataTable::~MetadataTable() = default;
 CpuTable::~CpuTable() = default;
 CpuFreqTable::~CpuFreqTable() = default;
 ThreadTable::~ThreadTable() = default;
 ProcessTable::~ProcessTable() = default;
+FiledescriptorTable::~FiledescriptorTable() = default;
 ClockSnapshotTable::~ClockSnapshotTable() = default;
 
 // profiler_tables.h
@@ -62,18 +67,15 @@
 HeapGraphReferenceTable::~HeapGraphReferenceTable() = default;
 VulkanMemoryAllocationsTable::~VulkanMemoryAllocationsTable() = default;
 PackageListTable::~PackageListTable() = default;
-AndroidGameInterventionListTable::~AndroidGameInterventionListTable() = default;
 ProfilerSmapsTable::~ProfilerSmapsTable() = default;
 GpuCounterGroupTable::~GpuCounterGroupTable() = default;
 
 // slice_tables.h
 SliceTable::~SliceTable() = default;
 FlowTable::~FlowTable() = default;
-ThreadSliceTable::~ThreadSliceTable() = default;
 SchedSliceTable::~SchedSliceTable() = default;
 GpuSliceTable::~GpuSliceTable() = default;
 GraphicsFrameSliceTable::~GraphicsFrameSliceTable() = default;
-DescribeSliceTable::~DescribeSliceTable() = default;
 ThreadStateTable::~ThreadStateTable() = default;
 ExpectedFrameTimelineSliceTable::~ExpectedFrameTimelineSliceTable() = default;
 ActualFrameTimelineSliceTable::~ActualFrameTimelineSliceTable() = default;
@@ -92,6 +94,13 @@
 SoftirqCounterTrackTable::~SoftirqCounterTrackTable() = default;
 GpuCounterTrackTable::~GpuCounterTrackTable() = default;
 PerfCounterTrackTable::~PerfCounterTrackTable() = default;
+EnergyCounterTrackTable::~EnergyCounterTrackTable() = default;
+UidCounterTrackTable::~UidCounterTrackTable() = default;
+EnergyPerUidCounterTrackTable::~EnergyPerUidCounterTrackTable() = default;
+
+// trace_proto_tables.h
+ExperimentalProtoPathTable::~ExperimentalProtoPathTable() = default;
+ExperimentalProtoContentTable::~ExperimentalProtoContentTable() = default;
 
 // memory_tables.h
 MemorySnapshotTable::~MemorySnapshotTable() = default;
diff --git a/src/trace_processor/tables/trace_proto_tables.h b/src/trace_processor/tables/trace_proto_tables.h
new file mode 100644
index 0000000..f49b3b3
--- /dev/null
+++ b/src/trace_processor/tables/trace_proto_tables.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_TRACE_PROCESSOR_TABLES_TRACE_PROTO_TABLES_H_
+#define SRC_TRACE_PROCESSOR_TABLES_TRACE_PROTO_TABLES_H_
+
+#include "src/trace_processor/tables/macros.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace tables {
+
+// Experimental table, subject to arbitrary breaking changes.
+#define PERFETTO_TP_EXPERIMENTAL_PROTO_PATH_TABLE_DEF(NAME, PARENT, C) \
+  NAME(ExperimentalProtoPathTable, "experimental_proto_path")          \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)                                    \
+  C(base::Optional<ExperimentalProtoPathTable::Id>, parent_id)         \
+  C(StringPool::Id, field_type)                                        \
+  C(base::Optional<StringPool::Id>, field_name)                        \
+  C(base::Optional<uint32_t>, arg_set_id)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_EXPERIMENTAL_PROTO_PATH_TABLE_DEF);
+
+#define PERFETTO_TP_EXPERIMENTAL_PROTO_CONTENT_TABLE_DEF(NAME, PARENT, C) \
+  NAME(ExperimentalProtoContentTable, "experimental_proto_content")       \
+  PERFETTO_TP_ROOT_TABLE(PARENT, C)                                       \
+  C(StringPool::Id, path)                                                 \
+  C(ExperimentalProtoPathTable::Id, path_id)                              \
+  C(int64_t, total_size)                                                  \
+  C(int64_t, size)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_EXPERIMENTAL_PROTO_CONTENT_TABLE_DEF);
+
+}  // namespace tables
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_TABLES_TRACE_PROTO_TABLES_H_
diff --git a/src/trace_processor/tables/track_tables.h b/src/trace_processor/tables/track_tables.h
index 0e6c9fc..ed24ef2 100644
--- a/src/trace_processor/tables/track_tables.h
+++ b/src/trace_processor/tables/track_tables.h
@@ -135,6 +135,44 @@
 
 PERFETTO_TP_TABLE(PERFETTO_TP_PERF_COUNTER_TRACK_DEF);
 
+// Energy consumers' values for energy descriptors in
+// energy_estimation_breakdown packet
+//
+// @param consumer_id id of a distinct energy consumer
+// @param consumer_type type of energy consumer
+// @param ordinal ordinal of energy consumer
+// @tablegroup Tracks
+#define PERFETTO_TP_ENERGY_COUNTER_TRACK_DEF(NAME, PARENT, C) \
+  NAME(EnergyCounterTrackTable, "energy_counter_track")       \
+  PARENT(PERFETTO_TP_COUNTER_TRACK_DEF, C)                    \
+  C(int32_t, consumer_id)                                     \
+  C(StringPool::Id, consumer_type)                            \
+  C(int32_t, ordinal)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_ENERGY_COUNTER_TRACK_DEF);
+
+// Energy per process values for per_uid in energy_estimation_breakdown packet
+//
+// @param uid id of distinct energy process
+// @tablegroup Tracks
+#define PERFETTO_TP_UID_COUNTER_TRACK_DEF(NAME, PARENT, C) \
+  NAME(UidCounterTrackTable, "uid_counter_track")          \
+  PARENT(PERFETTO_TP_COUNTER_TRACK_DEF, C)                 \
+  C(int32_t, uid)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_UID_COUNTER_TRACK_DEF);
+
+// Energy consumer values for per uid in uid_counter_track
+//
+// @param consumer_id of consumer of process
+// @tablegroup Tracks
+#define PERFETTO_TP_ENERGY_PER_UID_COUNTER_TRACK_DEF(NAME, PARENT, C) \
+  NAME(EnergyPerUidCounterTrackTable, "energy_per_uid_counter_track") \
+  PARENT(PERFETTO_TP_UID_COUNTER_TRACK_DEF, C)                        \
+  C(int32_t, consumer_id)
+
+PERFETTO_TP_TABLE(PERFETTO_TP_ENERGY_PER_UID_COUNTER_TRACK_DEF);
+
 }  // namespace tables
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/timestamped_trace_piece.h b/src/trace_processor/timestamped_trace_piece.h
deleted file mode 100644
index b356012..0000000
--- a/src/trace_processor/timestamped_trace_piece.h
+++ /dev/null
@@ -1,238 +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_TIMESTAMPED_TRACE_PIECE_H_
-#define SRC_TRACE_PROCESSOR_TIMESTAMPED_TRACE_PIECE_H_
-
-#include "perfetto/base/build_config.h"
-#include "perfetto/trace_processor/basic_types.h"
-#include "perfetto/trace_processor/ref_counted.h"
-#include "perfetto/trace_processor/trace_blob_view.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/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
-// uninitialized data in the move constructors / destructors. Disable those
-// warnings for TimestampedTracePiece and the types it contains.
-#if PERFETTO_BUILDFLAG(PERFETTO_COMPILER_GCC)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
-#endif
-
-namespace perfetto {
-namespace trace_processor {
-
-struct InlineSchedSwitch {
-  int64_t prev_state;
-  int32_t next_pid;
-  int32_t next_prio;
-  StringId next_comm;
-};
-
-struct InlineSchedWaking {
-  int32_t pid;
-  int32_t target_cpu;
-  int32_t prio;
-  StringId comm;
-};
-
-struct TracePacketData {
-  TraceBlobView packet;
-  RefPtr<PacketSequenceStateGeneration> sequence_state;
-};
-
-struct FtraceEventData {
-  TraceBlobView event;
-  RefPtr<PacketSequenceStateGeneration> sequence_state;
-};
-
-struct TrackEventData : public TracePacketData {
-  TrackEventData(TraceBlobView pv,
-                 RefPtr<PacketSequenceStateGeneration> generation)
-      : TracePacketData{std::move(pv), std::move(generation)} {}
-
-  static constexpr size_t kMaxNumExtraCounters = 8;
-
-  base::Optional<int64_t> thread_timestamp;
-  base::Optional<int64_t> thread_instruction_count;
-  double counter_value = 0;
-  std::array<double, kMaxNumExtraCounters> extra_counter_values = {};
-};
-
-// On Windows std::aligned_storage was broken before VS 2017 15.8 and the
-// compiler (even clang-cl) requires -D_ENABLE_EXTENDED_ALIGNED_STORAGE. Given
-// the alignment here is purely a performance enhancment with no other
-// functional requirement, disable it on Win.
-
-// A TimestampedTracePiece is (usually a reference to) a piece of a trace that
-// is sorted by TraceSorter.
-struct TimestampedTracePiece {
-  enum class Type : uint8_t {
-    kFtraceEvent,
-    kTracePacket,
-    kInlineSchedSwitch,
-    kInlineSchedWaking,
-    kJsonValue,
-    kFuchsiaRecord,
-    kTrackEvent,
-    kSystraceLine,
-    kInvalid,
-    kSize = kInvalid,
-  };
-
-  TimestampedTracePiece(int64_t ts, TracePacketData tpd)
-      : packet_data(std::move(tpd)), timestamp(ts), type(Type::kTracePacket) {}
-
-  TimestampedTracePiece(int64_t ts, FtraceEventData fed)
-      : ftrace_event(std::move(fed)), timestamp(ts), type(Type::kFtraceEvent) {}
-
-  TimestampedTracePiece(int64_t ts, std::string value)
-      : json_value(std::move(value)), timestamp(ts), type(Type::kJsonValue) {}
-
-  TimestampedTracePiece(int64_t ts, FuchsiaRecord fr)
-      : fuchsia_record(std::move(fr)),
-        timestamp(ts),
-        type(Type::kFuchsiaRecord) {}
-
-  TimestampedTracePiece(int64_t ts, TrackEventData ted)
-      : track_event_data(std::move(ted)),
-        timestamp(ts),
-        type(Type::kTrackEvent) {}
-
-  TimestampedTracePiece(int64_t ts, SystraceLine ted)
-      : systrace_line(std::move(ted)),
-        timestamp(ts),
-        type(Type::kSystraceLine) {}
-
-  TimestampedTracePiece(int64_t ts, InlineSchedSwitch iss)
-      : sched_switch(std::move(iss)),
-        timestamp(ts),
-        type(Type::kInlineSchedSwitch) {}
-
-  TimestampedTracePiece(int64_t ts, InlineSchedWaking isw)
-      : sched_waking(std::move(isw)),
-        timestamp(ts),
-        type(Type::kInlineSchedWaking) {}
-
-  TimestampedTracePiece(TimestampedTracePiece&& ttp) noexcept {
-    // Adopt |ttp|'s data. We have to use placement-new to fill the fields
-    // because their original values may be uninitialized and thus
-    // move-assignment won't work correctly.
-    switch (ttp.type) {
-      case Type::kInvalid:
-        break;
-      case Type::kFtraceEvent:
-        new (&ftrace_event) FtraceEventData(std::move(ttp.ftrace_event));
-        break;
-      case Type::kTracePacket:
-        new (&packet_data) TracePacketData(std::move(ttp.packet_data));
-        break;
-      case Type::kInlineSchedSwitch:
-        new (&sched_switch) InlineSchedSwitch(std::move(ttp.sched_switch));
-        break;
-      case Type::kInlineSchedWaking:
-        new (&sched_waking) InlineSchedWaking(std::move(ttp.sched_waking));
-        break;
-      case Type::kJsonValue:
-        new (&json_value) std::string(std::move(ttp.json_value));
-        break;
-      case Type::kFuchsiaRecord:
-        new (&fuchsia_record) FuchsiaRecord(std::move(ttp.fuchsia_record));
-        break;
-      case Type::kTrackEvent:
-        new (&track_event_data) TrackEventData(std::move(ttp.track_event_data));
-        break;
-      case Type::kSystraceLine:
-        new (&systrace_line) SystraceLine(std::move(ttp.systrace_line));
-    }
-    timestamp = ttp.timestamp;
-    type = ttp.type;
-
-    // Invalidate |ttp|.
-    ttp.type = Type::kInvalid;
-  }
-
-  TimestampedTracePiece& operator=(TimestampedTracePiece&& ttp) {
-    if (this != &ttp) {
-      // First invoke the destructor and then invoke the move constructor
-      // inline via placement-new to implement move-assignment.
-      this->~TimestampedTracePiece();
-      new (this) TimestampedTracePiece(std::move(ttp));
-    }
-    return *this;
-  }
-
-  TimestampedTracePiece(const TimestampedTracePiece&) = delete;
-  TimestampedTracePiece& operator=(const TimestampedTracePiece&) = delete;
-
-  ~TimestampedTracePiece() {
-    switch (type) {
-      case Type::kInvalid:
-      case Type::kInlineSchedSwitch:
-      case Type::kInlineSchedWaking:
-        break;
-      case Type::kFtraceEvent:
-        ftrace_event.~FtraceEventData();
-        break;
-      case Type::kTracePacket:
-        packet_data.~TracePacketData();
-        break;
-      case Type::kJsonValue:
-        json_value.~basic_string();
-        break;
-      case Type::kFuchsiaRecord:
-        fuchsia_record.~FuchsiaRecord();
-        break;
-      case Type::kTrackEvent:
-        track_event_data.~TrackEventData();
-        break;
-      case Type::kSystraceLine:
-        systrace_line.~SystraceLine();
-        break;
-    }
-  }
-
-  // Fields ordered for packing.
-
-  // Data for different types of TimestampedTracePiece.
-  union {
-    FtraceEventData ftrace_event;
-    TracePacketData packet_data;
-    InlineSchedSwitch sched_switch;
-    InlineSchedWaking sched_waking;
-    std::string json_value;
-    FuchsiaRecord fuchsia_record;
-    TrackEventData track_event_data;
-    SystraceLine systrace_line;
-  };
-
-  int64_t timestamp;
-  Type type;
-};
-
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#if PERFETTO_BUILDFLAG(PERFETTO_COMPILER_GCC)
-#pragma GCC diagnostic pop
-#endif
-
-#endif  // SRC_TRACE_PROCESSOR_TIMESTAMPED_TRACE_PIECE_H_
diff --git a/src/trace_processor/tp_metatrace.cc b/src/trace_processor/tp_metatrace.cc
index a9c435a..79e6dbc 100644
--- a/src/trace_processor/tp_metatrace.cc
+++ b/src/trace_processor/tp_metatrace.cc
@@ -20,32 +20,65 @@
 namespace trace_processor {
 namespace metatrace {
 
-bool g_enabled = false;
+namespace {
 
-void Enable() {
-  g_enabled = true;
+using ProtoEnum = protos::pbzero::MetatraceCategories;
+ProtoEnum MetatraceCategoriesToProtoEnum(MetatraceCategories categories) {
+  switch (categories) {
+    case MetatraceCategories::TOPLEVEL:
+      return ProtoEnum::TOPLEVEL;
+    case MetatraceCategories::FUNCTION:
+      return ProtoEnum::FUNCTION;
+    case MetatraceCategories::QUERY:
+      return ProtoEnum::QUERY;
+    case MetatraceCategories::ALL:
+      return ProtoEnum::ALL;
+    case MetatraceCategories::NONE:
+      return ProtoEnum::NONE;
+  }
+  return ProtoEnum::NONE;
+}
+
+}  // namespace
+
+Category g_enabled_categories = Category::NONE;
+
+void Enable(MetatraceConfig config) {
+  g_enabled_categories = MetatraceCategoriesToProtoEnum(config.categories);
+  if (config.override_buffer_size) {
+    RingBuffer::GetInstance()->Resize(config.override_buffer_size);
+  }
 }
 
 void DisableAndReadBuffer(std::function<void(Record*)> fn) {
-  g_enabled = false;
+  g_enabled_categories = Category::NONE;
   if (!fn)
     return;
   RingBuffer::GetInstance()->ReadAll(fn);
 }
 
-RingBuffer::RingBuffer() {
-  static_assert((kCapacity & (kCapacity - 1)) == 0,
+RingBuffer::RingBuffer() : data_(kDefaultCapacity) {
+  static_assert((kDefaultCapacity & (kDefaultCapacity - 1)) == 0,
                 "Capacity should be a power of 2");
 }
 
+void RingBuffer::Resize(size_t requested_capacity) {
+  size_t actual_capacity = 1;
+  while (actual_capacity < requested_capacity)
+    actual_capacity <<= 1;
+  data_.resize(actual_capacity);
+  start_idx_ = 0;
+  write_idx_ = 0;
+}
+
 void RingBuffer::ReadAll(std::function<void(Record*)> fn) {
   // Mark as reading so we don't get reentrancy in obtaining new
   // trace events.
   is_reading_ = true;
 
-  uint64_t start = (write_idx_ - start_idx_) < kCapacity
+  uint64_t start = (write_idx_ - start_idx_) < data_.size()
                        ? start_idx_
-                       : write_idx_ - kCapacity;
+                       : write_idx_ - data_.size();
   uint64_t end = write_idx_;
 
   // Increment the write index by kCapacity + 1. This ensures that if
@@ -54,7 +87,7 @@
   // This works because of the logic in ~ScopedEntry and
   // RingBuffer::HasOverwritten which ensures that we don't overwrite entries
   // more than kCapcity elements in the past.
-  write_idx_ += kCapacity + 1;
+  write_idx_ += data_.size() + 1;
 
   for (uint64_t i = start; i < end; ++i) {
     Record* record = At(i);
diff --git a/src/trace_processor/tp_metatrace.h b/src/trace_processor/tp_metatrace.h
index 3b4f856..b800483 100644
--- a/src/trace_processor/tp_metatrace.h
+++ b/src/trace_processor/tp_metatrace.h
@@ -25,6 +25,8 @@
 #include "perfetto/ext/base/metatrace_events.h"
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/ext/base/thread_checker.h"
+#include "perfetto/trace_processor/metatrace_config.h"
+#include "protos/perfetto/trace_processor/metatrace_categories.pbzero.h"
 
 // Trace processor maintains its own base implementation to avoid the
 // threading and task runners which are required by base's metatracing.
@@ -36,8 +38,10 @@
 namespace trace_processor {
 namespace metatrace {
 
+using Category = protos::pbzero::MetatraceCategories;
+
 // Stores whether meta-tracing is enabled.
-extern bool g_enabled;
+extern Category g_enabled_categories;
 
 inline uint64_t TraceTimeNowNs() {
   return static_cast<uint64_t>(base::GetBootTimeNs().count());
@@ -62,6 +66,17 @@
 
   // Adds an arg to the record.
   void AddArg(base::StringView key, base::StringView value) {
+#if PERFETTO_DCHECK_IS_ON()
+    // |key| and |value| should not contain any '\0' characters as it
+    // messes with the |args_buffer| which uses '\0' to deliniate different
+    // arguments.
+    for (char c : key) {
+      PERFETTO_DCHECK(c != '\0');
+    }
+    for (char c : value) {
+      PERFETTO_DCHECK(c != '\0');
+    }
+#endif
     size_t new_buffer_size = args_buffer_size + key.size() + value.size() + 2;
     args_buffer = static_cast<char*>(realloc(args_buffer, new_buffer_size));
 
@@ -94,7 +109,7 @@
 //     is enabled and read one-shot at the end of execution.
 class RingBuffer {
  public:
-  static constexpr uint32_t kCapacity = 256 * 1024;
+  static constexpr uint32_t kDefaultCapacity = 256 * 1024;
 
   RingBuffer();
   ~RingBuffer() = default;
@@ -112,7 +127,7 @@
     return std::make_pair(idx, record);
   }
 
-  Record* At(uint64_t idx) { return &data_[idx % kCapacity]; }
+  Record* At(uint64_t idx) { return &data_[idx % data_.size()]; }
 
   void ReadAll(std::function<void(Record*)>);
 
@@ -127,14 +142,19 @@
 
   // Returns whether the record at the |index| has been overwritten because
   // of wraps of the ring buffer.
-  bool HasOverwritten(uint64_t index) { return index + kCapacity < write_idx_; }
+  bool HasOverwritten(uint64_t index) {
+    return index + data_.size() < write_idx_;
+  }
+
+  // Request the ring buffer to be resized. Clears the existing buffer.
+  void Resize(size_t requested_capacity);
 
  private:
   bool is_reading_ = false;
 
   uint64_t start_idx_ = 0;
   uint64_t write_idx_ = 0;
-  std::array<Record, kCapacity> data_;
+  std::vector<Record> data_;
 
   PERFETTO_THREAD_CHECKER(thread_checker_)
 };
@@ -160,9 +180,10 @@
 
   template <typename Fn = void(Record*)>
   static ScopedEvent Create(
+      Category category,
       const char* event_id,
       Fn args_fn = [](Record*) {}) {
-    if (PERFETTO_LIKELY(!g_enabled))
+    if (PERFETTO_LIKELY((category & g_enabled_categories) == 0))
       return ScopedEvent();
 
     ScopedEvent event;
@@ -183,7 +204,7 @@
 };
 
 // Enables meta-tracing of trace-processor.
-void Enable();
+void Enable(MetatraceConfig config = {});
 
 // Disables meta-tracing of trace-processor and reads all records.
 void DisableAndReadBuffer(std::function<void(Record*)>);
diff --git a/src/trace_processor/trace_processor.cc b/src/trace_processor/trace_processor.cc
index 72b0b1d..cd6ebda 100644
--- a/src/trace_processor/trace_processor.cc
+++ b/src/trace_processor/trace_processor.cc
@@ -22,6 +22,8 @@
 namespace perfetto {
 namespace trace_processor {
 
+TraceProcessor::MetatraceConfig::MetatraceConfig() = default;
+
 // static
 std::unique_ptr<TraceProcessor> TraceProcessor::CreateInstance(
     const Config& config) {
diff --git a/src/trace_processor/trace_processor_context.cc b/src/trace_processor/trace_processor_context.cc
index a3392f2..29fa474 100644
--- a/src/trace_processor/trace_processor_context.cc
+++ b/src/trace_processor/trace_processor_context.cc
@@ -19,8 +19,10 @@
 #include "src/trace_processor/forwarding_trace_parser.h"
 #include "src/trace_processor/importers/common/args_tracker.h"
 #include "src/trace_processor/importers/common/args_translation_table.h"
+#include "src/trace_processor/importers/common/async_track_set_tracker.h"
 #include "src/trace_processor/importers/common/chunked_trace_reader.h"
 #include "src/trace_processor/importers/common/clock_tracker.h"
+#include "src/trace_processor/importers/common/deobfuscation_mapping_table.h"
 #include "src/trace_processor/importers/common/event_tracker.h"
 #include "src/trace_processor/importers/common/flow_tracker.h"
 #include "src/trace_processor/importers/common/global_args_tracker.h"
@@ -29,7 +31,6 @@
 #include "src/trace_processor/importers/common/slice_translation_table.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/async_track_set_tracker.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"
@@ -37,7 +38,7 @@
 #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/trace_sorter.h"
+#include "src/trace_processor/sorter/trace_sorter.h"
 #include "src/trace_processor/types/destructible.h"
 
 namespace perfetto {
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 3500d24..aa6de45 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -17,18 +17,23 @@
 #include "src/trace_processor/trace_processor_impl.h"
 
 #include <algorithm>
+#include <cstdint>
 #include <memory>
+#include <string>
+#include <type_traits>
+#include <unordered_map>
 
 #include "perfetto/base/logging.h"
 #include "perfetto/base/status.h"
 #include "perfetto/base/time.h"
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/string_splitter.h"
 #include "perfetto/ext/base/string_utils.h"
-#include "perfetto/ext/trace_processor/demangle.h"
+#include "perfetto/trace_processor/basic_types.h"
 #include "src/trace_processor/dynamic/ancestor_generator.h"
 #include "src/trace_processor/dynamic/connected_flow_generator.h"
 #include "src/trace_processor/dynamic/descendant_generator.h"
-#include "src/trace_processor/dynamic/describe_slice_generator.h"
 #include "src/trace_processor/dynamic/experimental_annotated_stack_generator.h"
 #include "src/trace_processor/dynamic/experimental_counter_dur_generator.h"
 #include "src/trace_processor/dynamic/experimental_flamegraph_generator.h"
@@ -36,8 +41,6 @@
 #include "src/trace_processor/dynamic/experimental_sched_upid_generator.h"
 #include "src/trace_processor/dynamic/experimental_slice_layout_generator.h"
 #include "src/trace_processor/dynamic/view_generator.h"
-#include "src/trace_processor/export_json.h"
-#include "src/trace_processor/importers/additional_modules.h"
 #include "src/trace_processor/importers/android_bugreport/android_bugreport_parser.h"
 #include "src/trace_processor/importers/common/clock_tracker.h"
 #include "src/trace_processor/importers/ftrace/sched_event_tracker.h"
@@ -46,26 +49,37 @@
 #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/json/json_utils.h"
+#include "src/trace_processor/importers/ninja/ninja_log_parser.h"
+#include "src/trace_processor/importers/proto/additional_modules.h"
+#include "src/trace_processor/importers/proto/content_analyzer.h"
 #include "src/trace_processor/importers/proto/metadata_tracker.h"
 #include "src/trace_processor/importers/systrace/systrace_trace_parser.h"
 #include "src/trace_processor/iterator_impl.h"
-#include "src/trace_processor/sqlite/create_function.h"
-#include "src/trace_processor/sqlite/create_view_function.h"
-#include "src/trace_processor/sqlite/register_function.h"
+#include "src/trace_processor/prelude/functions/create_function.h"
+#include "src/trace_processor/prelude/functions/create_view_function.h"
+#include "src/trace_processor/prelude/functions/import.h"
+#include "src/trace_processor/prelude/functions/pprof_functions.h"
+#include "src/trace_processor/prelude/functions/register_function.h"
+#include "src/trace_processor/prelude/functions/sqlite3_str_split.h"
+#include "src/trace_processor/prelude/functions/utils.h"
+#include "src/trace_processor/prelude/functions/window_functions.h"
+#include "src/trace_processor/prelude/operators/span_join_operator.h"
+#include "src/trace_processor/prelude/operators/window_operator.h"
 #include "src/trace_processor/sqlite/scoped_db.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/sqlite_utils.h"
 #include "src/trace_processor/sqlite/stats_table.h"
-#include "src/trace_processor/sqlite/window_operator_table.h"
 #include "src/trace_processor/tp_metatrace.h"
 #include "src/trace_processor/types/variadic.h"
 #include "src/trace_processor/util/protozero_to_text.h"
+#include "src/trace_processor/util/sql_modules.h"
 #include "src/trace_processor/util/status_macros.h"
 
+#include "protos/perfetto/common/builtin_clock.pbzero.h"
+#include "protos/perfetto/trace/clock_snapshot.pbzero.h"
 #include "protos/perfetto/trace/perfetto/perfetto_metatrace.pbzero.h"
 #include "protos/perfetto/trace/trace.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
@@ -74,6 +88,7 @@
 #include "src/trace_processor/metrics/metrics.descriptor.h"
 #include "src/trace_processor/metrics/metrics.h"
 #include "src/trace_processor/metrics/sql/amalgamated_sql_metrics.h"
+#include "src/trace_processor/stdlib/amalgamated_stdlib.h"
 
 // In Android and Chromium tree builds, we don't have the percentile module.
 // Just don't include it.
@@ -151,8 +166,7 @@
     PERFETTO_ELOG("Error initializing: %s", error);
     sqlite3_free(error);
   }
-  sqlite3_exec(db,
-               "CREATE TABLE trace_bounds(start_ts BIG INT, end_ts BIG INT)",
+  sqlite3_exec(db, "CREATE TABLE trace_bounds(start_ts BIGINT, end_ts BIGINT)",
                nullptr, nullptr, &error);
   if (error) {
     PERFETTO_ELOG("Error initializing: %s", error);
@@ -179,8 +193,8 @@
   // in the table is shown specially in the UI, and users can insert rows into
   // this table to draw more things.
   sqlite3_exec(db,
-               "CREATE TABLE debug_slices (id BIG INT, name STRING, ts BIG INT,"
-               "dur BIG INT, depth BIG INT)",
+               "CREATE TABLE debug_slices (id BIGINT, name STRING, ts BIGINT,"
+               "dur BIGINT, depth BIGINT)",
                nullptr, nullptr, &error);
   if (error) {
     PERFETTO_ELOG("Error initializing: %s", error);
@@ -302,167 +316,22 @@
                "FROM internal_args;",
                nullptr, nullptr, &error);
   MaybeRegisterError(error);
-}
 
-struct ExportJson : public SqlFunction {
-  using Context = TraceStorage;
-  static base::Status Run(TraceStorage* storage,
-                          size_t /*argc*/,
-                          sqlite3_value** argv,
-                          SqlValue& /*out*/,
-                          Destructors&);
-};
+  // TODO(lalitm): delete this any time after ~Feb 2023 when no version of the
+  // UI will be querying this anymore (describe_slice backing code was removed
+  // at end of November).
+  sqlite3_exec(db,
+               "CREATE TABLE describe_slice(id INT, type TEXT, "
+               "slice_id INT, description TEXT, doc_link TEXT);",
+               nullptr, nullptr, &error);
+  MaybeRegisterError(error);
 
-base::Status ExportJson::Run(TraceStorage* storage,
-                             size_t /*argc*/,
-                             sqlite3_value** argv,
-                             SqlValue& /*out*/,
-                             Destructors&) {
-  FILE* output;
-  if (sqlite3_value_type(argv[0]) == SQLITE_INTEGER) {
-    // Assume input is an FD.
-    output = fdopen(sqlite3_value_int(argv[0]), "w");
-    if (!output) {
-      return base::ErrStatus(
-          "EXPORT_JSON: Couldn't open output file from given FD");
-    }
-  } else {
-    const char* filename =
-        reinterpret_cast<const char*>(sqlite3_value_text(argv[0]));
-    output = fopen(filename, "w");
-    if (!output) {
-      return base::ErrStatus("EXPORT_JSON: Couldn't open output file");
-    }
-  }
-  return json::ExportJson(storage, output);
-}
-
-struct Hash : public SqlFunction {
-  static base::Status Run(void*,
-                          size_t argc,
-                          sqlite3_value** argv,
-                          SqlValue& out,
-                          Destructors&);
-};
-
-base::Status Hash::Run(void*,
-                       size_t argc,
-                       sqlite3_value** argv,
-                       SqlValue& out,
-                       Destructors&) {
-  base::Hash hash;
-  for (size_t i = 0; i < argc; ++i) {
-    sqlite3_value* value = argv[i];
-    int type = sqlite3_value_type(value);
-    switch (type) {
-      case SQLITE_INTEGER:
-        hash.Update(sqlite3_value_int64(value));
-        break;
-      case SQLITE_TEXT: {
-        const char* ptr =
-            reinterpret_cast<const char*>(sqlite3_value_text(value));
-        hash.Update(ptr, strlen(ptr));
-        break;
-      }
-      default:
-        return base::ErrStatus("HASH: arg %zu has unknown type %d", i, type);
-    }
-  }
-  out = SqlValue::Long(static_cast<int64_t>(hash.digest()));
-  return base::OkStatus();
-}
-
-struct Demangle : public SqlFunction {
-  static base::Status Run(void*,
-                          size_t argc,
-                          sqlite3_value** argv,
-                          SqlValue& out,
-                          Destructors& destructors);
-};
-
-base::Status Demangle::Run(void*,
-                           size_t argc,
-                           sqlite3_value** argv,
-                           SqlValue& out,
-                           Destructors& destructors) {
-  if (argc != 1)
-    return base::ErrStatus("Unsupported number of arg passed to DEMANGLE");
-  sqlite3_value* value = argv[0];
-  if (sqlite3_value_type(value) == SQLITE_NULL)
-    return base::OkStatus();
-
-  if (sqlite3_value_type(value) != SQLITE_TEXT)
-    return base::ErrStatus("Unsupported type of arg passed to DEMANGLE");
-
-  const char* mangled =
-      reinterpret_cast<const char*>(sqlite3_value_text(value));
-
-  std::unique_ptr<char, base::FreeDeleter> demangled =
-      demangle::Demangle(mangled);
-  if (!demangled)
-    return base::OkStatus();
-
-  destructors.string_destructor = free;
-  out = SqlValue::String(demangled.release());
-  return base::OkStatus();
-}
-
-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 RegisterLastNonNullFunction(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");
-  }
+  sqlite3_exec(db,
+               "CREATE VIEW thread_slice AS "
+               "SELECT * FROM slice "
+               "WHERE thread_dur is NOT NULL",
+               nullptr, nullptr, &error);
+  MaybeRegisterError(error);
 }
 
 struct ValueAtMaxTsContext {
@@ -553,116 +422,6 @@
   }
 }
 
-struct ExtractArg : public SqlFunction {
-  using Context = TraceStorage;
-  static base::Status Run(TraceStorage* storage,
-                          size_t argc,
-                          sqlite3_value** argv,
-                          SqlValue& out,
-                          Destructors& destructors);
-};
-
-base::Status ExtractArg::Run(TraceStorage* storage,
-                             size_t argc,
-                             sqlite3_value** argv,
-                             SqlValue& out,
-                             Destructors& destructors) {
-  if (argc != 2)
-    return base::ErrStatus("EXTRACT_ARG: 2 args required");
-
-  // If the arg set id is null, just return null as the result.
-  if (sqlite3_value_type(argv[0]) == SQLITE_NULL)
-    return base::OkStatus();
-
-  if (sqlite3_value_type(argv[0]) != SQLITE_INTEGER)
-    return base::ErrStatus("EXTRACT_ARG: 1st argument should be arg set id");
-
-  if (sqlite3_value_type(argv[1]) != SQLITE_TEXT)
-    return base::ErrStatus("EXTRACT_ARG: 2nd argument should be key");
-
-  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]));
-
-  base::Optional<Variadic> opt_value;
-  RETURN_IF_ERROR(storage->ExtractArg(arg_set_id, key, &opt_value));
-
-  if (!opt_value)
-    return base::OkStatus();
-
-  // This function always returns static strings (i.e. scoped to lifetime
-  // of the TraceStorage thread pool) so prevent SQLite from making copies.
-  destructors.string_destructor = sqlite_utils::kSqliteStatic;
-
-  switch (opt_value->type) {
-    case Variadic::kNull:
-      return base::OkStatus();
-    case Variadic::kInt:
-      out = SqlValue::Long(opt_value->int_value);
-      return base::OkStatus();
-    case Variadic::kUint:
-      out = SqlValue::Long(static_cast<int64_t>(opt_value->uint_value));
-      return base::OkStatus();
-    case Variadic::kString:
-      out =
-          SqlValue::String(storage->GetString(opt_value->string_value).data());
-      return base::OkStatus();
-    case Variadic::kReal:
-      out = SqlValue::Double(opt_value->real_value);
-      return base::OkStatus();
-    case Variadic::kBool:
-      out = SqlValue::Long(opt_value->bool_value);
-      return base::OkStatus();
-    case Variadic::kPointer:
-      out = SqlValue::Long(static_cast<int64_t>(opt_value->pointer_value));
-      return base::OkStatus();
-    case Variadic::kJson:
-      out = SqlValue::String(storage->GetString(opt_value->json_value).data());
-      return base::OkStatus();
-  }
-  PERFETTO_FATAL("For GCC");
-}
-
-struct AbsTimeStr : public SqlFunction {
-  using Context = ClockTracker;
-  static base::Status Run(ClockTracker* tracker,
-                          size_t argc,
-                          sqlite3_value** argv,
-                          SqlValue& out,
-                          Destructors& destructors);
-};
-
-base::Status AbsTimeStr::Run(ClockTracker* tracker,
-                             size_t argc,
-                             sqlite3_value** argv,
-                             SqlValue& out,
-                             Destructors& destructors) {
-  if (argc != 1) {
-    return base::ErrStatus("ABS_TIME_STR: 1 arg required");
-  }
-
-  // If the timestamp is null, just return null as the result.
-  if (sqlite3_value_type(argv[0]) == SQLITE_NULL) {
-    return base::OkStatus();
-  }
-  if (sqlite3_value_type(argv[0]) != SQLITE_INTEGER) {
-    return base::ErrStatus("ABS_TIME_STR: first argument should be timestamp");
-  }
-
-  int64_t ts = sqlite3_value_int64(argv[0]);
-  base::Optional<std::string> iso8601 = tracker->FromTraceTimeAsISO8601(ts);
-  if (!iso8601.has_value()) {
-    return base::OkStatus();
-  }
-
-  std::unique_ptr<char, base::FreeDeleter> s(
-      static_cast<char*>(malloc(iso8601->size() + 1)));
-  memcpy(s.get(), iso8601->c_str(), iso8601->size() + 1);
-
-  destructors.string_destructor = free;
-  out = SqlValue::String(s.release());
-  return base::OkStatus();
-}
-
 std::vector<std::string> SanitizeMetricMountPaths(
     const std::vector<std::string>& mount_paths) {
   std::vector<std::string> sanitized;
@@ -676,34 +435,6 @@
   return sanitized;
 }
 
-struct SourceGeq : public SqlFunction {
-  static base::Status Run(void*,
-                          size_t,
-                          sqlite3_value**,
-                          SqlValue&,
-                          Destructors&) {
-    return base::ErrStatus(
-        "SOURCE_GEQ should not be called from the global scope");
-  }
-};
-
-struct Glob : public SqlFunction {
-  static base::Status Run(void*,
-                          size_t,
-                          sqlite3_value** argv,
-                          SqlValue& out,
-                          Destructors&) {
-    const char* pattern =
-        reinterpret_cast<const char*>(sqlite3_value_text(argv[0]));
-    const char* text =
-        reinterpret_cast<const char*>(sqlite3_value_text(argv[1]));
-    if (pattern && text) {
-      out = SqlValue::Long(sqlite3_strglob(pattern, text) == 0);
-    }
-    return base::OkStatus();
-  }
-};
-
 void SetupMetrics(TraceProcessor* tp,
                   sqlite3* db,
                   std::vector<metrics::SqlMetricFile>* sql_metrics,
@@ -726,7 +457,7 @@
   bool skip_all_sql = std::find(extension_paths.begin(), extension_paths.end(),
                                 "") != extension_paths.end();
   if (!skip_all_sql) {
-    for (const auto& file_to_sql : metrics::sql_metrics::kFileToSql) {
+    for (const auto& file_to_sql : sql_metrics::kFileToSql) {
       if (base::StartsWithAny(file_to_sql.path, sanitized_extension_paths))
         continue;
       tp->RegisterMetric(file_to_sql.path, file_to_sql.sql);
@@ -779,15 +510,26 @@
   if (sqlite_utils::IsStmtDone(stmt))
     return;
 
-  // If the statement only has a single column and that column is named
-  // "suppress_query_output", treat it as a statement without output for
-  // accounting purposes. This is done so that embedders (e.g. shell) can
-  // strictly check that only the last query produces output while also
-  // providing an escape hatch for SELECT RUN_METRIC() invocations (which
-  // sadly produce output).
-  if (sqlite3_column_count(stmt) == 1 &&
-      strcmp(sqlite3_column_name(stmt, 0), "suppress_query_output") == 0) {
-    return;
+  if (sqlite3_column_count(stmt) == 1) {
+    sqlite3_value* value = sqlite3_column_value(stmt, 0);
+
+    // If the "VOID" pointer associated to the return value is not null,
+    // that means this is a function which is forced to return a value
+    // (because all functions in SQLite have to) but doesn't actually
+    // wait to (i.e. it wants to be treated like CREATE TABLE or similar).
+    // Because of this, ignore the return value of this function.
+    // See |WrapSqlFunction| for where this is set.
+    if (sqlite3_value_pointer(value, "VOID") != nullptr) {
+      return;
+    }
+
+    // If the statement only has a single column and that column is named
+    // "suppress_query_output", treat it as a statement without output for
+    // accounting purposes. This allows an escape hatch for cases where the
+    // user explicitly wants to ignore functions as having output.
+    if (strcmp(sqlite3_column_name(stmt, 0), "suppress_query_output") == 0) {
+      return;
+    }
   }
 
   // Otherwise, the statement has output and so increment the count.
@@ -826,7 +568,7 @@
   for (const char* rem_sql = sql.c_str(); rem_sql && rem_sql[0];) {
     ScopedStmt cur_stmt;
     {
-      PERFETTO_TP_TRACE("QUERY_PREPARE");
+      PERFETTO_TP_TRACE(metatrace::Category::QUERY, "QUERY_PREPARE");
       const char* tail = nullptr;
       RETURN_IF_ERROR(sqlite_utils::PrepareStmt(db, rem_sql, &cur_stmt, &tail));
       rem_sql = tail;
@@ -842,26 +584,34 @@
     // the previous statement so we don't have two clashing statements (e.g.
     // SELECT * FROM v and DROP VIEW v) partially stepped into.
     if (prev_stmt) {
-      PERFETTO_TP_TRACE(
-          "STMT_STEP_UNTIL_DONE", [&prev_stmt](metatrace::Record* record) {
-            record->AddArg("SQL", sqlite3_expanded_sql(*prev_stmt));
-          });
+      PERFETTO_TP_TRACE(metatrace::Category::QUERY, "STMT_STEP_UNTIL_DONE",
+                        [&prev_stmt](metatrace::Record* record) {
+                          auto expanded_sql =
+                              sqlite_utils::ExpandedSqlForStmt(*prev_stmt);
+                          record->AddArg("SQL", expanded_sql.get());
+                        });
       RETURN_IF_ERROR(sqlite_utils::StepStmtUntilDone(prev_stmt.get()));
     }
 
     PERFETTO_DLOG("Executing statement: %s", sqlite3_sql(*cur_stmt));
 
     {
-      PERFETTO_TP_TRACE(
-          "STMT_FIRST_STEP", [&cur_stmt](metatrace::Record* record) {
-            record->AddArg("SQL", sqlite3_expanded_sql(*cur_stmt));
-          });
+      PERFETTO_TP_TRACE(metatrace::Category::TOPLEVEL, "STMT_FIRST_STEP",
+                        [&cur_stmt](metatrace::Record* record) {
+                          auto expanded_sql =
+                              sqlite_utils::ExpandedSqlForStmt(*cur_stmt);
+                          record->AddArg("SQL", expanded_sql.get());
+                        });
 
       // Now step once into |cur_stmt| so that when we prepare the next statment
       // we will have executed any dependent bytecode in this one.
       int err = sqlite3_step(*cur_stmt);
-      if (err != SQLITE_ROW && err != SQLITE_DONE)
-        return base::ErrStatus("%s (errcode: %d)", sqlite3_errmsg(db), err);
+      if (err != SQLITE_ROW && err != SQLITE_DONE) {
+        return base::ErrStatus(
+            "%s", sqlite_utils::FormatErrorMessage(
+                      prev_stmt.get(), base::StringView(sql), db, err)
+                      .c_message());
+      }
     }
 
     // Increment the neecessary counts for the statement.
@@ -883,6 +633,45 @@
   return base::OkStatus();
 }
 
+const char* TraceTypeToString(TraceType trace_type) {
+  switch (trace_type) {
+    case kUnknownTraceType:
+      return "unknown";
+    case kProtoTraceType:
+      return "proto";
+    case kJsonTraceType:
+      return "json";
+    case kFuchsiaTraceType:
+      return "fuchsia";
+    case kSystraceTraceType:
+      return "systrace";
+    case kGzipTraceType:
+      return "gzip";
+    case kCtraceTraceType:
+      return "ctrace";
+    case kNinjaLogTraceType:
+      return "ninja_log";
+    case kAndroidBugreportTraceType:
+      return "android_bugreport";
+  }
+  PERFETTO_FATAL("For GCC");
+}
+
+// Register SQL functions only used in local development instances.
+void RegisterDevFunctions(sqlite3* db) {
+  RegisterFunction<WriteFile>(db, "WRITE_FILE", 2);
+}
+
+sql_modules::NameToModule GetStdlibModules() {
+  sql_modules::NameToModule modules;
+  for (const auto& file_to_sql : stdlib::kFileToSql) {
+    std::string import_key = sql_modules::GetImportKey(file_to_sql.path);
+    std::string module = sql_modules::GetModuleName(import_key);
+    modules.Insert(module, {}).first->push_back({import_key, file_to_sql.sql});
+  }
+  return modules;
+}
+
 }  // namespace
 
 template <typename View>
@@ -896,6 +685,8 @@
   context_.fuchsia_trace_tokenizer.reset(new FuchsiaTraceTokenizer(&context_));
   context_.fuchsia_trace_parser.reset(new FuchsiaTraceParser(&context_));
 
+  context_.ninja_log_parser.reset(new NinjaLogParser(&context_));
+
   context_.systrace_trace_parser.reset(new SystraceTraceParser(&context_));
 
   if (util::IsGzipSupported()) {
@@ -909,6 +700,10 @@
     context_.json_trace_parser.reset(new JsonTraceParser(&context_));
   }
 
+  if (context_.config.analyze_trace_proto_content) {
+    context_.content_analyzer.reset(new ProtoContentAnalyzer(&context_));
+  }
+
   RegisterAdditionalModules(&context_);
 
   sqlite3* db = nullptr;
@@ -920,8 +715,12 @@
   db_.reset(std::move(db));
 
   // New style function registration.
+  if (cfg.enable_dev_features) {
+    RegisterDevFunctions(db);
+  }
   RegisterFunction<Glob>(db, "glob", 2);
   RegisterFunction<Hash>(db, "HASH", -1);
+  RegisterFunction<Base64Encode>(db, "BASE64_ENCODE", 1);
   RegisterFunction<Demangle>(db, "DEMANGLE", 1);
   RegisterFunction<SourceGeq>(db, "SOURCE_GEQ", -1);
   RegisterFunction<ExportJson>(db, "EXPORT_JSON", 1, context_.storage.get(),
@@ -929,6 +728,8 @@
   RegisterFunction<ExtractArg>(db, "EXTRACT_ARG", 2, context_.storage.get());
   RegisterFunction<AbsTimeStr>(db, "ABS_TIME_STR", 1,
                                context_.clock_tracker.get());
+  RegisterFunction<ToMonotonic>(db, "TO_MONOTONIC", 1,
+                                context_.clock_tracker.get());
   RegisterFunction<CreateFunction>(
       db, "CREATE_FUNCTION", 3,
       std::unique_ptr<CreateFunction::Context>(
@@ -937,12 +738,28 @@
       db, "CREATE_VIEW_FUNCTION", 3,
       std::unique_ptr<CreateViewFunction::Context>(
           new CreateViewFunction::Context{db_.get()}));
+  RegisterFunction<Import>(db, "IMPORT", 1,
+                           std::unique_ptr<Import::Context>(new Import::Context{
+                               db_.get(), this, &sql_modules_}));
 
   // Old style function registration.
   // TODO(lalitm): migrate this over to using RegisterFunction once aggregate
   // functions are supported.
   RegisterLastNonNullFunction(db);
   RegisterValueAtMaxTsFunction(db);
+  {
+    base::Status status = PprofFunctions::Register(db, &context_);
+    if (!status.ok())
+      PERFETTO_ELOG("%s", status.c_message());
+  }
+
+  auto stdlib_modules = GetStdlibModules();
+  for (auto module_it = stdlib_modules.GetIterator(); module_it; ++module_it) {
+    base::Status status =
+        RegisterSqlModule({module_it.key(), module_it.value(), false});
+    if (!status.ok())
+      PERFETTO_ELOG("%s", status.c_message());
+  }
 
   SetupMetrics(this, *db_, &sql_metrics_, cfg.skip_builtin_metric_paths);
 
@@ -967,33 +784,33 @@
       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())));
+  RegisterDynamicTable(std::unique_ptr<AncestorGenerator>(new AncestorGenerator(
+      AncestorGenerator::Ancestor::kSlice, context_.storage.get())));
   RegisterDynamicTable(std::unique_ptr<AncestorGenerator>(
-      new AncestorGenerator(AncestorGenerator::Ancestor::kSlice, &context_)));
+      new AncestorGenerator(AncestorGenerator::Ancestor::kStackProfileCallsite,
+                            context_.storage.get())));
   RegisterDynamicTable(std::unique_ptr<AncestorGenerator>(new AncestorGenerator(
-      AncestorGenerator::Ancestor::kStackProfileCallsite, &context_)));
-  RegisterDynamicTable(std::unique_ptr<AncestorGenerator>(new AncestorGenerator(
-      AncestorGenerator::Ancestor::kSliceByStack, &context_)));
+      AncestorGenerator::Ancestor::kSliceByStack, context_.storage.get())));
   RegisterDynamicTable(
       std::unique_ptr<DescendantGenerator>(new DescendantGenerator(
-          DescendantGenerator::Descendant::kSlice, &context_)));
-  RegisterDynamicTable(
-      std::unique_ptr<DescendantGenerator>(new DescendantGenerator(
-          DescendantGenerator::Descendant::kSliceByStack, &context_)));
+          DescendantGenerator::Descendant::kSlice, context_.storage.get())));
+  RegisterDynamicTable(std::unique_ptr<DescendantGenerator>(
+      new DescendantGenerator(DescendantGenerator::Descendant::kSliceByStack,
+                              context_.storage.get())));
   RegisterDynamicTable(
       std::unique_ptr<ConnectedFlowGenerator>(new ConnectedFlowGenerator(
-          ConnectedFlowGenerator::Mode::kDirectlyConnectedFlow, &context_)));
-  RegisterDynamicTable(
-      std::unique_ptr<ConnectedFlowGenerator>(new ConnectedFlowGenerator(
-          ConnectedFlowGenerator::Mode::kPrecedingFlow, &context_)));
-  RegisterDynamicTable(
-      std::unique_ptr<ConnectedFlowGenerator>(new ConnectedFlowGenerator(
-          ConnectedFlowGenerator::Mode::kFollowingFlow, &context_)));
+          ConnectedFlowGenerator::Mode::kDirectlyConnectedFlow,
+          context_.storage.get())));
+  RegisterDynamicTable(std::unique_ptr<ConnectedFlowGenerator>(
+      new ConnectedFlowGenerator(ConnectedFlowGenerator::Mode::kPrecedingFlow,
+                                 context_.storage.get())));
+  RegisterDynamicTable(std::unique_ptr<ConnectedFlowGenerator>(
+      new ConnectedFlowGenerator(ConnectedFlowGenerator::Mode::kFollowingFlow,
+                                 context_.storage.get())));
   RegisterDynamicTable(std::unique_ptr<ExperimentalSchedUpidGenerator>(
       new ExperimentalSchedUpidGenerator(storage->sched_slice_table(),
                                          storage->thread_table())));
@@ -1012,10 +829,11 @@
   RegisterDbTable(storage->arg_table());
   RegisterDbTable(storage->thread_table());
   RegisterDbTable(storage->process_table());
+  RegisterDbTable(storage->filedescriptor_table());
 
   RegisterDbTable(storage->slice_table());
   RegisterDbTable(storage->flow_table());
-  RegisterDbTable(storage->thread_slice_table());
+  RegisterDbTable(storage->slice_table());
   RegisterDbTable(storage->sched_slice_table());
   RegisterDbTable(storage->thread_state_table());
   RegisterDbTable(storage->gpu_slice_table());
@@ -1036,6 +854,9 @@
   RegisterDbTable(storage->gpu_counter_track_table());
   RegisterDbTable(storage->gpu_counter_group_table());
   RegisterDbTable(storage->perf_counter_track_table());
+  RegisterDbTable(storage->energy_counter_track_table());
+  RegisterDbTable(storage->uid_counter_track_table());
+  RegisterDbTable(storage->energy_per_uid_counter_track_table());
 
   RegisterDbTable(storage->heap_graph_object_table());
   RegisterDbTable(storage->heap_graph_reference_table());
@@ -1049,10 +870,11 @@
   RegisterDbTable(storage->stack_profile_mapping_table());
   RegisterDbTable(storage->stack_profile_frame_table());
   RegisterDbTable(storage->package_list_table());
-  RegisterDbTable(storage->android_game_intervention_list_table());
   RegisterDbTable(storage->profiler_smaps_table());
 
   RegisterDbTable(storage->android_log_table());
+  RegisterDbTable(storage->android_dumpstate_table());
+  RegisterDbTable(storage->android_game_intervention_list_table());
 
   RegisterDbTable(storage->vulkan_memory_allocations_table());
 
@@ -1070,6 +892,11 @@
   RegisterDbTable(storage->process_memory_snapshot_table());
   RegisterDbTable(storage->memory_snapshot_node_table());
   RegisterDbTable(storage->memory_snapshot_edge_table());
+
+  RegisterDbTable(storage->experimental_proto_path_table());
+  RegisterDbTable(storage->experimental_proto_content_table());
+
+  RegisterDbTable(storage->experimental_missing_chrome_processes_table());
 }
 
 TraceProcessorImpl::~TraceProcessorImpl() = default;
@@ -1096,6 +923,10 @@
   context_.metadata_tracker->SetMetadata(
       metadata::trace_size_bytes,
       Variadic::Integer(static_cast<int64_t>(bytes_parsed_)));
+  const StringId trace_type_id =
+      context_.storage->InternString(TraceTypeToString(context_.trace_type));
+  context_.metadata_tracker->SetMetadata(metadata::trace_type,
+                                         Variadic::String(trace_type_id));
   BuildBoundsTable(*db_, context_.storage->GetTraceTimestampBoundsNs());
 }
 
@@ -1172,7 +1003,7 @@
 }
 
 Iterator TraceProcessorImpl::ExecuteQuery(const std::string& sql) {
-  PERFETTO_TP_TRACE("QUERY_EXECUTE");
+  PERFETTO_TP_TRACE(metatrace::Category::TOPLEVEL, "QUERY_EXECUTE");
 
   uint32_t sql_stats_row =
       context_.storage->mutable_sql_stats()->RecordQueryBegin(
@@ -1205,6 +1036,31 @@
   return field_idx != nullptr;
 }
 
+base::Status TraceProcessorImpl::RegisterSqlModule(SqlModule sql_module) {
+  sql_modules::RegisteredModule new_module;
+  std::string name = sql_module.name;
+  if (sql_modules_.Find(name) && !sql_module.allow_module_override) {
+    return base::ErrStatus(
+        "Module '%s' is already registered. Choose a different name.\n"
+        "If you want to replace the existing module using trace processor "
+        "shell, you need to pass the --dev flag and use --override-sql-module "
+        "to pass the module path.",
+        name.c_str());
+  }
+  for (auto const& name_and_sql : sql_module.files) {
+    if (sql_modules::GetModuleName(name_and_sql.first) != name) {
+      return base::ErrStatus(
+          "File import key doesn't match the module name. First part of import "
+          "key should be module name. Import key: %s, module name: %s.",
+          name_and_sql.first.c_str(), name.c_str());
+    }
+    new_module.import_key_to_file.Insert(name_and_sql.first,
+                                         {name_and_sql.second, false});
+  }
+  sql_modules_.Insert(name, std::move(new_module));
+  return base::OkStatus();
+}
+
 base::Status TraceProcessorImpl::RegisterMetric(const std::string& path,
                                                 const std::string& sql) {
   std::string stripped_sql;
@@ -1215,8 +1071,8 @@
     }
   }
 
-  // Check if the metric with the given path already exists and if it does, just
-  // update the SQL associated with it.
+  // Check if the metric with the given path already exists and if it does,
+  // just update the SQL associated with it.
   auto it = std::find_if(
       sql_metrics_.begin(), sql_metrics_.end(),
       [&path](const metrics::SqlMetricFile& m) { return m.path == path; });
@@ -1254,7 +1110,8 @@
       const auto& prev_path = field_it_and_inserted.first->second;
       PERFETTO_DCHECK(prev_path != path);
       return base::ErrStatus(
-          "RegisterMetric Error: Metric paths %s (which is already registered) "
+          "RegisterMetric Error: Metric paths %s (which is already "
+          "registered) "
           "and %s are both trying to output the proto field %s",
           prev_path.c_str(), path.c_str(), metric.proto_field_name->c_str());
     }
@@ -1333,32 +1190,94 @@
   return pool_.SerializeAsDescriptorSet();
 }
 
-void TraceProcessorImpl::EnableMetatrace() {
-  metatrace::Enable();
+void TraceProcessorImpl::EnableMetatrace(MetatraceConfig config) {
+  metatrace::Enable(config);
 }
 
+namespace {
+
+class StringInterner {
+ public:
+  StringInterner(protos::pbzero::PerfettoMetatrace& event,
+                 base::FlatHashMap<std::string, uint64_t>& interned_strings)
+      : event_(event), interned_strings_(interned_strings) {}
+
+  ~StringInterner() {
+    for (const auto& interned_string : new_interned_strings_) {
+      auto* interned_string_proto = event_.add_interned_strings();
+      interned_string_proto->set_iid(interned_string.first);
+      interned_string_proto->set_value(interned_string.second);
+    }
+  }
+
+  uint64_t InternString(const std::string& str) {
+    uint64_t new_iid = interned_strings_.size();
+    auto insert_result = interned_strings_.Insert(str, new_iid);
+    if (insert_result.second) {
+      new_interned_strings_.emplace_back(new_iid, str);
+    }
+    return *insert_result.first;
+  }
+
+ private:
+  protos::pbzero::PerfettoMetatrace& event_;
+  base::FlatHashMap<std::string, uint64_t>& interned_strings_;
+
+  base::SmallVector<std::pair<uint64_t, std::string>, 16> new_interned_strings_;
+};
+
+}  // namespace
+
 base::Status TraceProcessorImpl::DisableAndReadMetatrace(
     std::vector<uint8_t>* trace_proto) {
   protozero::HeapBuffered<protos::pbzero::Trace> trace;
-  metatrace::DisableAndReadBuffer([&trace](metatrace::Record* record) {
+
+  {
+    uint64_t realtime_timestamp = static_cast<uint64_t>(
+        std::chrono::system_clock::now().time_since_epoch() /
+        std::chrono::nanoseconds(1));
+    uint64_t boottime_timestamp = metatrace::TraceTimeNowNs();
+    auto* clock_snapshot = trace->add_packet()->set_clock_snapshot();
+    {
+      auto* realtime_clock = clock_snapshot->add_clocks();
+      realtime_clock->set_clock_id(
+          protos::pbzero::BuiltinClock::BUILTIN_CLOCK_REALTIME);
+      realtime_clock->set_timestamp(realtime_timestamp);
+    }
+    {
+      auto* boottime_clock = clock_snapshot->add_clocks();
+      boottime_clock->set_clock_id(
+          protos::pbzero::BuiltinClock::BUILTIN_CLOCK_BOOTTIME);
+      boottime_clock->set_timestamp(boottime_timestamp);
+    }
+  }
+
+  base::FlatHashMap<std::string, uint64_t> interned_strings;
+  metatrace::DisableAndReadBuffer([&trace, &interned_strings](
+                                      metatrace::Record* record) {
     auto packet = trace->add_packet();
     packet->set_timestamp(record->timestamp_ns);
     auto* evt = packet->set_perfetto_metatrace();
-    evt->set_event_name(record->event_name);
+
+    StringInterner interner(*evt, interned_strings);
+
+    evt->set_event_name_iid(interner.InternString(record->event_name));
     evt->set_event_duration_ns(record->duration_ns);
     evt->set_thread_id(1);  // Not really important, just required for the ui.
 
     if (record->args_buffer_size == 0)
       return;
 
-    base::StringSplitter s(record->args_buffer, record->args_buffer_size, '\0');
+    base::StringSplitter s(
+        record->args_buffer, record->args_buffer_size, '\0',
+        base::StringSplitter::EmptyTokenMode::ALLOW_EMPTY_TOKENS);
     for (; s.Next();) {
       auto* arg_proto = evt->add_args();
-      arg_proto->set_key(s.cur_token());
+      arg_proto->set_key_iid(interner.InternString(s.cur_token()));
 
       bool has_next = s.Next();
       PERFETTO_CHECK(has_next);
-      arg_proto->set_value(s.cur_token());
+      arg_proto->set_value_iid(interner.InternString(s.cur_token()));
     }
   });
   *trace_proto = trace.SerializeAsArray();
diff --git a/src/trace_processor/trace_processor_impl.h b/src/trace_processor/trace_processor_impl.h
index 17dfab2..60c390d 100644
--- a/src/trace_processor/trace_processor_impl.h
+++ b/src/trace_processor/trace_processor_impl.h
@@ -25,16 +25,19 @@
 #include <string>
 #include <vector>
 
+#include "perfetto/ext/base/flat_hash_map.h"
 #include "perfetto/ext/base/string_view.h"
 #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/create_function.h"
-#include "src/trace_processor/sqlite/create_view_function.h"
+#include "src/trace_processor/prelude/functions/create_function.h"
+#include "src/trace_processor/prelude/functions/create_view_function.h"
+#include "src/trace_processor/prelude/functions/import.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/util/sql_modules.h"
 
 #include "src/trace_processor/metrics/metrics.h"
 #include "src/trace_processor/util/descriptors.h"
@@ -68,6 +71,8 @@
   base::Status RegisterMetric(const std::string& path,
                               const std::string& sql) override;
 
+  base::Status RegisterSqlModule(SqlModule sql_module) override;
+
   base::Status ExtendMetricsProto(const uint8_t* data, size_t size) override;
 
   base::Status ExtendMetricsProto(
@@ -91,7 +96,7 @@
   std::string GetCurrentTraceName() override;
   void SetCurrentTraceName(const std::string&) override;
 
-  void EnableMetatrace() override;
+  void EnableMetatrace(MetatraceConfig config) override;
 
   base::Status DisableAndReadMetatrace(
       std::vector<uint8_t>* trace_proto) override;
@@ -102,8 +107,8 @@
 
   template <typename Table>
   void RegisterDbTable(const Table& table) {
-    DbSqliteTable::RegisterTable(*db_, query_cache_.get(), Table::Schema(),
-                                 &table, Table::Name());
+    DbSqliteTable::RegisterTable(*db_, query_cache_.get(), &table,
+                                 Table::Name());
   }
 
   void RegisterDynamicTable(std::unique_ptr<DynamicTableGenerator> generator) {
@@ -127,6 +132,9 @@
   std::unique_ptr<QueryCache> query_cache_;
 
   DescriptorPool pool_;
+
+  // Map from module name to module contents. Used for IMPORT function.
+  base::FlatHashMap<std::string, sql_modules::RegisteredModule> sql_modules_;
   std::vector<metrics::SqlMetricFile> sql_metrics_;
   std::unordered_map<std::string, std::string> proto_field_to_sql_metric_path_;
 
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index 3998b78..e3581e6 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -21,6 +21,8 @@
 #include <cinttypes>
 #include <functional>
 #include <iostream>
+#include <string>
+#include <unordered_map>
 #include <unordered_set>
 #include <vector>
 
@@ -34,11 +36,13 @@
 #include "perfetto/base/status.h"
 #include "perfetto/base/time.h"
 #include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/flat_hash_map.h"
 #include "perfetto/ext/base/getopt.h"
 #include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/string_splitter.h"
 #include "perfetto/ext/base/string_utils.h"
+#include "perfetto/ext/base/string_view.h"
 #include "perfetto/ext/base/version.h"
 
 #include "perfetto/trace_processor/read_trace.h"
@@ -48,6 +52,7 @@
 #include "src/trace_processor/metrics/metrics.h"
 #include "src/trace_processor/read_trace_internal.h"
 #include "src/trace_processor/util/proto_to_json.h"
+#include "src/trace_processor/util/sql_modules.h"
 #include "src/trace_processor/util/status_macros.h"
 
 #include "protos/perfetto/trace_processor/trace_processor.pbzero.h"
@@ -652,23 +657,56 @@
   }
 };
 
+metatrace::MetatraceCategories ParseMetatraceCategories(std::string s) {
+  using Cat = metatrace::MetatraceCategories;
+  std::transform(s.begin(), s.end(), s.begin(),
+                 [](unsigned char c) { return std::tolower(c); });
+  base::StringSplitter splitter(s, ',');
+
+  Cat result = Cat::NONE;
+  for (; splitter.Next();) {
+    std::string cur = splitter.cur_token();
+    if (cur == "all" || cur == "*") {
+      result = Cat::ALL;
+    } else if (cur == "toplevel") {
+      result = static_cast<Cat>(result | Cat::TOPLEVEL);
+    } else if (cur == "function") {
+      result = static_cast<Cat>(result | Cat::FUNCTION);
+    } else if (cur == "query") {
+      result = static_cast<Cat>(result | Cat::QUERY);
+    } else {
+      PERFETTO_ELOG("Unknown metatrace category %s", cur.data());
+      exit(1);
+    }
+  }
+  return result;
+}
+
 struct CommandLineOptions {
   std::string perf_file_path;
   std::string query_file_path;
   std::string pre_metrics_path;
   std::string sqlite_file_path;
+  std::string sql_module_path;
   std::string metric_names;
   std::string metric_output;
   std::string trace_file_path;
   std::string port_number;
+  std::string override_stdlib_path;
+  std::string override_sql_module_path;
   std::vector<std::string> raw_metric_extensions;
   bool launch_shell = false;
   bool enable_httpd = false;
   bool wide = false;
   bool force_full_sort = false;
   std::string metatrace_path;
+  size_t metatrace_buffer_capacity = 0;
+  metatrace::MetatraceCategories metatrace_categories =
+      metatrace::MetatraceCategories::ALL;
   bool dev = false;
   bool no_ftrace_raw = false;
+  bool analyze_trace_proto_content = false;
+  bool crop_track_events = false;
 };
 
 void PrintUsage(char** argv) {
@@ -691,9 +729,6 @@
                                       If used with --run-metrics, the query is
                                       executed after the selected metrics and
                                       the metrics output is suppressed.
- --pre-metrics FILE                   Read and execute an SQL query from a file.
-                                      This query is executed before the selected
-                                      metrics and can't output any results.
  -D, --httpd                          Enables the HTTP RPC server.
  --http-port PORT                     Specify what port to run HTTP RPC server.
  -i, --interactive                    Starts interactive mode even after a query
@@ -702,35 +737,62 @@
  -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
-                                      in-built metrics or SQL/proto files of
-                                      extension metrics.
- --metrics-output=[binary|text|json]  Allows the output of --run-metrics to be
-                                      specified in either proto binary, proto
-                                      text format or JSON format (default: proto
-                                      text).
  -m, --metatrace FILE                 Enables metatracing of trace processor
                                       writing the resulting trace into FILE.
+ --metatrace-buffer-capacity N        Sets metatrace event buffer to capture
+                                      last N events.
+ --metatrace-categories CATEGORIES    A comma-separated list of metatrace
+                                      categories to enable.
  --full-sort                          Forces the trace processor into performing
                                       a full sort ignoring any windowing
                                       logic.
- --metric-extension DISK_PATH@VIRTUAL_PATH
-                                      Loads metric proto and sql files from
-                                      DISK_PATH/protos and DISK_PATH/sql
-                                      respectively, and mounts them onto
-                                      VIRTUAL_PATH.
+ --no-ftrace-raw                      Prevents ingestion of typed ftrace events
+                                      into the raw table. This significantly
+                                      reduces the memory usage of trace
+                                      processor when loading traces containing
+                                      ftrace events.
+ --analyze-trace-proto-content        Enables trace proto content analysis in
+                                      trace processor.
+ --crop-track-events                  Ignores track event outside of the
+                                      range of interest in trace processor.
  --dev                                Enables features which are reserved for
                                       local development use only and
                                       *should not* be enabled on production
                                       builds. The features behind this flag can
                                       break at any time without any warning.
- --no-ftrace-raw                      Prevents ingestion of typed ftrace events
-                                      into the raw table. This significantly
-                                      reduces the memory usage of trace
-                                      processor when loading traces containing
-                                      ftrace events.)",
+
+Standard library: 
+ --add-sql-module MODULE_PATH         Files from the directory will be treated
+                                      as a new SQL module and can be used for
+                                      IMPORT. The name of the directory is the
+                                      module name.
+ --override-sql-module MODULE_PATH    Will override trace processor module with
+                                      passed contents. The outer directory will
+                                      specify the module name. Only allowed when
+                                      --dev is specified.
+ --override-stdlib=[path_to_stdlib]   Will override trace_processor/stdlib with
+                                      passed contents. The outer directory will
+                                      be ignored. Only allowed when --dev is
+                                      specified.
+
+Metrics:
+ --run-metrics x,y,z                  Runs a comma separated list of metrics and
+                                      prints the result as a TraceMetrics proto
+                                      to stdout. The specified can either be
+                                      in-built metrics or SQL/proto files of
+                                      extension metrics.
+ --pre-metrics FILE                   Read and execute an SQL query from a file.
+                                      This query is executed before the selected
+                                      metrics and can't output any results.
+ --metrics-output=[binary|text|json]  Allows the output of --run-metrics to be
+                                      specified in either proto binary, proto
+                                      text format or JSON format (default: proto
+                                      text).
+ --metric-extension DISK_PATH@VIRTUAL_PATH
+                                      Loads metric proto and sql files from
+                                      DISK_PATH/protos and DISK_PATH/sql
+                                      respectively, and mounts them onto
+                                      VIRTUAL_PATH.)",
                 argv[0]);
 }
 
@@ -742,30 +804,48 @@
     OPT_METRICS_OUTPUT,
     OPT_FORCE_FULL_SORT,
     OPT_HTTP_PORT,
+    OPT_ADD_SQL_MODULE,
     OPT_METRIC_EXTENSION,
     OPT_DEV,
+    OPT_OVERRIDE_STDLIB,
+    OPT_OVERRIDE_SQL_MODULE,
     OPT_NO_FTRACE_RAW,
+    OPT_METATRACE_BUFFER_CAPACITY,
+    OPT_METATRACE_CATEGORIES,
+    OPT_ANALYZE_TRACE_PROTO_CONTENT,
+    OPT_CROP_TRACK_EVENTS,
   };
 
   static const option long_options[] = {
       {"help", no_argument, nullptr, 'h'},
       {"version", no_argument, nullptr, 'v'},
-      {"wide", no_argument, nullptr, 'W'},
-      {"httpd", no_argument, nullptr, 'D'},
-      {"interactive", no_argument, nullptr, 'i'},
       {"debug", no_argument, nullptr, 'd'},
+      {"wide", no_argument, nullptr, 'W'},
       {"perf-file", required_argument, nullptr, 'p'},
       {"query-file", required_argument, nullptr, 'q'},
+      {"httpd", no_argument, nullptr, 'D'},
+      {"http-port", required_argument, nullptr, OPT_HTTP_PORT},
+      {"interactive", no_argument, nullptr, 'i'},
       {"export", required_argument, nullptr, 'e'},
       {"metatrace", required_argument, nullptr, 'm'},
+      {"metatrace-buffer-capacity", required_argument, nullptr,
+       OPT_METATRACE_BUFFER_CAPACITY},
+      {"metatrace-categories", required_argument, nullptr,
+       OPT_METATRACE_CATEGORIES},
+      {"full-sort", no_argument, nullptr, OPT_FORCE_FULL_SORT},
+      {"no-ftrace-raw", no_argument, nullptr, OPT_NO_FTRACE_RAW},
+      {"analyze-trace-proto-content", no_argument, nullptr,
+       OPT_ANALYZE_TRACE_PROTO_CONTENT},
+      {"crop-track-events", no_argument, nullptr, OPT_CROP_TRACK_EVENTS},
+      {"dev", no_argument, nullptr, OPT_DEV},
+      {"add-sql-module", required_argument, nullptr, OPT_ADD_SQL_MODULE},
+      {"override-sql-module", required_argument, nullptr,
+       OPT_OVERRIDE_SQL_MODULE},
+      {"override-stdlib", required_argument, nullptr, OPT_OVERRIDE_STDLIB},
       {"run-metrics", required_argument, nullptr, OPT_RUN_METRICS},
       {"pre-metrics", required_argument, nullptr, OPT_PRE_METRICS},
       {"metrics-output", required_argument, nullptr, OPT_METRICS_OUTPUT},
-      {"full-sort", no_argument, nullptr, OPT_FORCE_FULL_SORT},
-      {"http-port", required_argument, nullptr, OPT_HTTP_PORT},
       {"metric-extension", required_argument, nullptr, OPT_METRIC_EXTENSION},
-      {"dev", no_argument, nullptr, OPT_DEV},
-      {"no-ftrace-raw", no_argument, nullptr, OPT_NO_FTRACE_RAW},
       {nullptr, 0, nullptr, 0}};
 
   bool explicit_interactive = false;
@@ -783,17 +863,8 @@
       exit(0);
     }
 
-    if (option == 'i') {
-      explicit_interactive = true;
-      continue;
-    }
-
-    if (option == 'D') {
-#if PERFETTO_BUILDFLAG(PERFETTO_TP_HTTPD)
-      command_line_options.enable_httpd = true;
-#else
-      PERFETTO_FATAL("HTTP RPC module not supported in this build");
-#endif
+    if (option == 'd') {
+      EnableSQLiteVtableDebugging();
       continue;
     }
 
@@ -802,11 +873,6 @@
       continue;
     }
 
-    if (option == 'd') {
-      EnableSQLiteVtableDebugging();
-      continue;
-    }
-
     if (option == 'p') {
       command_line_options.perf_file_path = optarg;
       continue;
@@ -817,6 +883,25 @@
       continue;
     }
 
+    if (option == 'D') {
+#if PERFETTO_BUILDFLAG(PERFETTO_TP_HTTPD)
+      command_line_options.enable_httpd = true;
+#else
+      PERFETTO_FATAL("HTTP RPC module not supported in this build");
+#endif
+      continue;
+    }
+
+    if (option == OPT_HTTP_PORT) {
+      command_line_options.port_number = optarg;
+      continue;
+    }
+
+    if (option == 'i') {
+      explicit_interactive = true;
+      continue;
+    }
+
     if (option == 'e') {
       command_line_options.sqlite_file_path = optarg;
       continue;
@@ -827,18 +912,15 @@
       continue;
     }
 
-    if (option == OPT_PRE_METRICS) {
-      command_line_options.pre_metrics_path = optarg;
+    if (option == OPT_METATRACE_BUFFER_CAPACITY) {
+      command_line_options.metatrace_buffer_capacity =
+          static_cast<size_t>(atoi(optarg));
       continue;
     }
 
-    if (option == OPT_RUN_METRICS) {
-      command_line_options.metric_names = optarg;
-      continue;
-    }
-
-    if (option == OPT_METRICS_OUTPUT) {
-      command_line_options.metric_output = optarg;
+    if (option == OPT_METATRACE_CATEGORIES) {
+      command_line_options.metatrace_categories =
+          ParseMetatraceCategories(optarg);
       continue;
     }
 
@@ -847,13 +929,18 @@
       continue;
     }
 
-    if (option == OPT_HTTP_PORT) {
-      command_line_options.port_number = optarg;
+    if (option == OPT_NO_FTRACE_RAW) {
+      command_line_options.no_ftrace_raw = true;
       continue;
     }
 
-    if (option == OPT_METRIC_EXTENSION) {
-      command_line_options.raw_metric_extensions.push_back(optarg);
+    if (option == OPT_ANALYZE_TRACE_PROTO_CONTENT) {
+      command_line_options.analyze_trace_proto_content = true;
+      continue;
+    }
+
+    if (option == OPT_CROP_TRACK_EVENTS) {
+      command_line_options.crop_track_events = true;
       continue;
     }
 
@@ -862,8 +949,38 @@
       continue;
     }
 
-    if (option == OPT_NO_FTRACE_RAW) {
-      command_line_options.no_ftrace_raw = true;
+    if (option == OPT_ADD_SQL_MODULE) {
+      command_line_options.sql_module_path = optarg;
+      continue;
+    }
+
+    if (option == OPT_OVERRIDE_SQL_MODULE) {
+      command_line_options.override_sql_module_path = optarg;
+      continue;
+    }
+
+    if (option == OPT_OVERRIDE_STDLIB) {
+      command_line_options.override_stdlib_path = optarg;
+      continue;
+    }
+
+    if (option == OPT_RUN_METRICS) {
+      command_line_options.metric_names = optarg;
+      continue;
+    }
+
+    if (option == OPT_PRE_METRICS) {
+      command_line_options.pre_metrics_path = optarg;
+      continue;
+    }
+
+    if (option == OPT_METRICS_OUTPUT) {
+      command_line_options.metric_output = optarg;
+      continue;
+    }
+
+    if (option == OPT_METRIC_EXTENSION) {
+      command_line_options.raw_metric_extensions.push_back(optarg);
       continue;
     }
 
@@ -1040,6 +1157,83 @@
   return CheckForDuplicateMetricExtension(metric_extensions);
 }
 
+base::Status IncludeSqlModule(std::string root, bool allow_override) {
+  // Remove trailing slash
+  if (root.back() == '/')
+    root = root.substr(0, root.length() - 1);
+
+  if (!base::FileExists(root))
+    return base::ErrStatus("Directory %s does not exist.", root.c_str());
+
+  // Get module name
+  size_t last_slash = root.rfind('/');
+  if ((last_slash == std::string::npos) ||
+      (root.find(".") != std::string::npos))
+    return base::ErrStatus("Module path must point to the directory: %s",
+                           root.c_str());
+
+  std::string module_name = root.substr(last_slash + 1);
+
+  std::vector<std::string> paths;
+  RETURN_IF_ERROR(base::ListFilesRecursive(root, paths));
+  sql_modules::NameToModule modules;
+  for (const auto& path : paths) {
+    if (base::GetFileExtension(path) != ".sql")
+      continue;
+
+    std::string filename = root + "/" + path;
+    std::string file_contents;
+    if (!base::ReadFile(filename, &file_contents))
+      return base::ErrStatus("Cannot read file %s", filename.c_str());
+
+    std::string import_key =
+        module_name + "." + sql_modules::GetImportKey(path);
+    modules.Insert(module_name, {})
+        .first->push_back({import_key, file_contents});
+  }
+  for (auto module_it = modules.GetIterator(); module_it; ++module_it) {
+    auto status = g_tp->RegisterSqlModule(
+        {module_it.key(), module_it.value(), allow_override});
+    if (!status.ok())
+      return status;
+  }
+
+  return base::OkStatus();
+}
+
+base::Status LoadOverridenStdlib(std::string root) {
+  // Remove trailing slash
+  if (root.back() == '/') {
+    root = root.substr(0, root.length() - 1);
+  }
+
+  if (!base::FileExists(root)) {
+    return base::ErrStatus("Directory %s does not exist.", root.c_str());
+  }
+
+  std::vector<std::string> paths;
+  RETURN_IF_ERROR(base::ListFilesRecursive(root, paths));
+  sql_modules::NameToModule modules;
+  for (const auto& path : paths) {
+    if (base::GetFileExtension(path) != ".sql") {
+      continue;
+    }
+    std::string filename = root + "/" + path;
+    std::string file_contents;
+    if (!base::ReadFile(filename, &file_contents)) {
+      return base::ErrStatus("Cannot read file %s", filename.c_str());
+    }
+    std::string import_key = sql_modules::GetImportKey(path);
+    std::string module = sql_modules::GetModuleName(import_key);
+    modules.Insert(module, {}).first->push_back({import_key, file_contents});
+  }
+  for (auto module_it = modules.GetIterator(); module_it; ++module_it) {
+    g_tp->RegisterSqlModule({module_it.key(), module_it.value(), true});
+  }
+
+  return base::OkStatus();
+}
+
 base::Status LoadMetricExtensionProtos(const std::string& proto_root,
                                        const std::string& mount_path) {
   if (!base::FileExists(proto_root)) {
@@ -1294,6 +1488,54 @@
   return base::OkStatus();
 }
 
+base::Status MaybeWriteMetatrace(const std::string& metatrace_path) {
+  if (metatrace_path.empty()) {
+    return base::OkStatus();
+  }
+  std::vector<uint8_t> serialized;
+  base::Status status = g_tp->DisableAndReadMetatrace(&serialized);
+  if (!status.ok())
+    return status;
+
+  auto file = base::OpenFile(metatrace_path, O_CREAT | O_RDWR | O_TRUNC, 0600);
+  if (!file)
+    return base::ErrStatus("Unable to open metatrace file");
+
+  ssize_t res = base::WriteAll(*file, serialized.data(), serialized.size());
+  if (res < 0)
+    return base::ErrStatus("Error while writing metatrace file");
+  return base::OkStatus();
+}
+
+base::Status MaybeUpdateSqlModules(const CommandLineOptions& options) {
+  if (!options.override_stdlib_path.empty()) {
+    if (!options.dev)
+      return base::ErrStatus("Overriding stdlib requires --dev flag");
+
+    auto status = LoadOverridenStdlib(options.override_stdlib_path);
+    if (!status.ok())
+      return base::ErrStatus("Couldn't override stdlib: %s",
+                             status.c_message());
+  }
+
+  if (!options.override_sql_module_path.empty()) {
+    if (!options.dev)
+      return base::ErrStatus("Overriding stdlib modules requires --dev flag");
+
+    auto status = IncludeSqlModule(options.override_sql_module_path, true);
+    if (!status.ok())
+      return base::ErrStatus("Couldn't override stdlib module: %s",
+                             status.c_message());
+  }
+
+  if (!options.sql_module_path.empty()) {
+    auto status = IncludeSqlModule(options.sql_module_path, false);
+    if (!status.ok())
+      return base::ErrStatus("Couldn't add SQL module: %s", status.c_message());
+  }
+  return base::OkStatus();
+}
+
 base::Status TraceProcessorMain(int argc, char** argv) {
   CommandLineOptions options = ParseCommandLineOptions(argc, argv);
 
@@ -1302,6 +1544,11 @@
                             ? SortingMode::kForceFullSort
                             : SortingMode::kDefaultHeuristics;
   config.ingest_ftrace_in_raw_table = !options.no_ftrace_raw;
+  config.analyze_trace_proto_content = options.analyze_trace_proto_content;
+  config.drop_track_event_data_before =
+      options.crop_track_events
+          ? DropTrackEventDataBefore::kTrackEventRangeOfInterest
+          : DropTrackEventDataBefore::kNoDrop;
 
   std::vector<MetricExtension> metric_extensions;
   RETURN_IF_ERROR(ParseMetricExtensionPaths(
@@ -1311,12 +1558,26 @@
     config.skip_builtin_metric_paths.push_back(extension.virtual_path());
   }
 
+  if (options.dev) {
+    config.enable_dev_features = true;
+  }
+
   std::unique_ptr<TraceProcessor> tp = TraceProcessor::CreateInstance(config);
   g_tp = tp.get();
 
+  {
+    base::Status status = MaybeUpdateSqlModules(options);
+    if (!status.ok()) {
+      return status;
+    }
+  }
+
   // Enable metatracing as soon as possible.
   if (!options.metatrace_path.empty()) {
-    tp->EnableMetatrace();
+    metatrace::MetatraceConfig metatrace_config;
+    metatrace_config.override_buffer_size = options.metatrace_buffer_capacity;
+    metatrace_config.categories = options.metatrace_categories;
+    tp->EnableMetatrace(metatrace_config);
   }
 
   // We load all the metric extensions even when --run-metrics arg is not there,
@@ -1340,14 +1601,8 @@
     RETURN_IF_ERROR(PrintStats());
   }
 
-#if PERFETTO_BUILDFLAG(PERFETTO_TP_HTTPD)
-  if (options.enable_httpd) {
-    RunHttpRPCServer(std::move(tp), options.port_number);
-    PERFETTO_FATAL("Should never return");
-  }
-#endif
-
 #if PERFETTO_HAS_SIGNAL_H()
+  // Set up interrupt signal to allow the user to abort query.
   signal(SIGINT, [](int) { g_tp->InterruptQuery(); });
 #endif
 
@@ -1376,7 +1631,12 @@
   }
 
   if (!options.query_file_path.empty()) {
-    RETURN_IF_ERROR(RunQueries(options.query_file_path, true));
+    base::Status status = RunQueries(options.query_file_path, true);
+    if (!status.ok()) {
+      // Write metatrace if needed before exiting.
+      RETURN_IF_ERROR(MaybeWriteMetatrace(options.metatrace_path));
+      return status;
+    }
   }
   base::TimeNanos t_query = base::GetWallTimeNs() - t_query_start;
 
@@ -1384,6 +1644,28 @@
     RETURN_IF_ERROR(ExportTraceToDatabase(options.sqlite_file_path));
   }
 
+#if PERFETTO_BUILDFLAG(PERFETTO_TP_HTTPD)
+  if (options.enable_httpd) {
+#if PERFETTO_HAS_SIGNAL_H()
+    if (options.metatrace_path.empty()) {
+      // Restore the default signal handler to allow the user to terminate
+      // httpd server via Ctrl-C.
+      signal(SIGINT, SIG_DFL);
+    } else {
+      // Write metatrace to file before exiting.
+      static std::string* metatrace_path = &options.metatrace_path;
+      signal(SIGINT, [](int) {
+        MaybeWriteMetatrace(*metatrace_path);
+        exit(1);
+      });
+    }
+#endif
+
+    RunHttpRPCServer(std::move(tp), options.port_number);
+    PERFETTO_FATAL("Should never return");
+  }
+#endif
+
   if (options.launch_shell) {
     RETURN_IF_ERROR(StartInteractiveShell(
         InteractiveOptions{options.wide ? 40u : 20u, metric_format,
@@ -1392,21 +1674,7 @@
     RETURN_IF_ERROR(PrintPerfFile(options.perf_file_path, t_load, t_query));
   }
 
-  if (!options.metatrace_path.empty()) {
-    std::vector<uint8_t> serialized;
-    base::Status status = g_tp->DisableAndReadMetatrace(&serialized);
-    if (!status.ok())
-      return status;
-
-    auto file =
-        base::OpenFile(options.metatrace_path, O_CREAT | O_RDWR | O_TRUNC);
-    if (!file)
-      return base::ErrStatus("Unable to open metatrace file");
-
-    ssize_t res = base::WriteAll(*file, serialized.data(), serialized.size());
-    if (res < 0)
-      return base::ErrStatus("Error while writing metatrace file");
-  }
+  RETURN_IF_ERROR(MaybeWriteMetatrace(options.metatrace_path));
 
   return base::OkStatus();
 }
diff --git a/src/trace_processor/trace_processor_storage_impl.cc b/src/trace_processor/trace_processor_storage_impl.cc
index d6f25ae..3bea7eb 100644
--- a/src/trace_processor/trace_processor_storage_impl.cc
+++ b/src/trace_processor/trace_processor_storage_impl.cc
@@ -19,9 +19,9 @@
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/uuid.h"
 #include "src/trace_processor/forwarding_trace_parser.h"
-#include "src/trace_processor/importers/chrome_track_event.descriptor.h"
 #include "src/trace_processor/importers/common/args_tracker.h"
 #include "src/trace_processor/importers/common/args_translation_table.h"
+#include "src/trace_processor/importers/common/async_track_set_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/flow_tracker.h"
@@ -29,16 +29,17 @@
 #include "src/trace_processor/importers/common/slice_tracker.h"
 #include "src/trace_processor/importers/common/slice_translation_table.h"
 #include "src/trace_processor/importers/common/track_tracker.h"
-#include "src/trace_processor/importers/default_modules.h"
-#include "src/trace_processor/importers/proto/async_track_set_tracker.h"
+#include "src/trace_processor/importers/proto/chrome_track_event.descriptor.h"
+#include "src/trace_processor/importers/proto/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/packet_analyzer.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_reader.h"
 #include "src/trace_processor/importers/proto/stack_profile_tracker.h"
-#include "src/trace_processor/importers/track_event.descriptor.h"
-#include "src/trace_processor/trace_sorter.h"
+#include "src/trace_processor/importers/proto/track_event.descriptor.h"
+#include "src/trace_processor/sorter/trace_sorter.h"
 #include "src/trace_processor/util/descriptors.h"
 
 namespace perfetto {
@@ -134,6 +135,9 @@
   for (std::unique_ptr<ProtoImporterModule>& module : context_.modules) {
     module->NotifyEndOfFile();
   }
+  if (context_.content_analyzer) {
+    PacketAnalyzer::Get(&context_)->NotifyEndOfFile();
+  }
   context_.event_tracker->FlushPendingEvents();
   context_.slice_tracker->FlushPendingSlices();
   context_.heap_profile_tracker->NotifyEndOfFile();
@@ -146,6 +150,10 @@
   context.storage = std::move(context_.storage);
   context.heap_graph_tracker = std::move(context_.heap_graph_tracker);
   context.clock_tracker = std::move(context_.clock_tracker);
+  // "to_ftrace" textual converter of the "raw" table requires remembering the
+  // kernel version (inside system_info_tracker) to know how to textualise
+  // sched_switch.prev_state bitflags.
+  context.system_info_tracker = std::move(context_.system_info_tracker);
 
   context_ = std::move(context);
 }
diff --git a/src/trace_processor/trace_processor_storage_impl.h b/src/trace_processor/trace_processor_storage_impl.h
index 871a1cf..83d9400 100644
--- a/src/trace_processor/trace_processor_storage_impl.h
+++ b/src/trace_processor/trace_processor_storage_impl.h
@@ -42,7 +42,7 @@
   TraceProcessorContext* context() { return &context_; }
 
  protected:
-  base::Hash trace_hash_;
+  base::Hasher trace_hash_;
   TraceProcessorContext context_;
   bool unrecoverable_parse_error_ = false;
   size_t hash_input_size_remaining_ = 4096;
diff --git a/src/trace_processor/trace_sorter.cc b/src/trace_processor/trace_sorter.cc
deleted file mode 100644
index 76bf3f1..0000000
--- a/src/trace_processor/trace_sorter.cc
+++ /dev/null
@@ -1,221 +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 <algorithm>
-#include <memory>
-#include <utility>
-
-#include "perfetto/ext/base/utils.h"
-#include "src/trace_processor/importers/proto/proto_trace_parser.h"
-#include "src/trace_processor/timestamped_trace_piece.h"
-#include "src/trace_processor/trace_sorter.h"
-#include "src/trace_processor/trace_sorter_queue.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-TraceSorter::TraceSorter(TraceProcessorContext* context,
-                         std::unique_ptr<TraceParser> parser,
-                         SortingMode sorting_mode)
-    : context_(context),
-      parser_(std::move(parser)),
-      sorting_mode_(sorting_mode) {
-  const char* env = getenv("TRACE_PROCESSOR_SORT_ONLY");
-  bypass_next_stage_for_testing_ = env && !strcmp(env, "1");
-  if (bypass_next_stage_for_testing_)
-    PERFETTO_ELOG("TEST MODE: bypassing protobuf parsing stage");
-}
-
-TraceSorter::~TraceSorter() {
-  // If trace processor encountered a fatal error, it's possible for some events
-  // to have been pushed without evicting them by pushing to the next stage. Do
-  // that now.
-  for (auto& queue : queues_) {
-    for (const auto& event : queue.events_) {
-      // Calling this function without using the TimestampedTracePiece the same
-      // as just calling the destructor for the element.
-      EvictVariadicAsTtp(event);
-    }
-  }
-}
-
-void TraceSorter::Queue::Sort() {
-  PERFETTO_DCHECK(needs_sorting());
-  PERFETTO_DCHECK(sort_start_idx_ < events_.size());
-
-  // If sort_min_ts_ has been set, it will no long be max_int, and so will be
-  // smaller than max_ts_.
-  PERFETTO_DCHECK(sort_min_ts_ < max_ts_);
-
-  // We know that all events between [0, sort_start_idx_] are sorted. Within
-  // this range, perform a bound search and find the iterator for the min
-  // timestamp that broke the monotonicity. Re-sort from there to the end.
-  auto sort_end = events_.begin() + static_cast<ssize_t>(sort_start_idx_);
-  PERFETTO_DCHECK(std::is_sorted(events_.begin(), sort_end));
-  auto sort_begin = std::lower_bound(events_.begin(), sort_end, sort_min_ts_,
-                                     &TimestampedDescriptor::Compare);
-  std::sort(sort_begin, events_.end());
-  sort_start_idx_ = 0;
-  sort_min_ts_ = 0;
-
-  // At this point |events_| must be fully sorted
-  PERFETTO_DCHECK(std::is_sorted(events_.begin(), events_.end()));
-}
-
-// Removes all the events in |queues_| that are earlier than the given
-// packet index and moves them to the next parser stages, respecting global
-// timestamp order. This function is a "extract min from N sorted queues", with
-// some little cleverness: we know that events tend to be bursty, so events are
-// not going to be randomly distributed on the N |queues_|.
-// Upon each iteration this function finds the first two queues (if any) that
-// have the oldest events, and extracts events from the 1st until hitting the
-// min_ts of the 2nd. Imagine the queues are as follows:
-//
-//  q0           {min_ts: 10  max_ts: 30}
-//  q1    {min_ts:5              max_ts: 35}
-//  q2              {min_ts: 12    max_ts: 40}
-//
-// We know that we can extract all events from q1 until we hit ts=10 without
-// looking at any other queue. After hitting ts=10, we need to re-look to all of
-// them to figure out the next min-event.
-// There are more suitable data structures to do this (e.g. keeping a min-heap
-// to avoid re-scanning all the queues all the times) but doesn't seem worth it.
-// With Android traces (that have 8 CPUs) this function accounts for ~1-3% cpu
-// time in a profiler.
-void TraceSorter::SortAndExtractEventsUntilPacket(uint64_t limit_offset) {
-  constexpr int64_t kTsMax = std::numeric_limits<int64_t>::max();
-  for (;;) {
-    size_t min_queue_idx = 0;  // The index of the queue with the min(ts).
-
-    // The top-2 min(ts) among all queues.
-    // queues_[min_queue_idx].events.timestamp == min_queue_ts[0].
-    int64_t min_queue_ts[2]{kTsMax, kTsMax};
-
-    // This loop identifies the queue which starts with the earliest event and
-    // also remembers the earliest event of the 2nd queue (in min_queue_ts[1]).
-    bool has_queues_with_expired_events = false;
-    for (size_t i = 0; i < queues_.size(); i++) {
-      auto& queue = queues_[i];
-      if (queue.events_.empty())
-        continue;
-      PERFETTO_DCHECK(queue.min_ts_ >= global_min_ts_);
-      PERFETTO_DCHECK(queue.max_ts_ <= global_max_ts_);
-      if (!has_queues_with_expired_events || queue.min_ts_ < min_queue_ts[0]) {
-        min_queue_ts[1] = min_queue_ts[0];
-        min_queue_ts[0] = queue.min_ts_;
-        min_queue_idx = i;
-        has_queues_with_expired_events = true;
-      } else if (queue.min_ts_ < min_queue_ts[1]) {
-        min_queue_ts[1] = queue.min_ts_;
-      }
-    }
-    if (!has_queues_with_expired_events) {
-      // All the queues have events that start after the window (i.e. they are
-      // too recent and not eligible to be extracted given the current window).
-      break;
-    }
-
-    Queue& queue = queues_[min_queue_idx];
-    auto& events = queue.events_;
-    if (queue.needs_sorting())
-      queue.Sort();
-    PERFETTO_DCHECK(queue.min_ts_ == events.front().ts);
-    PERFETTO_DCHECK(queue.min_ts_ == global_min_ts_);
-
-    // Now that we identified the min-queue, extract all events from it until
-    // we hit either: (1) the min-ts of the 2nd queue or (2) the packet index
-    // limit, whichever comes first.
-    size_t num_extracted = 0;
-    for (auto& event : events) {
-      if (event.descriptor.offset() >= limit_offset ||
-          event.ts > min_queue_ts[1]) {
-        break;
-      }
-
-      ++num_extracted;
-      MaybePushEvent(min_queue_idx, event);
-    }  // for (event: events)
-
-    if (!num_extracted) {
-      // No events can be extracted from any of the queues. This means that
-      // either we hit the window or all queues are empty.
-      break;
-    }
-
-    // Now remove the entries from the event buffer and update the queue-local
-    // and global time bounds.
-    events.erase_front(num_extracted);
-
-    // After evicting elements we can empty memory in the front of the
-    // queue.
-    variadic_queue_.FreeMemory();
-
-    // Update the global_{min,max}_ts to reflect the bounds after extraction.
-    if (events.empty()) {
-      queue.min_ts_ = kTsMax;
-      queue.max_ts_ = 0;
-      global_min_ts_ = min_queue_ts[1];
-
-      // If we extraced the max entry from a queue (i.e. we emptied the queue)
-      // we need to recompute the global max, because it might have been the one
-      // just extracted.
-      global_max_ts_ = 0;
-      for (auto& q : queues_)
-        global_max_ts_ = std::max(global_max_ts_, q.max_ts_);
-    } else {
-      queue.min_ts_ = queue.events_.front().ts;
-      global_min_ts_ = std::min(queue.min_ts_, min_queue_ts[1]);
-    }
-  }  // for(;;)
-
-#if PERFETTO_DCHECK_IS_ON()
-  // Check that the global min/max are consistent.
-  int64_t dbg_min_ts = kTsMax;
-  int64_t dbg_max_ts = 0;
-  for (auto& q : queues_) {
-    dbg_min_ts = std::min(dbg_min_ts, q.min_ts_);
-    dbg_max_ts = std::max(dbg_max_ts, q.max_ts_);
-  }
-  PERFETTO_DCHECK(global_min_ts_ == dbg_min_ts);
-  PERFETTO_DCHECK(global_max_ts_ == dbg_max_ts);
-#endif
-}
-
-void TraceSorter::MaybePushEvent(size_t queue_idx,
-                                 const TimestampedDescriptor& ts_desc) {
-  int64_t timestamp = ts_desc.ts;
-  if (timestamp < latest_pushed_event_ts_)
-    context_->storage->IncrementStats(stats::sorter_push_event_out_of_order);
-
-  latest_pushed_event_ts_ = std::max(latest_pushed_event_ts_, timestamp);
-
-  TimestampedTracePiece ttp = EvictVariadicAsTtp(ts_desc);
-
-  if (PERFETTO_UNLIKELY(bypass_next_stage_for_testing_))
-    return;
-
-  if (queue_idx == 0) {
-    // queues_[0] is for non-ftrace packets.
-    parser_->ParseTracePacket(ts_desc.ts, std::move(ttp));
-  } else {
-    // Ftrace queues start at offset 1. So queues_[1] = cpu[0] and so on.
-    uint32_t cpu = static_cast<uint32_t>(queue_idx - 1);
-    parser_->ParseFtracePacket(cpu, ts_desc.ts, std::move(ttp));
-  }
-}
-
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/trace_sorter.h b/src/trace_processor/trace_sorter.h
deleted file mode 100644
index b2e213c..0000000
--- a/src/trace_processor/trace_sorter.h
+++ /dev/null
@@ -1,390 +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_SORTER_H_
-#define SRC_TRACE_PROCESSOR_TRACE_SORTER_H_
-
-#include <algorithm>
-#include <memory>
-#include <utility>
-#include <vector>
-
-#include "perfetto/ext/base/circular_queue.h"
-#include "perfetto/ext/base/utils.h"
-#include "perfetto/trace_processor/basic_types.h"
-#include "perfetto/trace_processor/trace_blob_view.h"
-#include "src/trace_processor/importers/common/trace_parser.h"
-#include "src/trace_processor/timestamped_trace_piece.h"
-#include "src/trace_processor/trace_sorter_queue.h"
-
-namespace perfetto {
-namespace trace_processor {
-
-namespace trace_sorter_internal {
-class VariadicQueue;
-}  // namespace trace_sorter_internal
-
-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
-// order. In order to support streaming use-cases, sorting happens within a
-// window.
-//
-// Events are held in the TraceSorter staging area (events_) until either:
-// 1. We can determine that it's safe to extract events by observing
-//  TracingServiceEvent Flush and ReadBuffer events
-// 2. The trace EOF is reached
-//
-// Incremental extraction
-//
-// Incremental extraction happens by using a combination of flush and read
-// buffer events from the tracing service. Note that incremental extraction
-// is only applicable for write_into_file traces; ring-buffer traces will
-// be sorted fully in-memory implicitly because there is only a single read
-// buffer call at the end.
-//
-// The algorithm for incremental extraction is explained in detail at
-// go/trace-sorting-is-complicated.
-//
-// Sorting algorithm
-//
-// The sorting algorithm is designed around the assumption that:
-// - Most events come from ftrace.
-// - Ftrace events are sorted within each cpu most of the times.
-//
-// Due to this, this class is oprerates as a streaming merge-sort of N+1 queues
-// (N = num cpus + 1 for non-ftrace events). Each queue in turn gets sorted (if
-// necessary) before proceeding with the global merge-sort-extract.
-//
-// When an event is pushed through, it is just appended to the end of one of
-// the N queues. While appending, we keep track of the fact that the queue
-// is still ordered or just lost ordering. When an out-of-order event is
-// detected on a queue we keep track of: (1) the offset within the queue where
-// the chaos begun, (2) the timestamp that broke the ordering.
-//
-// When we decide to extract events from the queues into the next stages of
-// the trace processor, we re-sort the events in the queue. Rather than
-// re-sorting everything all the times, we use the above knowledge to restrict
-// sorting to the (hopefully smaller) tail of the |events_| staging area.
-// At any time, the first partition of |events_| [0 .. sort_start_idx_) is
-// ordered, and the second partition [sort_start_idx_.. end] is not.
-// We use a logarithmic bound search operation to figure out what is the index
-// within the first partition where sorting should start, and sort all events
-// from there to the end.
-class TraceSorter {
- private:
-  using VariadicQueue = trace_sorter_internal::VariadicQueue;
-
- public:
-  enum class SortingMode {
-    kDefault,
-    kFullSort,
-  };
-
-  TraceSorter(TraceProcessorContext* context,
-              std::unique_ptr<TraceParser> parser,
-              SortingMode);
-  ~TraceSorter();
-
-  inline void PushTracePacket(int64_t timestamp,
-                              PacketSequenceState* state,
-                              TraceBlobView event) {
-    uint32_t offset = variadic_queue_.Append(
-        TracePacketData{std::move(event), state->current_generation()});
-    AppendNonFtraceEvent(timestamp, offset, Type::kTracePacket);
-  }
-
-  inline void PushJsonValue(int64_t timestamp, std::string json_value) {
-    uint32_t offset = variadic_queue_.Append(std::move(json_value));
-    AppendNonFtraceEvent(timestamp, offset, Type::kJsonValue);
-  }
-
-  inline void PushFuchsiaRecord(int64_t timestamp,
-                                FuchsiaRecord fuchsia_record) {
-    uint32_t offset = variadic_queue_.Append(std::move(fuchsia_record));
-    AppendNonFtraceEvent(timestamp, offset, Type::kFuchsiaRecord);
-  }
-
-  inline void PushSystraceLine(SystraceLine systrace_line) {
-    auto ts = systrace_line.ts;
-    auto offset = variadic_queue_.Append(std::move(systrace_line));
-    AppendNonFtraceEvent(ts, offset, Type::kSystraceLine);
-  }
-
-  inline void PushTrackEventPacket(int64_t timestamp,
-                                   TrackEventData track_event) {
-    uint32_t offset = variadic_queue_.Append(std::move(track_event));
-    AppendNonFtraceEvent(timestamp, offset, Type::kTrackEvent);
-  }
-
-  inline void PushFtraceEvent(uint32_t cpu,
-                              int64_t timestamp,
-                              TraceBlobView event,
-                              PacketSequenceState* state) {
-    auto* queue = GetQueue(cpu + 1);
-    uint32_t offset = variadic_queue_.Append(
-        FtraceEventData{std::move(event), state->current_generation()});
-    queue->Append(TimestampedDescriptor{
-        timestamp, Descriptor(offset, Type::kFtraceEvent)});
-    UpdateGlobalTs(queue);
-  }
-  inline void PushInlineFtraceEvent(uint32_t cpu,
-                                    int64_t timestamp,
-                                    InlineSchedSwitch inline_sched_switch) {
-    // TODO(rsavitski): if a trace has a mix of normal & "compact" events (being
-    // pushed through this function), the ftrace batches will no longer be fully
-    // sorted by timestamp. In such situations, we will have to sort at the end
-    // of the batch. We can do better as both sub-sequences are sorted however.
-    // Consider adding extra queues, or pushing them in a merge-sort fashion
-    // // instead.
-    auto* queue = GetQueue(cpu + 1);
-    uint32_t offset = variadic_queue_.Append(inline_sched_switch);
-    queue->Append(TimestampedDescriptor{
-        timestamp, Descriptor(offset, Type::kInlineSchedSwitch)});
-    UpdateGlobalTs(queue);
-  }
-  inline void PushInlineFtraceEvent(uint32_t cpu,
-                                    int64_t timestamp,
-                                    InlineSchedWaking inline_sched_waking) {
-    auto* queue = GetQueue(cpu + 1);
-
-    uint32_t offset = variadic_queue_.Append(inline_sched_waking);
-    queue->Append(TimestampedDescriptor{
-        timestamp,
-        Descriptor(offset, TimestampedTracePiece::Type::kInlineSchedWaking)});
-    UpdateGlobalTs(queue);
-  }
-
-  void ExtractEventsForced() {
-    uint32_t cur_mem_block_offset = variadic_queue_.NextOffset();
-    SortAndExtractEventsUntilPacket(cur_mem_block_offset);
-    queues_.clear();
-
-    offset_for_extraction_ = cur_mem_block_offset;
-    flushes_since_extraction_ = 0;
-  }
-
-  void NotifyFlushEvent() { flushes_since_extraction_++; }
-
-  void NotifyReadBufferEvent() {
-    if (sorting_mode_ == SortingMode::kFullSort ||
-        flushes_since_extraction_ < 2) {
-      return;
-    }
-
-    SortAndExtractEventsUntilPacket(offset_for_extraction_);
-    offset_for_extraction_ = variadic_queue_.NextOffset();
-    flushes_since_extraction_ = 0;
-  }
-
-  int64_t max_timestamp() const { return global_max_ts_; }
-
- private:
-  using Type = TimestampedTracePiece::Type;
-  // Stores offset and type of metadat.
-  struct Descriptor {
-   public:
-    static constexpr uint8_t kTypeBits = 4;
-    static constexpr uint64_t kTypeMask = (1 << kTypeBits) - 1;
-    static constexpr uint64_t kOffsetShift = kTypeBits;
-    static constexpr uint64_t kMaxType = kTypeMask;
-
-    static_assert(static_cast<uint8_t>(TimestampedTracePiece::Type::kSize) <=
-                      kTypeMask,
-                  "Too many bits for type");
-
-    Descriptor(uint32_t offset, TimestampedTracePiece::Type type)
-        : packed_value_((static_cast<uint64_t>(offset) << kOffsetShift) |
-                        static_cast<uint64_t>(type)) {}
-
-    uint32_t offset() const {
-      return static_cast<uint32_t>(packed_value_ >> kOffsetShift);
-    }
-
-    TimestampedTracePiece::Type type() const {
-      return static_cast<TimestampedTracePiece::Type>(packed_value_ &
-                                                      kTypeMask);
-    }
-
-   private:
-    uint64_t packed_value_ = 0;
-  };
-
-  struct TimestampedDescriptor {
-    int64_t ts;
-    Descriptor descriptor;
-
-    // For std::lower_bound().
-    static inline bool Compare(const TimestampedDescriptor& x, int64_t ts) {
-      return x.ts < ts;
-    }
-
-    // For std::sort().
-    inline bool operator<(const TimestampedDescriptor& desc) const {
-      return ts < desc.ts ||
-             (ts == desc.ts && descriptor.offset() < desc.descriptor.offset());
-    }
-
-    // For std::sort(). Without this the compiler will fall back on invoking
-    // move operators on temporary objects.
-    friend void swap(TimestampedDescriptor& a, TimestampedDescriptor& b) {
-      // TimestampedDescriptor is 16 bytes + trivially swappable so it can be
-      // done without doing any moving.
-      using AS =
-          typename std::aligned_storage<sizeof(TimestampedDescriptor),
-                                        alignof(TimestampedDescriptor)>::type;
-      using std::swap;
-      swap(reinterpret_cast<AS&>(a), reinterpret_cast<AS&>(b));
-    }
-  };
-
-  static_assert(sizeof(TimestampedDescriptor) == 16,
-                "TimestampeDescriptor cannot grow beyond 16 bytes");
-
-  struct Queue {
-    inline void Append(TimestampedDescriptor ts_desc) {
-      auto ts = ts_desc.ts;
-      events_.emplace_back(std::move(ts_desc));
-      min_ts_ = std::min(min_ts_, ts);
-
-      // Events are often seen in order.
-      if (PERFETTO_LIKELY(ts >= max_ts_)) {
-        max_ts_ = ts;
-      } else {
-        // The event is breaking ordering. The first time it happens, keep
-        // track of which index we are at. We know that everything before that
-        // is sorted (because events were pushed monotonically). Everything
-        // after that index, instead, will need a sorting pass before moving
-        // events to the next pipeline stage.
-        if (sort_start_idx_ == 0) {
-          PERFETTO_DCHECK(events_.size() >= 2);
-          sort_start_idx_ = events_.size() - 1;
-          sort_min_ts_ = ts;
-        } else {
-          sort_min_ts_ = std::min(sort_min_ts_, ts);
-        }
-      }
-
-      PERFETTO_DCHECK(min_ts_ <= max_ts_);
-    }
-
-    bool needs_sorting() const { return sort_start_idx_ != 0; }
-    void Sort();
-
-    base::CircularQueue<TimestampedDescriptor> events_;
-    int64_t min_ts_ = std::numeric_limits<int64_t>::max();
-    int64_t max_ts_ = 0;
-    size_t sort_start_idx_ = 0;
-    int64_t sort_min_ts_ = std::numeric_limits<int64_t>::max();
-  };
-
-  void SortAndExtractEventsUntilPacket(uint64_t limit_packet_idx);
-
-  inline Queue* GetQueue(size_t index) {
-    if (PERFETTO_UNLIKELY(index >= queues_.size()))
-      queues_.resize(index + 1);
-    return &queues_[index];
-  }
-
-  inline void AppendNonFtraceEvent(int64_t ts, uint32_t offset, Type type) {
-    Queue* queue = GetQueue(0);
-    queue->Append(TimestampedDescriptor{ts, Descriptor{offset, type}});
-    UpdateGlobalTs(queue);
-  }
-
-  inline void UpdateGlobalTs(Queue* queue) {
-    global_min_ts_ = std::min(global_min_ts_, queue->min_ts_);
-    global_max_ts_ = std::max(global_max_ts_, queue->max_ts_);
-  }
-
-  TimestampedTracePiece EvictVariadicAsTtp(
-      const TimestampedDescriptor& ts_desc) {
-    switch (ts_desc.descriptor.type()) {
-      case Type::kInlineSchedSwitch:
-        return EvictTypedVariadicAsTtp<InlineSchedSwitch>(ts_desc);
-      case Type::kInlineSchedWaking:
-        return EvictTypedVariadicAsTtp<InlineSchedWaking>(ts_desc);
-      case Type::kFtraceEvent:
-        return EvictTypedVariadicAsTtp<FtraceEventData>(ts_desc);
-      case Type::kTracePacket:
-        return EvictTypedVariadicAsTtp<TracePacketData>(ts_desc);
-      case Type::kTrackEvent:
-        return EvictTypedVariadicAsTtp<TrackEventData>(ts_desc);
-      case Type::kFuchsiaRecord:
-        return EvictTypedVariadicAsTtp<FuchsiaRecord>(ts_desc);
-      case Type::kJsonValue:
-        return EvictTypedVariadicAsTtp<std::string>(ts_desc);
-      case Type::kSystraceLine:
-        return EvictTypedVariadicAsTtp<SystraceLine>(ts_desc);
-      case Type::kInvalid:
-        PERFETTO_FATAL("Invalid TimestampedTracePiece type");
-    }
-    PERFETTO_FATAL("For GCC");
-  }
-
-  template <typename T>
-  TimestampedTracePiece EvictTypedVariadicAsTtp(
-      const TimestampedDescriptor& ts_desc) {
-    return TimestampedTracePiece(
-        ts_desc.ts, variadic_queue_.Evict<T>(ts_desc.descriptor.offset()));
-  }
-
-  void MaybePushEvent(size_t queue_idx, const TimestampedDescriptor& ts_desc)
-      PERFETTO_ALWAYS_INLINE;
-
-  TraceProcessorContext* context_;
-  std::unique_ptr<TraceParser> parser_;
-
-  // Whether we should ignore incremental extraction and just wait for
-  // forced extractionn at the end of the trace.
-  SortingMode sorting_mode_ = SortingMode::kDefault;
-
-  // The packet offset until which events should be extracted. Set based
-  // on the packet offset in |OnReadBuffer|.
-  uint32_t offset_for_extraction_ = 0;
-
-  // The number of flushes which have happened since the last incremental
-  // extraction.
-  uint32_t flushes_since_extraction_ = 0;
-
-  // Stores the metadata for each event type in a memory efficient manner.
-  VariadicQueue variadic_queue_;
-
-  // queues_[0] is the general (non-ftrace) queue.
-  // queues_[1] is the ftrace queue for CPU(0).
-  // queues_[x] is the ftrace queue for CPU(x - 1).
-  std::vector<Queue> queues_;
-
-  // max(e.timestamp for e in queues_).
-  int64_t global_max_ts_ = 0;
-
-  // min(e.timestamp for e in queues_).
-  int64_t global_min_ts_ = std::numeric_limits<int64_t>::max();
-
-  // Used for performance tests. True when setting
-  // TRACE_PROCESSOR_SORT_ONLY=1.
-  bool bypass_next_stage_for_testing_ = false;
-
-  // max(e.ts for e pushed to next stage)
-  int64_t latest_pushed_event_ts_ = std::numeric_limits<int64_t>::min();
-};
-
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_TRACE_SORTER_H_
diff --git a/src/trace_processor/trace_sorter_queue.h b/src/trace_processor/trace_sorter_queue.h
deleted file mode 100644
index 1a91c53..0000000
--- a/src/trace_processor/trace_sorter_queue.h
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2022 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_SORTER_QUEUE_H_
-#define SRC_TRACE_PROCESSOR_TRACE_SORTER_QUEUE_H_
-
-#include "src/trace_processor/timestamped_trace_piece.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace trace_sorter_internal {
-
-// 1MB is good tradeoff between having big enough memory blocks so that we don't
-// need to often append and remove blocks for big traces, but small enough to
-// not overuse memory for small traces.
-static constexpr uint32_t kDefaultSize = 1 * 1024 * 1024;  // 1MB
-
-// Used for storing the data for all different TimestampedTracePiece data types.
-class VariadicQueue {
- public:
-  VariadicQueue() : VariadicQueue(kDefaultSize) {}
-  ~VariadicQueue() {
-    // These checks verify that we evicted all elements from this queue. This is
-    // important as we need to call the destructor to make sure we're not
-    // leaking memory.
-    FreeMemory();
-    PERFETTO_CHECK(mem_blocks_.size() == 1);
-    PERFETTO_CHECK(mem_blocks_.back().empty());
-  }
-
-  VariadicQueue(const VariadicQueue&) = delete;
-  VariadicQueue& operator=(const VariadicQueue&) = delete;
-
-  VariadicQueue(VariadicQueue&&) = default;
-  VariadicQueue& operator=(VariadicQueue&&) noexcept = default;
-
-  // Moves TimestampedTracePiece data type to the end of the queue storage.
-  template <typename T>
-  uint32_t Append(T value) {
-    PERFETTO_DCHECK(!mem_blocks_.empty());
-
-    if (PERFETTO_UNLIKELY(!mem_blocks_.back().HasSpace<T>())) {
-      mem_blocks_.emplace_back(Block(block_size_));
-    }
-
-    auto& back_block = mem_blocks_.back();
-    PERFETTO_DCHECK(back_block.HasSpace<T>());
-    return GlobalMemOffsetFromLastBlockOffset(
-        back_block.Append(std::move(value)));
-  }
-
-  // Moves object out of queue storage.
-  template <typename T>
-  T Evict(uint32_t global_offset) {
-    uint32_t block = (global_offset / block_size_) - deleted_blocks_;
-    uint32_t block_offset = global_offset % block_size_;
-    return mem_blocks_[block].Evict<T>(block_offset);
-  }
-
-  // Clears the empty front of queue storage.
-  void FreeMemory() {
-    while (mem_blocks_.size() > 1 && mem_blocks_.front().empty()) {
-      mem_blocks_.pop_front();
-      deleted_blocks_++;
-    }
-  }
-
-  // Returns the offset value in which new element can be stored.
-  uint32_t NextOffset() const {
-    PERFETTO_DCHECK(!mem_blocks_.empty());
-    return GlobalMemOffsetFromLastBlockOffset(mem_blocks_.back().offset());
-  }
-
-  static VariadicQueue VariadicQueueForTesting(uint32_t size) {
-    return VariadicQueue(size);
-  }
-
- private:
-  // Implementation note: this class stores an extra 8 bytes in debug builds to
-  // store the size of the type stored inside.
-  class Block {
-   public:
-    explicit Block(uint32_t block_size)
-        : size_(block_size),
-          storage_(
-              base::AlignedAllocTyped<uint64_t>(size_ / sizeof(uint64_t))) {}
-
-    template <typename T>
-    bool HasSpace() const {
-#if PERFETTO_DCHECK_IS_ON()
-      return sizeof(T) + sizeof(uint64_t) <= size_ - offset_;
-#else
-      return sizeof(T) <= size_ - offset_;
-#endif
-    }
-
-    template <typename T>
-    uint32_t Append(T value) {
-      static_assert(alignof(T) <= 8,
-                    "Class must have at most 8 byte alignment");
-
-      PERFETTO_DCHECK(offset_ % 8 == 0);
-      PERFETTO_DCHECK(HasSpace<T>());
-
-      uint32_t cur_offset = offset_;
-      char* storage_begin_ptr = reinterpret_cast<char*>(storage_.get());
-      char* ptr = storage_begin_ptr + cur_offset;
-#if PERFETTO_DCHECK_IS_ON()
-      uint64_t* size_ptr = reinterpret_cast<uint64_t*>(ptr);
-      *size_ptr = sizeof(T);
-      ptr += sizeof(uint64_t);
-#endif
-      new (ptr) T(std::move(value));
-      num_elements_++;
-      ptr += sizeof(T);
-      offset_ = static_cast<uint32_t>(
-          base::AlignUp<8>(static_cast<uint32_t>(ptr - storage_begin_ptr)));
-      return cur_offset;
-    }
-
-    template <typename T>
-    T Evict(uint32_t offset) {
-      PERFETTO_DCHECK(offset < size_);
-      PERFETTO_DCHECK(offset % 8 == 0);
-
-      char* ptr = reinterpret_cast<char*>(storage_.get()) + offset;
-#if PERFETTO_DCHECK_IS_ON()
-      uint64_t size = *reinterpret_cast<uint64_t*>(ptr);
-      PERFETTO_DCHECK(size == sizeof(T));
-      ptr += sizeof(uint64_t);
-#endif
-      T* type_ptr = reinterpret_cast<T*>(ptr);
-      T out(std::move(*type_ptr));
-      type_ptr->~T();
-      num_elements_evicted_++;
-      return out;
-    }
-
-    uint32_t offset() const { return offset_; }
-    bool empty() const { return num_elements_ == num_elements_evicted_; }
-
-   private:
-    uint32_t size_;
-    uint32_t offset_ = 0;
-
-    uint32_t num_elements_ = 0;
-    uint32_t num_elements_evicted_ = 0;
-
-    base::AlignedUniquePtr<uint64_t> storage_;
-  };
-
-  explicit VariadicQueue(uint32_t block_size) : block_size_(block_size) {
-    mem_blocks_.emplace_back(Block(block_size_));
-  }
-
-  uint32_t GlobalMemOffsetFromLastBlockOffset(uint32_t block_offset) const {
-    return (deleted_blocks_ + static_cast<uint32_t>(mem_blocks_.size()) - 1) *
-               block_size_ +
-           block_offset;
-  }
-
-  std::deque<Block> mem_blocks_;
-
-  uint32_t block_size_ = kDefaultSize;
-  uint32_t deleted_blocks_ = 0;
-};
-
-}  // namespace trace_sorter_internal
-}  // namespace trace_processor
-}  // namespace perfetto
-
-#endif  // SRC_TRACE_PROCESSOR_TRACE_SORTER_QUEUE_H_
diff --git a/src/trace_processor/trace_sorter_queue_unittest.cc b/src/trace_processor/trace_sorter_queue_unittest.cc
deleted file mode 100644
index b233fe7..0000000
--- a/src/trace_processor/trace_sorter_queue_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/trace_sorter_queue.h"
-#include "gtest/gtest.h"
-#include "src/trace_processor/types/variadic.h"
-#include "test/gtest_and_gmock.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace trace_sorter_internal {
-
-#if PERFETTO_DCHECK_IS_ON()
-constexpr uint32_t RESERVED_SIZE_BYTES = 8ul;
-#else
-constexpr uint32_t RESERVED_SIZE_BYTES = 0ul;
-#endif
-
-using ::testing::_;
-
-TEST(VariadicQueueUnittest, AddAndEvict) {
-  VariadicQueue queue =
-      VariadicQueue::VariadicQueueForTesting(8 + RESERVED_SIZE_BYTES);
-  auto offset = queue.Append<int64_t>(10);
-  int64_t evicted_val = queue.Evict<int64_t>(offset);
-  ASSERT_EQ(evicted_val, 10l);
-}
-
-TEST(VariadicQueueUnittest, AddAndEvictFirstElement) {
-  VariadicQueue queue =
-      VariadicQueue::VariadicQueueForTesting(8 + RESERVED_SIZE_BYTES);
-  auto offset1 = queue.Append<int64_t>(10);
-  auto offset2 = queue.Append<int64_t>(20);
-  ASSERT_EQ(queue.Evict<int64_t>(offset1), 10);
-  ASSERT_EQ(queue.Evict<int64_t>(offset2), 20);
-}
-
-TEST(VariadicQueueUnittest, AppendAfterEviction) {
-  VariadicQueue queue =
-      VariadicQueue::VariadicQueueForTesting(8 + RESERVED_SIZE_BYTES);
-  auto offset = queue.Append<int64_t>(10);
-  ASSERT_EQ(queue.Evict<int64_t>(offset), 10);
-  offset = queue.Append<int64_t>(20);
-  ASSERT_EQ(queue.Evict<int64_t>(offset), 20);
-}
-
-TEST(VariadicQueueUnittest, FreeAllMemory) {
-  VariadicQueue queue =
-      VariadicQueue::VariadicQueueForTesting(8 + RESERVED_SIZE_BYTES);
-  auto offset1 = queue.Append<int64_t>(10);
-  auto offset2 = queue.Append<int64_t>(20);
-  ASSERT_EQ(queue.Evict<int64_t>(offset1), 10);
-  ASSERT_EQ(queue.Evict<int64_t>(offset2), 20);
-  queue.FreeMemory();
-}
-
-TEST(VariadicQueueUnittest, FreeMemoryPartially) {
-  VariadicQueue queue =
-      VariadicQueue::VariadicQueueForTesting(8 + RESERVED_SIZE_BYTES);
-  uint32_t offset1 = queue.Append<int64_t>(10);
-  uint32_t offset2 = queue.Append<int64_t>(20);
-  ASSERT_EQ(queue.Evict<int64_t>(offset1), 10);
-  queue.FreeMemory();
-  ASSERT_EQ(queue.Evict<int64_t>(offset2), 20);
-  queue.FreeMemory();
-}
-
-TEST(VariadicQueueUnittest, AppendDifferentSizes) {
-  VariadicQueue queue =
-      VariadicQueue::VariadicQueueForTesting(8 + RESERVED_SIZE_BYTES);
-  uint32_t offset_long_long = queue.Append<int64_t>(10);
-  uint32_t offset_int = queue.Append<int32_t>(20);
-  uint32_t offset_short = queue.Append<int16_t>(30);
-  uint32_t offset_char = queue.Append<char>('s');
-  ASSERT_EQ(queue.Evict<int64_t>(offset_long_long), 10l);
-  ASSERT_EQ(queue.Evict<int32_t>(offset_int), 20);
-  ASSERT_EQ(queue.Evict<int16_t>(offset_short), static_cast<int16_t>(30));
-  ASSERT_EQ(queue.Evict<char>(offset_char), 's');
-}
-
-}  // namespace trace_sorter_internal
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/trace_sorter_unittest.cc b/src/trace_processor/trace_sorter_unittest.cc
deleted file mode 100644
index 342cde9..0000000
--- a/src/trace_processor/trace_sorter_unittest.cc
+++ /dev/null
@@ -1,301 +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/importers/proto/proto_trace_parser.h"
-
-#include <map>
-#include <random>
-#include <vector>
-
-#include "perfetto/trace_processor/basic_types.h"
-#include "perfetto/trace_processor/trace_blob.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 "test/gtest_and_gmock.h"
-
-namespace perfetto {
-namespace trace_processor {
-namespace {
-
-using ::testing::_;
-using ::testing::InSequence;
-using ::testing::Invoke;
-using ::testing::MockFunction;
-using ::testing::NiceMock;
-
-class MockTraceParser : public ProtoTraceParser {
- public:
-  MockTraceParser(TraceProcessorContext* context) : ProtoTraceParser(context) {}
-
-  MOCK_METHOD4(MOCK_ParseFtracePacket,
-               void(uint32_t cpu,
-                    int64_t timestamp,
-                    const uint8_t* data,
-                    size_t length));
-
-  void ParseFtracePacket(uint32_t cpu,
-                         int64_t timestamp,
-                         TimestampedTracePiece ttp) override {
-    bool isNonCompact = ttp.type == TimestampedTracePiece::Type::kFtraceEvent;
-    MOCK_ParseFtracePacket(
-        cpu, timestamp, isNonCompact ? ttp.ftrace_event.event.data() : nullptr,
-        isNonCompact ? ttp.ftrace_event.event.length() : 0);
-  }
-
-  MOCK_METHOD3(MOCK_ParseTracePacket,
-               void(int64_t ts, const uint8_t* data, size_t length));
-
-  void ParseTracePacket(int64_t ts, TimestampedTracePiece ttp) override {
-    TraceBlobView& tbv = ttp.packet_data.packet;
-    MOCK_ParseTracePacket(ts, tbv.data(), tbv.length());
-  }
-};
-
-class MockTraceStorage : public TraceStorage {
- public:
-  MockTraceStorage() : TraceStorage() {}
-
-  MOCK_METHOD1(InternString, StringId(base::StringView view));
-};
-
-class TraceSorterTest : public ::testing::Test {
- public:
-  TraceSorterTest() : test_buffer_(TraceBlob::Allocate(8)) {
-    storage_ = new NiceMock<MockTraceStorage>();
-    context_.storage.reset(storage_);
-    CreateSorter();
-  }
-
-  void CreateSorter(bool full_sort = true) {
-    std::unique_ptr<MockTraceParser> parser(new MockTraceParser(&context_));
-    parser_ = parser.get();
-    auto sorting_mode = full_sort ? TraceSorter::SortingMode::kFullSort
-                                  : TraceSorter::SortingMode::kDefault;
-    context_.sorter.reset(
-        new TraceSorter(&context_, std::move(parser), sorting_mode));
-  }
-
- protected:
-  TraceProcessorContext context_;
-  MockTraceParser* parser_;
-  NiceMock<MockTraceStorage>* storage_;
-  TraceBlobView test_buffer_;
-};
-
-TEST_F(TraceSorterTest, TestFtrace) {
-  PacketSequenceState state(&context_);
-  TraceBlobView view = test_buffer_.slice_off(0, 1);
-  EXPECT_CALL(*parser_, MOCK_ParseFtracePacket(0, 1000, view.data(), 1));
-  context_.sorter->PushFtraceEvent(0 /*cpu*/, 1000 /*timestamp*/,
-                                   std::move(view), &state);
-  context_.sorter->ExtractEventsForced();
-}
-
-TEST_F(TraceSorterTest, TestTracePacket) {
-  PacketSequenceState state(&context_);
-  TraceBlobView view = test_buffer_.slice_off(0, 1);
-  EXPECT_CALL(*parser_, MOCK_ParseTracePacket(1000, view.data(), 1));
-  context_.sorter->PushTracePacket(1000, &state, std::move(view));
-  context_.sorter->ExtractEventsForced();
-}
-
-TEST_F(TraceSorterTest, Ordering) {
-  PacketSequenceState state(&context_);
-  TraceBlobView view_1 = test_buffer_.slice_off(0, 1);
-  TraceBlobView view_2 = test_buffer_.slice_off(0, 2);
-  TraceBlobView view_3 = test_buffer_.slice_off(0, 3);
-  TraceBlobView view_4 = test_buffer_.slice_off(0, 4);
-
-  InSequence s;
-
-  EXPECT_CALL(*parser_, MOCK_ParseFtracePacket(0, 1000, view_1.data(), 1));
-  EXPECT_CALL(*parser_, MOCK_ParseTracePacket(1001, view_2.data(), 2));
-  EXPECT_CALL(*parser_, MOCK_ParseTracePacket(1100, view_3.data(), 3));
-  EXPECT_CALL(*parser_, MOCK_ParseFtracePacket(2, 1200, view_4.data(), 4));
-
-  context_.sorter->PushFtraceEvent(2 /*cpu*/, 1200 /*timestamp*/,
-                                   std::move(view_4), &state);
-  context_.sorter->PushTracePacket(1001, &state, std::move(view_2));
-  context_.sorter->PushTracePacket(1100, &state, std::move(view_3));
-  context_.sorter->PushFtraceEvent(0 /*cpu*/, 1000 /*timestamp*/,
-                                   std::move(view_1), &state);
-  context_.sorter->ExtractEventsForced();
-}
-
-TEST_F(TraceSorterTest, IncrementalExtraction) {
-  CreateSorter(false);
-
-  PacketSequenceState state(&context_);
-
-  TraceBlobView view_1 = test_buffer_.slice_off(0, 1);
-  TraceBlobView view_2 = test_buffer_.slice_off(0, 2);
-  TraceBlobView view_3 = test_buffer_.slice_off(0, 3);
-  TraceBlobView view_4 = test_buffer_.slice_off(0, 4);
-  TraceBlobView view_5 = test_buffer_.slice_off(0, 5);
-
-  // Flush at the start of packet sequence to match behavior of the
-  // service.
-  context_.sorter->NotifyFlushEvent();
-  context_.sorter->PushTracePacket(1200, &state, std::move(view_2));
-  context_.sorter->PushTracePacket(1100, &state, std::move(view_1));
-
-  // No data should be exttracted at this point because we haven't
-  // seen two flushes yet.
-  context_.sorter->NotifyReadBufferEvent();
-
-  // Now that we've seen two flushes, we should be ready to start extracting
-  // data on the next OnReadBufer call (after two flushes as usual).
-  context_.sorter->NotifyFlushEvent();
-  context_.sorter->NotifyReadBufferEvent();
-
-  context_.sorter->NotifyFlushEvent();
-  context_.sorter->NotifyFlushEvent();
-  context_.sorter->PushTracePacket(1400, &state, std::move(view_4));
-  context_.sorter->PushTracePacket(1300, &state, std::move(view_3));
-
-  // This ReadBuffer call should finally extract until the first OnReadBuffer
-  // call.
-  {
-    InSequence s;
-    EXPECT_CALL(*parser_, MOCK_ParseTracePacket(1100, test_buffer_.data(), 1));
-    EXPECT_CALL(*parser_, MOCK_ParseTracePacket(1200, test_buffer_.data(), 2));
-  }
-  context_.sorter->NotifyReadBufferEvent();
-
-  context_.sorter->NotifyFlushEvent();
-  context_.sorter->PushTracePacket(1500, &state, std::move(view_5));
-
-  // Nothing should be extracted as we haven't seen the second flush.
-  context_.sorter->NotifyReadBufferEvent();
-
-  // Now we've seen the second flush we should extract the next two packets.
-  context_.sorter->NotifyFlushEvent();
-  {
-    InSequence s;
-    EXPECT_CALL(*parser_, MOCK_ParseTracePacket(1300, test_buffer_.data(), 3));
-    EXPECT_CALL(*parser_, MOCK_ParseTracePacket(1400, test_buffer_.data(), 4));
-  }
-  context_.sorter->NotifyReadBufferEvent();
-
-  // The forced extraction should get the last packet.
-  EXPECT_CALL(*parser_, MOCK_ParseTracePacket(1500, test_buffer_.data(), 5));
-  context_.sorter->ExtractEventsForced();
-}
-
-// Simulate a producer bug where the third packet is emitted
-// out of order. Verify that we track the stats correctly.
-TEST_F(TraceSorterTest, OutOfOrder) {
-  CreateSorter(false);
-
-  PacketSequenceState state(&context_);
-
-  TraceBlobView view_1 = test_buffer_.slice_off(0, 1);
-  TraceBlobView view_2 = test_buffer_.slice_off(0, 2);
-  TraceBlobView view_3 = test_buffer_.slice_off(0, 3);
-  TraceBlobView view_4 = test_buffer_.slice_off(0, 4);
-
-  context_.sorter->NotifyFlushEvent();
-  context_.sorter->NotifyFlushEvent();
-  context_.sorter->PushTracePacket(1200, &state, std::move(view_2));
-  context_.sorter->PushTracePacket(1100, &state, std::move(view_1));
-  context_.sorter->NotifyReadBufferEvent();
-
-  // Both of the packets should have been pushed through.
-  context_.sorter->NotifyFlushEvent();
-  context_.sorter->NotifyFlushEvent();
-  {
-    InSequence s;
-    EXPECT_CALL(*parser_, MOCK_ParseTracePacket(1100, test_buffer_.data(), 1));
-    EXPECT_CALL(*parser_, MOCK_ParseTracePacket(1200, test_buffer_.data(), 2));
-  }
-  context_.sorter->NotifyReadBufferEvent();
-
-  // Now, pass the third packet out of order.
-  context_.sorter->NotifyFlushEvent();
-  context_.sorter->NotifyFlushEvent();
-  context_.sorter->PushTracePacket(1150, &state, std::move(view_3));
-  context_.sorter->NotifyReadBufferEvent();
-
-  // The third packet should still be pushed through.
-  context_.sorter->NotifyFlushEvent();
-  context_.sorter->NotifyFlushEvent();
-  EXPECT_CALL(*parser_, MOCK_ParseTracePacket(1150, test_buffer_.data(), 3));
-  context_.sorter->NotifyReadBufferEvent();
-
-  // But we should also increment the stat that this was out of order.
-  ASSERT_EQ(
-      context_.storage->stats()[stats::sorter_push_event_out_of_order].value,
-      1);
-
-  // Push the fourth packet also out of order but after third.
-  context_.sorter->NotifyFlushEvent();
-  context_.sorter->NotifyFlushEvent();
-  context_.sorter->PushTracePacket(1170, &state, std::move(view_4));
-  context_.sorter->NotifyReadBufferEvent();
-
-  // The fourt packet should still be pushed through.
-  EXPECT_CALL(*parser_, MOCK_ParseTracePacket(1170, test_buffer_.data(), 4));
-  context_.sorter->ExtractEventsForced();
-
-  // But we should also increment the stat that this was out of order.
-  ASSERT_EQ(
-      context_.storage->stats()[stats::sorter_push_event_out_of_order].value,
-      2);
-}
-
-// Simulates a random stream of ftrace events happening on random CPUs.
-// Tests that the output of the TraceSorter matches the timestamp order
-// (% events happening at the same time on different CPUs).
-TEST_F(TraceSorterTest, MultiQueueSorting) {
-  PacketSequenceState state(&context_);
-  std::minstd_rand0 rnd_engine(0);
-  std::map<int64_t /*ts*/, std::vector<uint32_t /*cpu*/>> expectations;
-
-  EXPECT_CALL(*parser_, MOCK_ParseFtracePacket(_, _, _, _))
-      .WillRepeatedly(Invoke([&expectations](uint32_t cpu, int64_t timestamp,
-                                             const uint8_t*, size_t) {
-        EXPECT_EQ(expectations.begin()->first, timestamp);
-        auto& cpus = expectations.begin()->second;
-        bool cpu_found = false;
-        for (auto it = cpus.begin(); it < cpus.end(); it++) {
-          if (*it != cpu)
-            continue;
-          cpu_found = true;
-          cpus.erase(it);
-          break;
-        }
-        if (cpus.empty())
-          expectations.erase(expectations.begin());
-        EXPECT_TRUE(cpu_found);
-      }));
-
-  for (int i = 0; i < 1000; i++) {
-    int64_t ts = abs(static_cast<int64_t>(rnd_engine()));
-    int num_cpus = rnd_engine() % 3;
-    for (int j = 0; j < num_cpus; j++) {
-      uint32_t cpu = static_cast<uint32_t>(rnd_engine() % 32);
-      expectations[ts].push_back(cpu);
-      context_.sorter->PushFtraceEvent(cpu, ts, TraceBlobView(), &state);
-    }
-  }
-
-  context_.sorter->ExtractEventsForced();
-  EXPECT_TRUE(expectations.empty());
-}
-
-}  // namespace
-}  // namespace trace_processor
-}  // namespace perfetto
diff --git a/src/trace_processor/types/task_state.cc b/src/trace_processor/types/task_state.cc
index 1588829..91df527 100644
--- a/src/trace_processor/types/task_state.cc
+++ b/src/trace_processor/types/task_state.cc
@@ -16,118 +16,121 @@
 
 #include "src/trace_processor/types/task_state.h"
 
-#include <stdint.h>
-#include <algorithm>
-
-#include "perfetto/base/logging.h"
-#include "perfetto/ext/base/string_writer.h"
+#include <string.h>
 
 namespace perfetto {
 namespace trace_processor {
 namespace ftrace_utils {
 
+// static
+TaskState TaskState::FromRawPrevState(
+    uint16_t raw_state,
+    base::Optional<VersionNumber> kernel_version) {
+  return TaskState(raw_state, kernel_version);
+}
+
+// static
+TaskState TaskState::FromSystrace(const char* state_str) {
+  return TaskState(state_str);
+}
+
+// static
+TaskState TaskState::FromParsedFlags(uint16_t parsed_state) {
+  TaskState ret;
+  ret.parsed_ = parsed_state;
+  return ret;
+}
+
+// See header for extra details.
+//
+// Note to maintainers: going forward, the most likely "breaking" changes are:
+// * a new flag is added to TASK_REPORT (see include/linux/sched.h kernel src)
+// * a new report-specific flag is added above TASK_REPORT
+// In both cases, this will change the value of TASK_REPORT_MAX that is used to
+// report preemption in sched_switch. We'll need to modify this class to keep
+// up, or make traced_probes record the sched_switch format string in traces.
+//
+// Note to maintainers: if changing the default kernel assumption or the 4.4
+// codepath, you'll need to update ToRawStateOnlyForSystraceConversions().
 TaskState::TaskState(uint16_t raw_state,
                      base::Optional<VersionNumber> opt_version) {
+  // Values up to and including 0x20 (EXIT_ZOMBIE) never changed, so map them
+  // directly onto ParsedFlag (we use the same flag bits for convenience).
+  parsed_ = raw_state & (0x40 - 1);
+
+  // Parsing upper bits depends on kernel version. Default to 4.4 because old
+  // perfetto traces don't record kernel version.
   auto version = VersionNumber{4, 4};
   if (opt_version) {
     version = opt_version.value();
   }
-  max_state_ = version < VersionNumber{4, 9} ? 2048 : 4096;
 
-  if (raw_state > max_state_) {
-    state_ = 0;
-  } else {
-    state_ |= kValid;
-  }
+  // Kernels 4.14+: flags up to and including 0x40 (TASK_PARKED) are reported
+  // with their scheduler values. Whereas flags 0x80 (normally TASK_DEAD) and
+  // above are masked off and repurposed for reporting-specific values.
+  if (version >= VersionNumber{4, 14}) {
+    if (raw_state & 0x40)  // TASK_PARKED
+      parsed_ |= kParked;
 
-  if (version < VersionNumber{4, 14}) {
-    state_ |= raw_state;
+    // REPORT_TASK_IDLE (0x80), which reports the TASK_IDLE composite state
+    // (TASK_UNINTERRUPTIBLE | TASK_NOLOAD):
+    if (raw_state & 0x80) {
+      parsed_ |= kIdle;
+    }
+
+    // REPORT_TASK_MAX that sched_switch uses to report preemption. At the time
+    // of writing 0x100 because REPORT_TASK_IDLE is the only report-specific
+    // flag:
+    if (raw_state & 0x100)
+      parsed_ |= kPreempted;
+
+    // Attempt to notice REPORT_TASK_MAX changing. If this dcheck fires, please
+    // file a bug report against perfetto. Exactly 4.14 kernels are excluded
+    // from the dcheck since there are known instances of such kernels that
+    // still use the old flag mask in practice. So we'll still mark the states
+    // as invalid but not crash debug builds.
+    if (raw_state & 0xfe00) {
+      parsed_ = kInvalid;
+      PERFETTO_DCHECK((version == VersionNumber{4, 14}));
+    }
     return;
   }
-  // All values below kTaskDead are consistent between kernels.
-  state_ |= raw_state & (kTaskDead - 1);
 
-  // Only values up to 0x80 (plus max_state) are relevant in kernels >= 4.14.
-  // See
-  // https://android.googlesource.com/kernel/msm.git/+/refs/heads/android-msm-coral-4.14-android10-qpr1/include/trace/events/sched.h#219
-  if (raw_state & 0x40) {
-    state_ |= kParked;
+  // Before 4.14, sched_switch reported the full set of scheduler flags
+  // (without masking down to TASK_REPORT). Note: several flags starting at
+  // 0x40 have a different value to the above because 4.14 reordered them.
+  // See https://github.com/torvalds/linux/commit/8ef9925b02.
+  if (raw_state & 0x40)  // TASK_DEAD
+    parsed_ |= kTaskDead;
+  if (raw_state & 0x80)  // TASK_WAKEKILL
+    parsed_ |= kWakeKill;
+  if (raw_state & 0x100)  // TASK_WAKING
+    parsed_ |= kWaking;
+  if (raw_state & 0x200)  // TASK_PARKED
+    parsed_ |= kParked;
+  if (raw_state & 0x400)  // TASK_NOLOAD
+    parsed_ |= kNoLoad;
+
+  // Convert kUninterruptibleSleep+kNoLoad into kIdle since that's what it
+  // means, and the UI can present the latter better.
+  // See https://github.com/torvalds/linux/commit/80ed87c8a9ca.
+  if (parsed_ == (kUninterruptibleSleep | kNoLoad)) {
+    parsed_ = kIdle;
   }
-  if (raw_state & 0x80) {
-    state_ |= kTaskDead;
-  }
-  if (raw_state & max_state_) {
-    state_ |= max_state_;
-  }
-}
 
-TaskState::TaskState(const char* state_str) {
-  bool invalid_char = false;
-  bool is_runnable = false;
-  for (size_t i = 0; state_str[i] != '\0'; i++) {
-    char c = state_str[i];
-    if (is_kernel_preempt()) {
-      // No other character should be encountered after '+'.
-      invalid_char = true;
-      break;
-    } else if (c == '+') {
-      state_ |= max_state_;
-      continue;
-    }
-
-    if (is_runnable) {
-      // We should not encounter any character apart from '+' if runnable.
-      invalid_char = true;
-      break;
-    }
-
-    if (c == 'R') {
-      if (state_ != 0) {
-        // We should not encounter R if we already have set other atoms.
-        invalid_char = true;
-        break;
-      } else {
-        is_runnable = true;
-        continue;
-      }
-    }
-
-    if (c == 'S')
-      state_ |= Atom::kInterruptibleSleep;
-    else if (c == 'D')
-      state_ |= Atom::kUninterruptibleSleep;
-    else if (c == 'T')
-      state_ |= Atom::kStopped;
-    else if (c == 't')
-      state_ |= Atom::kTraced;
-    else if (c == 'X')
-      state_ |= Atom::kExitDead;
-    else if (c == 'Z')
-      state_ |= Atom::kExitZombie;
-    else if (c == 'x' || c == 'I')
-      // On Linux kernels 4.14+, the character for task dead changed
-      // from 'x' to 'I'.
-      state_ |= Atom::kTaskDead;
-    else if (c == 'K')
-      state_ |= Atom::kWakeKill;
-    else if (c == 'W')
-      state_ |= Atom::kWaking;
-    else if (c == 'P')
-      state_ |= Atom::kParked;
-    else if (c == 'N')
-      state_ |= Atom::kNoLoad;
-    else if (c == '|')
-      continue;
-    else {
-      invalid_char = true;
-      break;
-    }
-  }
-  bool no_state = !is_runnable && state_ == 0;
-  if (invalid_char || no_state || state_ > max_state_) {
-    state_ = 0;
+  // Kernel version range [4.8, 4.14) has TASK_NEW, hence preemption
+  // (TASK_STATE_MAX) is 0x1000. We don't decode TASK_NEW itself since it will
+  // never show up in sched_switch.
+  if (version >= VersionNumber{4, 8}) {
+    if (raw_state & 0x1000)
+      parsed_ |= kPreempted;
   } else {
-    state_ |= kValid;
+    // Kernel (..., 4.8), preemption (TASK_STATE_MAX) is 0x800. Assume all
+    // kernels in this range have the 4.4 state of the bitmask. This is most
+    // likely incorrect on <4.2 as that's when TASK_NOLOAD was introduced
+    // (which means preemption is reported at a different bit).
+    if (raw_state & 0x800)
+      parsed_ |= kPreempted;
   }
 }
 
@@ -136,77 +139,120 @@
     return TaskStateStr{"?"};
   }
 
+  // Character aliases follow sched_switch's format string.
   char buffer[32];
   size_t pos = 0;
-
-  // This mapping is given by the file
-  // https://android.googlesource.com/kernel/msm.git/+/android-msm-wahoo-4.4-pie-qpr1/include/trace/events/sched.h#155
-  // Some of these flags are ignored in later kernels but we output them anyway.
   if (is_runnable()) {
     buffer[pos++] = 'R';
+    if (parsed_ & kPreempted) {
+      buffer[pos++] = '+';
+      PERFETTO_DCHECK(parsed_ == kPreempted);
+    }
   } else {
-    if (state_ & Atom::kInterruptibleSleep)
-      buffer[pos++] = 'S';
-    if (state_ & Atom::kUninterruptibleSleep) {
+    auto append = [&](ParsedFlag flag, char c) {
+      if (!(parsed_ & flag))
+        return;
       if (separator && pos != 0)
         buffer[pos++] = separator;
-      buffer[pos++] = 'D';  // D for (D)isk sleep
-    }
-    if (state_ & Atom::kStopped) {
-      if (separator && pos != 0)
-        buffer[pos++] = separator;
-      buffer[pos++] = 'T';
-    }
-    if (state_ & Atom::kTraced) {
-      if (separator && pos != 0)
-        buffer[pos++] = separator;
-      buffer[pos++] = 't';
-    }
-    if (state_ & Atom::kExitDead) {
-      if (separator && pos != 0)
-        buffer[pos++] = separator;
-      buffer[pos++] = 'X';
-    }
-    if (state_ & Atom::kExitZombie) {
-      if (separator && pos != 0)
-        buffer[pos++] = separator;
-      buffer[pos++] = 'Z';
-    }
-    if (state_ & Atom::kTaskDead) {
-      if (separator && pos != 0)
-        buffer[pos++] = separator;
-      buffer[pos++] = 'I';
-    }
-    if (state_ & Atom::kWakeKill) {
-      if (separator && pos != 0)
-        buffer[pos++] = separator;
-      buffer[pos++] = 'K';
-    }
-    if (state_ & Atom::kWaking) {
-      if (separator && pos != 0)
-        buffer[pos++] = separator;
-      buffer[pos++] = 'W';
-    }
-    if (state_ & Atom::kParked) {
-      if (separator && pos != 0)
-        buffer[pos++] = separator;
-      buffer[pos++] = 'P';
-    }
-    if (state_ & Atom::kNoLoad) {
-      if (separator && pos != 0)
-        buffer[pos++] = separator;
-      buffer[pos++] = 'N';
-    }
+      buffer[pos++] = c;
+    };
+    append(kInterruptibleSleep, 'S');
+    append(kUninterruptibleSleep, 'D');  // (D)isk sleep
+    append(kStopped, 'T');
+    append(kTraced, 't');
+    append(kExitDead, 'X');
+    append(kExitZombie, 'Z');
+    append(kParked, 'P');
+    append(kTaskDead, 'x');
+    append(kWakeKill, 'K');
+    append(kWaking, 'W');
+    append(kNoLoad, 'N');
+    append(kIdle, 'I');
   }
 
-  if (is_kernel_preempt())
-    buffer[pos++] = '+';
-
   TaskStateStr output{};
-  memcpy(output.data(), buffer, std::min(pos, output.size() - 1));
+  size_t sz = (pos < output.size() - 1) ? pos : output.size() - 1;
+  memcpy(output.data(), buffer, sz);
   return output;
 }
 
+// Used when parsing systrace, i.e. textual ftrace output.
+TaskState::TaskState(const char* state_str) {
+  parsed_ = 0;
+  if (!state_str || state_str[0] == '\0') {
+    parsed_ = kInvalid;
+    return;
+  }
+
+  // R or R+, otherwise invalid
+  if (state_str[0] == 'R') {
+    parsed_ = kRunnable;
+    if (!strncmp(state_str, "R+", 3))
+      parsed_ |= kPreempted;
+    return;
+  }
+
+  for (size_t i = 0; state_str[i] != '\0'; i++) {
+    char c = state_str[i];
+    if (c == 'R' || c == '+') {
+      parsed_ = kInvalid;
+      return;
+    }
+    if (c == '|')
+      continue;
+
+    auto parse = [&](ParsedFlag flag, char symbol) {
+      if (c == symbol)
+        parsed_ |= flag;
+      return c == symbol;
+    };
+    bool recognized = false;
+    recognized |= parse(kInterruptibleSleep, 'S');
+    recognized |= parse(kUninterruptibleSleep, 'D');  // (D)isk sleep
+    recognized |= parse(kStopped, 'T');
+    recognized |= parse(kTraced, 't');
+    recognized |= parse(kExitDead, 'X');
+    recognized |= parse(kExitZombie, 'Z');
+    recognized |= parse(kParked, 'P');
+    recognized |= parse(kTaskDead, 'x');
+    recognized |= parse(kWakeKill, 'K');
+    recognized |= parse(kWaking, 'W');
+    recognized |= parse(kNoLoad, 'N');
+    recognized |= parse(kIdle, 'I');
+    if (!recognized) {
+      parsed_ = kInvalid;
+      return;
+    }
+  }
+}
+
+// Hard-assume 4.4 flag layout per the header rationale.
+uint16_t TaskState::ToRawStateOnlyForSystraceConversions() const {
+  if (parsed_ == kInvalid)
+    return 0xffff;
+
+  if (parsed_ == kPreempted)
+    return 0x0800;
+
+  uint16_t ret = parsed_ & (0x40 - 1);
+  if (parsed_ & kTaskDead)
+    ret |= 0x40;
+  if (parsed_ & kWakeKill)
+    ret |= 0x80;
+  if (parsed_ & kWaking)
+    ret |= 0x100;
+  if (parsed_ & kParked)
+    ret |= 0x200;
+  if (parsed_ & kNoLoad)
+    ret |= 0x400;
+
+  // Expand kIdle into the underlying kUninterruptibleSleep + kNoLoad.
+  if (parsed_ & kIdle)
+    ret |= (0x2 | 0x400);
+
+  return ret;
+}
+
 }  // namespace ftrace_utils
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/types/task_state.h b/src/trace_processor/types/task_state.h
index b078951..89908dc 100644
--- a/src/trace_processor/types/task_state.h
+++ b/src/trace_processor/types/task_state.h
@@ -17,78 +17,110 @@
 #ifndef SRC_TRACE_PROCESSOR_TYPES_TASK_STATE_H_
 #define SRC_TRACE_PROCESSOR_TYPES_TASK_STATE_H_
 
-#include <stddef.h>
+#include <stdint.h>
 #include <array>
-#include <utility>
 
-#include "perfetto/base/logging.h"
 #include "perfetto/ext/base/optional.h"
-#include "perfetto/ext/base/string_view.h"
-#include "perfetto/ext/base/string_writer.h"
 #include "src/trace_processor/types/version_number.h"
 
 namespace perfetto {
 namespace trace_processor {
 namespace ftrace_utils {
 
-// A strongly typed representation of the TaskState enum given in sched_switch
-// events.
+// Linux kernel scheduling events (sched_switch) contain a bitmask of the
+// switched-out task's state (prev_state). Perfetto doesn't record the event
+// format string during tracing, the trace contains only the raw bitmask as an
+// integer. Certain kernel versions made backwards incompatible changes to the
+// bitmask's raw representation, so this class guesses how to decode the flags
+// based on the kernel's major+minor version as recorded in the trace. Note:
+// this means we can be wrong if patch backports change the flags, or the
+// kernel diverged from upstream. But this has worked well enough in practice
+// so far.
+//
+// There are three specific kernel version intervals we handle:
+// * [4.14, ...)
+// * [4.8, 4.14)
+// * (..., 4.8), where we assume the 4.4 bitmask
+//
+// (Therefore kernels before 4.2 most likely have incorrect preemption flag
+// parsing.)
+//
+// For 4.14, we assume that the kernel has a backport of the bugfix
+// https://github.com/torvalds/linux/commit/3f5fe9fe ("sched/debug: Fix task
+// state recording/printout"). In other words, traces collected on unpatched
+// 4.14 kernels will have incorrect flags decoded.
 class TaskState {
  public:
   using TaskStateStr = std::array<char, 4>;
 
-  // The ordering and values of these fields comes from the kernel in the file
-  // https://android.googlesource.com/kernel/msm.git/+/android-msm-wahoo-4.4-pie-qpr1/include/linux/sched.h#212
-  enum Atom : uint16_t {
-    kRunnable = 0,
-    kInterruptibleSleep = 1,
-    kUninterruptibleSleep = 2,
-    kStopped = 4,
-    kTraced = 8,
-    kExitDead = 16,
-    kExitZombie = 32,
-    kTaskDead = 64,
-    kWakeKill = 128,
-    kWaking = 256,
-    kParked = 512,
-    kNoLoad = 1024,
-    // This was added in kernel v4.9 but is never used.
-    kTaskNew = 2048,
+  // We transcode the raw bitmasks into a set of these flags to make them
+  // kernel version agnostic.
+  //
+  // Warning: do NOT depend on the numeric values of these constants, and
+  // especially do NOT attempt to use these constants when operating on raw
+  // prev_state masks unless you're changing task_state.cc itself.
+  enum ParsedFlag : uint16_t {
+    kRunnable = 0x0000,  // no flag (besides kPreempted) means "running"
+    kInterruptibleSleep = 0x0001,
+    kUninterruptibleSleep = 0x0002,
+    kStopped = 0x0004,
+    kTraced = 0x0008,
+    kExitDead = 0x0010,
+    kExitZombie = 0x0020,
 
-    kValid = 0x8000,
+    // Starting from here, different kernels have different values:
+    kParked = 0x0040,
+
+    // No longer reported on 4.14+:
+    kTaskDead = 0x0080,
+    kWakeKill = 0x0100,
+    kWaking = 0x0200,
+    kNoLoad = 0x0400,
+
+    // Special states that don't map onto the scheduler's constants:
+    kIdle = 0x4000,
+    kPreempted = 0x8000,  // exclusive as only running tasks can be preempted
+
+    // Sentinel value that is an invalid combination of flags:
+    kInvalid = 0xffff
   };
 
-  TaskState() = default;
-  explicit TaskState(uint16_t raw_state,
-                     base::Optional<VersionNumber> = VersionNumber{4, 4});
-  explicit TaskState(const char* state_str);
+  static TaskState FromRawPrevState(
+      uint16_t raw_state,
+      base::Optional<VersionNumber> kernel_version);
+  static TaskState FromSystrace(const char* state_str);
+  static TaskState FromParsedFlags(uint16_t parsed_state);
 
-  // Returns if this TaskState has a valid representation.
-  bool is_valid() const { return state_ & kValid; }
+  // TODO(rsavitski): consider moving the factory methods to an optional return
+  // type instead.
+  bool is_valid() const { return parsed_ != kInvalid; }
 
-  // Returns the string representation of this (valid) TaskState. This array
-  // is null terminated. |separator| specifies if a separator should be printed
-  // between the atoms (default: \0 meaning no separator).
-  // Note: This function CHECKs that |is_valid()| is true.
+  // Returns the textual representation of this state as a null-terminated
+  // array. |separator| specifies if a separator should be printed between the
+  // atoms (default: \0 meaning no separator).
   TaskStateStr ToString(char separator = '\0') const;
 
-  // Returns the raw state this class can be recreated from.
-  uint16_t raw_state() const {
-    PERFETTO_DCHECK(is_valid());
-    return state_ & ~kValid;
-  }
+  // Converts the TaskState back to the raw format, to be used only when
+  // parsing systrace.
+  // NB: this makes a hard assumption on the 4.4 flag layout, since systrace
+  // files don't specify a kernel version, so when trace_processor later calls
+  // FromRawPrevState to construct sched.end_state column values, it'll default
+  // to the 4.4 layout.
+  // TODO(rsavitski): can we get rid of this entirely and avoid the
+  // str -> TaskState -> uint16_t -> str conversion chain?
+  uint16_t ToRawStateOnlyForSystraceConversions() const;
 
-  // Returns if this TaskState is runnable.
-  bool is_runnable() const { return ((state_ & (max_state_ - 1)) == 0); }
-
-  // Returns whether kernel preemption caused the exit state.
-  bool is_kernel_preempt() const { return state_ & max_state_; }
+  uint16_t ParsedForTesting() const { return parsed_; }
 
  private:
-  // One of Atom - based on given raw_state and version. Will have isValid set
-  // if valid.
-  uint16_t state_ = 0;
-  uint16_t max_state_ = 2048;
+  TaskState() = default;
+  explicit TaskState(uint16_t raw_state,
+                     base::Optional<VersionNumber> kernel_version);
+  explicit TaskState(const char* state_str);
+
+  bool is_runnable() const { return !(parsed_ & ~kPreempted); }
+
+  uint16_t parsed_ = 0;
 };
 
 }  // namespace ftrace_utils
diff --git a/src/trace_processor/types/task_state_unittests.cc b/src/trace_processor/types/task_state_unittests.cc
index e790178..decc199 100644
--- a/src/trace_processor/types/task_state_unittests.cc
+++ b/src/trace_processor/types/task_state_unittests.cc
@@ -23,66 +23,132 @@
 namespace ftrace_utils {
 namespace {
 
-using ::testing::ElementsAre;
+TEST(TaskStateUnittest, PrevStateDefaultsToKernelVersion4p4) {
+  auto from_raw = [](uint16_t raw) {
+    return TaskState::FromRawPrevState(raw, base::nullopt);
+  };
 
-TEST(TaskStateUnittest, Invalid) {
-  TaskState state;
-  ASSERT_FALSE(state.is_valid());
+  // No kernel version -> default to 4.4
+  EXPECT_STREQ(from_raw(0x0).ToString().data(), "R");
+  EXPECT_STREQ(from_raw(0x1).ToString().data(), "S");
+  EXPECT_STREQ(from_raw(0x2).ToString().data(), "D");
+  EXPECT_STREQ(from_raw(0x4).ToString().data(), "T");
+  EXPECT_STREQ(from_raw(0x8).ToString().data(), "t");
+  EXPECT_STREQ(from_raw(0x10).ToString().data(), "X");
+  EXPECT_STREQ(from_raw(0x20).ToString().data(), "Z");
+
+  EXPECT_STREQ(from_raw(0x40).ToString().data(), "x");
+  EXPECT_STREQ(from_raw(0x80).ToString().data(), "K");
+  EXPECT_STREQ(from_raw(0x100).ToString().data(), "W");
+  EXPECT_STREQ(from_raw(0x200).ToString().data(), "P");
+  EXPECT_STREQ(from_raw(0x400).ToString().data(), "N");
+
+  EXPECT_STREQ(from_raw(0x800).ToString().data(), "R+");
+
+  // composite states:
+  EXPECT_STREQ(from_raw(0x82).ToString().data(), "DK");
+  EXPECT_STREQ(from_raw(0x102).ToString().data(), "DW");
 }
 
-TEST(TaskStateUnittest, Smoke) {
-  auto state = TaskState(static_cast<uint16_t>(0u));
-  ASSERT_TRUE(state.is_valid());
+TEST(TaskStateUnittest, KernelVersion4p8) {
+  auto from_raw = [](uint16_t raw) {
+    return TaskState::FromRawPrevState(raw, VersionNumber{4, 8});
+  };
 
-  ASSERT_STREQ(state.ToString().data(), "R");
-  ASSERT_STREQ(TaskState(1).ToString().data(), "S");
-  ASSERT_STREQ(TaskState(2).ToString().data(), "D");
-  ASSERT_STREQ(TaskState(4).ToString().data(), "T");
-  ASSERT_STREQ(TaskState(8).ToString().data(), "t");
-  ASSERT_STREQ(TaskState(16).ToString().data(), "X");
-  ASSERT_STREQ(TaskState(32).ToString().data(), "Z");
-  ASSERT_STREQ(TaskState(64).ToString().data(), "I");
-  ASSERT_STREQ(TaskState(128).ToString().data(), "K");
-  ASSERT_STREQ(TaskState(256).ToString().data(), "W");
-  ASSERT_STREQ(TaskState(512).ToString().data(), "P");
-  ASSERT_STREQ(TaskState(1024).ToString().data(), "N");
+  // Same as defaults (4.4) except for preempt flag.
+  EXPECT_STREQ(from_raw(0x0).ToString().data(), "R");
+  EXPECT_STREQ(from_raw(0x1).ToString().data(), "S");
+  EXPECT_STREQ(from_raw(0x2).ToString().data(), "D");
+  EXPECT_STREQ(from_raw(0x4).ToString().data(), "T");
+  EXPECT_STREQ(from_raw(0x8).ToString().data(), "t");
+  EXPECT_STREQ(from_raw(0x10).ToString().data(), "X");
+  EXPECT_STREQ(from_raw(0x20).ToString().data(), "Z");
+
+  EXPECT_STREQ(from_raw(0x40).ToString().data(), "x");
+  EXPECT_STREQ(from_raw(0x80).ToString().data(), "K");
+  EXPECT_STREQ(from_raw(0x100).ToString().data(), "W");
+  EXPECT_STREQ(from_raw(0x200).ToString().data(), "P");
+  EXPECT_STREQ(from_raw(0x400).ToString().data(), "N");
+
+  EXPECT_STREQ(from_raw(0x1000).ToString().data(), "R+");
 }
 
-TEST(TaskStateUnittest, MultipleState) {
-  ASSERT_STREQ(TaskState(130).ToString().data(), "DK");
-  ASSERT_STREQ(TaskState(258).ToString().data(), "DW");
+TEST(TaskStateUnittest, KernelVersion4p14) {
+  auto from_raw = [](uint16_t raw) {
+    return TaskState::FromRawPrevState(raw, VersionNumber{4, 14});
+  };
 
-  ASSERT_EQ(TaskState("D|K").raw_state(), 130);
-  ASSERT_EQ(TaskState("D|W").raw_state(), 258);
+  EXPECT_STREQ(from_raw(0x0).ToString().data(), "R");
+  EXPECT_STREQ(from_raw(0x1).ToString().data(), "S");
+  EXPECT_STREQ(from_raw(0x2).ToString().data(), "D");
+  EXPECT_STREQ(from_raw(0x4).ToString().data(), "T");
+  EXPECT_STREQ(from_raw(0x8).ToString().data(), "t");
+  EXPECT_STREQ(from_raw(0x10).ToString().data(), "X");
+  EXPECT_STREQ(from_raw(0x20).ToString().data(), "Z");
+
+  EXPECT_STREQ(from_raw(0x40).ToString().data(), "P");
+  EXPECT_STREQ(from_raw(0x80).ToString().data(), "I");
+
+  EXPECT_STREQ(from_raw(0x100).ToString().data(), "R+");
 }
 
-TEST(TaskStateUnittest, KernelVersion) {
-  auto state = TaskState(static_cast<uint16_t>(0u), VersionNumber{4, 14});
-  ASSERT_TRUE(state.is_valid());
-
-  ASSERT_STREQ(state.ToString().data(), "R");
-  ASSERT_STREQ(TaskState(1, VersionNumber{4, 14}).ToString().data(), "S");
-  ASSERT_STREQ(TaskState(2, VersionNumber{4, 14}).ToString().data(), "D");
-  ASSERT_STREQ(TaskState(4, VersionNumber{4, 14}).ToString().data(), "T");
-  ASSERT_STREQ(TaskState(8, VersionNumber{4, 14}).ToString().data(), "t");
-  ASSERT_STREQ(TaskState(16, VersionNumber{4, 14}).ToString().data(), "X");
-  ASSERT_STREQ(TaskState(32, VersionNumber{4, 14}).ToString().data(), "Z");
-  ASSERT_STREQ(TaskState(64, VersionNumber{4, 14}).ToString().data(), "P");
-  ASSERT_STREQ(TaskState(128, VersionNumber{4, 14}).ToString().data(), "I");
-
-  // Any without a specific state but less than max are runnable in this kernel.
-  ASSERT_STREQ(TaskState(256, VersionNumber{4, 14}).ToString().data(), "R");
-  ASSERT_STREQ(TaskState(512, VersionNumber{4, 14}).ToString().data(), "R");
-  ASSERT_STREQ(TaskState(1024, VersionNumber{4, 14}).ToString().data(), "R");
-  ASSERT_STREQ(TaskState(2048, VersionNumber{4, 14}).ToString().data(), "R");
+TEST(TaskStateUnittest, PreemptedFlag) {
+  // Historical TASK_STATE_MAX as of 4.4:
+  {
+    TaskState state = TaskState::FromRawPrevState(0x0800, base::nullopt);
+    EXPECT_STREQ(state.ToString().data(), "R+");
+  }
+  // TASK_STATE_MAX moved due to TASK_NEW:
+  {
+    TaskState state = TaskState::FromRawPrevState(0x1000, VersionNumber{4, 8});
+    EXPECT_STREQ(state.ToString().data(), "R+");
+  }
+  // sched_switch changed to use TASK_REPORT_MAX with one report-specific flag
+  // (TASK_REPORT_IDLE):
+  {
+    TaskState state = TaskState::FromRawPrevState(0x0100, VersionNumber{4, 14});
+    EXPECT_STREQ(state.ToString().data(), "R+");
+  }
+  {
+    TaskState state = TaskState::FromRawPrevState(0x0100, VersionNumber{6, 0});
+    EXPECT_STREQ(state.ToString().data(), "R+");
+  }
 }
 
-TEST(TaskStateUnittest, MaxValueKernelVersion) {
-  // Max value means pre-empted but is different for each kernel version.
-  ASSERT_STREQ(TaskState(2048).ToString().data(), "R+");
-  ASSERT_STREQ(TaskState(2048, VersionNumber{4, 8}).ToString().data(), "R+");
-  ASSERT_STREQ(TaskState(4096, VersionNumber{4, 14}).ToString().data(), "R+");
-  ASSERT_STREQ(TaskState(4096, VersionNumber{4, 19}).ToString().data(), "R+");
+TEST(TaskStateUnittest, FromParsedFlags) {
+  {
+    TaskState state =
+        TaskState::FromParsedFlags(TaskState::kInterruptibleSleep);
+    EXPECT_STREQ(state.ToString().data(), "S");
+  }
+  {
+    TaskState state = TaskState::FromParsedFlags(TaskState::kParked);
+    EXPECT_STREQ(state.ToString().data(), "P");
+  }
+  {
+    TaskState state = TaskState::FromParsedFlags(TaskState::kRunnable |
+                                                 TaskState::kPreempted);
+    EXPECT_STREQ(state.ToString().data(), "R+");
+  }
+}
+
+// Covers both:
+// * parsing from systrace format ("prev_state=D|K")
+// * traceconv serializing the "raw" table into systrace format
+TEST(TaskStateUnittest, Systrace) {
+  auto roundtrip = [](const char* in) {
+    uint16_t raw =
+        TaskState::FromSystrace(in).ToRawStateOnlyForSystraceConversions();
+    return TaskState::FromRawPrevState(raw, base::nullopt).ToString('|');
+  };
+
+  EXPECT_STREQ(roundtrip("R").data(), "R");
+  EXPECT_STREQ(roundtrip("R+").data(), "R+");
+  EXPECT_STREQ(roundtrip("S").data(), "S");
+  EXPECT_STREQ(roundtrip("P").data(), "P");
+  EXPECT_STREQ(roundtrip("x").data(), "x");
+  EXPECT_STREQ(roundtrip("D|K").data(), "D|K");
+  EXPECT_STREQ(roundtrip("I").data(), "I");
 }
 
 }  // namespace
diff --git a/src/trace_processor/types/tcp_state.h b/src/trace_processor/types/tcp_state.h
index a51deaa..175971b 100644
--- a/src/trace_processor/types/tcp_state.h
+++ b/src/trace_processor/types/tcp_state.h
@@ -26,6 +26,8 @@
 constexpr int kAfNet6 = 10;
 // Sock TCP protocol Definition, from include/uapi/linux/in.h.
 constexpr int kIpprotoTcp = 6;
+// Sock UDP protocol Definition, from include/uapi/linux/in.h.
+constexpr int kIpprotoUdp = 17;
 // Skb IPV4 Protocol Definition, from include/uapi/linux/if_ether.h.
 constexpr int kEthPIp = 0x800;
 // Skb IPV6 Protocol Definition, from include/uapi/linux/if_ether.h.
diff --git a/src/trace_processor/types/trace_processor_context.h b/src/trace_processor/types/trace_processor_context.h
index 95376c1..12aa16b 100644
--- a/src/trace_processor/types/trace_processor_context.h
+++ b/src/trace_processor/types/trace_processor_context.h
@@ -26,12 +26,25 @@
 namespace perfetto {
 namespace trace_processor {
 
+enum TraceType {
+  kUnknownTraceType,
+  kProtoTraceType,
+  kJsonTraceType,
+  kFuchsiaTraceType,
+  kSystraceTraceType,
+  kGzipTraceType,
+  kCtraceTraceType,
+  kNinjaLogTraceType,
+  kAndroidBugreportTraceType,
+};
+
 class ArgsTracker;
 class ArgsTranslationTable;
 class AsyncTrackSetTracker;
 class AndroidProbesTracker;
 class ChunkedTraceReader;
 class ClockTracker;
+class DeobfuscationMappingTable;
 class EventTracker;
 class ForwardingTraceParser;
 class FtraceModule;
@@ -41,7 +54,9 @@
 class HeapProfileTracker;
 class PerfSampleTracker;
 class MetadataTracker;
+class PacketAnalyzer;
 class ProtoImporterModule;
+class TrackEventModule;
 class ProcessTracker;
 class SliceTracker;
 class SliceTranslationTable;
@@ -98,22 +113,26 @@
   std::unique_ptr<Destructible> sched_tracker;           // SchedEventTracker
   std::unique_ptr<Destructible> syscall_tracker;         // SyscallTracker
   std::unique_ptr<Destructible> system_info_tracker;     // SystemInfoTracker
+  std::unique_ptr<Destructible> v4l2_tracker;            // V4l2Tracker
+  std::unique_ptr<Destructible> virtio_video_tracker;    // VirtioVideoTracker
   std::unique_ptr<Destructible> systrace_parser;         // SystraceParser
   std::unique_ptr<Destructible> thread_state_tracker;    // ThreadStateTracker
   std::unique_ptr<Destructible> i2c_tracker;             // I2CTracker
+  std::unique_ptr<Destructible> content_analyzer;
 
   // 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.
+  // are only available in the lib target.
   std::unique_ptr<ChunkedTraceReader> json_trace_tokenizer;
   std::unique_ptr<ChunkedTraceReader> fuchsia_trace_tokenizer;
+  std::unique_ptr<ChunkedTraceReader> ninja_log_parser;
   std::unique_ptr<ChunkedTraceReader> android_bugreport_parser;
   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.
+  // are only available in the lib target.
   std::unique_ptr<TraceParser> json_trace_parser;
   std::unique_ptr<TraceParser> fuchsia_trace_parser;
 
@@ -125,12 +144,18 @@
   // TracePacket.
   std::vector<std::vector<ProtoImporterModule*>> modules_by_field;
   std::vector<std::unique_ptr<ProtoImporterModule>> modules;
+  // Pointers to modules from the modules vector that need to be called for
+  // all fields.
+  std::vector<ProtoImporterModule*> modules_for_all_fields;
   FtraceModule* ftrace_module = nullptr;
+  TrackEventModule* track_module = nullptr;
 
   // Marks whether the uuid was read from the trace.
   // If the uuid was NOT read, the uuid will be made from the hash of the first
   // 4KB of the trace.
   bool uuid_found_in_trace = false;
+
+  TraceType trace_type = kUnknownTraceType;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/types/variadic.cc b/src/trace_processor/types/variadic.cc
index 837bfeb..9a26bd5 100644
--- a/src/trace_processor/types/variadic.cc
+++ b/src/trace_processor/types/variadic.cc
@@ -19,7 +19,9 @@
 namespace perfetto {
 namespace trace_processor {
 
+#if !PERFETTO_IS_AT_LEAST_CPP17()
 constexpr const char* Variadic::kTypeNames[];
+#endif
 
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/types/version_number.h b/src/trace_processor/types/version_number.h
index 9119cea..c013bcd 100644
--- a/src/trace_processor/types/version_number.h
+++ b/src/trace_processor/types/version_number.h
@@ -26,10 +26,13 @@
   uint32_t major;
   uint32_t minor;
 
-  bool operator<(const VersionNumber& other) {
+  bool operator==(const VersionNumber& other) const {
+    return std::tie(major, minor) == std::tie(other.major, other.minor);
+  }
+  bool operator<(const VersionNumber& other) const {
     return std::tie(major, minor) < std::tie(other.major, other.minor);
   }
-  bool operator>=(const VersionNumber& other) {
+  bool operator>=(const VersionNumber& other) const {
     return std::tie(major, minor) >= std::tie(other.major, other.minor);
   }
 };
diff --git a/src/trace_processor/util/BUILD.gn b/src/trace_processor/util/BUILD.gn
index ec24986..98ff445 100644
--- a/src/trace_processor/util/BUILD.gn
+++ b/src/trace_processor/util/BUILD.gn
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 import("../../../gn/perfetto.gni")
+import("../../../gn/perfetto_cc_proto_descriptor.gni")
 
 # Track event args parsing logic here is tentatitively planned to eventually
 # move to src/util and will be used to implement writing typed args in console
@@ -29,6 +30,21 @@
   ]
 }
 
+source_set("stdlib") {
+  sources = [ "sql_modules.h" ]
+}
+
+source_set("bump_allocator") {
+  sources = [
+    "bump_allocator.cc",
+    "bump_allocator.h",
+  ]
+  deps = [
+    "../../../gn:default_deps",
+    "../../base",
+  ]
+}
+
 source_set("gzip") {
   sources = [
     "gzip_utils.cc",
@@ -68,7 +84,7 @@
     "../../../protos/perfetto/trace/track_event:zero",
     "../../base",
     "../../protozero",
-    "../importers:gen_cc_track_event_descriptor",
+    "../importers/proto:gen_cc_track_event_descriptor",
   ]
 }
 
@@ -93,7 +109,7 @@
     "../../../protos/perfetto/trace_processor:zero",
     "../../base",
     "../../protozero",
-    "../importers:gen_cc_track_event_descriptor",
+    "../importers/proto:gen_cc_track_event_descriptor",
   ]
 }
 
@@ -111,7 +127,7 @@
     "../../../protos/perfetto/trace/track_event:zero",
     "../../../protos/perfetto/trace_processor:zero",
     "../../protozero",
-    "../importers:gen_cc_track_event_descriptor",
+    "../importers/proto:gen_cc_track_event_descriptor",
   ]
 
   public_deps = [
@@ -139,14 +155,39 @@
   }
 }
 
+source_set("glob") {
+  sources = [
+    "glob.cc",
+    "glob.h",
+  ]
+  deps = [
+    "../../../gn:default_deps",
+    "../../base",
+  ]
+}
+
+source_set("sql_argument") {
+  sources = [
+    "sql_argument.cc",
+    "sql_argument.h",
+  ]
+  deps = [
+    "../../../gn:default_deps",
+    "../../../include/perfetto/base",
+    "../../../include/perfetto/ext/base",
+    "../../../include/perfetto/trace_processor",
+    "../containers",
+  ]
+}
+
 source_set("proto_profiler") {
   sources = [
     "proto_profiler.cc",
     "proto_profiler.h",
   ]
   deps = [
+    ":descriptors",
     "../../../gn:default_deps",
-    "../../../gn:protobuf_full",
     "../../../protos/perfetto/common:zero",
     "../../../protos/third_party/pprof:zero",
     "../../base",
@@ -154,20 +195,48 @@
   ]
 }
 
+source_set("profile_builder") {
+  sources = [
+    "annotated_callsites.cc",
+    "annotated_callsites.h",
+    "profile_builder.cc",
+    "profile_builder.h",
+  ]
+  deps = [
+    "../../../gn:default_deps",
+    "../../../include/perfetto/ext/trace_processor:demangle",
+    "../../../include/perfetto/protozero:protozero",
+    "../../../protos/third_party/pprof:zero",
+    "../../base",
+    "../containers",
+    "../storage",
+    "../tables",
+    "../types",
+  ]
+}
+
 source_set("unittests") {
   sources = [
+    "bump_allocator_unittest.cc",
     "debug_annotation_parser_unittest.cc",
+    "glob_unittest.cc",
+    "proto_profiler_unittest.cc",
     "proto_to_args_parser_unittest.cc",
     "protozero_to_text_unittests.cc",
+    "sql_argument_unittest.cc",
     "streaming_line_reader_unittest.cc",
     "zip_reader_unittest.cc",
   ]
   testonly = true
   deps = [
+    ":bump_allocator",
     ":descriptors",
+    ":glob",
     ":gzip",
+    ":proto_profiler",
     ":proto_to_args_parser",
     ":protozero_to_text",
+    ":sql_argument",
     ":zip_reader",
     "..:gen_cc_test_messages_descriptor",
     "../../../gn:default_deps",
@@ -177,10 +246,24 @@
     "../../../protos/perfetto/trace/track_event:zero",
     "../../protozero",
     "../../protozero:testing_messages_zero",
-    "../importers:gen_cc_track_event_descriptor",
+    "../importers/proto:gen_cc_track_event_descriptor",
   ]
   if (enable_perfetto_zlib) {
     sources += [ "gzip_utils_unittest.cc" ]
     deps += [ "../../../gn:zlib" ]
   }
 }
+
+if (enable_perfetto_benchmarks) {
+  source_set("benchmarks") {
+    testonly = true
+    deps = [
+      ":glob",
+      "../../../gn:benchmark",
+      "../../../gn:default_deps",
+      "../../../gn:sqlite",
+      "../../base",
+    ]
+    sources = [ "glob_benchmark.cc" ]
+  }
+}
diff --git a/src/trace_processor/util/annotated_callsites.cc b/src/trace_processor/util/annotated_callsites.cc
new file mode 100644
index 0000000..5e23130
--- /dev/null
+++ b/src/trace_processor/util/annotated_callsites.cc
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2021 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/util/annotated_callsites.h"
+
+#include <iostream>
+
+#include "perfetto/ext/base/optional.h"
+#include "src/trace_processor/tables/profiler_tables.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+AnnotatedCallsites::AnnotatedCallsites(const TraceProcessorContext* context)
+    : context_(*context),
+      // String to identify trampoline frames. If the string does not exist in
+      // TraceProcessor's StringPool (nullopt) then there will be no trampoline
+      // frames in the trace so there is no point in adding it to the pool to do
+      // all comparisons, instead we initialize the member to nullopt and the
+      // string comparisons will all fail.
+      art_jni_trampoline_(
+          context->storage->string_pool().GetId("art_jni_trampoline")) {}
+
+AnnotatedCallsites::State AnnotatedCallsites::GetState(
+    base::Optional<CallsiteId> id) {
+  if (!id) {
+    return State::kInitial;
+  }
+  auto it = states_.find(*id);
+  if (it != states_.end()) {
+    return it->second;
+  }
+
+  State state =
+      Get(*context_.storage->stack_profile_callsite_table().FindById(*id))
+          .first;
+  states_.emplace(*id, state);
+  return state;
+}
+
+std::pair<AnnotatedCallsites::State, CallsiteAnnotation>
+AnnotatedCallsites::Get(
+    const tables::StackProfileCallsiteTable::ConstRowReference& callsite) {
+  State state = GetState(callsite.parent_id());
+
+  // Keep immediate callee of a JNI trampoline, but keep tagging all
+  // successive libart frames as common.
+  if (state == State::kKeepNext) {
+    return {State::kEraseLibart, CallsiteAnnotation::kNone};
+  }
+
+  // Special-case "art_jni_trampoline" frames, keeping their immediate callee
+  // even if it is in libart, as it could be a native implementation of a
+  // managed method. Example for "java.lang.reflect.Method.Invoke":
+  //   art_jni_trampoline
+  //   art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobjectArray*)
+  //
+  // Simpleperf also relies on this frame name, so it should be fairly stable.
+  // TODO(rsavitski): consider detecting standard JNI upcall entrypoints -
+  // _JNIEnv::Call*. These are sometimes inlined into other DSOs, so erasing
+  // only the libart frames does not clean up all of the JNI-related frames.
+  auto frame = *context_.storage->stack_profile_frame_table().FindById(
+      callsite.frame_id());
+  // art_jni_trampoline_ could be nullopt if the string does not exist in the
+  // StringPool, but that also means no frame will ever have that name.
+  if (art_jni_trampoline_.has_value() &&
+      frame.name() == art_jni_trampoline_.value()) {
+    return {State::kKeepNext, CallsiteAnnotation::kCommonFrame};
+  }
+
+  MapType map_type = GetMapType(frame.mapping());
+
+  // Annotate managed frames.
+  if (map_type == MapType::kArtInterp ||  //
+      map_type == MapType::kArtJit ||     //
+      map_type == MapType::kArtAot) {
+    // Now know to be in a managed callstack - erase subsequent ART frames.
+    if (state == State::kInitial) {
+      state = State::kEraseLibart;
+    }
+
+    if (map_type == MapType::kArtInterp)
+      return {state, CallsiteAnnotation::kArtInterpreted};
+    if (map_type == MapType::kArtJit)
+      return {state, CallsiteAnnotation::kArtJit};
+    if (map_type == MapType::kArtAot)
+      return {state, CallsiteAnnotation::kArtAot};
+  }
+
+  // Mixed callstack, tag libart frames as uninteresting (common-frame).
+  // Special case a subset of interpreter implementation frames as
+  // "common-frame-interp" using frame name prefixes. Those functions are
+  // actually executed, whereas the managed "interp" frames are synthesised as
+  // their caller by the unwinding library (based on the dex_pc virtual
+  // register restored using the libart's DWARF info). The heuristic covers
+  // the "nterp" and "switch" interpreter implementations.
+  //
+  // Example:
+  //  <towards root>
+  //  android.view.WindowLayout.computeFrames [interp]
+  //  nterp_op_iget_object_slow_path [common-frame-interp]
+  //
+  // This annotation is helpful when trying to answer "what mode was the
+  // process in?" based on the leaf frame of the callstack. As we want to
+  // classify such cases as interpreted, even though the leaf frame is
+  // libart.so.
+  //
+  // For "switch" interpreter, we match any frame starting with
+  // "art::interpreter::" according to itanium mangling.
+  if (state == State::kEraseLibart && map_type == MapType::kNativeLibart) {
+    NullTermStringView fname = context_.storage->GetString(frame.name());
+    if (fname.StartsWith("nterp_") || fname.StartsWith("Nterp") ||
+        fname.StartsWith("ExecuteNterp") ||
+        fname.StartsWith("ExecuteSwitchImpl") ||
+        fname.StartsWith("_ZN3art11interpreter")) {
+      return {state, CallsiteAnnotation::kCommonFrameInterp};
+    }
+    return {state, CallsiteAnnotation::kCommonFrame};
+  }
+
+  return {state, CallsiteAnnotation::kNone};
+}
+
+AnnotatedCallsites::MapType AnnotatedCallsites::GetMapType(MappingId id) {
+  auto it = map_types_.find(id);
+  if (it != map_types_.end()) {
+    return it->second;
+  }
+
+  return map_types_
+      .emplace(id, ClassifyMap(context_.storage->GetString(
+                       context_.storage->stack_profile_mapping_table()
+                           .FindById(id)
+                           ->name())))
+      .first->second;
+}
+
+AnnotatedCallsites::MapType AnnotatedCallsites::ClassifyMap(
+    NullTermStringView map) {
+  if (map.empty())
+    return MapType::kOther;
+
+  // Primary mapping where modern ART puts jitted code.
+  // The Zygote's JIT region is inherited by all descendant apps, so it can
+  // still appear in their callstacks.
+  if (map.StartsWith("/memfd:jit-cache") ||
+      map.StartsWith("/memfd:jit-zygote-cache")) {
+    return MapType::kArtJit;
+  }
+
+  size_t last_slash_pos = map.rfind('/');
+  if (last_slash_pos != NullTermStringView::npos) {
+    base::StringView suffix = map.substr(last_slash_pos);
+    if (suffix.StartsWith("/libart.so") || suffix.StartsWith("/libartd.so"))
+      return MapType::kNativeLibart;
+  }
+
+  size_t extension_pos = map.rfind('.');
+  if (extension_pos != NullTermStringView::npos) {
+    base::StringView suffix = map.substr(extension_pos);
+    if (suffix.StartsWith(".so"))
+      return MapType::kNativeOther;
+    // unqualified dex
+    if (suffix.StartsWith(".dex"))
+      return MapType::kArtInterp;
+    // dex with verification speedup info, produced by dex2oat
+    if (suffix.StartsWith(".vdex"))
+      return MapType::kArtInterp;
+    // possibly uncompressed dex in a jar archive
+    if (suffix.StartsWith(".jar"))
+      return MapType::kArtInterp;
+    // android package (zip file), this can contain uncompressed dexes or
+    // native libraries that are mmap'd directly into the process. We rely on
+    // libunwindstack's MapInfo::GetFullName, which suffixes the mapping with
+    // "!lib.so" if it knows that the referenced piece of the archive is an
+    // uncompressed ELF file. So an unadorned ".apk" is assumed to be a dex
+    // file.
+    if (suffix.StartsWith(".apk"))
+      return MapType::kArtInterp;
+    // ahead of time compiled ELFs
+    if (suffix.StartsWith(".oat"))
+      return MapType::kArtAot;
+    // older/alternative name for .oat
+    if (suffix.StartsWith(".odex"))
+      return MapType::kArtAot;
+  }
+  return MapType::kOther;
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/util/annotated_callsites.h b/src/trace_processor/util/annotated_callsites.h
new file mode 100644
index 0000000..0fbb555
--- /dev/null
+++ b/src/trace_processor/util/annotated_callsites.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2021 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_ANNOTATED_CALLSITES_H_
+#define SRC_TRACE_PROCESSOR_UTIL_ANNOTATED_CALLSITES_H_
+
+#include <unordered_map>
+#include "perfetto/ext/base/optional.h"
+#include "src/trace_processor/containers/string_pool.h"
+#include "src/trace_processor/storage/trace_storage.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class TraceProcessorContext;
+
+enum class CallsiteAnnotation {
+  kNone,
+  kCommonFrame,
+  kCommonFrameInterp,
+  kArtInterpreted,
+  kArtJit,
+  kArtAot,
+};
+
+// Helper class to augment callsite with (currently Android-specific)
+// annotations. A given callsite will always have the same annotation. This
+// class will internally cache already computed annotations. An annotation
+// depends only of the current callsite and the annotations of its parent
+// callsites (going to the root).
+class AnnotatedCallsites {
+ public:
+  explicit AnnotatedCallsites(const TraceProcessorContext* context);
+
+  CallsiteAnnotation GetAnnotation(
+      const tables::StackProfileCallsiteTable::ConstRowReference& callsite) {
+    return Get(callsite).second;
+  }
+
+ private:
+  enum class MapType {
+    kArtInterp,
+    kArtJit,
+    kArtAot,
+    kNativeLibart,
+    kNativeOther,
+    kOther
+  };
+
+  // Annotation FSM states:
+  // * kInitial: default, native-only callstacks never leave this state.
+  // * kEraseLibart: we've seen a managed frame, and will now "erase" (i.e. tag
+  //                 as a common-frame) frames belonging to the ART runtime.
+  // * kKeepNext: we've seen a special JNI trampoline for managed->native
+  //              transition, keep the immediate child (even if it is in ART),
+  //              and then go back to kEraseLibart.
+  // Regardless of the state, managed frames get annotated with their execution
+  // mode, based on the mapping.
+  enum class State { kInitial, kEraseLibart, kKeepNext };
+
+  State GetState(base::Optional<CallsiteId> id);
+
+  std::pair<State, CallsiteAnnotation> Get(
+      const tables::StackProfileCallsiteTable::ConstRowReference& callsite);
+
+  MapType GetMapType(MappingId id);
+  MapType ClassifyMap(NullTermStringView map);
+
+  const TraceProcessorContext& context_;
+  const base::Optional<StringPool::Id> art_jni_trampoline_;
+
+  std::unordered_map<MappingId, MapType> map_types_;
+  std::unordered_map<CallsiteId, State> states_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_UTIL_ANNOTATED_CALLSITES_H_
diff --git a/src/trace_processor/util/bump_allocator.cc b/src/trace_processor/util/bump_allocator.cc
new file mode 100644
index 0000000..1c00a8c
--- /dev/null
+++ b/src/trace_processor/util/bump_allocator.cc
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2023 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/util/bump_allocator.h"
+
+#include "perfetto/base/compiler.h"
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/utils.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+// TODO(b/266983484): consider using base::PagedMemory unless a) we are on a
+// platform where that doesn't make sense (WASM) b) we are trying to do heap
+// profiling.
+base::AlignedUniquePtr<uint8_t[]> Allocate(uint32_t size) {
+  uint8_t* ptr = static_cast<uint8_t*>(base::AlignedAlloc(8, size));
+  // Poison the region to try and catch out of bound accesses.
+  PERFETTO_ASAN_POISON(ptr, size);
+  return base::AlignedUniquePtr<uint8_t[]>(ptr);
+}
+
+}  // namespace
+
+BumpAllocator::BumpAllocator() = default;
+
+BumpAllocator::~BumpAllocator() {
+  for (const auto& chunk : chunks_) {
+    PERFETTO_CHECK(chunk.unfreed_allocations == 0);
+  }
+}
+
+BumpAllocator::AllocId BumpAllocator::Alloc(uint32_t size) {
+  // Size is required to be a multiple of 8 to avoid needing to deal with
+  // alignment. It must also be at most kChunkSize as we do not support cross
+  // chunk spanning allocations.
+  PERFETTO_DCHECK(size % 8 == 0);
+  PERFETTO_DCHECK(size <= kChunkSize);
+
+  // Fast path: check if we have space to service this allocation in the current
+  // chunk.
+  base::Optional<AllocId> alloc_id = TryAllocInLastChunk(size);
+  if (alloc_id) {
+    return *alloc_id;
+  }
+
+  // Slow path: we don't have enough space in the last chunk so we create one.
+  Chunk chunk;
+  chunk.allocation = Allocate(kChunkSize);
+  chunks_.emplace_back(std::move(chunk));
+
+  // Ensure that we haven't exceeded the maximum number of chunks.
+  PERFETTO_CHECK(LastChunkIndex() < kMaxChunkCount);
+
+  // This time the allocation should definitely succeed in the last chunk (which
+  // we just added).
+  alloc_id = TryAllocInLastChunk(size);
+  PERFETTO_CHECK(alloc_id);
+  return *alloc_id;
+}
+
+void BumpAllocator::Free(AllocId id) {
+  Chunk& chunk = chunks_.at(ChunkIndexToQueueIndex(id.chunk_index));
+  PERFETTO_DCHECK(chunk.unfreed_allocations > 0);
+  chunk.unfreed_allocations--;
+}
+
+void* BumpAllocator::GetPointer(AllocId id) {
+  uint32_t queue_index = ChunkIndexToQueueIndex(id.chunk_index);
+  return chunks_.at(queue_index).allocation.get() + id.chunk_offset;
+}
+
+uint32_t BumpAllocator::EraseFrontFreeChunks() {
+  uint32_t to_erase_chunks = 0;
+  for (; to_erase_chunks < chunks_.size(); ++to_erase_chunks) {
+    // Break on the first chunk which still has unfreed allocations.
+    if (chunks_.at(to_erase_chunks).unfreed_allocations > 0) {
+      break;
+    }
+  }
+  chunks_.erase_front(to_erase_chunks);
+  erased_front_chunks_count_ += to_erase_chunks;
+  return to_erase_chunks;
+}
+
+uint32_t BumpAllocator::PastEndSerializedId() {
+  if (chunks_.empty()) {
+    return AllocId{erased_front_chunks_count_, 0}.Serialize();
+  }
+  return AllocId{LastChunkIndex(), chunks_.back().bump_offset}.Serialize();
+}
+
+base::Optional<BumpAllocator::AllocId> BumpAllocator::TryAllocInLastChunk(
+    uint32_t size) {
+  if (chunks_.empty()) {
+    return base::nullopt;
+  }
+
+  // TODO(266983484): consider switching this to bump downwards instead of
+  // upwards for more efficient code generation.
+  Chunk& chunk = chunks_.back();
+
+  // Verify some invariants:
+  // 1) The allocation must exist
+  // 2) The bump must be in the bounds of the chunk.
+  PERFETTO_DCHECK(chunk.allocation);
+  PERFETTO_DCHECK(chunk.bump_offset <= kChunkSize);
+
+  // If the end of the allocation ends up after this chunk, we cannot service it
+  // in this chunk.
+  uint32_t alloc_offset = chunk.bump_offset;
+  uint32_t new_bump_offset = chunk.bump_offset + size;
+  if (new_bump_offset > kChunkSize) {
+    return base::nullopt;
+  }
+
+  // Set the new offset equal to the end of this allocation and increment the
+  // unfreed allocation counter.
+  chunk.bump_offset = new_bump_offset;
+  chunk.unfreed_allocations++;
+
+  // Unpoison the allocation range to allow access to it on ASAN builds.
+  PERFETTO_ASAN_UNPOISON(chunk.allocation.get() + alloc_offset, size);
+
+  return AllocId{LastChunkIndex(), alloc_offset};
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/util/bump_allocator.h b/src/trace_processor/util/bump_allocator.h
new file mode 100644
index 0000000..cd5e593
--- /dev/null
+++ b/src/trace_processor/util/bump_allocator.h
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2023 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_BUMP_ALLOCATOR_H_
+#define SRC_TRACE_PROCESSOR_UTIL_BUMP_ALLOCATOR_H_
+
+#include <cmath>
+#include <cstdint>
+#include <cstring>
+#include <limits>
+#include <memory>
+#include "perfetto/ext/base/circular_queue.h"
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/utils.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+// A simple memory allocator which "bumps" a pointer to service allocations.
+// See [1] for more details for an overview of bump allocators.
+//
+// This implementation works by obtaining a large chunk of memory from the
+// system allocator (i.e. from malloc). Every allocation uses that chunk as long
+// as there is free space inside. Once an allocation is requested which does not
+// fit in that chunk, a new chunk is requested from the system.
+//
+// IMPORTANT: all allocations returned from this allocator are 8-aligned and
+// all allocation sizes must be a multiple of 8.
+//
+// IMPORTANT: this allocator can allocate a total of 4GB of memory (2^32). Once
+// this is exhausted, any further allocation will cause a CHECK.
+//
+// IMPORTANT: all allocations *must* be explicitly freed before destroying this
+// object. The destructor will CHECK if it detects any allocation which is
+// unfreed.
+//
+// [1] https://rust-hosted-langs.github.io/book/chapter-simple-bump.html
+class BumpAllocator {
+ public:
+  // The limit on the total amount of memory which can be allocated. Required
+  // as we can only address 4GB of memory with AllocId.
+  static constexpr uint64_t kAllocLimit = 4ull * 1024 * 1024 * 1024;  // 4GB
+
+  // The size of the "large chunk" requested from the system allocator.
+  // The size of this value trades-off between unused memory use vs CPU cost
+  // of going to the system allocator. 64KB feels a good trade-off there.
+  static constexpr uint32_t kChunkSize = 64u * 1024;  // 64KB
+
+  // The maximum number of chunks which this allocator can have.
+  static constexpr uint32_t kMaxChunkCount = kAllocLimit / kChunkSize;
+
+  // The number of bits used to represent the offset the chunk in AllocId.
+  //
+  // This is simply log2(kChunkSize): we have a separate constant as log2 is
+  // not a constexpr function: the static assets below verify this stays in
+  // sync.
+  static constexpr uint32_t kChunkOffsetAllocIdBits = 16u;
+
+  // The number of bits used to represent the chunk index in AllocId.
+  static constexpr uint32_t kChunkIndexAllocIdBits =
+      32u - kChunkOffsetAllocIdBits;
+
+  // Represents an allocation returned from the allocator. We return this
+  // instead of just returning a pointer to allow looking up a chunk an
+  // allocation belongs to without needing having to scan chunks.
+  struct AllocId {
+    uint32_t chunk_index : kChunkIndexAllocIdBits;
+    uint32_t chunk_offset : kChunkOffsetAllocIdBits;
+
+    uint32_t Serialize() const {
+      return static_cast<uint32_t>(chunk_index) << kChunkOffsetAllocIdBits |
+             chunk_offset;
+    }
+
+    static AllocId FromSerialized(uint32_t serialized) {
+      AllocId id;
+      id.chunk_index = serialized >> kChunkOffsetAllocIdBits;
+      id.chunk_offset = serialized;
+      return id;
+    }
+  };
+  static_assert(sizeof(AllocId) == sizeof(uint32_t),
+                "AllocId should be 32-bit in size to allow serialization");
+  static_assert(
+      kMaxChunkCount == (1 << kChunkIndexAllocIdBits),
+      "Max chunk count must match the number of bits used for chunk indices");
+  static_assert(
+      kChunkSize == (1 << kChunkOffsetAllocIdBits),
+      "Chunk size must match the number of bits used for offset within chunk");
+  static_assert(kAllocLimit == 1ull << sizeof(AllocId) * 8,
+                "Total limit on allocations must be equal to the number of "
+                "bits used for AllocId");
+
+  BumpAllocator();
+
+  // Verifies that all calls to |Alloc| were paired with matching calls to
+  // |Free|.
+  ~BumpAllocator();
+
+  BumpAllocator(BumpAllocator&&) noexcept = default;
+  BumpAllocator& operator=(BumpAllocator&&) noexcept = default;
+
+  // Allocates |size| bytes of memory. |size| must be a multiple of 8 and less
+  // than or equal to |kChunkSize|.
+  //
+  // Returns an |AllocId| which can be converted to a pointer using
+  // |GetPointer|.
+  AllocId Alloc(uint32_t size);
+
+  // Frees an allocation previously allocated by |Alloc|. This function is *not*
+  // idempotent.
+  //
+  // Once this function returns, |id| is no longer valid for any use. Trying
+  // to use it further (e.g. to passing to other methods including Free itself)
+  // will cause undefined behaviour.
+  void Free(AllocId id);
+
+  // Given an AllocId, returns a pointer which can be read from/written to.
+  //
+  // The caller is only allowed to access up to |size| bytes, where |size| ==
+  // the |size| argument to Alloc.
+  void* GetPointer(AllocId);
+
+  // Removes chunks from the start of this allocator where all the allocations
+  // in the chunks have been freed. This releases the memory back to the system.
+  //
+  // Returns the number of chunks freed.
+  uint32_t EraseFrontFreeChunks();
+
+  // Returns a "past the end" serialized AllocId i.e. a serialized value
+  // greater than all previously returned AllocIds.
+  uint32_t PastEndSerializedId();
+
+  // Returns the number of erased chunks from the start of this allocator.
+  //
+  // This value may change any time |EraseFrontFreeChunks| is called but is
+  // constant otherwise.
+  uint32_t erased_front_chunks_count() const {
+    return erased_front_chunks_count_;
+  }
+
+ private:
+  struct Chunk {
+    // The allocation from the system for this chunk. Because all allocations
+    // need to be 8 byte aligned, the chunk also needs to be 8-byte aligned.
+    // base::AlignedUniquePtr ensures this is the case.
+    base::AlignedUniquePtr<uint8_t[]> allocation;
+
+    // The bump offset relative to |allocation.data|. Incremented to service
+    // Alloc requests.
+    uint32_t bump_offset = 0;
+
+    // The number of unfreed allocations in this chunk.
+    uint32_t unfreed_allocations = 0;
+  };
+
+  // Tries to allocate |size| bytes in the final chunk in |chunks_|. Returns
+  // an AllocId if this was successful or base::nullopt otherwise.
+  base::Optional<AllocId> TryAllocInLastChunk(uint32_t size);
+
+  uint32_t ChunkIndexToQueueIndex(uint32_t chunk_index) const {
+    return chunk_index - erased_front_chunks_count_;
+  }
+  uint32_t QueueIndexToChunkIndex(uint32_t index_in_chunks_vec) const {
+    return erased_front_chunks_count_ + index_in_chunks_vec;
+  }
+  uint32_t LastChunkIndex() const {
+    PERFETTO_DCHECK(!chunks_.empty());
+    return QueueIndexToChunkIndex(static_cast<uint32_t>(chunks_.size() - 1));
+  }
+
+  base::CircularQueue<Chunk> chunks_;
+  uint32_t erased_front_chunks_count_ = 0;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_UTIL_BUMP_ALLOCATOR_H_
diff --git a/src/trace_processor/util/bump_allocator_unittest.cc b/src/trace_processor/util/bump_allocator_unittest.cc
new file mode 100644
index 0000000..4608be6
--- /dev/null
+++ b/src/trace_processor/util/bump_allocator_unittest.cc
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2023 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/util/bump_allocator.h"
+
+#include <limits>
+#include <random>
+#include <vector>
+
+#include "perfetto/ext/base/utils.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+class BumpAllocatorUnittest : public ::testing::Test {
+ public:
+  // Allocates |size| bytes of memory with aligned to |align|, writes |size|
+  // bytes in the region, reads |size| bytes and then frees the memory.
+  //
+  // Very useful to check that none of the internal DCHECKs of the allocator
+  // fire.
+  void AllocateWriteReadAndFree(uint32_t size) {
+    BumpAllocator::AllocId id = allocator_.Alloc(size);
+    uint8_t* ptr = static_cast<uint8_t*>(allocator_.GetPointer(id));
+
+    std::vector<uint8_t> data(size);
+    for (uint32_t i = 0; i < size; ++i) {
+      data[i] = static_cast<uint8_t>(rnd_engine_() &
+                                     std::numeric_limits<uint8_t>::max());
+    }
+    memcpy(ptr, data.data(), size);
+    ASSERT_EQ(memcmp(ptr, data.data(), size), 0);
+    allocator_.Free(id);
+  }
+
+ protected:
+  std::minstd_rand0 rnd_engine_;
+  BumpAllocator allocator_;
+};
+
+TEST_F(BumpAllocatorUnittest, AllocSmoke) {
+  AllocateWriteReadAndFree(8);
+  AllocateWriteReadAndFree(16);
+  AllocateWriteReadAndFree(24);
+  AllocateWriteReadAndFree(64);
+  AllocateWriteReadAndFree(1024);
+  AllocateWriteReadAndFree(BumpAllocator::kChunkSize);
+
+  allocator_.EraseFrontFreeChunks();
+}
+
+TEST_F(BumpAllocatorUnittest, EraseFrontAtAnyTime) {
+  BumpAllocator::AllocId id = allocator_.Alloc(8);
+  allocator_.EraseFrontFreeChunks();
+  allocator_.Free(id);
+  allocator_.EraseFrontFreeChunks();
+}
+
+TEST_F(BumpAllocatorUnittest, Serialize) {
+  BumpAllocator::AllocId id = allocator_.Alloc(8);
+  ASSERT_EQ(id.Serialize(), 0u);
+  ASSERT_EQ(allocator_.PastEndSerializedId(), 8u);
+  allocator_.Free(id);
+
+  id = allocator_.Alloc(8);
+  ASSERT_EQ(id.Serialize(), 8u);
+  allocator_.Free(id);
+
+  id = allocator_.Alloc(BumpAllocator::kChunkSize);
+  ASSERT_EQ(id.Serialize(), BumpAllocator::kChunkSize);
+  allocator_.Free(id);
+}
+
+TEST_F(BumpAllocatorUnittest, HighNumberSerialize) {
+  BumpAllocator::AllocId id = BumpAllocator::AllocId::FromSerialized(1138352);
+  ASSERT_EQ(id.chunk_index, 1138352 / BumpAllocator::kChunkSize);
+  ASSERT_EQ(id.chunk_offset, 1138352 % BumpAllocator::kChunkSize);
+  ASSERT_EQ(id.Serialize(), 1138352u);
+}
+
+TEST_F(BumpAllocatorUnittest, EraseFrontAccounting) {
+  AllocateWriteReadAndFree(8);
+  ASSERT_EQ(allocator_.EraseFrontFreeChunks(), 1u);
+  ASSERT_EQ(allocator_.erased_front_chunks_count(), 1u);
+  AllocateWriteReadAndFree(8);
+  ASSERT_EQ(allocator_.EraseFrontFreeChunks(), 1u);
+  ASSERT_EQ(allocator_.erased_front_chunks_count(), 2u);
+}
+
+TEST_F(BumpAllocatorUnittest, EraseFrontFreeChunk) {
+  AllocateWriteReadAndFree(8);
+  allocator_.EraseFrontFreeChunks();
+
+  auto past_id =
+      BumpAllocator::AllocId::FromSerialized(allocator_.PastEndSerializedId());
+  ASSERT_EQ(past_id.chunk_index, 1u);
+  ASSERT_EQ(past_id.chunk_offset, 0u);
+
+  auto id = allocator_.Alloc(8);
+  ASSERT_EQ(id.chunk_index, past_id.chunk_index);
+  ASSERT_EQ(id.chunk_offset, past_id.chunk_offset);
+  allocator_.Free(id);
+}
+
+TEST_F(BumpAllocatorUnittest, StressTest) {
+  std::minstd_rand0 rnd_engine;
+  for (int i = 0; i < 1000; i++) {
+    uint32_t size =
+        static_cast<uint32_t>((rnd_engine() * 8) % BumpAllocator::kChunkSize);
+    AllocateWriteReadAndFree(size);
+  }
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/util/glob.cc b/src/trace_processor/util/glob.cc
new file mode 100644
index 0000000..f930683
--- /dev/null
+++ b/src/trace_processor/util/glob.cc
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2022 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/util/glob.h"
+
+#include "perfetto/ext/base/string_utils.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace util {
+
+GlobMatcher::GlobMatcher(base::StringView pattern_str)
+    : pattern_(pattern_str.size() + 1) {
+  base::StringCopy(pattern_.data(), pattern_str.data(), pattern_.size());
+
+  base::StringView pattern(pattern_.data());
+
+  // Note: see the class header for how this algorithm works.
+  uint32_t segment_start = 0;
+  uint32_t segment_potential_matched_chars = 0;
+  auto create_segment = [this, &segment_start, &segment_potential_matched_chars,
+                         &pattern](size_t i) {
+    base::StringView segment = pattern.substr(segment_start, i - segment_start);
+    PERFETTO_DCHECK(segment_potential_matched_chars <= segment.size());
+    if (!segment.empty()) {
+      PERFETTO_DCHECK(segment_potential_matched_chars > 0);
+      segments_.emplace_back(Segment{segment, segment_potential_matched_chars});
+    }
+    return segment.empty();
+  };
+
+  for (uint32_t i = 0; i < pattern.size(); ++i) {
+    char c = pattern.at(i);
+
+    // If we don't have a star, we are only matching a single character (but
+    // potentially with a character class which contains >1 character).
+    if (c != '*') {
+      if (c == '[') {
+        base::StringView cclass = ExtractCharacterClass(pattern.substr(i + 1));
+        contains_char_class_or_question_ |= !cclass.empty();
+
+        // Skip the current '[' character.
+        ++i;
+
+        // Skip the whole character class. This will leave i pointing at the
+        // terminating character (i.e. ']'). With the ++i in the loop, this will
+        // correctly lead us going past the whole class.
+        i += cclass.size();
+      }
+
+      contains_char_class_or_question_ |= c == '?';
+      ++segment_potential_matched_chars;
+      continue;
+    }
+
+    // Add the characters collected so far as a segment.
+    create_segment(i);
+    segment_start = i + 1;
+    segment_potential_matched_chars = 0;
+  }
+
+  // Ensure we add any remaining characters as a segment.
+  bool empty_segment = create_segment(pattern.size());
+  leading_star_ = !pattern.empty() && pattern.at(0) == '*';
+  trailing_star_ = !pattern.empty() && empty_segment;
+}
+
+bool GlobMatcher::Matches(base::StringView in) {
+  // If there are no segments, that means the pattern is either '' or '*'
+  // (or '**', '***' etc which is really the same as '*'). This means
+  // we are match if either a) there is a leading star (== trailing star) or
+  // b) the input string is empty.
+  if (segments_.empty()) {
+    PERFETTO_DCHECK(leading_star_ == trailing_star_);
+    return leading_star_ || in.empty();
+  }
+
+  // If there is only one segment and no stars we need an equality match.
+  // As we still need to handle '[..]' and '?', we cannot just use string
+  // equality. We *can* however use StartsWith and check the matched
+  // characters is equal to the length of the input: this is basically the
+  // same as checking equality.
+  if (segments_.size() == 1 && !leading_star_ && !trailing_star_) {
+    return segments_.front().matched_chars == in.size() &&
+           StartsWith(in, segments_.front());
+  }
+
+  // If there's no leading star, the first segment needs to be handled
+  // separately because it *needs* to be anchored to the left of the
+  // string rather than appearing at some point later.
+  if (!leading_star_ && !StartsWith(in, segments_.front())) {
+    return false;
+  }
+
+  // Similarily, if there's no trailing star, the last segment needs to be
+  // "anchored" to the right of the string.
+  if (!trailing_star_ && !EndsWith(in, segments_.back())) {
+    return false;
+  }
+
+  // For any segment we haven't checked, they needs to appear in the string
+  // sequentially with possibly some characters separating them. To handle
+  // this, we just need to iteratively find each segment, starting from the
+  // previous segment.
+  Segment* segment_start = segments_.begin() + (leading_star_ ? 0 : 1);
+  Segment* segment_end = segments_.end() - (trailing_star_ ? 0 : 1);
+  size_t find_idx = leading_star_ ? 0 : segments_.front().matched_chars;
+  for (Segment* segment = segment_start; segment < segment_end; ++segment) {
+    size_t pos = Find(in, *segment, find_idx);
+    if (pos == base::StringView::npos) {
+      return false;
+    }
+    find_idx = pos + segment->matched_chars;
+  }
+
+  // Every segment has been found to match so far including the leading and
+  // trailing one so the entire string matches!
+  return true;
+}
+
+bool GlobMatcher::StartsWithSlow(base::StringView in, const Segment& segment) {
+  base::StringView pattern = segment.pattern;
+  for (uint32_t i = 0, p = 0; p < pattern.size(); ++i, ++p) {
+    // We've run out of characters to consume in the input but still have more
+    // to consume in the pattern: |in| cannot possibly start with |pattern|.
+    if (i >= in.size()) {
+      return false;
+    }
+
+    char in_c = in.at(i);
+    char pattern_c = pattern.at(p);
+
+    // '?' matches any character.
+    if (pattern_c == '?') {
+      continue;
+    }
+
+    // '[' signifies the start of a character class.
+    if (pattern_c == '[') {
+      base::StringView cclass = ExtractCharacterClass(pattern.substr(p + 1));
+      if (!MatchesCharacterClass(in_c, cclass)) {
+        return false;
+      }
+
+      // Skip the current '[' character.
+      p++;
+
+      // Skip the whole character class. This will leave i pointing at the
+      // terminating character (i.e. ']'). With the ++i in the loop, this will
+      // correctly lead us going past the whole class.
+      p += cclass.size();
+      continue;
+    }
+
+    // Anything else is just an ordinary character.
+    if (in_c != pattern_c) {
+      return false;
+    }
+  }
+  return true;
+}
+
+base::StringView GlobMatcher::ExtractCharacterClass(base::StringView in) {
+  if (in.empty())
+    return base::StringView();
+
+  // We should always skip the first real character: it could be ']' but if
+  // so, it is treated as a normal character because empty classes are not
+  // valid.
+  bool invert = in.at(0) == '^';
+  size_t end_idx = in.find(']', invert ? 2 : 1);
+  return end_idx == base::StringView::npos ? base::StringView()
+                                           : in.substr(0, end_idx);
+}
+
+bool GlobMatcher::MatchesCharacterClass(char in, base::StringView char_class) {
+  PERFETTO_DCHECK(!char_class.empty());
+
+  const char* start = char_class.data();
+  const char* end = start + char_class.size();
+
+  bool invert = *start == '^';
+  start += invert;
+
+  PERFETTO_DCHECK(start != end);
+
+  for (auto* ptr = start; ptr != end; ++ptr) {
+    char cur = *ptr;
+
+    // If we see a '-' at any point except at the start or end of the string,
+    // it represents a matching range (e.g. a-z represents matching any
+    // character between a and z).
+    if (cur == '-' && ptr != start && ptr != end - 1) {
+      // Look at the previous and next characters in the class and check if the
+      // character falls in the range.
+      char range_start = ptr[-1];
+      char range_end = ptr[1];
+      if (range_start <= in && in <= range_end) {
+        return !invert;
+      }
+      continue;
+    }
+
+    // We match a character in the class.
+    if (in == cur) {
+      return !invert;
+    }
+  }
+
+  // If we're here, nothing in the class matched: return whether the class was
+  // inverted as this would actually be a match.
+  return invert;
+}
+
+}  // namespace util
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/util/glob.h b/src/trace_processor/util/glob.h
new file mode 100644
index 0000000..4a413bb
--- /dev/null
+++ b/src/trace_processor/util/glob.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2022 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_GLOB_H_
+#define SRC_TRACE_PROCESSOR_UTIL_GLOB_H_
+
+#include <vector>
+
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/small_vector.h"
+#include "perfetto/ext/base/string_splitter.h"
+#include "perfetto/ext/base/string_view.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace util {
+
+// Lightweight implementation of matching on UNIX glob patterns, maintaining
+// compatibility of syntax and semantics used by SQLite.
+//
+// Usage:
+//  GlobMatcher matcher = GlobMatcher::FromPattern("*foo*");
+//  for (auto string : strings) {
+//    if (matcher.Matches(string)) {
+//      <do something>
+//    }
+//  }
+//
+// This is a class instead of a free function to allow preprocessing the
+// pattern (e.g. to compute Kleene star offsets). This can create big savings
+// because trace processsor needs to match the same pattern on many strings
+// when filtering tables.
+//
+// Implementation:
+// The algorithm used in this class is similar to the "alternative"
+// algorithm proposed in [1].
+//
+// We preprocess the pattern (in the constructor) to split the pattern on *,
+// accounting for character classes. This breaks the pattern in "segments": our
+// name for the parts of the pattern between the stars.
+//
+// Then at match time, we go through each segment and check if it matches part
+// of the string. The number of character matched defines the search start-point
+// for the next segment. As described in [1], we don't need to do any
+// backtracking which removes the exponential component of the algorithm and
+// consequently simplifies the code.
+//
+// The subtle parts are:
+// 1) the first and last segments - they need to be "anchored" to the
+//    beginning and end of the string respectively. If not, they fail the match
+//    straight away.
+// 2) leading/trailing stars: they counteract the above point and "unanchor"
+//    the first and last segments respectively by allowing them to happen
+//    somewhere after/before the beginning/end.
+//
+// [1] https://research.swtch.com/glob
+class GlobMatcher {
+ public:
+  // Creates a glob matcher from a pattern.
+  static GlobMatcher FromPattern(base::StringView pattern_str) {
+    return GlobMatcher(std::move(pattern_str));
+  }
+
+  // Checks the provided string against the pattern and returns whether it
+  // matches.
+  bool Matches(base::StringView input);
+
+ private:
+  // Represents a portion of the pattern in between two * characters.
+  struct Segment {
+    // The portion of the pattern in the segment. Note that this will not
+    // contain a free '*' (i.e. outside a character class).
+    base::StringView pattern;
+
+    // The number of consumed characters in an input string if this segment
+    // matches.
+    uint32_t matched_chars;
+  };
+
+  // It would be very rare for a glob pattern to have more than 4 stars so
+  // reserve stack space for that many segments.
+  static constexpr uint32_t kMaxSegmentsOnStack = 4;
+
+  explicit GlobMatcher(base::StringView pattern);
+
+  // Returns whether |input| starts with the pattern in |segment| following
+  // glob matching rules.
+  bool StartsWith(base::StringView input, const Segment& segment) {
+    if (!contains_char_class_or_question_) {
+      return input.StartsWith(segment.pattern);
+    }
+    return StartsWithSlow(input, segment);
+  }
+
+  // Returns whether |input| ends with the pattern in |segment| following
+  // glob matching rules.
+  bool EndsWith(base::StringView input, const Segment& segment) {
+    if (!contains_char_class_or_question_) {
+      return input.EndsWith(segment.pattern);
+    }
+    // Ending with |segment| is the same as taking the substring of |in|
+    size_t start = input.size() - segment.matched_chars;
+    return StartsWithSlow(input.substr(start), segment);
+  }
+
+  // Returns the index where |input| matches the pattern in |segment|
+  // following glob matching rules or base::StringView::npos, if no such index
+  // exists.
+  size_t Find(base::StringView input, const Segment& segment, size_t start) {
+    if (!contains_char_class_or_question_) {
+      return input.find(segment.pattern, start);
+    }
+    for (uint32_t i = 0; i < input.size(); ++i) {
+      if (StartsWithSlow(input.substr(i), segment)) {
+        return i;
+      }
+    }
+    return base::StringView::npos;
+  }
+
+  // Given a StringView starting at the boundary of a character class, returns
+  // a StringView containing only the parts inside the [] or base::StringView()
+  // if no character class exists.
+  static base::StringView ExtractCharacterClass(base::StringView input);
+
+  // Matches |in| against the given character class.
+  static bool MatchesCharacterClass(char input, base::StringView char_class);
+
+  bool StartsWithSlow(base::StringView input, const Segment& segment);
+
+  // IMPORTANT: this should *not* be modified after the constructor as we store
+  // pointers to the data inside here.
+  // Note: this vector also allocates space for the null-terminator so is +1
+  // the "traditional" size of the string.
+  std::vector<char> pattern_;
+
+  // Chunks of the |pattern_| tokenized on '*'. See the class comment for more
+  // info.
+  base::SmallVector<Segment, kMaxSegmentsOnStack> segments_;
+
+  bool leading_star_ = false;
+  bool trailing_star_ = false;
+  bool contains_char_class_or_question_ = false;
+};
+
+}  // namespace util
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_UTIL_GLOB_H_
diff --git a/src/trace_processor/util/glob_benchmark.cc b/src/trace_processor/util/glob_benchmark.cc
new file mode 100644
index 0000000..7cd5ca0
--- /dev/null
+++ b/src/trace_processor/util/glob_benchmark.cc
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2022 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/util/glob.h"
+
+#include <benchmark/benchmark.h>
+#include <sqlite3.h>
+
+#include "perfetto/ext/base/scoped_file.h"
+
+namespace {
+
+using namespace perfetto;
+using benchmark::Counter;
+using perfetto::trace_processor::util::GlobMatcher;
+
+static const char kAndroidGlob[] = "*android*";
+static const char kLaunchingGlob[] = "launching: *";
+static const char kChoreographerGlob[] = "Choreographer#doFrame*";
+static const char kQuestionMarkGlob[] = "Choreo?rapher#doFrame*";
+static const char kCharClassGlob[] = "Choreo[a-z]rapher#doFrame*";
+
+std::vector<std::string> LoadTraceStrings(benchmark::State& state) {
+  std::vector<std::string> strs;
+  // This requires that the user has downloaded the file
+  // go/perfetto-benchmark-slice-strings into /tmp/trace_strings. The file is
+  // too big (220 MB after uncompression) and it's not worth adding it to the
+  // //test/data. Also it contains data from a team member's phone and cannot
+  // be public.
+  base::ScopedFstream f(fopen("/tmp/slice_strings", "re"));
+  if (!f) {
+    state.SkipWithError(
+        "Test strings missing. Googlers: download "
+        "go/perfetto-benchmark-slice-strings and save into /tmp/slice_strings");
+    return strs;
+  }
+  char line[4096];
+  while (fgets(line, sizeof(line), *f)) {
+    strs.emplace_back(base::StringView(line).ToStdString());
+  }
+  return strs;
+}
+
+template <class... Args>
+static void BM_Glob(benchmark::State& state, Args&&... args) {
+  auto args_tuple = std::make_tuple(std::move(args)...);
+
+  std::vector<std::string> strs = LoadTraceStrings(state);
+  GlobMatcher glob = GlobMatcher::FromPattern(std::get<0>(args_tuple));
+  for (auto _ : state) {
+    for (const std::string& str : strs)
+      benchmark::DoNotOptimize(glob.Matches(base::StringView(str)));
+    benchmark::ClobberMemory();
+  }
+  state.counters["str/s"] = Counter(static_cast<double>(strs.size()),
+                                    Counter::kIsIterationInvariantRate);
+  state.counters["s/str"] =
+      Counter(static_cast<double>(strs.size()),
+              Counter::kIsIterationInvariantRate | Counter::kInvert);
+}
+
+BENCHMARK_CAPTURE(BM_Glob, android, kAndroidGlob);
+BENCHMARK_CAPTURE(BM_Glob, launching, kLaunchingGlob);
+BENCHMARK_CAPTURE(BM_Glob, choreographer, kChoreographerGlob);
+BENCHMARK_CAPTURE(BM_Glob, question_mark, kQuestionMarkGlob);
+BENCHMARK_CAPTURE(BM_Glob, char_class, kCharClassGlob);
+
+template <class... Args>
+static void BM_SqliteGlob(benchmark::State& state, Args&&... args) {
+  auto args_tuple = std::make_tuple(std::move(args)...);
+  const char* glob = std::get<0>(args_tuple);
+
+  std::vector<std::string> strs = LoadTraceStrings(state);
+  for (auto _ : state) {
+    for (const std::string& str : strs)
+      benchmark::DoNotOptimize(sqlite3_strglob(glob, str.c_str()));
+    benchmark::ClobberMemory();
+  }
+  state.counters["str/s"] = Counter(static_cast<double>(strs.size()),
+                                    Counter::kIsIterationInvariantRate);
+  state.counters["s/str"] =
+      Counter(static_cast<double>(strs.size()),
+              Counter::kIsIterationInvariantRate | Counter::kInvert);
+}
+
+BENCHMARK_CAPTURE(BM_SqliteGlob, android, kAndroidGlob);
+BENCHMARK_CAPTURE(BM_SqliteGlob, launching, kLaunchingGlob);
+BENCHMARK_CAPTURE(BM_SqliteGlob, slice, kChoreographerGlob);
+BENCHMARK_CAPTURE(BM_SqliteGlob, question_mark, kQuestionMarkGlob);
+BENCHMARK_CAPTURE(BM_SqliteGlob, char_class, kCharClassGlob);
+
+}  // namespace
diff --git a/src/trace_processor/util/glob_unittest.cc b/src/trace_processor/util/glob_unittest.cc
new file mode 100644
index 0000000..7682c24
--- /dev/null
+++ b/src/trace_processor/util/glob_unittest.cc
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2022 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/util/glob.h"
+
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace util {
+namespace {
+
+TEST(GlobUnittest, EmptyPattern) {
+  GlobMatcher matcher = GlobMatcher::FromPattern("");
+
+  // Matching patterns.
+  ASSERT_TRUE(matcher.Matches(""));
+
+  // Non-matching patterns.
+  ASSERT_FALSE(matcher.Matches("A"));
+  ASSERT_FALSE(matcher.Matches("AXBC"));
+  ASSERT_FALSE(matcher.Matches("ABXC"));
+}
+
+TEST(GlobUnittest, JustStar) {
+  GlobMatcher matcher = GlobMatcher::FromPattern("*");
+
+  // Matching patterns.
+  ASSERT_TRUE(matcher.Matches(""));
+  ASSERT_TRUE(matcher.Matches("A"));
+  ASSERT_TRUE(matcher.Matches("ABCD"));
+}
+
+TEST(GlobUnittest, NoStars) {
+  GlobMatcher matcher = GlobMatcher::FromPattern("ABC");
+
+  // Matching patterns.
+  ASSERT_TRUE(matcher.Matches("ABC"));
+
+  // Non-matching patterns.
+  ASSERT_FALSE(matcher.Matches("AXBC"));
+  ASSERT_FALSE(matcher.Matches("ABXC"));
+  ASSERT_FALSE(matcher.Matches("ABABABBC"));
+  ASSERT_FALSE(matcher.Matches("AAAAAAABABABBC"));
+  ASSERT_FALSE(matcher.Matches("ABCD"));
+  ASSERT_FALSE(matcher.Matches("ABBBBBB"));
+  ASSERT_FALSE(matcher.Matches("BCA"));
+}
+
+TEST(GlobUnittest, InteriorOnly) {
+  GlobMatcher matcher = GlobMatcher::FromPattern("A*B*C");
+
+  // Matching patterns.
+  ASSERT_TRUE(matcher.Matches("ABC"));
+  ASSERT_TRUE(matcher.Matches("AXBC"));
+  ASSERT_TRUE(matcher.Matches("ABXC"));
+  ASSERT_TRUE(matcher.Matches("ABABABBC"));
+  ASSERT_TRUE(matcher.Matches("AAAAAAABABABBC"));
+
+  // Non-matching patterns.
+  ASSERT_FALSE(matcher.Matches("ABCD"));
+  ASSERT_FALSE(matcher.Matches("ABBBBBB"));
+  ASSERT_FALSE(matcher.Matches("BCA"));
+}
+
+TEST(GlobUnittest, ComplexInterior) {
+  GlobMatcher matcher = GlobMatcher::FromPattern("AB*CAB");
+
+  // Matching patterns.
+  ASSERT_TRUE(matcher.Matches("ABCAB"));
+  ASSERT_TRUE(matcher.Matches("ABCCAB"));
+  ASSERT_TRUE(matcher.Matches("ABCABCAB"));
+  ASSERT_TRUE(matcher.Matches("ABCABCABCABABABCAB"));
+  ASSERT_TRUE(matcher.Matches("ABXCAB"));
+
+  // Non-matching patterns.
+  ASSERT_FALSE(matcher.Matches("ABXCABCABCA"));
+  ASSERT_FALSE(matcher.Matches("ABXCABCABAB"));
+  ASSERT_FALSE(matcher.Matches("ABXCABCABCB"));
+}
+
+TEST(GlobUnittest, LeadingAndTrailing) {
+  GlobMatcher matcher = GlobMatcher::FromPattern("*BC*");
+
+  // Matching patterns.
+  ASSERT_TRUE(matcher.Matches("ABC"));
+  ASSERT_TRUE(matcher.Matches("ABABABBC"));
+  ASSERT_TRUE(matcher.Matches("AAAAAAABABABBC"));
+  ASSERT_TRUE(matcher.Matches("ABCD"));
+  ASSERT_TRUE(matcher.Matches("BCA"));
+  ASSERT_TRUE(matcher.Matches("AXBC"));
+
+  // Non-matching patterns.
+  ASSERT_FALSE(matcher.Matches("ABXC"));
+  ASSERT_FALSE(matcher.Matches("ABBBBBB"));
+}
+
+TEST(GlobUnittest, Leading) {
+  GlobMatcher matcher = GlobMatcher::FromPattern("*BC");
+
+  // Matching patterns.
+  ASSERT_TRUE(matcher.Matches("ABC"));
+  ASSERT_TRUE(matcher.Matches("AAAAAAABABABBC"));
+  ASSERT_TRUE(matcher.Matches("ABABABBC"));
+  ASSERT_TRUE(matcher.Matches("AXBC"));
+
+  // Non-matching patterns.
+  ASSERT_FALSE(matcher.Matches("ABXC"));
+  ASSERT_FALSE(matcher.Matches("ABCD"));
+  ASSERT_FALSE(matcher.Matches("ABBBBBB"));
+  ASSERT_FALSE(matcher.Matches("BCA"));
+}
+
+TEST(GlobUnittest, Trailing) {
+  GlobMatcher matcher = GlobMatcher::FromPattern("AB*");
+
+  // Matching patterns.
+  ASSERT_TRUE(matcher.Matches("ABC"));
+  ASSERT_TRUE(matcher.Matches("ABXC"));
+  ASSERT_TRUE(matcher.Matches("ABABABBC"));
+  ASSERT_TRUE(matcher.Matches("ABCD"));
+  ASSERT_TRUE(matcher.Matches("ABBBBBB"));
+
+  // Non-matching patterns.
+  ASSERT_FALSE(matcher.Matches("AAAAAAABABABBC"));
+  ASSERT_FALSE(matcher.Matches("AXBC"));
+  ASSERT_FALSE(matcher.Matches("BCA"));
+}
+
+TEST(GlobUnittest, QuestionMarks) {
+  GlobMatcher matcher = GlobMatcher::FromPattern("AB?*CAB");
+
+  // Matching patterns.
+  ASSERT_TRUE(matcher.Matches("ABCCAB"));
+  ASSERT_TRUE(matcher.Matches("ABDCAB"));
+  ASSERT_TRUE(matcher.Matches("ABCABDDDDDCAB"));
+  ASSERT_TRUE(matcher.Matches("ABXCABCAB"));
+  ASSERT_TRUE(matcher.Matches("ABXCABCABCABABABCAB"));
+  ASSERT_TRUE(matcher.Matches("ABCAB"));
+
+  // Non-matching patterns.
+  ASSERT_FALSE(matcher.Matches("ABXCA"));
+  ASSERT_FALSE(matcher.Matches("ABXCABCABCA"));
+}
+
+TEST(GlobUnittest, CharacterClassRange) {
+  GlobMatcher matcher = GlobMatcher::FromPattern("AB[a-zA-Z]CAB");
+
+  // Matching patterns.
+  ASSERT_TRUE(matcher.Matches("ABaCAB"));
+  ASSERT_TRUE(matcher.Matches("ABcCAB"));
+  ASSERT_TRUE(matcher.Matches("ABzCAB"));
+  ASSERT_TRUE(matcher.Matches("ABACAB"));
+  ASSERT_TRUE(matcher.Matches("ABDCAB"));
+  ASSERT_TRUE(matcher.Matches("ABZCAB"));
+
+  // Non-matching patterns.
+  ASSERT_FALSE(matcher.Matches("AB1CAB"));
+  ASSERT_FALSE(matcher.Matches("ABaaCAB"));
+  ASSERT_FALSE(matcher.Matches("ABaACAB"));
+  ASSERT_FALSE(matcher.Matches("AB-CAB"));
+}
+
+TEST(GlobUnittest, CharacterClassNormal) {
+  GlobMatcher matcher = GlobMatcher::FromPattern("AB[abcAZe]CAB");
+
+  // Matching patterns.
+  ASSERT_TRUE(matcher.Matches("ABaCAB"));
+  ASSERT_TRUE(matcher.Matches("ABcCAB"));
+  ASSERT_TRUE(matcher.Matches("ABACAB"));
+  ASSERT_TRUE(matcher.Matches("ABZCAB"));
+  ASSERT_TRUE(matcher.Matches("ABeCAB"));
+
+  // Non-matching patterns.
+  ASSERT_FALSE(matcher.Matches("ABBCAB"));
+  ASSERT_FALSE(matcher.Matches("ABCCAB"));
+  ASSERT_FALSE(matcher.Matches("ABCABaCAB"));
+}
+
+TEST(GlobUnittest, CharacterClassMultiple) {
+  GlobMatcher matcher = GlobMatcher::FromPattern("*[rR][eE][nN]*");
+
+  // Matching patterns.
+  ASSERT_TRUE(matcher.Matches("renderScreenImplLock"));
+
+  // Non-matching patterns.
+  ASSERT_FALSE(matcher.Matches("updateVrFlinger"));
+  ASSERT_FALSE(matcher.Matches("waitForever"));
+}
+
+TEST(GlobUnittest, CharacterClassMixed) {
+  GlobMatcher matcher = GlobMatcher::FromPattern("AB[abcf-zA-DEFG-Z]CAB");
+
+  // Matching patterns.
+  ASSERT_TRUE(matcher.Matches("ABaCAB"));
+  ASSERT_TRUE(matcher.Matches("ABbCAB"));
+  ASSERT_TRUE(matcher.Matches("ABhCAB"));
+  ASSERT_TRUE(matcher.Matches("ABACAB"));
+  ASSERT_TRUE(matcher.Matches("ABHCAB"));
+  ASSERT_TRUE(matcher.Matches("ABZCAB"));
+
+  // Non-matching patterns.
+  ASSERT_FALSE(matcher.Matches("ABeCAB"));
+}
+
+TEST(GlobUnittest, CharacterClassInvert) {
+  GlobMatcher matcher = GlobMatcher::FromPattern("AB[^a-zA]CAB");
+
+  // Matching patterns.
+  ASSERT_TRUE(matcher.Matches("ABHCAB"));
+  ASSERT_TRUE(matcher.Matches("ABZCAB"));
+
+  // Non-matching patterns.
+  ASSERT_FALSE(matcher.Matches("ABhCAB"));
+  ASSERT_FALSE(matcher.Matches("ABaCAB"));
+  ASSERT_FALSE(matcher.Matches("ABbCAB"));
+  ASSERT_FALSE(matcher.Matches("ABACAB"));
+}
+
+TEST(GlobUnittest, CharacterClassNestedDash) {
+  GlobMatcher matcher = GlobMatcher::FromPattern("AB[-]CAB");
+
+  // Matching patterns.
+  ASSERT_TRUE(matcher.Matches("AB-CAB"));
+
+  // Non-matching patterns.
+  ASSERT_FALSE(matcher.Matches("AB]CAB"));
+}
+
+TEST(GlobUnittest, CharacterClassNestedOpenSquare) {
+  GlobMatcher matcher = GlobMatcher::FromPattern("AB[[]CAB");
+
+  // Matching patterns.
+  ASSERT_TRUE(matcher.Matches("AB[CAB"));
+
+  // Non-matching patterns.
+  ASSERT_FALSE(matcher.Matches("AB]CAB"));
+}
+
+TEST(GlobUnittest, CharacterClassNestedClosedSquare) {
+  GlobMatcher matcher = GlobMatcher::FromPattern("AB[]]CAB");
+
+  // Matching patterns.
+  ASSERT_TRUE(matcher.Matches("AB]CAB"));
+
+  // Non-matching patterns.
+  ASSERT_FALSE(matcher.Matches("AB[CAB"));
+}
+
+TEST(GlobUnittest, Complex) {
+  GlobMatcher matcher = GlobMatcher::FromPattern("AB*[C-D]?*F*CAB");
+
+  // Matching patterns.
+  ASSERT_TRUE(matcher.Matches("ABDDDDDDCIFJKNFCAB"));
+
+  // Non-matching patterns.
+  ASSERT_FALSE(matcher.Matches("ABDDDDDDCIFJKNFAB"));
+}
+
+}  // namespace
+}  // namespace util
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/util/profile_builder.cc b/src/trace_processor/util/profile_builder.cc
new file mode 100644
index 0000000..f859d53
--- /dev/null
+++ b/src/trace_processor/util/profile_builder.cc
@@ -0,0 +1,530 @@
+/*
+ * Copyright (C) 2022 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/util/profile_builder.h"
+#include <algorithm>
+#include <cstdint>
+#include <iostream>
+#include <iterator>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/string_utils.h"
+#include "perfetto/ext/base/string_view.h"
+#include "perfetto/ext/trace_processor/demangle.h"
+#include "src/trace_processor/containers/string_pool.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/annotated_callsites.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+base::StringView ToString(CallsiteAnnotation annotation) {
+  switch (annotation) {
+    case CallsiteAnnotation::kNone:
+      return "";
+    case CallsiteAnnotation::kArtAot:
+      return "aot";
+    case CallsiteAnnotation::kArtInterpreted:
+      return "interp";
+    case CallsiteAnnotation::kArtJit:
+      return "jit";
+    case CallsiteAnnotation::kCommonFrame:
+      return "common-frame";
+    case CallsiteAnnotation::kCommonFrameInterp:
+      return "common-frame-interp";
+  }
+  PERFETTO_FATAL("For GCC");
+}
+
+}  // namespace
+
+GProfileBuilder::StringTable::StringTable(
+    protozero::HeapBuffered<third_party::perftools::profiles::pbzero::Profile>*
+        result,
+    const StringPool* string_pool)
+    : string_pool_(*string_pool), result_(*result) {
+  // String at index 0 of the string table must be the empty string (see
+  // profile.proto)
+  int64_t empty_index = WriteString("");
+  PERFETTO_CHECK(empty_index == kEmptyStringIndex);
+}
+
+int64_t GProfileBuilder::StringTable::InternString(base::StringView str) {
+  if (str.empty()) {
+    return kEmptyStringIndex;
+  }
+  auto hash = str.Hash();
+  auto it = seen_strings_.find(hash);
+  if (it != seen_strings_.end()) {
+    return it->second;
+  }
+
+  auto pool_id = string_pool_.GetId(str);
+  int64_t index = pool_id ? InternString(*pool_id) : WriteString(str);
+
+  seen_strings_.insert({hash, index});
+  return index;
+}
+
+int64_t GProfileBuilder::StringTable::InternString(
+    StringPool::Id string_pool_id) {
+  auto it = seen_string_pool_ids_.find(string_pool_id);
+  if (it != seen_string_pool_ids_.end()) {
+    return it->second;
+  }
+
+  NullTermStringView str = string_pool_.Get(string_pool_id);
+
+  int64_t index = str.empty() ? kEmptyStringIndex : WriteString(str);
+  seen_string_pool_ids_.insert({string_pool_id, index});
+  return index;
+}
+
+int64_t GProfileBuilder::StringTable::GetAnnotatedString(
+    StringPool::Id str,
+    CallsiteAnnotation annotation) {
+  if (str.is_null() || annotation == CallsiteAnnotation::kNone) {
+    return InternString(str);
+  }
+  return GetAnnotatedString(string_pool_.Get(str), annotation);
+}
+
+int64_t GProfileBuilder::StringTable::GetAnnotatedString(
+    base::StringView str,
+    CallsiteAnnotation annotation) {
+  if (str.empty() || annotation == CallsiteAnnotation::kNone) {
+    return InternString(str);
+  }
+  return InternString(base::StringView(
+      str.ToStdString() + " [" + ToString(annotation).ToStdString() + "]"));
+}
+
+int64_t GProfileBuilder::StringTable::WriteString(base::StringView str) {
+  result_->add_string_table(str.data(), str.size());
+  return next_index_++;
+}
+
+GProfileBuilder::MappingKey::MappingKey(
+    const tables::StackProfileMappingTable::ConstRowReference& mapping,
+    StringTable& string_table) {
+  size = static_cast<uint64_t>(mapping.end() - mapping.start());
+  file_offset = static_cast<uint64_t>(mapping.exact_offset());
+  build_id_or_filename = string_table.InternString(mapping.build_id());
+  if (build_id_or_filename == kEmptyStringIndex) {
+    build_id_or_filename = string_table.InternString(mapping.name());
+  }
+}
+
+GProfileBuilder::Mapping::Mapping(
+    const tables::StackProfileMappingTable::ConstRowReference& mapping,
+    const StringPool& string_pool,
+    StringTable& string_table)
+    : memory_start(static_cast<uint64_t>(mapping.start())),
+      memory_limit(static_cast<uint64_t>(mapping.end())),
+      file_offset(static_cast<uint64_t>(mapping.exact_offset())),
+      filename(string_table.InternString(mapping.name())),
+      build_id(string_table.InternString(mapping.build_id())),
+      filename_str(string_pool.Get(mapping.name()).ToStdString()) {}
+
+// Do some very basic scoring.
+int64_t GProfileBuilder::Mapping::ComputeMainBinaryScore() const {
+  constexpr const char* kBadSuffixes[] = {".so"};
+  constexpr const char* kBadPrefixes[] = {"/apex", "/system", "/[", "["};
+
+  int64_t score = 0;
+  if (build_id != kEmptyStringIndex) {
+    score += 10;
+  }
+
+  if (filename != kEmptyStringIndex) {
+    score += 10;
+  }
+
+  if (debug_info.has_functions) {
+    score += 10;
+  }
+  if (debug_info.has_filenames) {
+    score += 10;
+  }
+  if (debug_info.has_line_numbers) {
+    score += 10;
+  }
+  if (debug_info.has_inline_frames) {
+    score += 10;
+  }
+
+  if (memory_limit == memory_start) {
+    score -= 1000;
+  }
+
+  for (const char* suffix : kBadSuffixes) {
+    if (base::EndsWith(filename_str, suffix)) {
+      score -= 1000;
+      break;
+    }
+  }
+
+  for (const char* prefix : kBadPrefixes) {
+    if (base::StartsWith(filename_str, prefix)) {
+      score -= 1000;
+      break;
+    }
+  }
+
+  return score;
+}
+
+GProfileBuilder::GProfileBuilder(const TraceProcessorContext* context,
+                                 const std::vector<ValueType>& sample_types,
+                                 bool annotated)
+    : context_(*context),
+      string_table_(&result_, &context->storage->string_pool()) {
+  if (annotated) {
+    annotations_.emplace(context);
+  }
+  WriteSampleTypes(sample_types);
+}
+
+GProfileBuilder::~GProfileBuilder() = default;
+
+void GProfileBuilder::WriteSampleTypes(
+    const std::vector<ValueType>& sample_types) {
+  for (const auto& value_type : sample_types) {
+    // Write strings first
+    int64_t type =
+        string_table_.InternString(base::StringView(value_type.type));
+    int64_t unit =
+        string_table_.InternString(base::StringView(value_type.unit));
+    // Add message later, remember protozero does not allow you to interleave
+    // these write calls.
+    auto* sample_type = result_->add_sample_type();
+    sample_type->set_type(type);
+    sample_type->set_unit(unit);
+  }
+}
+
+bool GProfileBuilder::AddSample(uint32_t callsite_id,
+                                const protozero::PackedVarInt& values) {
+  PERFETTO_CHECK(!finalized_);
+
+  const protozero::PackedVarInt& location_ids =
+      GetLocationIdsForCallsite(CallsiteId(callsite_id));
+  if (location_ids.size() == 0) {
+    return false;
+  }
+  auto* sample = result_->add_sample();
+  sample->set_value(values);
+  sample->set_location_id(location_ids);
+  return true;
+}
+
+void GProfileBuilder::Finalize() {
+  if (finalized_) {
+    return;
+  }
+  WriteMappings();
+  WriteFunctions();
+  WriteLocations();
+  finalized_ = true;
+}
+
+std::string GProfileBuilder::Build() {
+  Finalize();
+  return result_.SerializeAsString();
+}
+
+CallsiteAnnotation GProfileBuilder::GetAnnotation(
+    const tables::StackProfileCallsiteTable::ConstRowReference& callsite) {
+  if (!annotations_) {
+    return CallsiteAnnotation::kNone;
+  }
+
+  return annotations_->GetAnnotation(callsite);
+}
+
+const protozero::PackedVarInt& GProfileBuilder::GetLocationIdsForCallsite(
+    const CallsiteId& callsite_id) {
+  auto it = cached_location_ids_.find(callsite_id);
+  if (it != cached_location_ids_.end()) {
+    return it->second;
+  }
+
+  protozero::PackedVarInt& location_ids = cached_location_ids_[callsite_id];
+
+  const auto& cs_table = context_.storage->stack_profile_callsite_table();
+
+  base::Optional<tables::StackProfileCallsiteTable::ConstRowReference>
+      start_ref = cs_table.FindById(callsite_id);
+  if (!start_ref) {
+    return location_ids;
+  }
+
+  location_ids.Append(WriteLocationIfNeeded(*start_ref));
+
+  base::Optional<CallsiteId> parent_id = start_ref->parent_id();
+  while (parent_id) {
+    auto parent_ref = cs_table.FindById(*parent_id);
+    location_ids.Append(WriteLocationIfNeeded(*parent_ref));
+    parent_id = parent_ref->parent_id();
+  }
+
+  return location_ids;
+}
+
+uint64_t GProfileBuilder::WriteLocationIfNeeded(
+    const tables::StackProfileCallsiteTable::ConstRowReference& callsite) {
+  AnnotatedFrameId key{callsite.frame_id(), GetAnnotation(callsite)};
+  auto it = seen_locations_.find(key);
+  if (it != seen_locations_.end()) {
+    return it->second;
+  }
+
+  auto& frames = context_.storage->stack_profile_frame_table();
+  auto frame = *frames.FindById(key.frame_id);
+
+  const auto& mappings = context_.storage->stack_profile_mapping_table();
+  auto mapping = *mappings.FindById(frame.mapping());
+  uint64_t mapping_id = WriteMappingIfNeeded(mapping);
+
+  uint64_t& id =
+      locations_[Location{mapping_id, static_cast<uint64_t>(frame.rel_pc()),
+                          GetLines(frame, key.annotation, mapping_id)}];
+
+  if (id == 0) {
+    id = locations_.size();
+  }
+
+  seen_locations_.insert({key, id});
+
+  return id;
+}
+
+void GProfileBuilder::WriteLocations() {
+  for (const auto& entry : locations_) {
+    auto* location = result_->add_location();
+    location->set_id(entry.second);
+    location->set_mapping_id(entry.first.mapping_id);
+    location->set_address(entry.first.rel_pc +
+                          GetMapping(entry.first.mapping_id).memory_start);
+
+    for (const Line& line : entry.first.lines) {
+      auto* l = location->add_line();
+      l->set_function_id(line.function_id);
+      if (line.line != 0) {
+        l->set_line(line.line);
+      }
+    }
+  }
+}
+
+std::vector<GProfileBuilder::Line> GProfileBuilder::GetLines(
+    const tables::StackProfileFrameTable::ConstRowReference& frame,
+    CallsiteAnnotation annotation,
+    uint64_t mapping_id) {
+  std::vector<Line> lines =
+      GetLinesForSymbolSetId(frame.symbol_set_id(), annotation, mapping_id);
+  if (lines.empty()) {
+    uint64_t function_id = WriteFunctionIfNeeded(frame, annotation, mapping_id);
+    lines.push_back({function_id, 0});
+  }
+
+  return lines;
+}
+
+std::vector<GProfileBuilder::Line> GProfileBuilder::GetLinesForSymbolSetId(
+    base::Optional<uint32_t> symbol_set_id,
+    CallsiteAnnotation annotation,
+    uint64_t mapping_id) {
+  if (!symbol_set_id) {
+    return {};
+  }
+
+  auto& symbols = context_.storage->symbol_table();
+
+  using RowRef =
+      perfetto::trace_processor::tables::SymbolTable::ConstRowReference;
+  std::vector<RowRef> symbol_set;
+  for (auto it = symbols.FilterToIterator(
+           {symbols.symbol_set_id().eq(*symbol_set_id)});
+       it; ++it) {
+    symbol_set.push_back(it.row_reference());
+  }
+
+  std::sort(symbol_set.begin(), symbol_set.end(),
+            [](const RowRef& a, const RowRef& b) { return a.id() < b.id(); });
+
+  std::vector<GProfileBuilder::Line> lines;
+  for (const RowRef& symbol : symbol_set) {
+    lines.push_back({WriteFunctionIfNeeded(symbol, annotation, mapping_id),
+                     symbol.line_number()});
+  }
+
+  GetMapping(mapping_id).debug_info.has_inline_frames = true;
+  GetMapping(mapping_id).debug_info.has_line_numbers = true;
+
+  return lines;
+}
+
+uint64_t GProfileBuilder::WriteFunctionIfNeeded(
+    const tables::SymbolTable::ConstRowReference& symbol,
+    CallsiteAnnotation annotation,
+    uint64_t mapping_id) {
+  int64_t name = string_table_.GetAnnotatedString(symbol.name(), annotation);
+  int64_t filename = string_table_.InternString(symbol.source_file());
+
+  auto ins = functions_.insert(
+      {Function{name, kEmptyStringIndex, filename}, functions_.size() + 1});
+  uint64_t id = ins.first->second;
+
+  if (ins.second) {
+    if (name != kEmptyStringIndex) {
+      GetMapping(mapping_id).debug_info.has_functions = true;
+    }
+    if (filename != kEmptyStringIndex) {
+      GetMapping(mapping_id).debug_info.has_filenames = true;
+    }
+  }
+
+  return id;
+}
+
+uint64_t GProfileBuilder::WriteFunctionIfNeeded(
+    const tables::StackProfileFrameTable::ConstRowReference& frame,
+    CallsiteAnnotation annotation,
+    uint64_t mapping_id) {
+  AnnotatedFrameId key{frame.id(), annotation};
+  auto it = seen_functions_.find(key);
+  if (it != seen_functions_.end()) {
+    return it->second;
+  }
+
+  int64_t system_name = string_table_.InternString(frame.name());
+  int64_t name = kEmptyStringIndex;
+
+  if (frame.deobfuscated_name()) {
+    name = string_table_.GetAnnotatedString(*frame.deobfuscated_name(),
+                                            annotation);
+  } else if (system_name != kEmptyStringIndex) {
+    std::unique_ptr<char, base::FreeDeleter> demangled =
+        demangle::Demangle(context_.storage->GetString(frame.name()).c_str());
+    if (demangled) {
+      name = string_table_.GetAnnotatedString(demangled.get(), annotation);
+    } else {
+      // demangling failed, expected if the name wasn't mangled. In any case
+      // reuse the system_name as this is what UI will usually display.
+      name = string_table_.GetAnnotatedString(frame.name(), annotation);
+    }
+  }
+
+  auto ins = functions_.insert(
+      {Function{name, system_name, kEmptyStringIndex}, functions_.size() + 1});
+  uint64_t id = ins.first->second;
+  seen_functions_.insert({key, id});
+
+  if (ins.second &&
+      (name != kEmptyStringIndex || system_name != kEmptyStringIndex)) {
+    GetMapping(mapping_id).debug_info.has_functions = true;
+  }
+
+  return id;
+}
+
+void GProfileBuilder::WriteFunctions() {
+  for (const auto& entry : functions_) {
+    auto* func = result_->add_function();
+    func->set_id(entry.second);
+    if (entry.first.name != 0) {
+      func->set_name(entry.first.name);
+    }
+    if (entry.first.system_name != 0) {
+      func->set_system_name(entry.first.system_name);
+    }
+    if (entry.first.filename != 0) {
+      func->set_filename(entry.first.filename);
+    }
+  }
+}
+
+uint64_t GProfileBuilder::WriteMappingIfNeeded(
+    const tables::StackProfileMappingTable::ConstRowReference& mapping_ref) {
+  auto it = seen_mappings_.find(mapping_ref.id());
+  if (it != seen_mappings_.end()) {
+    return it->second;
+  }
+
+  auto ins = mapping_keys_.insert(
+      {MappingKey(mapping_ref, string_table_), mapping_keys_.size() + 1});
+
+  if (ins.second) {
+    mappings_.push_back(
+        Mapping(mapping_ref, context_.storage->string_pool(), string_table_));
+  }
+
+  return ins.first->second;
+}
+
+void GProfileBuilder::WriteMapping(uint64_t mapping_id) {
+  const Mapping& mapping = GetMapping(mapping_id);
+  auto m = result_->add_mapping();
+  m->set_id(mapping_id);
+  m->set_memory_start(mapping.memory_start);
+  m->set_memory_limit(mapping.memory_limit);
+  m->set_file_offset(mapping.file_offset);
+  m->set_filename(mapping.filename);
+  m->set_build_id(mapping.build_id);
+  m->set_has_functions(mapping.debug_info.has_functions);
+  m->set_has_filenames(mapping.debug_info.has_filenames);
+  m->set_has_line_numbers(mapping.debug_info.has_line_numbers);
+  m->set_has_inline_frames(mapping.debug_info.has_inline_frames);
+}
+
+void GProfileBuilder::WriteMappings() {
+  // The convention in pprof files is to write the mapping for the main binary
+  // first. So lets do just that.
+  base::Optional<uint64_t> main_mapping_id = GuessMainBinary();
+  if (main_mapping_id) {
+    WriteMapping(*main_mapping_id);
+  }
+
+  for (size_t i = 0; i < mappings_.size(); ++i) {
+    uint64_t mapping_id = i + 1;
+    if (main_mapping_id && *main_mapping_id == mapping_id) {
+      continue;
+    }
+    WriteMapping(mapping_id);
+  }
+}
+
+base::Optional<uint64_t> GProfileBuilder::GuessMainBinary() const {
+  std::vector<int64_t> mapping_scores;
+
+  for (const auto& mapping : mappings_) {
+    mapping_scores.push_back(mapping.ComputeMainBinaryScore());
+  }
+
+  auto it = std::max_element(mapping_scores.begin(), mapping_scores.end());
+
+  if (it == mapping_scores.end()) {
+    return base::nullopt;
+  }
+
+  return static_cast<uint64_t>(std::distance(mapping_scores.begin(), it) + 1);
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/util/profile_builder.h b/src/trace_processor/util/profile_builder.h
new file mode 100644
index 0000000..3c9d1f3
--- /dev/null
+++ b/src/trace_processor/util/profile_builder.h
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2022 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_PROFILE_BUILDER_H_
+#define SRC_TRACE_PROCESSOR_UTIL_PROFILE_BUILDER_H_
+
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/string_view.h"
+#include "perfetto/protozero/packed_repeated_fields.h"
+#include "perfetto/protozero/scattered_heap_buffer.h"
+#include "protos/third_party/pprof/profile.pbzero.h"
+#include "src/trace_processor/containers/string_pool.h"
+#include "src/trace_processor/storage/trace_storage.h"
+#include "src/trace_processor/tables/profiler_tables.h"
+#include "src/trace_processor/util/annotated_callsites.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <functional>
+#include <unordered_map>
+#include <vector>
+
+namespace perfetto {
+namespace trace_processor {
+
+class TraceProcessorContext;
+
+// Builds a |perftools.profiles.Profile| proto.
+class GProfileBuilder {
+ public:
+  struct ValueType {
+    std::string type;
+    std::string unit;
+  };
+
+  // |sample_types| A description of the values stored with each sample.
+  // |annotated| Whether to annotate callstack frames.
+  //
+  // Important: Annotations might interfere with certain aggregations, as we
+  // will could have a frame that is annotated with different annotations. That
+  // will lead to multiple functions being generated (sane name, line etc, but
+  // different annotation). Since there is no field in a Profile proto to track
+  // these annotations we extend the function name (my_func [annotation]), so
+  // from pprof perspective we now have different functions. So in flame graphs
+  // for example you will have one separate slice for each of these same
+  // functions with different annotations.
+  GProfileBuilder(const TraceProcessorContext* context,
+                  const std::vector<ValueType>& sample_types,
+                  bool annotated);
+  ~GProfileBuilder();
+
+  // Returns false if the callsite_id was not found and no sample was added.
+  bool AddSample(uint32_t callsite_id, const protozero::PackedVarInt& values);
+
+  // Finalizes the profile and returns the serialized proto. Can be called
+  // multiple times but after the first invocation `AddSample` calls will have
+  // no effect.
+  std::string Build();
+
+ private:
+  static constexpr int64_t kEmptyStringIndex = 0;
+
+  // Strings are stored in the `Profile` in a table and referenced by their
+  // index. This helper class takes care of all the book keeping.
+  // `TraceProcessor` uses its own `StringPool` for strings. This helper
+  // provides convenient ways of dealing with `StringPool::Id` values instead of
+  // actual string. This class ensures that two equal strings will have the same
+  // index, so you can compare them instead of the actual strings.
+  class StringTable {
+   public:
+    // |result| This is the `Profile` proto we are building. Strings will be
+    // added to it as necessary. |string_pool| `StringPool` to quey for strings
+    // passed as `StringPool:Id`
+    StringTable(protozero::HeapBuffered<
+                    third_party::perftools::profiles::pbzero::Profile>* result,
+                const StringPool* string_pool);
+
+    // Adds the given string to the table, if not currently present, and returns
+    // the index to it. Might write data to the infligt `Profile` so it should
+    // not be called while in the middle of writing a message to the proto.
+    int64_t InternString(base::StringView str);
+    // Adds a string stored in the `TraceProcessor` `StringPool` to the table,
+    // if not currently present, and returns the index to it. Might write data
+    // to the inflight `Profile` so it should not be called while in the middle
+    // of writing a message to the proto.
+    int64_t InternString(StringPool::Id id);
+
+    int64_t GetAnnotatedString(StringPool::Id str,
+                               CallsiteAnnotation annotation);
+    int64_t GetAnnotatedString(base::StringView str,
+                               CallsiteAnnotation annotation);
+
+   private:
+    // Unconditionally writes the given string to the table and returns its
+    // index.
+    int64_t WriteString(base::StringView str);
+
+    const StringPool& string_pool_;
+    protozero::HeapBuffered<third_party::perftools::profiles::pbzero::Profile>&
+        result_;
+
+    std::unordered_map<StringPool::Id, int64_t> seen_string_pool_ids_;
+    // Maps strings (hashes thereof) to indexes in the table.
+    std::unordered_map<uint64_t, int64_t> seen_strings_;
+    // Index where the next string will be written to
+    int64_t next_index_{0};
+  };
+
+  struct AnnotatedFrameId {
+    struct Hash {
+      size_t operator()(const AnnotatedFrameId& id) const {
+        return static_cast<size_t>(perfetto::base::Hasher::Combine(
+            id.frame_id.value, static_cast<int>(id.annotation)));
+      }
+    };
+
+    FrameId frame_id;
+    CallsiteAnnotation annotation;
+
+    bool operator==(const AnnotatedFrameId& other) const {
+      return frame_id == other.frame_id && annotation == other.annotation;
+    }
+  };
+
+  struct Line {
+    uint64_t function_id;
+    int64_t line;
+    bool operator==(const Line& other) const {
+      return function_id == other.function_id && line == other.line;
+    }
+  };
+
+  // Location, MappingKey, Mapping, Function, and Line are helper structs to
+  // deduplicate entities. We do not write these directly to the proto Profile
+  // but instead stage them and write them out during `Finalize`. Samples on the
+  // other hand are directly written to the proto.
+
+  struct Location {
+    struct Hash {
+      size_t operator()(const Location& loc) const {
+        perfetto::base::Hasher hasher;
+        hasher.UpdateAll(loc.mapping_id, loc.rel_pc, loc.lines.size());
+        for (const auto& line : loc.lines) {
+          hasher.UpdateAll(line.function_id, line.line);
+        }
+        return static_cast<size_t>(hasher.digest());
+      }
+    };
+
+    uint64_t mapping_id;
+    uint64_t rel_pc;
+    std::vector<Line> lines;
+
+    bool operator==(const Location& other) const {
+      return mapping_id == other.mapping_id && rel_pc == other.rel_pc &&
+             lines == other.lines;
+    }
+  };
+
+  // Mappings are tricky. We could have samples for different processes and
+  // given address space layout randomization the same mapping could be located
+  // at different addresses. MappingKey has the set of properties that uniquely
+  // identify mapping in order to deduplicate rows in the stack_profile_mapping
+  // table.
+  struct MappingKey {
+    struct Hash {
+      size_t operator()(const MappingKey& mapping) const {
+        perfetto::base::Hasher hasher;
+        hasher.UpdateAll(mapping.size, mapping.file_offset,
+                         mapping.build_id_or_filename);
+        return static_cast<size_t>(hasher.digest());
+      }
+    };
+
+    explicit MappingKey(
+        const tables::StackProfileMappingTable::ConstRowReference& mapping,
+        StringTable& string_table);
+
+    bool operator==(const MappingKey& other) const {
+      return size == other.size && file_offset == other.file_offset &&
+             build_id_or_filename == other.build_id_or_filename;
+    }
+
+    uint64_t size;
+    uint64_t file_offset;
+    int64_t build_id_or_filename;
+  };
+
+  // Keeps track of what debug information is available for a mapping.
+  // TODO(carlscab): We could be a bit more "clever" here. Currently if there is
+  // debug info for at least one frame we flag the mapping as having debug info.
+  // We could use some heuristic instead, e.g. if x% for frames have the info
+  // etc.
+  struct DebugInfo {
+    bool has_functions{false};
+    bool has_filenames{false};
+    bool has_line_numbers{false};
+    bool has_inline_frames{false};
+  };
+
+  struct Mapping {
+    explicit Mapping(
+        const tables::StackProfileMappingTable::ConstRowReference& mapping,
+        const StringPool& string_pool,
+        StringTable& string_table);
+
+    // Heuristic to determine if this maps to the main binary. Bigger scores
+    // mean higher likelihood.
+    int64_t ComputeMainBinaryScore() const;
+
+    const uint64_t memory_start;
+    const uint64_t memory_limit;
+    const uint64_t file_offset;
+    const int64_t filename;
+    const int64_t build_id;
+
+    const std::string filename_str;
+
+    DebugInfo debug_info;
+  };
+
+  struct Function {
+    struct Hash {
+      size_t operator()(const Function& func) const {
+        return static_cast<size_t>(perfetto::base::Hasher::Combine(
+            func.name, func.system_name, func.filename));
+      }
+    };
+
+    int64_t name;
+    int64_t system_name;
+    int64_t filename;
+
+    bool operator==(const Function& other) const {
+      return name == other.name && system_name == other.system_name &&
+             filename == other.filename;
+    }
+  };
+
+  CallsiteAnnotation GetAnnotation(
+      const tables::StackProfileCallsiteTable::ConstRowReference& callsite);
+
+  const protozero::PackedVarInt& GetLocationIdsForCallsite(
+      const CallsiteId& callsite_id);
+
+  std::vector<Line> GetLinesForSymbolSetId(
+      base::Optional<uint32_t> symbol_set_id,
+      CallsiteAnnotation annotation,
+      uint64_t mapping_id);
+
+  std::vector<Line> GetLines(
+      const tables::StackProfileFrameTable::ConstRowReference& frame,
+      CallsiteAnnotation annotation,
+      uint64_t mapping_id);
+
+  uint64_t WriteLocationIfNeeded(
+      const tables::StackProfileCallsiteTable::ConstRowReference& callsite);
+
+  uint64_t WriteFunctionIfNeeded(
+      const tables::SymbolTable::ConstRowReference& symbol,
+      CallsiteAnnotation annotation,
+
+      uint64_t mapping_id);
+  uint64_t WriteFunctionIfNeeded(
+      const tables::StackProfileFrameTable::ConstRowReference& frame,
+      CallsiteAnnotation annotation,
+      uint64_t mapping_id);
+
+  uint64_t WriteMappingIfNeeded(
+      const tables::StackProfileMappingTable::ConstRowReference& mapping);
+  void WriteMappings();
+  void WriteMapping(uint64_t mapping_id);
+  void WriteFunctions();
+  void WriteLocations();
+
+  void WriteSampleTypes(const std::vector<ValueType>& sample_types);
+
+  void Finalize();
+
+  Mapping& GetMapping(uint64_t mapping_id) {
+    return mappings_[static_cast<size_t>(mapping_id - 1)];
+  }
+
+  // Goes over the list of staged mappings and tries to determine which is the
+  // most likely main binary.
+  base::Optional<uint64_t> GuessMainBinary() const;
+
+  // Profile proto being serialized.
+  protozero::HeapBuffered<third_party::perftools::profiles::pbzero::Profile>
+      result_;
+
+  const TraceProcessorContext& context_;
+  StringTable string_table_;
+
+  bool finalized_{false};
+  base::Optional<AnnotatedCallsites> annotations_;
+
+  // Caches a CallsiteId (callstack) to the list of locations emitted to the
+  // profile.
+  std::unordered_map<CallsiteId, protozero::PackedVarInt> cached_location_ids_;
+
+  // Helpers to map TraceProcessor rows to already written Profile entities
+  // (their ids).
+  std::unordered_map<AnnotatedFrameId, uint64_t, AnnotatedFrameId::Hash>
+      seen_locations_;
+  std::unordered_map<AnnotatedFrameId, uint64_t, AnnotatedFrameId::Hash>
+      seen_functions_;
+  std::unordered_map<MappingId, uint64_t> seen_mappings_;
+
+  // Helpers to deduplicate entries. Map entity to its id. These also serve as a
+  // staging area until written out to the profile proto during `Finalize`. Ids
+  // are consecutive integers starting at 1. (Ids with value 0 are not allowed).
+  // Ids are not unique across entities (i.e. there can be a mapping_id = 1 and
+  // a function_id = 1)
+  std::unordered_map<Location, uint64_t, Location::Hash> locations_;
+  std::unordered_map<MappingKey, uint64_t, MappingKey::Hash> mapping_keys_;
+  std::unordered_map<Function, uint64_t, Function::Hash> functions_;
+  // Staging area for Mappings. mapping_id - 1 = index in the vector.
+  std::vector<Mapping> mappings_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_UTIL_PROFILE_BUILDER_H_
diff --git a/src/trace_processor/util/proto_profiler.cc b/src/trace_processor/util/proto_profiler.cc
index b91eecb..1bcd3ff 100644
--- a/src/trace_processor/util/proto_profiler.cc
+++ b/src/trace_processor/util/proto_profiler.cc
@@ -16,6 +16,10 @@
 
 #include "src/trace_processor/util/proto_profiler.h"
 
+#include <utility>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/ext/base/string_utils.h"
 #include "perfetto/protozero/packed_repeated_fields.h"
 #include "perfetto/protozero/proto_decoder.h"
 #include "perfetto/protozero/proto_utils.h"
@@ -25,18 +29,139 @@
 namespace util {
 
 namespace {
-using ::google::protobuf::FieldDescriptor;
+using ::perfetto::protos::pbzero::FieldDescriptorProto;
 using ::protozero::proto_utils::ProtoWireType;
+
+// Takes a type full name, and returns only the final part.
+// For example, .perfetto.protos.TracePacket -> TracePacket
+std::string GetFieldTypeName(const std::string& full_type_name) {
+  auto pos = full_type_name.rfind('.');
+  if (pos == std::string::npos) {
+    return full_type_name;
+  }
+  return full_type_name.substr(pos + 1);
+}
+
+std::string GetLeafTypeName(uint32_t type_id) {
+  std::string raw_name = FieldDescriptorProto::Type_Name(
+      static_cast<FieldDescriptorProto::Type>(type_id));
+  return base::StripPrefix(base::ToLower(raw_name), "type_");
+}
+
 }  // namespace
 
-base::FlatHashMap<SizeProfileComputer::FieldPath,
-                  SizeProfileComputer::SizeSamples,
-                  SizeProfileComputer::FieldPathHasher>
-SizeProfileComputer::Compute(const uint8_t* ptr,
-                             size_t size,
-                             const google::protobuf::Descriptor* descriptor) {
-  ComputeInner(ptr, size, descriptor);
-  return std::move(path_to_samples_);
+SizeProfileComputer::Field::Field(uint32_t field_idx_in,
+                                  const FieldDescriptor* field_descriptor_in,
+                                  uint32_t type_in,
+                                  const ProtoDescriptor* proto_descriptor_in)
+    : field_idx(field_idx_in),
+      type(type_in),
+      field_descriptor(field_descriptor_in),
+      proto_descriptor(proto_descriptor_in) {}
+
+std::string SizeProfileComputer::Field::field_name() const {
+  if (field_descriptor)
+    return "#" + field_descriptor->name();
+  return "#unknown";
+}
+
+std::string SizeProfileComputer::Field::type_name() const {
+  if (proto_descriptor)
+    return GetFieldTypeName(proto_descriptor->full_name());
+  return GetLeafTypeName(type);
+}
+
+SizeProfileComputer::SizeProfileComputer(DescriptorPool* pool,
+                                         const std::string& message_type)
+    : pool_(pool) {
+  auto message_idx = pool_->FindDescriptorIdx(message_type);
+  if (!message_idx) {
+    PERFETTO_ELOG("Cannot find descriptor for type %s", message_type.c_str());
+    return;
+  }
+  root_message_idx_ = *message_idx;
+}
+
+void SizeProfileComputer::Reset(const uint8_t* ptr, size_t size) {
+  state_stack_.clear();
+  field_path_.clear();
+  protozero::ProtoDecoder decoder(ptr, size);
+  const ProtoDescriptor* descriptor = &pool_->descriptors()[root_message_idx_];
+  state_stack_.push_back(State{descriptor, std::move(decoder), size, 0});
+  field_path_.emplace_back(0, nullptr, root_message_idx_, descriptor);
+}
+
+base::Optional<size_t> SizeProfileComputer::GetNext() {
+  base::Optional<size_t> result;
+  if (state_stack_.empty())
+    return result;
+
+  if (field_path_.size() > state_stack_.size()) {
+    // The leaf path needs to be popped to continue iterating on the current
+    // proto.
+    field_path_.pop_back();
+    PERFETTO_DCHECK(field_path_.size() == state_stack_.size());
+  }
+  State& state = state_stack_.back();
+
+  for (;;) {
+    if (state.decoder.bytes_left() == 0) {
+      break;
+    }
+
+    protozero::Field field = state.decoder.ReadField();
+    if (!field.valid()) {
+      PERFETTO_ELOG("Field not valid (can mean field id >1000)");
+      break;
+    }
+
+    ProtoWireType type = field.type();
+    size_t field_size = GetFieldSize(field);
+
+    state.overhead -= field_size;
+    const FieldDescriptor* field_descriptor =
+        state.descriptor->FindFieldByTag(field.id());
+    if (!field_descriptor) {
+      state.unknown += field_size;
+      continue;
+    }
+
+    bool is_message_type =
+        field_descriptor->type() == FieldDescriptorProto::TYPE_MESSAGE;
+    if (type == ProtoWireType::kLengthDelimited && is_message_type) {
+      auto message_idx =
+          pool_->FindDescriptorIdx(field_descriptor->resolved_type_name());
+
+      if (!message_idx) {
+        PERFETTO_ELOG("Cannot find descriptor for type %s",
+                      field_descriptor->resolved_type_name().c_str());
+        return result;
+      }
+
+      protozero::ProtoDecoder decoder(field.data(), field.size());
+      const ProtoDescriptor* descriptor = &pool_->descriptors()[*message_idx];
+      field_path_.emplace_back(field.id(), field_descriptor, *message_idx,
+                               descriptor);
+      state_stack_.push_back(
+          State{descriptor, std::move(decoder), field.size(), 0U});
+      return GetNext();
+    } else {
+      field_path_.emplace_back(field.id(), field_descriptor,
+                               field_descriptor->type(), nullptr);
+      result.emplace(field_size);
+      return result;
+    }
+  }
+  if (state.unknown) {
+    field_path_.emplace_back(uint32_t(-1), nullptr, 0U, nullptr);
+    result.emplace(state.unknown);
+    state.unknown = 0;
+    return result;
+  }
+
+  result.emplace(state.overhead);
+  state_stack_.pop_back();
+  return result;
 }
 
 size_t SizeProfileComputer::GetFieldSize(const protozero::Field& f) {
@@ -55,67 +180,6 @@
   PERFETTO_FATAL("unexpected field type");  // for gcc
 }
 
-void SizeProfileComputer::ComputeInner(
-    const uint8_t* ptr,
-    size_t size,
-    const google::protobuf::Descriptor* descriptor) {
-  size_t overhead = size;
-  size_t unknown = 0;
-  protozero::ProtoDecoder decoder(ptr, size);
-
-  stack_.push_back(descriptor->name());
-
-  // Compute the size of each sub-field of this message, subtracting it
-  // from overhead and possible adding it to unknown.
-  for (;;) {
-    if (decoder.bytes_left() == 0)
-      break;
-    protozero::Field field = decoder.ReadField();
-    if (!field.valid()) {
-      PERFETTO_ELOG("Field not valid (can mean field id >1000)");
-      break;
-    }
-
-    int id = field.id();
-    ProtoWireType type = field.type();
-    size_t field_size = GetFieldSize(field);
-
-    overhead -= field_size;
-    const FieldDescriptor* field_descriptor = descriptor->FindFieldByNumber(id);
-    if (!field_descriptor) {
-      unknown += field_size;
-      continue;
-    }
-
-    stack_.push_back("#" + field_descriptor->name());
-    bool is_message_type =
-        field_descriptor->type() == FieldDescriptor::TYPE_MESSAGE;
-    if (type == ProtoWireType::kLengthDelimited && is_message_type) {
-      ComputeInner(field.data(), field.size(),
-                   field_descriptor->message_type());
-    } else {
-      stack_.push_back(field_descriptor->type_name());
-      Sample(field_size);
-      stack_.pop_back();
-    }
-    stack_.pop_back();
-  }
-
-  if (unknown) {
-    stack_.push_back("#:unknown:");
-    Sample(unknown);
-    stack_.pop_back();
-  }
-
-  // Anything not blamed on a child is overhead for this message.
-  Sample(overhead);
-  stack_.pop_back();
-}
-
-void SizeProfileComputer::Sample(size_t size) {
-  path_to_samples_[stack_].push_back(size);
-}
-
 }  // namespace util
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/util/proto_profiler.h b/src/trace_processor/util/proto_profiler.h
index 71a4fba..ebc3670 100644
--- a/src/trace_processor/util/proto_profiler.h
+++ b/src/trace_processor/util/proto_profiler.h
@@ -20,13 +20,9 @@
 #include <algorithm>
 #include <vector>
 
-#include <google/protobuf/compiler/importer.h>
-#include <google/protobuf/dynamic_message.h>
-#include <google/protobuf/io/zero_copy_stream_impl.h>
-
-#include "perfetto/ext/base/flat_hash_map.h"
-
+#include "perfetto/ext/base/hash.h"
 #include "perfetto/protozero/field.h"
+#include "src/trace_processor/util/descriptors.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -34,36 +30,69 @@
 
 class SizeProfileComputer {
  public:
-  using FieldPath = std::vector<std::string>;
-  using SizeSamples = std::vector<size_t>;
+  struct Field {
+    Field(uint32_t field_idx_in,
+          const FieldDescriptor* field_descriptor_in,
+          uint32_t type_in,
+          const ProtoDescriptor* proto_descriptor_in);
 
+    bool has_field_name() const {
+      return field_descriptor || field_idx == static_cast<uint32_t>(-1);
+    }
+
+    std::string field_name() const;
+    std::string type_name() const;
+
+    bool operator==(const Field& other) const {
+      return field_idx == other.field_idx && type == other.type;
+    }
+
+    uint32_t field_idx;
+    uint32_t type;
+    const FieldDescriptor* field_descriptor;
+    const ProtoDescriptor* proto_descriptor;
+  };
+
+  using FieldPath = std::vector<Field>;
   struct FieldPathHasher {
-    using argument_type = std::vector<std::string>;
+    using argument_type = FieldPath;
     using result_type = size_t;
 
     result_type operator()(const argument_type& p) const {
       size_t h = 0u;
-      for (auto v : p)
-        h ^= std::hash<std::string>{}(v);
+      for (auto v : p) {
+        h += (std::hash<uint32_t>{}(v.field_idx) +
+              std::hash<uint32_t>{}(v.type));
+        h = (h << 5) - h;
+      }
       return h;
     }
   };
 
-  // Returns a list of samples (i.e. all encountered field sizes) for each
-  // field path in trace proto.
+  explicit SizeProfileComputer(DescriptorPool* pool,
+                               const std::string& message_type);
+
+  // Re-initializes the computer to iterate over samples (i.e. all encountered
+  // field sizes) for each field path in trace proto contained in the given
+  // range.
   // TODO(kraskevich): consider switching to internal DescriptorPool.
-  base::FlatHashMap<FieldPath, SizeSamples, FieldPathHasher> Compute(
-      const uint8_t* ptr,
-      size_t size,
-      const google::protobuf::Descriptor* descriptor);
+  void Reset(const uint8_t* ptr, size_t size);
+
+  // Returns the next sample size, or nullopt if data is exhausted. The
+  // associated path can be queried with GetPath().
+  base::Optional<size_t> GetNext();
+
+  // Returns the field path associated with the last sample returned by
+  // GetNext().
+  const FieldPath& GetPath() const { return field_path_; }
+
+  operator bool() const;
 
  private:
-  void ComputeInner(const uint8_t* ptr,
-                    size_t size,
-                    const google::protobuf::Descriptor* descriptor);
-  void Sample(size_t size);
   size_t GetFieldSize(const protozero::Field& f);
 
+  DescriptorPool* pool_;
+  uint32_t root_message_idx_;
   // The current 'stack' we're considering as we parse the protobuf.
   // For example if we're currently looking at the varint field baz which is
   // nested inside message Bar which is in turn a field named bar on the message
@@ -71,10 +100,16 @@
   // We keep track of both the field names (#bar, #baz) and the field types
   // (Foo, Bar, int) as sometimes we are intrested in which fields are big
   // and sometimes which types are big.
-  std::vector<std::string> stack_;
+  FieldPath field_path_;
 
-  // Information about each field path seen.
-  base::FlatHashMap<FieldPath, SizeSamples, FieldPathHasher> path_to_samples_;
+  // Internal state used to iterate over field path.
+  struct State {
+    const ProtoDescriptor* descriptor;
+    protozero::ProtoDecoder decoder;
+    size_t overhead;
+    size_t unknown;
+  };
+  std::vector<State> state_stack_;
 };
 
 }  // namespace util
diff --git a/src/trace_processor/util/proto_profiler_unittest.cc b/src/trace_processor/util/proto_profiler_unittest.cc
new file mode 100644
index 0000000..181bc97
--- /dev/null
+++ b/src/trace_processor/util/proto_profiler_unittest.cc
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2022 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 "test/gtest_and_gmock.h"
+
+#include "perfetto/protozero/scattered_heap_buffer.h"
+#include "protos/perfetto/trace/trace.pbzero.h"
+#include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "protos/perfetto/trace/track_event/chrome_mojo_event_info.pbzero.h"
+#include "src/protozero/test/example_proto/test_messages.pbzero.h"
+#include "src/trace_processor/test_messages.descriptor.h"
+#include "src/trace_processor/util/proto_profiler.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace util {
+namespace {
+
+using ::testing::UnorderedElementsAreArray;
+
+TEST(ProtoProfiler, TestMessage) {
+  protozero::HeapBuffered<protozero::test::protos::pbzero::NestedA> message;
+  message->add_repeated_a()->set_value_b()->set_value_c(1);
+  message->add_repeated_a()->set_value_b()->set_value_c(2);
+  message->set_super_nested()->set_value_c(3);
+  const std::vector<uint8_t> bytes = message.SerializeAsArray();
+
+  DescriptorPool pool;
+  pool.AddFromFileDescriptorSet(kTestMessagesDescriptor.data(),
+                                kTestMessagesDescriptor.size());
+  SizeProfileComputer computer(&pool, ".protozero.test.protos.NestedA");
+  computer.Reset(bytes.data(), bytes.size());
+
+  // Convert to vector for test matcher.
+  using Item = std::pair<std::vector<std::string>, size_t>;
+  std::vector<Item> got;
+  for (auto sample = computer.GetNext(); sample; sample = computer.GetNext()) {
+    std::vector<std::string> path;
+    for (const auto& field : computer.GetPath()) {
+      if (field.has_field_name())
+        path.push_back(field.field_name());
+      path.push_back(field.type_name());
+    }
+    got.emplace_back(path, *sample);
+  }
+  std::vector<Item> expected{
+      {{"NestedA"}, 15},
+      {{"NestedA", "#repeated_a", "NestedB"}, 5},
+      {{"NestedA", "#repeated_a", "NestedB"}, 5},
+      {{"NestedA", "#repeated_a", "NestedB", "#value_b", "NestedC"}, 1},
+      {{"NestedA", "#repeated_a", "NestedB", "#value_b", "NestedC"}, 1},
+      {{"NestedA", "#repeated_a", "NestedB", "#value_b", "NestedC", "#value_c",
+        "int32"},
+       1},
+      {{"NestedA", "#repeated_a", "NestedB", "#value_b", "NestedC", "#value_c",
+        "int32"},
+       1},
+      {{"NestedA", "#super_nested", "NestedC"}, 1},
+      {{"NestedA", "#super_nested", "NestedC", "#value_c", "int32"}, 1}};
+
+  EXPECT_THAT(got, UnorderedElementsAreArray(expected));
+}
+
+}  // namespace
+}  // namespace util
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/util/proto_to_args_parser.h b/src/trace_processor/util/proto_to_args_parser.h
index 86fbcd1..5e54775 100644
--- a/src/trace_processor/util/proto_to_args_parser.h
+++ b/src/trace_processor/util/proto_to_args_parser.h
@@ -95,6 +95,8 @@
 
     virtual PacketSequenceStateGeneration* seq_state() = 0;
 
+    virtual int64_t packet_timestamp() { return 0; }
+
     template <typename FieldMetadata>
     typename FieldMetadata::cpp_field_type::Decoder* GetInternedMessage(
         protozero::proto_utils::internal::FieldMetadataHelper<FieldMetadata>,
diff --git a/src/trace_processor/util/protozero_to_text.cc b/src/trace_processor/util/protozero_to_text.cc
index 4b71306..90897ad 100644
--- a/src/trace_processor/util/protozero_to_text.cc
+++ b/src/trace_processor/util/protozero_to_text.cc
@@ -16,6 +16,7 @@
 
 #include "src/trace_processor/util/protozero_to_text.h"
 
+#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/string_view.h"
 #include "perfetto/protozero/proto_decoder.h"
@@ -24,7 +25,7 @@
 #include "src/trace_processor/util/descriptors.h"
 
 // This is the highest level that this protozero to text supports.
-#include "src/trace_processor/importers/track_event.descriptor.h"
+#include "src/trace_processor/importers/proto/track_event.descriptor.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -113,6 +114,31 @@
   out->erase(out->size() - 2);
 }
 
+void PrintUnknownVarIntField(uint16_t id, int64_t value, std::string* out) {
+  StrAppend(out, std::to_string(id), ": ", std::to_string(value));
+}
+
+void PrintEnumField(const FieldDescriptor& fd,
+                    const DescriptorPool& pool,
+                    uint16_t id,
+                    int32_t enum_value,
+                    std::string* out) {
+  auto opt_enum_descriptor_idx =
+      pool.FindDescriptorIdx(fd.resolved_type_name());
+  if (!opt_enum_descriptor_idx) {
+    PrintUnknownVarIntField(id, enum_value, out);
+    return;
+  }
+  auto opt_enum_string =
+      pool.descriptors()[*opt_enum_descriptor_idx].FindEnumString(enum_value);
+  // If the enum value is unknown, treat it like a completely unknown field.
+  if (!opt_enum_string) {
+    PrintUnknownVarIntField(id, enum_value, out);
+    return;
+  }
+  StrAppend(out, fd.name(), ": ", *opt_enum_string);
+}
+
 std::string FormattedFieldDescriptorName(
     const FieldDescriptor& field_descriptor) {
   if (field_descriptor.is_extension()) {
@@ -154,26 +180,14 @@
     case FieldDescriptorProto::TYPE_BOOL:
       StrAppend(out, fd->name(), ": ", field.as_bool() ? "true" : "false");
       return;
-    case FieldDescriptorProto::TYPE_ENUM: {
-      // If the enum value is unknown, treat it like a completely unknown field.
-      auto opt_enum_descriptor_idx =
-          pool.FindDescriptorIdx(fd->resolved_type_name());
-      if (!opt_enum_descriptor_idx)
-        break;
-      auto opt_enum_string =
-          pool.descriptors()[*opt_enum_descriptor_idx].FindEnumString(
-              field.as_int32());
-      if (!opt_enum_string)
-        break;
-      StrAppend(out, fd->name(), ": ", *opt_enum_string);
+    case FieldDescriptorProto::TYPE_ENUM:
+      PrintEnumField(*fd, pool, field.id(), field.as_int32(), out);
       return;
-    }
     case 0:
     default:
-      break;
+      PrintUnknownVarIntField(field.id(), field.as_int64(), out);
+      return;
   }
-  StrAppend(out, std::to_string(field.id()), ": ",
-            std::to_string(field.as_uint64()));
 }
 
 void PrintFixed32Field(const FieldDescriptor* fd,
@@ -232,6 +246,7 @@
                       const protozero::Field& field,
                       NewLinesMode new_lines_mode,
                       const std::string& indents,
+                      const DescriptorPool& pool,
                       std::string* out) {
   const bool include_new_lines = new_lines_mode == kIncludeNewLines;
   bool err = false;
@@ -247,7 +262,12 @@
         StrAppend(out, " ");
       }
     }
-    StrAppend(out, fd.name(), ": ", std::to_string(value));
+    std::string serialized_value;
+    if (fd.type() == FieldDescriptorProto::TYPE_ENUM) {
+      PrintEnumField(fd, pool, field.id(), static_cast<int32_t>(value), out);
+    } else {
+      StrAppend(out, fd.name(), ": ", std::to_string(value));
+    }
     first_output = false;
   }
 
@@ -294,49 +314,58 @@
       return;
     case FieldDescriptorProto::TYPE_DOUBLE:
       PrintPackedField<protozero::proto_utils::ProtoWireType::kFixed64, double>(
-          *fd, field, new_lines_mode, *indents, out);
+          *fd, field, new_lines_mode, *indents, pool, out);
       return;
     case FieldDescriptorProto::TYPE_FLOAT:
       PrintPackedField<protozero::proto_utils::ProtoWireType::kFixed32, float>(
-          *fd, field, new_lines_mode, *indents, out);
+          *fd, field, new_lines_mode, *indents, pool, out);
       return;
     case FieldDescriptorProto::TYPE_INT64:
       PrintPackedField<protozero::proto_utils::ProtoWireType::kVarInt, int64_t>(
-          *fd, field, new_lines_mode, *indents, out);
+          *fd, field, new_lines_mode, *indents, pool, out);
       return;
     case FieldDescriptorProto::TYPE_UINT64:
       PrintPackedField<protozero::proto_utils::ProtoWireType::kVarInt,
-                       uint64_t>(*fd, field, new_lines_mode, *indents, out);
+                       uint64_t>(*fd, field, new_lines_mode, *indents, pool,
+                                 out);
       return;
     case FieldDescriptorProto::TYPE_INT32:
       PrintPackedField<protozero::proto_utils::ProtoWireType::kVarInt, int32_t>(
-          *fd, field, new_lines_mode, *indents, out);
+          *fd, field, new_lines_mode, *indents, pool, out);
       return;
     case FieldDescriptorProto::TYPE_FIXED64:
       PrintPackedField<protozero::proto_utils::ProtoWireType::kFixed64,
-                       uint64_t>(*fd, field, new_lines_mode, *indents, out);
+                       uint64_t>(*fd, field, new_lines_mode, *indents, pool,
+                                 out);
       return;
     case FieldDescriptorProto::TYPE_FIXED32:
       PrintPackedField<protozero::proto_utils::ProtoWireType::kFixed32,
-                       uint32_t>(*fd, field, new_lines_mode, *indents, out);
+                       uint32_t>(*fd, field, new_lines_mode, *indents, pool,
+                                 out);
       return;
     case FieldDescriptorProto::TYPE_UINT32:
       PrintPackedField<protozero::proto_utils::ProtoWireType::kVarInt,
-                       uint32_t>(*fd, field, new_lines_mode, *indents, out);
+                       uint32_t>(*fd, field, new_lines_mode, *indents, pool,
+                                 out);
       return;
     case FieldDescriptorProto::TYPE_SFIXED32:
       PrintPackedField<protozero::proto_utils::ProtoWireType::kFixed32,
-                       int32_t>(*fd, field, new_lines_mode, *indents, out);
+                       int32_t>(*fd, field, new_lines_mode, *indents, pool,
+                                out);
       return;
     case FieldDescriptorProto::TYPE_SFIXED64:
       PrintPackedField<protozero::proto_utils::ProtoWireType::kFixed64,
-                       int64_t>(*fd, field, new_lines_mode, *indents, out);
+                       int64_t>(*fd, field, new_lines_mode, *indents, pool,
+                                out);
+      return;
+    case FieldDescriptorProto::TYPE_ENUM:
+      PrintPackedField<protozero::proto_utils::ProtoWireType::kVarInt, int32_t>(
+          *fd, field, new_lines_mode, *indents, pool, out);
       return;
     // Our protoc plugin cannot generate code for packed repeated fields with
     // these types. Output a comment and then fall back to the raw field_id:
     // string representation.
     case FieldDescriptorProto::TYPE_BOOL:
-    case FieldDescriptorProto::TYPE_ENUM:
     case FieldDescriptorProto::TYPE_SINT32:
     case FieldDescriptorProto::TYPE_SINT64:
       StrAppend(out, "# Packed type ", std::to_string(type),
diff --git a/src/trace_processor/util/protozero_to_text_unittests.cc b/src/trace_processor/util/protozero_to_text_unittests.cc
index 6a0624e..f9b5db6 100644
--- a/src/trace_processor/util/protozero_to_text_unittests.cc
+++ b/src/trace_processor/util/protozero_to_text_unittests.cc
@@ -18,14 +18,15 @@
 
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/protozero/scattered_heap_buffer.h"
-#include "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.pbzero.h"
-#include "protos/perfetto/trace/track_event/track_event.pbzero.h"
 #include "src/protozero/test/example_proto/test_messages.pbzero.h"
-#include "src/trace_processor/importers/track_event.descriptor.h"
+#include "src/trace_processor/importers/proto/track_event.descriptor.h"
 #include "src/trace_processor/test_messages.descriptor.h"
 #include "src/trace_processor/util/descriptors.h"
 #include "test/gtest_and_gmock.h"
 
+#include "protos/perfetto/trace/track_event/chrome_compositor_scheduler_state.pbzero.h"
+#include "protos/perfetto/trace/track_event/track_event.pbzero.h"
+
 namespace perfetto {
 namespace trace_processor {
 namespace protozero_to_text {
@@ -596,6 +597,57 @@
                   StartsWith("field_double: 42.125")));
 }
 
+TEST_F(ProtozeroToTextTestMessageTest, FieldLengthLimitedPackedSmallEnum) {
+  protozero::HeapBuffered<PackedRepeatedFields> msg;
+  protozero::PackedVarInt buf;
+  buf.Append(1);
+  buf.Append(0);
+  buf.Append(-1);
+  msg->set_small_enum(buf);
+
+  EXPECT_THAT(
+      base::SplitString(
+          ProtozeroToText(pool_, ".protozero.test.protos.PackedRepeatedFields",
+                          msg.SerializeAsArray(), kIncludeNewLines),
+          "\n"),
+      ElementsAre(Eq("small_enum: TO_BE"), Eq("small_enum: NOT_TO_BE"),
+                  Eq("51: -1")));
+}
+
+TEST_F(ProtozeroToTextTestMessageTest, FieldLengthLimitedPackedSignedEnum) {
+  protozero::HeapBuffered<PackedRepeatedFields> msg;
+  protozero::PackedVarInt buf;
+  buf.Append(1);
+  buf.Append(0);
+  buf.Append(-1);
+  buf.Append(-100);
+  msg->set_signed_enum(buf);
+
+  EXPECT_THAT(
+      base::SplitString(
+          ProtozeroToText(pool_, ".protozero.test.protos.PackedRepeatedFields",
+                          msg.SerializeAsArray(), kIncludeNewLines),
+          "\n"),
+      ElementsAre(Eq("signed_enum: POSITIVE"), Eq("signed_enum: NEUTRAL"),
+                  Eq("signed_enum: NEGATIVE"), Eq("52: -100")));
+}
+
+TEST_F(ProtozeroToTextTestMessageTest, FieldLengthLimitedPackedBigEnum) {
+  protozero::HeapBuffered<PackedRepeatedFields> msg;
+  protozero::PackedVarInt buf;
+  buf.Append(10);
+  buf.Append(100500);
+  buf.Append(-1);
+  msg->set_big_enum(buf);
+
+  EXPECT_THAT(
+      base::SplitString(
+          ProtozeroToText(pool_, ".protozero.test.protos.PackedRepeatedFields",
+                          msg.SerializeAsArray(), kIncludeNewLines),
+          "\n"),
+      ElementsAre(Eq("big_enum: BEGIN"), Eq("big_enum: END"), Eq("53: -1")));
+}
+
 TEST_F(ProtozeroToTextTestMessageTest, FieldLengthLimitedPackedFixedErrShort) {
   protozero::HeapBuffered<PackedRepeatedFields> msg;
   std::string buf;
diff --git a/src/trace_processor/util/sql_argument.cc b/src/trace_processor/util/sql_argument.cc
new file mode 100644
index 0000000..3fc0b48
--- /dev/null
+++ b/src/trace_processor/util/sql_argument.cc
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2022 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/util/sql_argument.h"
+
+#include "perfetto/ext/base/string_utils.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace sql_argument {
+
+bool IsValidName(base::StringView str) {
+  auto pred = [](char c) { return !(isalnum(c) || c == '_'); };
+  return std::find_if(str.begin(), str.end(), pred) == str.end();
+}
+
+base::Optional<Type> ParseType(base::StringView str) {
+  if (str == "BOOL") {
+    return Type::kBool;
+  } else if (str == "INT") {
+    return Type::kInt;
+  } else if (str == "UINT") {
+    return Type::kUint;
+  } else if (str == "LONG") {
+    return Type::kLong;
+  } else if (str == "FLOAT") {
+    return Type::kFloat;
+  } else if (str == "DOUBLE") {
+    return Type::kDouble;
+  } else if (str == "STRING") {
+    return Type::kString;
+  } else if (str == "PROTO") {
+    return Type::kProto;
+  } else if (str == "BYTES") {
+    return Type::kBytes;
+  }
+  return base::nullopt;
+}
+
+const char* TypeToHumanFriendlyString(sql_argument::Type type) {
+  using Type = sql_argument::Type;
+  switch (type) {
+    case Type::kBool:
+      return "BOOL";
+    case Type::kInt:
+      return "INT";
+    case Type::kUint:
+      return "UINT";
+    case Type::kLong:
+      return "LONG";
+    case Type::kFloat:
+      return "FLOAT";
+    case Type::kDouble:
+      return "DOUBLE";
+    case Type::kString:
+      return "STRING";
+    case Type::kProto:
+      return "PROTO";
+    case Type::kBytes:
+      return "BYTES";
+  }
+  PERFETTO_FATAL("For GCC");
+}
+
+SqlValue::Type TypeToSqlValueType(sql_argument::Type type) {
+  using Type = sql_argument::Type;
+  switch (type) {
+    case Type::kBool:
+    case Type::kInt:
+    case Type::kUint:
+    case Type::kLong:
+      return SqlValue::kLong;
+    case Type::kFloat:
+    case Type::kDouble:
+      return SqlValue::kDouble;
+    case Type::kString:
+      return SqlValue::kString;
+    case Type::kProto:
+    case Type::kBytes:
+      return SqlValue::kBytes;
+  }
+  PERFETTO_FATAL("For GCC");
+}
+
+base::Status ParseArgumentDefinitions(const std::string& args,
+                                      std::vector<ArgumentDefinition>& out) {
+  std::string trimmed_args = base::TrimWhitespace(args);
+  for (const auto& arg : base::SplitString(trimmed_args, ",")) {
+    const auto& arg_name_and_type =
+        (base::SplitString(base::TrimWhitespace(arg), " "));
+    if (arg_name_and_type.size() != 2) {
+      return base::ErrStatus(
+          "argument '%s' in function prototype should be of the form `name "
+          "TYPE`",
+          arg.c_str());
+    }
+
+    const auto& arg_name = arg_name_and_type[0];
+    const auto& arg_type_str = arg_name_and_type[1];
+    if (!IsValidName(base::StringView(arg_name)))
+      return base::ErrStatus("argument '%s' is not alphanumeric", arg.c_str());
+
+    auto opt_arg_type = ParseType(base::StringView(arg_type_str));
+    if (!opt_arg_type) {
+      return base::ErrStatus("unknown argument type in argument '%s'",
+                             arg.c_str());
+    }
+    out.emplace_back("$" + arg_name, *opt_arg_type);
+  }
+  return base::OkStatus();
+}
+
+}  // namespace sql_argument
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/util/sql_argument.h b/src/trace_processor/util/sql_argument.h
new file mode 100644
index 0000000..7b7b9b3
--- /dev/null
+++ b/src/trace_processor/util/sql_argument.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2022 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_SQL_ARGUMENT_H_
+#define SRC_TRACE_PROCESSOR_UTIL_SQL_ARGUMENT_H_
+
+#include "perfetto/base/status.h"
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/trace_processor/basic_types.h"
+#include "src/trace_processor/containers/null_term_string_view.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace sql_argument {
+
+// Possible types which can be specified in SQL.
+// This differs from SqlValue::Type by allowing specifying richer
+// types (e.g. kBool, kInt, kUint and kLong all map to
+// SqlValue::Type::kLong). This allows more accurate type checking
+// and, when lots of values are stored, reduced memory usage.
+enum class Type {
+  kBool,
+  kInt,
+  kUint,
+  kLong,
+  kFloat,
+  kDouble,
+  kString,
+  kProto,
+  kBytes,
+};
+
+// Represents the definition of an argument from SQL. See
+// |ParseArgumentDefinitions| for more details.
+class ArgumentDefinition {
+ public:
+  ArgumentDefinition(std::string dollar_name, Type type)
+      : dollar_name_(std::move(dollar_name)), type_(type) {
+    PERFETTO_DCHECK(!dollar_name_.empty() && dollar_name_[0] == '$');
+  }
+
+  NullTermStringView dollar_name() const {
+    return NullTermStringView(dollar_name_);
+  }
+
+  NullTermStringView name() const {
+    return NullTermStringView(dollar_name_.c_str() + 1,
+                              dollar_name_.size() - 1);
+  }
+
+  Type type() const { return type_; }
+
+  bool operator==(const ArgumentDefinition& other) const {
+    return dollar_name_ == other.dollar_name_ && type_ == other.type_;
+  }
+
+ private:
+  std::string dollar_name_;
+  Type type_;
+};
+
+// Returns whether the given |name| is considered valid.
+//
+// Names are valid if they only contain alpha-numeric characters or underscores.
+bool IsValidName(base::StringView name);
+
+// Parses a string containing a type from SQL and converts it to a Type enum
+// value.
+// Returns base::nullopt if |type| did not correspond to any of the enum values.
+base::Optional<Type> ParseType(base::StringView type);
+
+// Converts an argument type to a string for printing (e.g. in error messages
+// etc).
+const char* TypeToHumanFriendlyString(sql_argument::Type type);
+
+// Converts an argument type to the equivalent SqlValue::Type.
+SqlValue::Type TypeToSqlValueType(sql_argument::Type type);
+
+// Parses a string containing argument definitions from SQL and converts it into
+// a typed list of ArgumentDefintion structs
+//
+// An argument defintion is a variable name followed by a type. Variable names
+// only contain alpha-numeric characters or underscores. Types must be one of
+// the types corresponding to the Type enum.
+//
+// The expected form of |args| is comma-separated list of argument definitions.
+// For example: foo BYTES, bar PROTO, baz INT, foobar STRING
+base::Status ParseArgumentDefinitions(const std::string& args,
+                                      std::vector<ArgumentDefinition>& out);
+
+}  // namespace sql_argument
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_UTIL_SQL_ARGUMENT_H_
diff --git a/src/trace_processor/util/sql_argument_unittest.cc b/src/trace_processor/util/sql_argument_unittest.cc
new file mode 100644
index 0000000..f75ceea
--- /dev/null
+++ b/src/trace_processor/util/sql_argument_unittest.cc
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2021 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/util/sql_argument.h"
+
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace sql_argument {
+namespace {
+
+void ParseArgsSuccessfully(const std::string& args,
+                           std::vector<ArgumentDefinition> expected) {
+  std::vector<ArgumentDefinition> actual;
+  base::Status status = ParseArgumentDefinitions(args, actual);
+  ASSERT_TRUE(status.ok()) << status.c_message();
+  ASSERT_EQ(expected, actual);
+}
+
+void ParseArgsWithFailure(const std::string& args) {
+  std::vector<ArgumentDefinition> actual;
+  ASSERT_FALSE(ParseArgumentDefinitions(args, actual).ok());
+}
+
+TEST(SqlArgumentTest, IsValidName) {
+  ASSERT_TRUE(IsValidName("foo"));
+  ASSERT_TRUE(IsValidName("bar"));
+  ASSERT_TRUE(IsValidName("foo_bar"));
+  ASSERT_TRUE(IsValidName("foo1234"));
+  ASSERT_TRUE(IsValidName("1234Foo"));
+  ASSERT_FALSE(IsValidName("foo-bar"));
+  ASSERT_FALSE(IsValidName("foo#123"));
+}
+
+TEST(SqlArgumentTest, ParseType) {
+  ASSERT_EQ(ParseType("PROTO"), Type::kProto);
+  ASSERT_EQ(ParseType("BOOL"), Type::kBool);
+  ASSERT_EQ(ParseType("UNKNOWN"), base::nullopt);
+  ASSERT_EQ(ParseType("UINT"), Type::kUint);
+}
+
+TEST(SqlArgumentTest, TypeToFriendlyString) {
+  ASSERT_STREQ(TypeToHumanFriendlyString(Type::kProto), "PROTO");
+  ASSERT_STREQ(TypeToHumanFriendlyString(Type::kBool), "BOOL");
+  ASSERT_STREQ(TypeToHumanFriendlyString(Type::kUint), "UINT");
+}
+
+TEST(SqlArgumentTest, TypeToSqlValueType) {
+  ASSERT_EQ(TypeToSqlValueType(Type::kProto), SqlValue::Type::kBytes);
+  ASSERT_EQ(TypeToSqlValueType(Type::kBool), SqlValue::Type::kLong);
+  ASSERT_EQ(TypeToSqlValueType(Type::kUint), SqlValue::Type::kLong);
+}
+
+TEST(SqlArgumentTest, ParseArguments) {
+  ParseArgsSuccessfully("", {});
+  ParseArgsSuccessfully("foo UINT", {ArgumentDefinition("$foo", Type::kUint)});
+  ParseArgsSuccessfully("foo UINT, bar LONG, baz PROTO",
+                        {ArgumentDefinition("$foo", Type::kUint),
+                         ArgumentDefinition("$bar", Type::kLong),
+                         ArgumentDefinition("$baz", Type::kProto)});
+  ParseArgsSuccessfully("\nfoo UINT,\n bar LONG, baz PROTO\n",
+                        {ArgumentDefinition("$foo", Type::kUint),
+                         ArgumentDefinition("$bar", Type::kLong),
+                         ArgumentDefinition("$baz", Type::kProto)});
+  ParseArgsSuccessfully("foo123 UINT",
+                        {ArgumentDefinition("$foo123", Type::kUint)});
+
+  ParseArgsWithFailure("foo");
+  ParseArgsWithFailure("foo bar UINT, baz UINT");
+  ParseArgsWithFailure("foo UINT32");
+  ParseArgsWithFailure("foo#bar UINT");
+  ParseArgsWithFailure("foo-bar UINT");
+}
+
+}  // namespace
+}  // namespace sql_argument
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/util/sql_modules.h b/src/trace_processor/util/sql_modules.h
new file mode 100644
index 0000000..a2d5933
--- /dev/null
+++ b/src/trace_processor/util/sql_modules.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 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_SQL_MODULES_H_
+#define SRC_TRACE_PROCESSOR_UTIL_SQL_MODULES_H_
+
+#include <string>
+
+#include "perfetto/ext/base/flat_hash_map.h"
+#include "perfetto/ext/base/string_splitter.h"
+#include "perfetto/ext/base/string_view.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace sql_modules {
+
+using NameToModule =
+    base::FlatHashMap<std::string,
+                      std::vector<std::pair<std::string, std::string>>>;
+
+// Map from import key to sql file. Import key is the string used in IMPORT
+// function.
+struct RegisteredModule {
+  struct ModuleFile {
+    std::string sql;
+    bool imported;
+  };
+  base::FlatHashMap<std::string, ModuleFile> import_key_to_file;
+};
+
+inline std::string ReplaceSlashWithDot(std::string str) {
+  size_t found = str.find('/');
+  while (found != std::string::npos) {
+    str.replace(found, 1, ".");
+    found = str.find('/');
+  }
+  return str;
+}
+
+inline std::string GetImportKey(std::string path) {
+  base::StringView path_view(path);
+  auto path_no_extension = path_view.substr(0, path_view.rfind('.'));
+  return ReplaceSlashWithDot(path_no_extension.ToStdString());
+}
+
+inline std::string GetModuleName(std::string str) {
+  size_t found = str.find('.');
+  if (found == std::string::npos) {
+    return str;
+  }
+  return str.substr(0, found);
+}
+
+}  // namespace sql_modules
+}  // namespace trace_processor
+}  // namespace perfetto
+#endif  // SRC_TRACE_PROCESSOR_UTIL_SQL_MODULES_H_
diff --git a/src/trace_processor/util/zip_reader.cc b/src/trace_processor/util/zip_reader.cc
index 7b5c0d9..d1651f2 100644
--- a/src/trace_processor/util/zip_reader.cc
+++ b/src/trace_processor/util/zip_reader.cc
@@ -114,7 +114,7 @@
         cur_.hdr.compression = ReadAndAdvance<uint16_t>(&hdr_it);
         cur_.hdr.mtime = ReadAndAdvance<uint16_t>(&hdr_it);
         cur_.hdr.mdate = ReadAndAdvance<uint16_t>(&hdr_it);
-        cur_.hdr.crc32 = ReadAndAdvance<uint32_t>(&hdr_it);
+        cur_.hdr.checksum = ReadAndAdvance<uint32_t>(&hdr_it);
         cur_.hdr.compressed_size = ReadAndAdvance<uint32_t>(&hdr_it);
         cur_.hdr.uncompressed_size = ReadAndAdvance<uint32_t>(&hdr_it);
         cur_.hdr.fname_len = ReadAndAdvance<uint16_t>(&hdr_it);
@@ -232,9 +232,9 @@
   const auto* crc_data = reinterpret_cast<const ::Bytef*>(out_data->data());
   auto crc_len = static_cast<::uInt>(out_data->size());
   auto actual_crc32 = static_cast<uint32_t>(::crc32(0u, crc_data, crc_len));
-  if (actual_crc32 != hdr_.crc32) {
+  if (actual_crc32 != hdr_.checksum) {
     return base::ErrStatus("Zip CRC32 failure on %s (actual: %x, expected: %x)",
-                           hdr_.fname.c_str(), actual_crc32, hdr_.crc32);
+                           hdr_.fname.c_str(), actual_crc32, hdr_.checksum);
   }
 #endif
 
diff --git a/src/trace_processor/util/zip_reader.h b/src/trace_processor/util/zip_reader.h
index 96bcdf2..2f34d45 100644
--- a/src/trace_processor/util/zip_reader.h
+++ b/src/trace_processor/util/zip_reader.h
@@ -112,7 +112,7 @@
     uint16_t version = 0;
     uint16_t flags = 0;
     uint16_t compression = 0;
-    uint32_t crc32 = 0;
+    uint32_t checksum = 0;
     uint16_t mtime = 0;
     uint16_t mdate = 0;
     uint32_t compressed_size = 0;
diff --git a/src/trace_processor/views/slice_views.h b/src/trace_processor/views/slice_views.h
index 61923d0..d83a0f3 100644
--- a/src/trace_processor/views/slice_views.h
+++ b/src/trace_processor/views/slice_views.h
@@ -33,6 +33,7 @@
   PERFETTO_TP_VIEW_EXPORT_FROM_COLS(PERFETTO_TP_SLICE_TABLE_DEF, FCOL)      \
   COL(utid, track, utid)                                                    \
   COL(thread_name, thread, name)                                            \
+  COL(upid, thread, upid)                                                   \
   FROM(tables::SliceTable, slice)                                           \
   JOIN(tables::ThreadTrackTable, track, id, slice, track_id, View::kNoFlag) \
   JOIN(tables::ThreadTable, thread, id, track, utid,                        \
diff --git a/src/traceconv/BUILD.gn b/src/traceconv/BUILD.gn
index 544d9a9..97c7524 100644
--- a/src/traceconv/BUILD.gn
+++ b/src/traceconv/BUILD.gn
@@ -114,9 +114,9 @@
 source_set("main") {
   deps = [
     ":lib",
-    "../base:version",
     "../../gn:default_deps",
     "../../include/perfetto/ext/base:base",
+    "../base:version",
   ]
   sources = [ "main.cc" ]
 }
diff --git a/src/traceconv/main.cc b/src/traceconv/main.cc
index d038422..1570281 100644
--- a/src/traceconv/main.cc
+++ b/src/traceconv/main.cc
@@ -32,7 +32,10 @@
 #include "src/traceconv/trace_to_systrace.h"
 #include "src/traceconv/trace_to_text.h"
 
-#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <fcntl.h>
+#include <io.h>
+#else
 #include <unistd.h>
 #endif
 
@@ -137,11 +140,17 @@
     input_stream = &std::cin;
   }
 
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  // We don't want the runtime to replace "\n" with "\r\n" on `std::cout`.
+  _setmode(_fileno(stdout), _O_BINARY);
+#endif
+
   std::ostream* output_stream;
   std::ofstream file_ostream;
   if (positional_args.size() > 2) {
     const char* file_path = positional_args[2];
-    file_ostream.open(file_path, std::ios_base::out | std::ios_base::trunc);
+    file_ostream.open(file_path, std::ios_base::out | std::ios_base::trunc |
+                                     std::ios_base::binary);
     if (!file_ostream.is_open())
       PERFETTO_FATAL("Could not open %s", file_path);
     output_stream = &file_ostream;
diff --git a/src/traceconv/pprof_builder.cc b/src/traceconv/pprof_builder.cc
index a5f724f..07aa6f9 100644
--- a/src/traceconv/pprof_builder.cc
+++ b/src/traceconv/pprof_builder.cc
@@ -115,7 +115,7 @@
 template <>
 struct std::hash<Function> {
   size_t operator()(const Function& loc) const {
-    perfetto::base::Hash hasher;
+    perfetto::base::Hasher hasher;
     hasher.Update(loc.name_id.raw_id());
     hasher.Update(loc.system_name_id.raw_id());
     hasher.Update(loc.filename_id.raw_id());
@@ -126,7 +126,7 @@
 template <>
 struct std::hash<Location> {
   size_t operator()(const Location& loc) const {
-    perfetto::base::Hash hasher;
+    perfetto::base::Hasher hasher;
     hasher.Update(loc.mapping_id);
     hasher.Update(loc.single_function_id);
     for (auto line : loc.inlined_functions) {
@@ -642,14 +642,22 @@
   const char* aggregator;
   const char* filter;
 };
-const View kSpaceView{"space", "bytes", "SUM(size)", nullptr};
-const View kAllocSpaceView{"alloc_space", "bytes", "SUM(size)", "size >= 0"};
-const View kAllocObjectsView{"alloc_objects", "count", "sum(count)",
-                             "size >= 0"};
-const View kObjectsView{"objects", "count", "SUM(count)", nullptr};
 
-const View kViews[] = {kAllocObjectsView, kObjectsView, kAllocSpaceView,
-                       kSpaceView};
+const View kMallocViews[] = {
+    {"Total malloc count", "count", "sum(count)", "size >= 0"},
+    {"Total malloc size", "bytes", "SUM(size)", "size >= 0"},
+    {"Unreleased malloc count", "count", "SUM(count)", nullptr},
+    {"Unreleased malloc size", "bytes", "SUM(size)", nullptr}};
+
+const View kGenericViews[] = {
+    {"Total count", "count", "sum(count)", "size >= 0"},
+    {"Total size", "bytes", "SUM(size)", "size >= 0"},
+    {"Unreleased count", "count", "SUM(count)", nullptr},
+    {"Unreleased size", "bytes", "SUM(size)", nullptr}};
+
+const View kJavaSamplesViews[] = {
+    {"Total allocation count", "count", "SUM(count)", nullptr},
+    {"Total allocation size", "bytes", "SUM(size)", nullptr}};
 
 static bool VerifyPIDStats(trace_processor::TraceProcessor* tp, uint64_t pid) {
   bool success = true;
@@ -692,10 +700,10 @@
     trace_processor::TraceProcessor* tp,
     uint64_t upid,
     uint64_t ts,
-    const char* heap_name) {
+    const char* heap_name,
+    const std::vector<View>& views) {
   std::vector<Iterator> view_its;
-  for (size_t i = 0; i < base::ArraySize(kViews); ++i) {
-    const View& v = kViews[i];
+  for (const View& v : views) {
     std::string query = "SELECT hpa.callsite_id ";
     query +=
         ", " + std::string(v.aggregator) + " FROM heap_profile_allocation hpa ";
@@ -717,7 +725,7 @@
   for (;;) {
     bool all_next = true;
     bool any_next = false;
-    for (size_t i = 0; i < base::ArraySize(kViews); ++i) {
+    for (size_t i = 0; i < view_its->size(); ++i) {
       Iterator& it = (*view_its)[i];
       bool next = it.Next();
       if (!it.Status().ok()) {
@@ -736,7 +744,7 @@
 
     protozero::PackedVarInt sample_values;
     int64_t callstack_id = -1;
-    for (size_t i = 0; i < base::ArraySize(kViews); ++i) {
+    for (size_t i = 0; i < view_its->size(); ++i) {
       if (i == 0) {
         callstack_id = (*view_its)[i].Get(0).AsLong();
       } else if (callstack_id != (*view_its)[i].Get(0).AsLong()) {
@@ -782,15 +790,23 @@
     if (!VerifyPIDStats(tp, profile_pid))
       any_fail = true;
 
+    std::vector<View> views;
+    if (base::StringView(heap_name) == "libc.malloc") {
+      views.assign(std::begin(kMallocViews), std::end(kMallocViews));
+    } else if (base::StringView(heap_name) == "com.android.art") {
+      views.assign(std::begin(kJavaSamplesViews), std::end(kJavaSamplesViews));
+    } else {
+      views.assign(std::begin(kGenericViews), std::end(kGenericViews));
+    }
+
     std::vector<std::pair<std::string, std::string>> sample_types;
-    for (size_t i = 0; i < base::ArraySize(kViews); ++i) {
-      sample_types.emplace_back(std::string(kViews[i].type),
-                                std::string(kViews[i].unit));
+    for (const View& view : views) {
+      sample_types.emplace_back(view.type, view.unit);
     }
     builder.WriteSampleTypes(sample_types);
 
     std::vector<Iterator> view_its =
-        BuildViewIterators(tp, upid, ts, heap_name);
+        BuildViewIterators(tp, upid, ts, heap_name, views);
     std::string profile_proto;
     if (WriteAllocations(&builder, &view_its)) {
       profile_proto = builder.CompleteProfile(tp);
diff --git a/src/traceconv/trace_to_json.cc b/src/traceconv/trace_to_json.cc
index 429b4d7..7986f24 100644
--- a/src/traceconv/trace_to_json.cc
+++ b/src/traceconv/trace_to_json.cc
@@ -20,6 +20,7 @@
 
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/scoped_file.h"
+#include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/temp_file.h"
 #include "perfetto/trace_processor/trace_processor.h"
 #include "src/traceconv/utils.h"
@@ -45,9 +46,9 @@
   // Write userspace trace to a temporary file.
   // TODO(eseckler): Support streaming the result out of TP directly instead.
   auto file = base::TempFile::Create();
-  char query[100];
-  sprintf(query, "select export_json(\"%s\")", file.path().c_str());
-  auto it = tp->ExecuteQuery(query);
+  base::StackString<100> query("select export_json(\"%s\")",
+                               file.path().c_str());
+  auto it = tp->ExecuteQuery(query.ToStdString());
 
   if (!it.Next()) {
     auto status = it.Status();
diff --git a/src/traceconv/trace_to_systrace.cc b/src/traceconv/trace_to_systrace.cc
index d89f0d6..6b2f380 100644
--- a/src/traceconv/trace_to_systrace.cc
+++ b/src/traceconv/trace_to_systrace.cc
@@ -27,6 +27,7 @@
 
 #include "perfetto/base/build_config.h"
 #include "perfetto/base/logging.h"
+#include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/string_writer.h"
 #include "perfetto/ext/base/utils.h"
 #include "perfetto/trace_processor/trace_processor.h"
@@ -247,15 +248,15 @@
 
   // 2. Write the actual events.
   if (truncate_keep == Keep::kEnd && raw_events > max_ftrace_events) {
-    char end_truncate[150];
-    sprintf(end_truncate, "%s limit %d offset %d", kRawEventsQuery,
-            max_ftrace_events, raw_events - max_ftrace_events);
-    if (!q_writer.RunQuery(end_truncate, raw_callback))
+    base::StackString<150> end_truncate("%s limit %d offset %d",
+                                        kRawEventsQuery, max_ftrace_events,
+                                        raw_events - max_ftrace_events);
+    if (!q_writer.RunQuery(end_truncate.ToStdString(), raw_callback))
       return 1;
   } else if (truncate_keep == Keep::kStart) {
-    char start_truncate[150];
-    sprintf(start_truncate, "%s limit %d", kRawEventsQuery, max_ftrace_events);
-    if (!q_writer.RunQuery(start_truncate, raw_callback))
+    base::StackString<150> start_truncate("%s limit %d", kRawEventsQuery,
+                                          max_ftrace_events);
+    if (!q_writer.RunQuery(start_truncate.ToStdString(), raw_callback))
       return 1;
   } else {
     if (!q_writer.RunQuery(kRawEventsQuery, raw_callback))
diff --git a/src/traceconv/trace_to_text_unittest.cc b/src/traceconv/trace_to_text_unittest.cc
index a4f577d..2e90290 100644
--- a/src/traceconv/trace_to_text_unittest.cc
+++ b/src/traceconv/trace_to_text_unittest.cc
@@ -28,10 +28,10 @@
 namespace trace_to_text {
 
 // Given a file, compute the checksum/hash of file.
-// Learn more @ base::Hash.
+// Learn more @ base::Hasher.
 // Precondition: File should exist and be accessible.
 static uint64_t FileHash(const string& filename) {
-  base::Hash hash;
+  base::Hasher hash;
   std::ifstream input_f(filename, std::ios::binary);
   PERFETTO_DCHECK(input_f.good());
   char buffer[4096];
diff --git a/src/traced/probes/android_system_property/android_system_property_data_source.cc b/src/traced/probes/android_system_property/android_system_property_data_source.cc
index 650aa46..b9ba2b8 100644
--- a/src/traced/probes/android_system_property/android_system_property_data_source.cc
+++ b/src/traced/probes/android_system_property/android_system_property_data_source.cc
@@ -108,6 +108,13 @@
     }
   }
   packet->Finalize();
+  // For most data sources we would not want to flush every time we have
+  // something to write. However this source tends to emit very slowly and it is
+  // very possible that it would only flush at the end of the trace - at which
+  // point it might not be able to write anything (e.g. DISCARD buffer might be
+  // full). Taking the hit of 4kB each time we write seems reasonable to make
+  // this behave more predictably.
+  writer_->Flush();
 }
 
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
diff --git a/src/traced/probes/filesystem/prefix_finder.cc b/src/traced/probes/filesystem/prefix_finder.cc
index 4dca5c8..cc1113e 100644
--- a/src/traced/probes/filesystem/prefix_finder.cc
+++ b/src/traced/probes/filesystem/prefix_finder.cc
@@ -108,7 +108,7 @@
 #endif
   perfetto::base::StringSplitter s(std::move(path), '/');
   Node* cur = &root_;
-  for (size_t i = 0; s.Next(); ++i) {
+  for (; s.Next();) {
     char* token = s.cur_token();
     Node* next = cur->MaybeChild(token);
     if (next == nullptr)
diff --git a/src/traced/probes/ftrace/BUILD.gn b/src/traced/probes/ftrace/BUILD.gn
index c3e18ef..dd74c14 100644
--- a/src/traced/probes/ftrace/BUILD.gn
+++ b/src/traced/probes/ftrace/BUILD.gn
@@ -65,14 +65,15 @@
   sources = [
     "cpu_reader_unittest.cc",
     "cpu_stats_parser_unittest.cc",
-    "discover_vendor_tracepoints_unittest.cc",
     "event_info_unittest.cc",
     "ftrace_config_muxer_unittest.cc",
     "ftrace_config_unittest.cc",
     "ftrace_controller_unittest.cc",
+    "ftrace_print_filter_unittest.cc",
     "ftrace_procfs_unittest.cc",
     "printk_formats_parser_unittest.cc",
     "proto_translation_table_unittest.cc",
+    "vendor_tracepoints_unittest.cc",
   ]
 }
 
@@ -120,6 +121,7 @@
     "../../../android_internal:lazy_library_loader",
     "../../../base",
     "../../../kallsyms",
+    "../../../kernel_utils:syscall_table",
     "../../../protozero",
     "format_parser",
   ]
@@ -134,8 +136,6 @@
     "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",
@@ -149,12 +149,16 @@
     "ftrace_data_source.cc",
     "ftrace_data_source.h",
     "ftrace_metadata.h",
+    "ftrace_print_filter.cc",
+    "ftrace_print_filter.h",
     "ftrace_stats.cc",
     "ftrace_stats.h",
     "printk_formats_parser.cc",
     "printk_formats_parser.h",
     "proto_translation_table.cc",
     "proto_translation_table.h",
+    "vendor_tracepoints.cc",
+    "vendor_tracepoints.h",
   ]
 }
 
diff --git a/src/traced/probes/ftrace/cpu_reader.cc b/src/traced/probes/ftrace/cpu_reader.cc
index baed3c7..9403af4 100644
--- a/src/traced/probes/ftrace/cpu_reader.cc
+++ b/src/traced/probes/ftrace/cpu_reader.cc
@@ -37,6 +37,7 @@
 #include "src/traced/probes/ftrace/ftrace_config_muxer.h"
 #include "src/traced/probes/ftrace/ftrace_controller.h"
 #include "src/traced/probes/ftrace/ftrace_data_source.h"
+#include "src/traced/probes/ftrace/ftrace_print_filter.h"
 #include "src/traced/probes/ftrace/proto_translation_table.h"
 
 #include "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
@@ -622,6 +623,9 @@
           const CompactSchedWakingFormat& sched_waking_format =
               table->compact_sched_format().sched_waking;
 
+          bool ftrace_print_filter_enabled =
+              ds_config->print_filter.has_value();
+
           // compact sched_switch
           if (compact_sched_enabled &&
               ftrace_event_id == sched_switch_format.event_id) {
@@ -640,12 +644,21 @@
             ParseSchedWakingCompact(start, timestamp, &sched_waking_format,
                                     compact_sched_buffer, metadata);
 
+          } else if (ftrace_print_filter_enabled &&
+                     ftrace_event_id == ds_config->print_filter->event_id()) {
+            if (ds_config->print_filter->IsEventInteresting(start, next)) {
+              protos::pbzero::FtraceEvent* event = bundle->add_event();
+              event->set_timestamp(timestamp);
+              if (!ParseEvent(ftrace_event_id, start, next, table, ds_config,
+                              event, metadata))
+                return 0;
+            }
           } else {
             // Common case: parse all other types of enabled events.
             protos::pbzero::FtraceEvent* event = bundle->add_event();
             event->set_timestamp(timestamp);
-            if (!ParseEvent(ftrace_event_id, start, next, table, event,
-                            metadata))
+            if (!ParseEvent(ftrace_event_id, start, next, table, ds_config,
+                            event, metadata))
               return 0;
           }
         }
@@ -664,6 +677,7 @@
                            const uint8_t* start,
                            const uint8_t* end,
                            const ProtoTranslationTable* table,
+                           const FtraceDataSourceConfig* ds_config,
                            protozero::Message* message,
                            FtraceMetadata* metadata) {
   PERFETTO_DCHECK(start < end);
@@ -697,6 +711,14 @@
                                   field.ftrace_name);
       success &= ParseField(field, start, end, table, generic_field, metadata);
     }
+  } else if (PERFETTO_UNLIKELY(
+                 info.proto_field_id ==
+                 protos::pbzero::FtraceEvent::kSysEnterFieldNumber)) {
+    success &= ParseSysEnter(info, start, end, nested, metadata);
+  } else if (PERFETTO_UNLIKELY(
+                 info.proto_field_id ==
+                 protos::pbzero::FtraceEvent::kSysExitFieldNumber)) {
+    success &= ParseSysExit(info, start, end, ds_config, nested, metadata);
   } else {  // Parse all other events.
     for (const Field& field : info.fields) {
       success &= ParseField(field, start, end, table, nested, metadata);
@@ -823,6 +845,97 @@
   PERFETTO_FATAL("Unexpected translation strategy");
 }
 
+bool CpuReader::ParseSysEnter(const Event& info,
+                              const uint8_t* start,
+                              const uint8_t* end,
+                              protozero::Message* message,
+                              FtraceMetadata* /* metadata */) {
+  if (info.fields.size() != 2) {
+    PERFETTO_DLOG("Unexpected number of fields for sys_enter");
+    return false;
+  }
+  const auto& id_field = info.fields[0];
+  const auto& args_field = info.fields[1];
+  if (start + id_field.ftrace_size + args_field.ftrace_size > end) {
+    return false;
+  }
+  // field:long id;
+  if (id_field.ftrace_type != kFtraceInt32 &&
+      id_field.ftrace_type != kFtraceInt64) {
+    return false;
+  }
+  const int64_t syscall_id = ReadSignedFtraceValue(
+      start + id_field.ftrace_offset, id_field.ftrace_type);
+  message->AppendVarInt(id_field.proto_field_id, syscall_id);
+  // field:unsigned long args[6];
+  // proto_translation_table will only allow exactly 6-element array, so we can
+  // make the same hard assumption here.
+  constexpr uint16_t arg_count = 6;
+  size_t element_size = 0;
+  if (args_field.ftrace_type == kFtraceUint32) {
+    element_size = 4u;
+  } else if (args_field.ftrace_type == kFtraceUint64) {
+    element_size = 8u;
+  } else {
+    return false;
+  }
+  for (uint16_t i = 0; i < arg_count; ++i) {
+    const uint8_t* element_ptr =
+        start + args_field.ftrace_offset + i * element_size;
+    uint64_t arg_value = 0;
+    if (element_size == 8) {
+      arg_value = ReadValue<uint64_t>(element_ptr);
+    } else {
+      arg_value = ReadValue<uint32_t>(element_ptr);
+    }
+    message->AppendVarInt(args_field.proto_field_id, arg_value);
+  }
+  return true;
+}
+
+bool CpuReader::ParseSysExit(const Event& info,
+                             const uint8_t* start,
+                             const uint8_t* end,
+                             const FtraceDataSourceConfig* ds_config,
+                             protozero::Message* message,
+                             FtraceMetadata* metadata) {
+  if (info.fields.size() != 2) {
+    PERFETTO_DLOG("Unexpected number of fields for sys_exit");
+    return false;
+  }
+  const auto& id_field = info.fields[0];
+  const auto& ret_field = info.fields[1];
+  if (start + id_field.ftrace_size + ret_field.ftrace_size > end) {
+    return false;
+  }
+  //    field:long id;
+  if (id_field.ftrace_type != kFtraceInt32 &&
+      id_field.ftrace_type != kFtraceInt64) {
+    return false;
+  }
+  const int64_t syscall_id = ReadSignedFtraceValue(
+      start + id_field.ftrace_offset, id_field.ftrace_type);
+  message->AppendVarInt(id_field.proto_field_id, syscall_id);
+  //    field:long ret;
+  if (ret_field.ftrace_type != kFtraceInt32 &&
+      ret_field.ftrace_type != kFtraceInt64) {
+    return false;
+  }
+  const int64_t syscall_ret = ReadSignedFtraceValue(
+      start + ret_field.ftrace_offset, ret_field.ftrace_type);
+  message->AppendVarInt(ret_field.proto_field_id, syscall_ret);
+  // for any syscalls which return a new filedescriptor
+  // we mark the fd as potential candidate for scraping
+  // if the call succeeded and is within fd bounds
+  if (ds_config->syscalls_returning_fd.count(syscall_id) && syscall_ret >= 0 &&
+      syscall_ret <= std::numeric_limits<int>::max()) {
+    const auto pid = metadata->last_seen_common_pid;
+    const auto syscall_ret_u = static_cast<uint64_t>(syscall_ret);
+    metadata->fds.insert(std::make_pair(pid, syscall_ret_u));
+  }
+  return true;
+}
+
 // Parse a sched_switch event according to pre-validated format, and buffer the
 // individual fields in the current compact batch. See the code populating
 // |CompactSchedSwitchFormat| for the assumptions made around the format, which
diff --git a/src/traced/probes/ftrace/cpu_reader.h b/src/traced/probes/ftrace/cpu_reader.h
index 414da31..e0953d6 100644
--- a/src/traced/probes/ftrace/cpu_reader.h
+++ b/src/traced/probes/ftrace/cpu_reader.h
@@ -205,6 +205,7 @@
                          const uint8_t* start,
                          const uint8_t* end,
                          const ProtoTranslationTable* table,
+                         const FtraceDataSourceConfig* ds_config,
                          protozero::Message* message,
                          FtraceMetadata* metadata);
 
@@ -215,6 +216,21 @@
                          protozero::Message* message,
                          FtraceMetadata* metadata);
 
+  // Parse a sys_enter event according to the pre-validated expected format
+  static bool ParseSysEnter(const Event& info,
+                            const uint8_t* start,
+                            const uint8_t* end,
+                            protozero::Message* message,
+                            FtraceMetadata* metadata);
+
+  // Parse a sys_exit event according to the pre-validated expected format
+  static bool ParseSysExit(const Event& info,
+                           const uint8_t* start,
+                           const uint8_t* end,
+                           const FtraceDataSourceConfig* ds_config,
+                           protozero::Message* message,
+                           FtraceMetadata* metadata);
+
   // Parse a sched_switch event according to pre-validated format, and buffer
   // the individual fields in the given compact encoding batch.
   static void ParseSchedSwitchCompact(const uint8_t* start,
diff --git a/src/traced/probes/ftrace/cpu_reader_benchmark.cc b/src/traced/probes/ftrace/cpu_reader_benchmark.cc
index f6f7038..a029bc7 100644
--- a/src/traced/probes/ftrace/cpu_reader_benchmark.cc
+++ b/src/traced/probes/ftrace/cpu_reader_benchmark.cc
@@ -21,12 +21,19 @@
 #include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
 #include "src/traced/probes/ftrace/cpu_reader.h"
 #include "src/traced/probes/ftrace/ftrace_config_muxer.h"
+#include "src/traced/probes/ftrace/ftrace_print_filter.h"
 #include "src/traced/probes/ftrace/proto_translation_table.h"
 #include "src/traced/probes/ftrace/test/cpu_reader_support.h"
 
+namespace perfetto {
 namespace {
 
-perfetto::ExamplePage g_full_page_sched_switch{
+using ::perfetto::protos::pbzero::FtraceEventBundle;
+using ::protozero::ScatteredStreamWriter;
+using ::protozero::ScatteredStreamWriterNullDelegate;
+
+// A page full of "sched/sched_switch" events.
+ExamplePage g_full_page_sched_switch{
     "synthetic",
     R"(
 00000000: 31f2 7622 1a00 0000 b40f 0000 0000 0000  1.v"............
@@ -288,41 +295,561 @@
     )",
 };
 
-}  // namespace
+// A page full of "ftrace/print" events.
+ExamplePage g_full_page_print{
+    "synthetic",
+    R"(
+00000000: df34 b6e2 e2a9 0000 e00f 0000 0000 0000  .4..............
+00000010: 0700 0000 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000020: ffff ffff 3333 3333 0a00 0000 f348 0700  ....3333.....H..
+00000030: 47d0 0900 0500 0000 4f56 0700 84fc 9a9f  G.......OV......
+00000040: ffff ffff 3434 3434 0a00 722f 7531 3435  ....4444..r/u145
+00000050: 07ab 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000060: ffff ffff 3535 3535 0a00 0700 6b77 6f72  ....5555....kwor
+00000070: c7b0 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000080: ffff ffff 3636 3636 0a00 0100 4001 0103  ....6666....@...
+00000090: a7b8 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+000000a0: ffff ffff 3737 3737 0a00 0000 0100 0000  ....7777........
+000000b0: 47ac 0900 0500 0000 4f56 0700 84fc 9a9f  G.......OV......
+000000c0: ffff ffff 3838 3838 0a00 0000 f348 0700  ....8888.....H..
+000000d0: 47d9 0900 0500 0000 4f56 0700 84fc 9a9f  G.......OV......
+000000e0: ffff ffff 3939 3939 0a00 722f 7531 3435  ....9999..r/u145
+000000f0: c7b9 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000100: ffff ffff 4141 4141 0a00 0700 6b77 6f72  ....AAAA....kwor
+00000110: c7b6 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000120: ffff ffff 4242 4242 0a00 0100 4001 0103  ....BBBB....@...
+00000130: c7a6 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000140: ffff ffff 4343 4343 0a00 0000 0100 0000  ....CCCC........
+00000150: c7ab 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000160: ffff ffff 4444 4444 0a00 0000 f348 0700  ....DDDD.....H..
+00000170: 27b3 0900 0500 0000 4f56 0700 84fc 9a9f  '.......OV......
+00000180: ffff ffff 4545 4545 0a00 722f 7531 3435  ....EEEE..r/u145
+00000190: 27b0 0900 0500 0000 4f56 0700 84fc 9a9f  '.......OV......
+000001a0: ffff ffff 4646 4646 0a00 0700 6b77 6f72  ....FFFF....kwor
+000001b0: c778 0c00 0500 0000 4f56 0700 84fc 9a9f  .x......OV......
+000001c0: ffff ffff 3030 3030 0a00 0100 4001 0103  ....0000....@...
+000001d0: 27b8 0900 0500 0000 4f56 0700 84fc 9a9f  '.......OV......
+000001e0: ffff ffff 3131 3131 0a00 0000 0100 0000  ....1111........
+000001f0: 27af 0900 0500 0000 4f56 0700 84fc 9a9f  '.......OV......
+00000200: ffff ffff 3232 3232 0a00 0000 f348 0700  ....2222.....H..
+00000210: 27a5 0900 0500 0000 4f56 0700 84fc 9a9f  '.......OV......
+00000220: ffff ffff 3333 3333 0a00 722f 7531 3435  ....3333..r/u145
+00000230: 07b7 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000240: ffff ffff 3434 3434 0a00 0700 6b77 6f72  ....4444....kwor
+00000250: 47ac 0900 0500 0000 4f56 0700 84fc 9a9f  G.......OV......
+00000260: ffff ffff 3535 3535 0a00 0100 4001 0103  ....5555....@...
+00000270: 27ad 0900 0500 0000 4f56 0700 84fc 9a9f  '.......OV......
+00000280: ffff ffff 3636 3636 0a00 0000 0100 0000  ....6666........
+00000290: e7b6 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+000002a0: ffff ffff 3737 3737 0a00 0000 f348 0700  ....7777.....H..
+000002b0: 47b1 0900 0500 0000 4f56 0700 84fc 9a9f  G.......OV......
+000002c0: ffff ffff 3838 3838 0a00 722f 7531 3435  ....8888..r/u145
+000002d0: a7a7 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+000002e0: ffff ffff 3939 3939 0a00 0700 6b77 6f72  ....9999....kwor
+000002f0: 87a9 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000300: ffff ffff 4141 4141 0a00 0100 4001 0103  ....AAAA....@...
+00000310: 27cb 0900 0500 0000 4f56 0700 84fc 9a9f  '.......OV......
+00000320: ffff ffff 4242 4242 0a00 0000 0100 0000  ....BBBB........
+00000330: 47c0 0900 0500 0000 4f56 0700 84fc 9a9f  G.......OV......
+00000340: ffff ffff 4343 4343 0a00 0000 f348 0700  ....CCCC.....H..
+00000350: 67ae 0900 0500 0000 4f56 0700 84fc 9a9f  g.......OV......
+00000360: ffff ffff 4444 4444 0a00 722f 7531 3435  ....DDDD..r/u145
+00000370: 47b0 0900 0500 0000 4f56 0700 84fc 9a9f  G.......OV......
+00000380: ffff ffff 4545 4545 0a00 0700 6b77 6f72  ....EEEE....kwor
+00000390: 87a6 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+000003a0: ffff ffff 4646 4646 0a00 0100 4001 0103  ....FFFF....@...
+000003b0: 07a2 0c00 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+000003c0: ffff ffff 3030 3030 0a00 0000 0100 0000  ....0000........
+000003d0: e71f 0a00 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+000003e0: ffff ffff 3131 3131 0a00 0000 f348 0700  ....1111.....H..
+000003f0: 67c6 0900 0500 0000 4f56 0700 84fc 9a9f  g.......OV......
+00000400: ffff ffff 3232 3232 0a00 722f 7531 3435  ....2222..r/u145
+00000410: 67b2 0900 0500 0000 4f56 0700 84fc 9a9f  g.......OV......
+00000420: ffff ffff 3333 3333 0a00 0700 6b77 6f72  ....3333....kwor
+00000430: 27af 0900 0500 0000 4f56 0700 84fc 9a9f  '.......OV......
+00000440: ffff ffff 3434 3434 0a00 0100 4001 0103  ....4444....@...
+00000450: 07d8 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000460: ffff ffff 3535 3535 0a00 0000 0100 0000  ....5555........
+00000470: 87ba 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000480: ffff ffff 3636 3636 0a00 0000 f348 0700  ....6666.....H..
+00000490: 27c3 0900 0500 0000 4f56 0700 84fc 9a9f  '.......OV......
+000004a0: ffff ffff 3737 3737 0a00 722f 7531 3435  ....7777..r/u145
+000004b0: 27bb 0900 0500 0000 4f56 0700 84fc 9a9f  '.......OV......
+000004c0: ffff ffff 3838 3838 0a00 0700 6b77 6f72  ....8888....kwor
+000004d0: c7a6 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+000004e0: ffff ffff 3939 3939 0a00 0100 4001 0103  ....9999....@...
+000004f0: e7ac 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000500: ffff ffff 4141 4141 0a00 0000 0100 0000  ....AAAA........
+00000510: e7a5 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000520: ffff ffff 4242 4242 0a00 0000 f348 0700  ....BBBB.....H..
+00000530: c7ab 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000540: ffff ffff 4343 4343 0a00 722f 7531 3435  ....CCCC..r/u145
+00000550: 07aa 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000560: ffff ffff 4444 4444 0a00 0700 6b77 6f72  ....DDDD....kwor
+00000570: 27b1 0900 0500 0000 4f56 0700 84fc 9a9f  '.......OV......
+00000580: ffff ffff 4545 4545 0a00 0100 4001 0103  ....EEEE....@...
+00000590: e7ae 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+000005a0: ffff ffff 4646 4646 0a00 0000 0100 0000  ....FFFF........
+000005b0: 876e 0c00 0500 0000 4f56 0700 84fc 9a9f  .n......OV......
+000005c0: ffff ffff 3030 3030 0a00 0000 f348 0700  ....0000.....H..
+000005d0: 87b0 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+000005e0: ffff ffff 3131 3131 0a00 722f 7531 3435  ....1111..r/u145
+000005f0: a7de 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000600: ffff ffff 3232 3232 0a00 0700 6b77 6f72  ....2222....kwor
+00000610: c7bf 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000620: ffff ffff 3333 3333 0a00 0100 4001 0103  ....3333....@...
+00000630: e7ad 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000640: ffff ffff 3434 3434 0a00 0000 2e00 0000  ....4444........
+00000650: 67c2 0900 0500 0000 4f56 0700 84fc 9a9f  g.......OV......
+00000660: ffff ffff 3535 3535 0a00 0000 f348 0700  ....5555.....H..
+00000670: 87ec 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000680: ffff ffff 3636 3636 0a00 722f 7531 3435  ....6666..r/u145
+00000690: e7b9 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+000006a0: ffff ffff 3737 3737 0a00 0700 6b77 6f72  ....7777....kwor
+000006b0: e7aa 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+000006c0: ffff ffff 3838 3838 0a00 0100 4001 0103  ....8888....@...
+000006d0: 07a9 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+000006e0: ffff ffff 3939 3939 0a00 0000 0100 0000  ....9999........
+000006f0: 87a6 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000700: ffff ffff 4141 4141 0a00 0000 f348 0700  ....AAAA.....H..
+00000710: c7a6 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000720: ffff ffff 4242 4242 0a00 722f 7531 3435  ....BBBB..r/u145
+00000730: 67d0 0900 0500 0000 4f56 0700 84fc 9a9f  g.......OV......
+00000740: ffff ffff 4343 4343 0a00 0700 6b77 6f72  ....CCCC....kwor
+00000750: 27b6 0900 0500 0000 4f56 0700 84fc 9a9f  '.......OV......
+00000760: ffff ffff 4444 4444 0a00 0100 4001 0103  ....DDDD....@...
+00000770: e7ba 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000780: ffff ffff 4545 4545 0a00 0000 0100 0000  ....EEEE........
+00000790: 07ce 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+000007a0: ffff ffff 4646 4646 0a00 0000 f348 0700  ....FFFF.....H..
+000007b0: c768 0c00 0500 0000 4f56 0700 84fc 9a9f  .h......OV......
+000007c0: ffff ffff 3030 3030 0a00 722f 7531 3435  ....0000..r/u145
+000007d0: 67de 0900 0500 0000 4f56 0700 84fc 9a9f  g.......OV......
+000007e0: ffff ffff 3131 3131 0a00 0700 6b77 6f72  ....1111....kwor
+000007f0: 27b9 0900 0500 0000 4f56 0700 84fc 9a9f  '.......OV......
+00000800: ffff ffff 3232 3232 0a00 0100 4001 0103  ....2222....@...
+00000810: c7b2 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000820: ffff ffff 3333 3333 0a00 0000 0100 0000  ....3333........
+00000830: 67b1 0900 0500 0000 4f56 0700 84fc 9a9f  g.......OV......
+00000840: ffff ffff 3434 3434 0a00 0000 f348 0700  ....4444.....H..
+00000850: c7b2 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000860: ffff ffff 3535 3535 0a00 722f 7531 3435  ....5555..r/u145
+00000870: c7c1 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000880: ffff ffff 3636 3636 0a00 0700 6b77 6f72  ....6666....kwor
+00000890: 87ac 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+000008a0: ffff ffff 3737 3737 0a00 0100 4001 0103  ....7777....@...
+000008b0: c7b2 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+000008c0: ffff ffff 3838 3838 0a00 0000 0100 0000  ....8888........
+000008d0: 879e 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+000008e0: ffff ffff 3939 3939 0a00 0000 f348 0700  ....9999.....H..
+000008f0: 67ae 0900 0500 0000 4f56 0700 84fc 9a9f  g.......OV......
+00000900: ffff ffff 4141 4141 0a00 722f 7531 3435  ....AAAA..r/u145
+00000910: 07a6 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000920: ffff ffff 4242 4242 0a00 0700 6b77 6f72  ....BBBB....kwor
+00000930: a7a2 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000940: ffff ffff 4343 4343 0a00 0100 4001 0103  ....CCCC....@...
+00000950: 67a4 0900 0500 0000 4f56 0700 84fc 9a9f  g.......OV......
+00000960: ffff ffff 4444 4444 0a00 0000 0100 0000  ....DDDD........
+00000970: 87a5 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000980: ffff ffff 4545 4545 0a00 0000 5417 0700  ....EEEE....T...
+00000990: 47b1 0900 0500 0000 4f56 0700 84fc 9a9f  G.......OV......
+000009a0: ffff ffff 4646 4646 0a00 722f 7531 3435  ....FFFF..r/u145
+000009b0: e75e 0c00 0500 0000 4f56 0700 84fc 9a9f  .^......OV......
+000009c0: ffff ffff 3030 3030 0a00 0700 6b77 6f72  ....0000....kwor
+000009d0: a7bc 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+000009e0: ffff ffff 3131 3131 0a00 0100 4001 0103  ....1111....@...
+000009f0: c7a8 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000a00: ffff ffff 3232 3232 0a00 0000 2e00 0000  ....2222........
+00000a10: c7af 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000a20: ffff ffff 3333 3333 0a00 0000 5417 0700  ....3333....T...
+00000a30: 67aa 0900 0500 0000 4f56 0700 84fc 9a9f  g.......OV......
+00000a40: ffff ffff 3434 3434 0a00 722f 7531 3435  ....4444..r/u145
+00000a50: 07bd 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000a60: ffff ffff 3535 3535 0a00 0700 6b77 6f72  ....5555....kwor
+00000a70: 67b2 0900 0500 0000 4f56 0700 84fc 9a9f  g.......OV......
+00000a80: ffff ffff 3636 3636 0a00 0100 4001 0103  ....6666....@...
+00000a90: 27b2 0900 0500 0000 4f56 0700 84fc 9a9f  '.......OV......
+00000aa0: ffff ffff 3737 3737 0a00 0000 2e00 0000  ....7777........
+00000ab0: a731 0a00 0500 0000 4f56 0700 84fc 9a9f  .1......OV......
+00000ac0: ffff ffff 3838 3838 0a00 0000 5417 0700  ....8888....T...
+00000ad0: e7bd 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000ae0: ffff ffff 3939 3939 0a00 722f 7531 3435  ....9999..r/u145
+00000af0: c7ad 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000b00: ffff ffff 4141 4141 0a00 0700 6b77 6f72  ....AAAA....kwor
+00000b10: 47af 0900 0500 0000 4f56 0700 84fc 9a9f  G.......OV......
+00000b20: ffff ffff 4242 4242 0a00 0100 4001 0103  ....BBBB....@...
+00000b30: c7d2 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000b40: ffff ffff 4343 4343 0a00 0000 2e00 0000  ....CCCC........
+00000b50: 67a6 0900 0500 0000 4f56 0700 84fc 9a9f  g.......OV......
+00000b60: ffff ffff 4444 4444 0a00 0000 5417 0700  ....DDDD....T...
+00000b70: 47aa 0900 0500 0000 4f56 0700 84fc 9a9f  G.......OV......
+00000b80: ffff ffff 4545 4545 0a00 722f 7531 3435  ....EEEE..r/u145
+00000b90: 07b6 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000ba0: ffff ffff 4646 4646 0a00 0700 6b77 6f72  ....FFFF....kwor
+00000bb0: 876b 0c00 0500 0000 4f56 0700 84fc 9a9f  .k......OV......
+00000bc0: ffff ffff 3030 3030 0a00 0100 4001 0103  ....0000....@...
+00000bd0: 47bc 0900 0500 0000 4f56 0700 84fc 9a9f  G.......OV......
+00000be0: ffff ffff 3131 3131 0a00 0000 2e00 0000  ....1111........
+00000bf0: a7b1 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000c00: ffff ffff 3232 3232 0a00 0000 5417 0700  ....2222....T...
+00000c10: a7bb 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000c20: ffff ffff 3333 3333 0a00 722f 7531 3435  ....3333..r/u145
+00000c30: 47b4 0900 0500 0000 4f56 0700 84fc 9a9f  G.......OV......
+00000c40: ffff ffff 3434 3434 0a00 0700 6b77 6f72  ....4444....kwor
+00000c50: 0710 0a00 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000c60: ffff ffff 3535 3535 0a00 0100 4001 0103  ....5555....@...
+00000c70: 27cb 0900 0500 0000 4f56 0700 84fc 9a9f  '.......OV......
+00000c80: ffff ffff 3636 3636 0a00 0000 2e00 0000  ....6666........
+00000c90: c7a8 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000ca0: ffff ffff 3737 3737 0a00 0000 5417 0700  ....7777....T...
+00000cb0: 47c6 0900 0500 0000 4f56 0700 84fc 9a9f  G.......OV......
+00000cc0: ffff ffff 3838 3838 0a00 722f 7531 3435  ....8888..r/u145
+00000cd0: 07c1 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000ce0: ffff ffff 3939 3939 0a00 0700 6b77 6f72  ....9999....kwor
+00000cf0: 47b3 0900 0500 0000 4f56 0700 84fc 9a9f  G.......OV......
+00000d00: ffff ffff 4141 4141 0a00 0300 4001 0103  ....AAAA....@...
+00000d10: a7ad 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000d20: ffff ffff 4242 4242 0a00 0000 2e00 0000  ....BBBB........
+00000d30: 87b3 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000d40: ffff ffff 4343 4343 0a00 0000 5417 0700  ....CCCC....T...
+00000d50: 07b8 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000d60: ffff ffff 4444 4444 0a00 722f 7531 3435  ....DDDD..r/u145
+00000d70: 07b6 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000d80: ffff ffff 4545 4545 0a00 0700 6b77 6f72  ....EEEE....kwor
+00000d90: 27b4 0900 0500 0000 4f56 0700 84fc 9a9f  '.......OV......
+00000da0: ffff ffff 4646 4646 0a00 0100 4001 0103  ....FFFF....@...
+00000db0: 2761 0c00 0500 0000 4f56 0700 84fc 9a9f  'a......OV......
+00000dc0: ffff ffff 3030 3030 0a00 0000 2e00 0000  ....0000........
+00000dd0: 87b6 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000de0: ffff ffff 3131 3131 0a00 0000 5417 0700  ....1111....T...
+00000df0: 87ae 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000e00: ffff ffff 3232 3232 0a00 722f 7531 3435  ....2222..r/u145
+00000e10: c7ac 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000e20: ffff ffff 3333 3333 0a00 0700 6b77 6f72  ....3333....kwor
+00000e30: 27c0 0900 0500 0000 4f56 0700 84fc 9a9f  '.......OV......
+00000e40: ffff ffff 3434 3434 0a00 0100 4001 0103  ....4444....@...
+00000e50: 07b2 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000e60: ffff ffff 3535 3535 0a00 0000 2e00 0000  ....5555........
+00000e70: 67ae 0900 0500 0000 4f56 0700 84fc 9a9f  g.......OV......
+00000e80: ffff ffff 3636 3636 0a00 0000 5417 0700  ....6666....T...
+00000e90: a7af 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000ea0: ffff ffff 3737 3737 0a00 722f 7531 3435  ....7777..r/u145
+00000eb0: e7aa 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000ec0: ffff ffff 3838 3838 0a00 0700 6b77 6f72  ....8888....kwor
+00000ed0: 07ae 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000ee0: ffff ffff 3939 3939 0a00 0100 4001 0103  ....9999....@...
+00000ef0: 87af 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000f00: ffff ffff 4141 4141 0a00 0000 2e00 0000  ....AAAA........
+00000f10: 07ae 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000f20: ffff ffff 4242 4242 0a00 0000 5417 0700  ....BBBB....T...
+00000f30: 47a0 0900 0500 0000 4f56 0700 84fc 9a9f  G.......OV......
+00000f40: ffff ffff 4343 4343 0a00 722f 7531 3435  ....CCCC..r/u145
+00000f50: e7d5 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000f60: ffff ffff 4444 4444 0a00 0700 6b77 6f72  ....DDDD....kwor
+00000f70: 87bc 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000f80: ffff ffff 4545 4545 0a00 0100 4001 0103  ....EEEE....@...
+00000f90: e7c3 0900 0500 0000 4f56 0700 84fc 9a9f  ........OV......
+00000fa0: ffff ffff 4646 4646 0a00 0000 2e00 0000  ....FFFF........
+00000fb0: 475a 0c00 0500 0000 4f56 0700 84fc 9a9f  GZ......OV......
+00000fc0: ffff ffff 3030 3030 0a00 0000 5417 0700  ....0000....T...
+00000fd0: 47bc 0900 0500 0000 4f56 0700 84fc 9a9f  G.......OV......
+00000fe0: ffff ffff 3131 3131 0a00 722f 7531 3435  ....1111..r/u145
+00000ff0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
+    )",
+};
 
-using perfetto::CompactSchedBuffer;
-using perfetto::CpuReader;
-using perfetto::DisabledCompactSchedConfigForTesting;
-using perfetto::EventFilter;
-using perfetto::ExamplePage;
-using perfetto::FtraceDataSourceConfig;
-using perfetto::FtraceMetadata;
-using perfetto::GetTable;
-using perfetto::GroupAndName;
-using perfetto::PageFromXxd;
-using perfetto::ProtoTranslationTable;
-using perfetto::protos::pbzero::FtraceEventBundle;
-using protozero::ScatteredStreamWriter;
-using protozero::ScatteredStreamWriterNullDelegate;
+// A page full of "ftrace/print" atrace "C|pid|0000" events
+ExamplePage g_full_page_atrace_print{
+    "synthetic",
+    R"(
+00000000: 5c07 2181 3e14 0800 e40f 00c0 ffff ffff  \.!.>...........
+00000010: 0800 0000 0500 0000 39ec 1700 042e 7b99  ........9.....{.
+00000020: ffff ffff 437c 3131 317c 3333 3333 0a00  ....C|111|3333..
+00000030: 0000 0000 8836 0c00 0500 0000 39ec 1700  .....6......9...
+00000040: 042e 7b99 ffff ffff 437c 3131 317c 3434  ..{.....C|111|44
+00000050: 3434 0a00 0000 0000 c84e 0c00 0500 0000  44.......N......
+00000060: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+00000070: 317c 3535 3535 0a00 0000 0000 4847 0c00  1|5555......HG..
+00000080: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+00000090: 437c 3131 317c 3636 3636 0a00 0000 0000  C|111|6666......
+000000a0: 4835 0c00 0500 0000 39ec 1700 042e 7b99  H5......9.....{.
+000000b0: ffff ffff 437c 3131 317c 3737 3737 0a00  ....C|111|7777..
+000000c0: 0000 0000 8830 0c00 0500 0000 39ec 1700  .....0......9...
+000000d0: 042e 7b99 ffff ffff 437c 3131 317c 3838  ..{.....C|111|88
+000000e0: 3838 0a00 0000 0000 683d 0c00 0500 0000  88......h=......
+000000f0: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+00000100: 317c 3939 3939 0a00 0000 0000 a84a 0e00  1|9999.......J..
+00000110: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+00000120: 437c 3131 317c 4141 4141 0a00 0000 0000  C|111|AAAA......
+00000130: c861 0c00 0500 0000 39ec 1700 042e 7b99  .a......9.....{.
+00000140: ffff ffff 437c 3131 317c 4242 4242 0a00  ....C|111|BBBB..
+00000150: 0000 0000 2837 0c00 0500 0000 39ec 1700  ....(7......9...
+00000160: 042e 7b99 ffff ffff 437c 3131 317c 4343  ..{.....C|111|CC
+00000170: 4343 0a00 0000 0000 283b 0c00 0500 0000  CC......(;......
+00000180: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+00000190: 317c 4444 4444 0a00 0000 0000 2838 0c00  1|DDDD......(8..
+000001a0: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+000001b0: 437c 3131 317c 4545 4545 0a00 0000 0000  C|111|EEEE......
+000001c0: a830 0c00 0500 0000 39ec 1700 042e 7b99  .0......9.....{.
+000001d0: ffff ffff 437c 3131 317c 4646 4646 0a00  ....C|111|FFFF..
+000001e0: 0000 0000 08f7 1200 0500 0000 39ec 1700  ............9...
+000001f0: 042e 7b99 ffff ffff 437c 3131 317c 3030  ..{.....C|111|00
+00000200: 3030 0a00 0000 0000 e83f 0c00 0500 0000  00.......?......
+00000210: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+00000220: 317c 3131 3131 0a00 0000 0000 6845 0c00  1|1111......hE..
+00000230: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+00000240: 437c 3131 317c 3232 3232 0a00 0000 0000  C|111|2222......
+00000250: 48c6 0c00 0500 0000 39ec 1700 042e 7b99  H.......9.....{.
+00000260: ffff ffff 437c 3131 317c 3333 3333 0a00  ....C|111|3333..
+00000270: 0000 0000 4843 0c00 0500 0000 39ec 1700  ....HC......9...
+00000280: 042e 7b99 ffff ffff 437c 3131 317c 3434  ..{.....C|111|44
+00000290: 3434 0a00 0000 0000 6899 0c00 0500 0000  44......h.......
+000002a0: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+000002b0: 317c 3535 3535 0a00 0000 0000 2841 0c00  1|5555......(A..
+000002c0: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+000002d0: 437c 3131 317c 3636 3636 0a00 0000 0000  C|111|6666......
+000002e0: c83a 0c00 0500 0000 39ec 1700 042e 7b99  .:......9.....{.
+000002f0: ffff ffff 437c 3131 317c 3737 3737 0a00  ....C|111|7777..
+00000300: 0000 0000 c836 0c00 0500 0000 39ec 1700  .....6......9...
+00000310: 042e 7b99 ffff ffff 437c 3131 317c 3838  ..{.....C|111|88
+00000320: 3838 0a00 0000 0000 e82a 0c00 0500 0000  88.......*......
+00000330: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+00000340: 317c 3939 3939 0a00 0000 0000 0834 0c00  1|9999.......4..
+00000350: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+00000360: 437c 3131 317c 4141 4141 0a00 0000 0000  C|111|AAAA......
+00000370: e883 0c00 0500 0000 39ec 1700 042e 7b99  ........9.....{.
+00000380: ffff ffff 437c 3131 317c 4242 4242 0a00  ....C|111|BBBB..
+00000390: 0000 0000 684f 0c00 0500 0000 39ec 1700  ....hO......9...
+000003a0: 042e 7b99 ffff ffff 437c 3131 317c 4343  ..{.....C|111|CC
+000003b0: 4343 0a00 0000 0000 c83a 0c00 0500 0000  CC.......:......
+000003c0: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+000003d0: 317c 4444 4444 0a00 0000 0000 8836 0c00  1|DDDD.......6..
+000003e0: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+000003f0: 437c 3131 317c 4545 4545 0a00 0000 0000  C|111|EEEE......
+00000400: c826 0c00 0500 0000 39ec 1700 042e 7b99  .&......9.....{.
+00000410: ffff ffff 437c 3131 317c 4646 4646 0a00  ....C|111|FFFF..
+00000420: 0000 0000 68ee 1200 0500 0000 39ec 1700  ....h.......9...
+00000430: 042e 7b99 ffff ffff 437c 3131 317c 3030  ..{.....C|111|00
+00000440: 3030 0a00 0000 0000 4843 0c00 0500 0000  00......HC......
+00000450: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+00000460: 317c 3131 3131 0a00 0000 0000 883f 0c00  1|1111.......?..
+00000470: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+00000480: 437c 3131 317c 3232 3232 0a00 0000 0000  C|111|2222......
+00000490: a824 0c00 0500 0000 39ec 1700 042e 7b99  .$......9.....{.
+000004a0: ffff ffff 437c 3131 317c 3333 3333 0a00  ....C|111|3333..
+000004b0: 0000 0000 e888 0d00 0500 0000 39ec 1700  ............9...
+000004c0: 042e 7b99 ffff ffff 437c 3131 317c 3434  ..{.....C|111|44
+000004d0: 3434 0a00 0000 0000 4853 0c00 0500 0000  44......HS......
+000004e0: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+000004f0: 317c 3535 3535 0a00 0000 0000 683f 0c00  1|5555......h?..
+00000500: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+00000510: 437c 3131 317c 3636 3636 0a00 0000 0000  C|111|6666......
+00000520: 2839 0c00 0500 0000 39ec 1700 042e 7b99  (9......9.....{.
+00000530: ffff ffff 437c 3131 317c 3737 3737 0a00  ....C|111|7777..
+00000540: 0000 0000 4869 0c00 0500 0000 39ec 1700  ....Hi......9...
+00000550: 042e 7b99 ffff ffff 437c 3131 317c 3838  ..{.....C|111|88
+00000560: 3838 0a00 0000 0000 e861 0c00 0500 0000  88.......a......
+00000570: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+00000580: 317c 3939 3939 0a00 0000 0000 4840 0c00  1|9999......H@..
+00000590: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+000005a0: 437c 3131 317c 4141 4141 0a00 0000 0000  C|111|AAAA......
+000005b0: a84c 0c00 0500 0000 39ec 1700 042e 7b99  .L......9.....{.
+000005c0: ffff ffff 437c 3131 317c 4242 4242 0a00  ....C|111|BBBB..
+000005d0: 0000 0000 2837 0c00 0500 0000 39ec 1700  ....(7......9...
+000005e0: 042e 7b99 ffff ffff 437c 3131 317c 4343  ..{.....C|111|CC
+000005f0: 4343 0a00 0000 0000 c831 0c00 0500 0000  CC.......1......
+00000600: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+00000610: 317c 4444 4444 0a00 0000 0000 a823 0c00  1|DDDD.......#..
+00000620: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+00000630: 437c 3131 317c 4545 4545 0a00 0000 0000  C|111|EEEE......
+00000640: 8824 0c00 0500 0000 39ec 1700 042e 7b99  .$......9.....{.
+00000650: ffff ffff 437c 3131 317c 4646 4646 0a00  ....C|111|FFFF..
+00000660: 0000 0000 6867 1500 0500 0000 39ec 1700  ....hg......9...
+00000670: 042e 7b99 ffff ffff 437c 3131 317c 3030  ..{.....C|111|00
+00000680: 3030 0a00 0000 0000 284e 0c00 0500 0000  00......(N......
+00000690: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+000006a0: 317c 3131 3131 0a00 0000 0000 8847 0c00  1|1111.......G..
+000006b0: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+000006c0: 437c 3131 317c 3232 3232 0a00 0000 0000  C|111|2222......
+000006d0: 6835 0c00 0500 0000 39ec 1700 042e 7b99  h5......9.....{.
+000006e0: ffff ffff 437c 3131 317c 3333 3333 0a00  ....C|111|3333..
+000006f0: 0000 0000 084e 0c00 0500 0000 39ec 1700  .....N......9...
+00000700: 042e 7b99 ffff ffff 437c 3131 317c 3434  ..{.....C|111|44
+00000710: 3434 0a00 0000 0000 c844 0c00 0500 0000  44.......D......
+00000720: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+00000730: 317c 3535 3535 0a00 0000 0000 6825 0c00  1|5555......h%..
+00000740: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+00000750: 437c 3131 317c 3636 3636 0a00 0000 0000  C|111|6666......
+00000760: 282e 0c00 0500 0000 39ec 1700 042e 7b99  (.......9.....{.
+00000770: ffff ffff 437c 3131 317c 3737 3737 0a00  ....C|111|7777..
+00000780: 0000 0000 8832 0c00 0500 0000 39ec 1700  .....2......9...
+00000790: 042e 7b99 ffff ffff 437c 3131 317c 3838  ..{.....C|111|88
+000007a0: 3838 0a00 0000 0000 0831 0c00 0500 0000  88.......1......
+000007b0: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+000007c0: 317c 3939 3939 0a00 0000 0000 e83b 0c00  1|9999.......;..
+000007d0: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+000007e0: 437c 3131 317c 4141 4141 0a00 0000 0000  C|111|AAAA......
+000007f0: e842 0c00 0500 0000 39ec 1700 042e 7b99  .B......9.....{.
+00000800: ffff ffff 437c 3131 317c 4242 4242 0a00  ....C|111|BBBB..
+00000810: 0000 0000 682d 0c00 0500 0000 39ec 1700  ....h-......9...
+00000820: 042e 7b99 ffff ffff 437c 3131 317c 4343  ..{.....C|111|CC
+00000830: 4343 0a00 0000 0000 4834 0c00 0500 0000  CC......H4......
+00000840: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+00000850: 317c 4444 4444 0a00 0000 0000 887f 0c00  1|DDDD..........
+00000860: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+00000870: 437c 3131 317c 4545 4545 0a00 0000 0000  C|111|EEEE......
+00000880: 0848 0c00 0500 0000 39ec 1700 042e 7b99  .H......9.....{.
+00000890: ffff ffff 437c 3131 317c 4646 4646 0a00  ....C|111|FFFF..
+000008a0: 0000 0000 680e 1300 0500 0000 39ec 1700  ....h.......9...
+000008b0: 042e 7b99 ffff ffff 437c 3131 317c 3030  ..{.....C|111|00
+000008c0: 3030 0a00 0000 0000 e847 0c00 0500 0000  00.......G......
+000008d0: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+000008e0: 317c 3131 3131 0a00 0000 0000 684f 0c00  1|1111......hO..
+000008f0: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+00000900: 437c 3131 317c 3232 3232 0a00 0000 0000  C|111|2222......
+00000910: 4844 0c00 0500 0000 39ec 1700 042e 7b99  HD......9.....{.
+00000920: ffff ffff 437c 3131 317c 3333 3333 0a00  ....C|111|3333..
+00000930: 0000 0000 e83e 0c00 0500 0000 39ec 1700  .....>......9...
+00000940: 042e 7b99 ffff ffff 437c 3131 317c 3434  ..{.....C|111|44
+00000950: 3434 0a00 0000 0000 882c 0c00 0500 0000  44.......,......
+00000960: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+00000970: 317c 3535 3535 0a00 0000 0000 4825 0c00  1|5555......H%..
+00000980: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+00000990: 437c 3131 317c 3636 3636 0a00 0000 0000  C|111|6666......
+000009a0: 0827 0c00 0500 0000 39ec 1700 042e 7b99  .'......9.....{.
+000009b0: ffff ffff 437c 3131 317c 3737 3737 0a00  ....C|111|7777..
+000009c0: 0000 0000 882f 0c00 0500 0000 39ec 1700  ...../......9...
+000009d0: 042e 7b99 ffff ffff 437c 3131 317c 3838  ..{.....C|111|88
+000009e0: 3838 0a00 0000 0000 e835 0c00 0500 0000  88.......5......
+000009f0: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+00000a00: 317c 3939 3939 0a00 0000 0000 48e1 0c00  1|9999......H...
+00000a10: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+00000a20: 437c 3131 317c 4141 4141 0a00 0000 0000  C|111|AAAA......
+00000a30: e85a 0c00 0500 0000 39ec 1700 042e 7b99  .Z......9.....{.
+00000a40: ffff ffff 437c 3131 317c 4242 4242 0a00  ....C|111|BBBB..
+00000a50: 0000 0000 2830 0c00 0500 0000 39ec 1700  ....(0......9...
+00000a60: 042e 7b99 ffff ffff 437c 3131 317c 4343  ..{.....C|111|CC
+00000a70: 4343 0a00 0000 0000 c833 0c00 0500 0000  CC.......3......
+00000a80: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+00000a90: 317c 4444 4444 0a00 0000 0000 c838 0c00  1|DDDD.......8..
+00000aa0: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+00000ab0: 437c 3131 317c 4545 4545 0a00 0000 0000  C|111|EEEE......
+00000ac0: c82a 0c00 0500 0000 39ec 1700 042e 7b99  .*......9.....{.
+00000ad0: ffff ffff 437c 3131 317c 4646 4646 0a00  ....C|111|FFFF..
+00000ae0: 0000 0000 08e8 1200 0500 0000 39ec 1700  ............9...
+00000af0: 042e 7b99 ffff ffff 437c 3131 317c 3030  ..{.....C|111|00
+00000b00: 3030 0a00 0000 0000 283b 0c00 0500 0000  00......(;......
+00000b10: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+00000b20: 317c 3131 3131 0a00 0000 0000 882d 0c00  1|1111.......-..
+00000b30: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+00000b40: 437c 3131 317c 3232 3232 0a00 0000 0000  C|111|2222......
+00000b50: 8842 0c00 0500 0000 39ec 1700 042e 7b99  .B......9.....{.
+00000b60: ffff ffff 437c 3131 317c 3333 3333 0a00  ....C|111|3333..
+00000b70: 0000 0000 e88e 0c00 0500 0000 39ec 1700  ............9...
+00000b80: 042e 7b99 ffff ffff 437c 3131 317c 3434  ..{.....C|111|44
+00000b90: 3434 0a00 0000 0000 c848 0c00 0500 0000  44.......H......
+00000ba0: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+00000bb0: 317c 3535 3535 0a00 0000 0000 2840 0e00  1|5555......(@..
+00000bc0: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+00000bd0: 437c 3131 317c 3636 3636 0a00 0000 0000  C|111|6666......
+00000be0: a878 0c00 0500 0000 39ec 1700 042e 7b99  .x......9.....{.
+00000bf0: ffff ffff 437c 3131 317c 3737 3737 0a00  ....C|111|7777..
+00000c00: 0000 0000 8837 0c00 0500 0000 39ec 1700  .....7......9...
+00000c10: 042e 7b99 ffff ffff 437c 3131 317c 3838  ..{.....C|111|88
+00000c20: 3838 0a00 0000 0000 482e 0c00 0500 0000  88......H.......
+00000c30: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+00000c40: 317c 3939 3939 0a00 0000 0000 6832 0c00  1|9999......h2..
+00000c50: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+00000c60: 437c 3131 317c 4141 4141 0a00 0000 0000  C|111|AAAA......
+00000c70: 483a 0c00 0500 0000 39ec 1700 042e 7b99  H:......9.....{.
+00000c80: ffff ffff 437c 3131 317c 4242 4242 0a00  ....C|111|BBBB..
+00000c90: 0000 0000 4830 0c00 0500 0000 39ec 1700  ....H0......9...
+00000ca0: 042e 7b99 ffff ffff 437c 3131 317c 4343  ..{.....C|111|CC
+00000cb0: 4343 0a00 0000 0000 282e 0c00 0500 0000  CC......(.......
+00000cc0: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+00000cd0: 317c 4444 4444 0a00 0000 0000 c81f 1c00  1|DDDD..........
+00000ce0: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+00000cf0: 437c 3131 317c 4545 4545 0a00 0000 0000  C|111|EEEE......
+00000d00: 6833 0c00 0500 0000 39ec 1700 042e 7b99  h3......9.....{.
+00000d10: ffff ffff 437c 3131 317c 4646 4646 0a00  ....C|111|FFFF..
+00000d20: 0000 0000 28ee 1200 0500 0000 39ec 1700  ....(.......9...
+00000d30: 042e 7b99 ffff ffff 437c 3131 317c 3030  ..{.....C|111|00
+00000d40: 3030 0a00 0000 0000 083e 0c00 0500 0000  00.......>......
+00000d50: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+00000d60: 317c 3131 3131 0a00 0000 0000 8848 0c00  1|1111.......H..
+00000d70: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+00000d80: 437c 3131 317c 3232 3232 0a00 0000 0000  C|111|2222......
+00000d90: 2832 0c00 0500 0000 39ec 1700 042e 7b99  (2......9.....{.
+00000da0: ffff ffff 437c 3131 317c 3333 3333 0a00  ....C|111|3333..
+00000db0: 0000 0000 e8b9 1400 0500 0000 39ec 1700  ............9...
+00000dc0: 042e 7b99 ffff ffff 437c 3131 317c 3434  ..{.....C|111|44
+00000dd0: 3434 0a00 0000 0000 28ff 0c00 0500 0000  44......(.......
+00000de0: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+00000df0: 317c 3535 3535 0a00 0000 0000 08f3 0c00  1|5555..........
+00000e00: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+00000e10: 437c 3131 317c 3636 3636 0a00 0000 0000  C|111|6666......
+00000e20: 88f0 0c00 0500 0000 39ec 1700 042e 7b99  ........9.....{.
+00000e30: ffff ffff 437c 3131 317c 3737 3737 0a00  ....C|111|7777..
+00000e40: 0000 0000 88e8 1300 0500 0000 39ec 1700  ............9...
+00000e50: 042e 7b99 ffff ffff 437c 3131 317c 3838  ..{.....C|111|88
+00000e60: 3838 0a00 0000 0000 e8c0 1600 0500 0000  88..............
+00000e70: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+00000e80: 317c 3939 3939 0a00 0000 0000 c878 0e00  1|9999.......x..
+00000e90: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+00000ea0: 437c 3131 317c 4141 4141 0a00 0000 0000  C|111|AAAA......
+00000eb0: 0889 0e00 0500 0000 39ec 1700 042e 7b99  ........9.....{.
+00000ec0: ffff ffff 437c 3131 317c 4242 4242 0a00  ....C|111|BBBB..
+00000ed0: 0000 0000 286f 0e00 0500 0000 39ec 1700  ....(o......9...
+00000ee0: 042e 7b99 ffff ffff 437c 3131 317c 4343  ..{.....C|111|CC
+00000ef0: 4343 0a00 0000 0000 4874 0e00 0500 0000  CC......Ht......
+00000f00: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+00000f10: 317c 4444 4444 0a00 0000 0000 8869 0e00  1|DDDD.......i..
+00000f20: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+00000f30: 437c 3131 317c 4545 4545 0a00 0000 0000  C|111|EEEE......
+00000f40: 8870 0e00 0500 0000 39ec 1700 042e 7b99  .p......9.....{.
+00000f50: ffff ffff 437c 3131 317c 4646 4646 0a00  ....C|111|FFFF..
+00000f60: 0000 0000 6892 1600 0500 0000 39ec 1700  ....h.......9...
+00000f70: 042e 7b99 ffff ffff 437c 3131 317c 3030  ..{.....C|111|00
+00000f80: 3030 0a00 0000 0000 e82e 0f00 0500 0000  00..............
+00000f90: 39ec 1700 042e 7b99 ffff ffff 437c 3131  9.....{.....C|11
+00000fa0: 317c 3131 3131 0a00 0000 0000 08ab 0e00  1|1111..........
+00000fb0: 0500 0000 39ec 1700 042e 7b99 ffff ffff  ....9.....{.....
+00000fc0: 437c 3131 317c 3232 3232 0a00 0000 0000  C|111|2222......
+00000fd0: 2885 0e00 0500 0000 39ec 1700 042e 7b99  (.......9.....{.
+00000fe0: ffff ffff 437c 3131 317c 3333 3333 0a00  ....C|111|3333..
+00000ff0: 0000 0000 5301 0000 0000 0000 0000 0000  ....S...........
+    )",
+};
 
-// Benchmark for the core logic of the ftrace binary format decoding.
-static void BM_ParsePageFullOfSchedSwitch(benchmark::State& state) {
-  const ExamplePage* test_case = &g_full_page_sched_switch;
-
-  ScatteredStreamWriterNullDelegate delegate(perfetto::base::kPageSize);
+void DoParse(const ExamplePage& test_case,
+             const std::vector<GroupAndName>& enabled_events,
+             base::Optional<FtraceConfig::PrintFilter> print_filter,
+             benchmark::State& state) {
+  ScatteredStreamWriterNullDelegate delegate(base::kPageSize);
   ScatteredStreamWriter stream(&delegate);
   protozero::RootMessage<FtraceEventBundle> writer;
 
-  ProtoTranslationTable* table = GetTable(test_case->name);
-  auto page = PageFromXxd(test_case->data);
+  ProtoTranslationTable* table = GetTable(test_case.name);
+  auto page = PageFromXxd(test_case.data);
 
   FtraceDataSourceConfig ds_config{EventFilter{},
+                                   EventFilter{},
                                    DisabledCompactSchedConfigForTesting(),
+                                   base::nullopt,
                                    {},
                                    {},
-                                   false /*symbolize_ksyms*/};
-  ds_config.event_filter.AddEnabledEvent(
-      table->EventToFtraceId(GroupAndName("sched", "sched_switch")));
+                                   false /*symbolize_ksyms*/,
+                                   false /*preserve_ftrace_buffer*/,
+                                   {}};
+  if (print_filter.has_value()) {
+    ds_config.print_filter =
+        FtracePrintFilterConfig::Create(print_filter.value(), table);
+    PERFETTO_CHECK(ds_config.print_filter.has_value());
+  }
+  for (const GroupAndName& enabled_event : enabled_events) {
+    ds_config.event_filter.AddEnabledEvent(
+        table->EventToFtraceId(enabled_event));
+  }
 
   FtraceMetadata metadata{};
   while (state.KeepRunning()) {
@@ -331,7 +858,7 @@
     std::unique_ptr<CompactSchedBuffer> compact_buffer(
         new CompactSchedBuffer());
     const uint8_t* parse_pos = page.get();
-    perfetto::base::Optional<CpuReader::PageHeader> page_header =
+    base::Optional<CpuReader::PageHeader> page_header =
         CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
 
     if (!page_header.has_value())
@@ -344,4 +871,50 @@
     metadata.Clear();
   }
 }
+
+void BM_ParsePageFullOfSchedSwitch(benchmark::State& state) {
+  DoParse(g_full_page_sched_switch, {GroupAndName("sched", "sched_switch")},
+          base::nullopt, state);
+}
 BENCHMARK(BM_ParsePageFullOfSchedSwitch);
+
+void BM_ParsePageFullOfPrint(benchmark::State& state) {
+  DoParse(g_full_page_print, {GroupAndName("ftrace", "print")}, base::nullopt,
+          state);
+}
+BENCHMARK(BM_ParsePageFullOfPrint);
+
+void BM_ParsePageFullOfPrintWithFilterRules(benchmark::State& state) {
+  FtraceConfig::PrintFilter filter_conf;
+  for (int i = 0; i < state.range(0); i++) {
+    auto* rule = filter_conf.add_rules();
+    rule->set_prefix("X");  // This rule will not match
+    rule->set_allow(false);
+  }
+  DoParse(g_full_page_print, {GroupAndName("ftrace", "print")}, filter_conf,
+          state);
+}
+BENCHMARK(BM_ParsePageFullOfPrintWithFilterRules)->DenseRange(0, 16, 1);
+
+void BM_ParsePageFullOfAtracePrint(benchmark::State& state) {
+  DoParse(g_full_page_atrace_print, {GroupAndName("ftrace", "print")},
+          base::nullopt, state);
+}
+BENCHMARK(BM_ParsePageFullOfAtracePrint);
+
+void BM_ParsePageFullOfAtracePrintWithFilterRules(benchmark::State& state) {
+  FtraceConfig::PrintFilter filter_conf;
+  for (int i = 0; i < state.range(0); i++) {
+    auto* rule = filter_conf.add_rules();
+    auto* atrace = rule->mutable_atrace_msg();
+    atrace->set_type("C");
+    atrace->set_prefix("X");  // This rule will not match
+    rule->set_allow(false);
+  }
+  DoParse(g_full_page_print, {GroupAndName("ftrace", "print")}, filter_conf,
+          state);
+}
+BENCHMARK(BM_ParsePageFullOfAtracePrintWithFilterRules)->DenseRange(0, 16, 1);
+
+}  // namespace
+}  // namespace perfetto
diff --git a/src/traced/probes/ftrace/cpu_reader_fuzzer.cc b/src/traced/probes/ftrace/cpu_reader_fuzzer.cc
index 84dfda0..76908c9 100644
--- a/src/traced/probes/ftrace/cpu_reader_fuzzer.cc
+++ b/src/traced/probes/ftrace/cpu_reader_fuzzer.cc
@@ -55,10 +55,14 @@
 
   FtraceMetadata metadata{};
   FtraceDataSourceConfig ds_config{EventFilter{},
+                                   EventFilter{},
                                    DisabledCompactSchedConfigForTesting(),
+                                   base::nullopt,
                                    {},
                                    {},
-                                   /*symbolize_ksyms=*/false};
+                                   /*symbolize_ksyms=*/false,
+                                   /*preserve_ftrace_buffer=*/false,
+                                   {}};
   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 fcc428a..74ee145 100644
--- a/src/traced/probes/ftrace/cpu_reader_unittest.cc
+++ b/src/traced/probes/ftrace/cpu_reader_unittest.cc
@@ -18,6 +18,7 @@
 
 #include <string.h>
 #include <sys/stat.h>
+#include <sys/syscall.h>
 
 #include "perfetto/base/build_config.h"
 #include "perfetto/ext/base/utils.h"
@@ -39,6 +40,7 @@
 #include "protos/perfetto/trace/ftrace/ftrace_event_bundle.gen.h"
 #include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
 #include "protos/perfetto/trace/ftrace/power.gen.h"
+#include "protos/perfetto/trace/ftrace/raw_syscalls.gen.h"
 #include "protos/perfetto/trace/ftrace/sched.gen.h"
 #include "protos/perfetto/trace/ftrace/task.gen.h"
 #include "protos/perfetto/trace/trace_packet.gen.h"
@@ -60,15 +62,18 @@
 using testing::StartsWith;
 
 namespace perfetto {
-
 namespace {
 
 FtraceDataSourceConfig EmptyConfig() {
   return FtraceDataSourceConfig{EventFilter{},
+                                EventFilter{},
                                 DisabledCompactSchedConfigForTesting(),
+                                base::nullopt,
                                 {},
                                 {},
-                                false /*symbolize_ksyms*/};
+                                false /*symbolize_ksyms*/,
+                                false /*preserve_ftrace_buffer*/,
+                                {}};
 }
 
 constexpr uint64_t kNanoInSecond = 1000 * 1000 * 1000;
@@ -185,8 +190,6 @@
   uint8_t* ptr_;
 };
 
-}  // namespace
-
 TEST(PageFromXxdTest, OneLine) {
   std::string text = R"(
     00000000: 0000 0000 0000 0000 0000 0000 0000 0000  ................
@@ -755,6 +758,87 @@
   }
 }
 
+TEST(CpuReaderTest, ParsePrintWithAndWithoutFilter) {
+  const ExamplePage* test_case = &g_three_prints;
+
+  ProtoTranslationTable* table = GetTable(test_case->name);
+  auto page = PageFromXxd(test_case->data);
+
+  FtraceMetadata metadata{};
+  std::unique_ptr<CompactSchedBuffer> compact_buffer(new CompactSchedBuffer());
+  const uint8_t* parse_pos = page.get();
+  base::Optional<CpuReader::PageHeader> page_header =
+      CpuReader::ParsePageHeader(&parse_pos, table->page_header_size_len());
+
+  const uint8_t* page_end = page.get() + base::kPageSize;
+  ASSERT_TRUE(page_header.has_value());
+  ASSERT_FALSE(page_header->lost_events);
+  ASSERT_LE(parse_pos + page_header->size, page_end);
+
+  {
+    FtraceDataSourceConfig ds_config_no_filter = EmptyConfig();
+    ds_config_no_filter.event_filter.AddEnabledEvent(
+        table->EventToFtraceId(GroupAndName("ftrace", "print")));
+
+    BundleProvider bundle_provider(base::kPageSize);
+    size_t evt_bytes = CpuReader::ParsePagePayload(
+        parse_pos, &page_header.value(), table, &ds_config_no_filter,
+        compact_buffer.get(), bundle_provider.writer(), &metadata);
+    ASSERT_GE(evt_bytes, 0u);
+
+    auto bundle = bundle_provider.ParseProto();
+    using ::testing::Pointee;
+    using ::testing::Property;
+    EXPECT_THAT(
+        bundle,
+        Pointee(Property(
+            &protos::gen::FtraceEventBundle::event,
+            ElementsAre(Property(&protos::gen::FtraceEvent::print,
+                                 Property(&protos::gen::PrintFtraceEvent::buf,
+                                          "Hello, world!\n")),
+                        Property(&protos::gen::FtraceEvent::print,
+                                 Property(&protos::gen::PrintFtraceEvent::buf,
+                                          "Good afternoon, world!\n")),
+                        Property(&protos::gen::FtraceEvent::print,
+                                 Property(&protos::gen::PrintFtraceEvent::buf,
+                                          "Goodbye, world!\n"))))));
+  }
+
+  {
+    FtraceDataSourceConfig ds_config_with_filter = EmptyConfig();
+    ds_config_with_filter.event_filter.AddEnabledEvent(
+        table->EventToFtraceId(GroupAndName("ftrace", "print")));
+
+    FtraceConfig::PrintFilter conf;
+    auto* rule = conf.add_rules();
+    rule->set_prefix("Good ");
+    rule->set_allow(false);
+    ds_config_with_filter.print_filter =
+        FtracePrintFilterConfig::Create(conf, table);
+    ASSERT_TRUE(ds_config_with_filter.print_filter.has_value());
+
+    BundleProvider bundle_provider(base::kPageSize);
+    size_t evt_bytes = CpuReader::ParsePagePayload(
+        parse_pos, &page_header.value(), table, &ds_config_with_filter,
+        compact_buffer.get(), bundle_provider.writer(), &metadata);
+    ASSERT_GE(evt_bytes, 0u);
+
+    auto bundle = bundle_provider.ParseProto();
+    using ::testing::Pointee;
+    using ::testing::Property;
+    EXPECT_THAT(
+        bundle,
+        Pointee(Property(
+            &protos::gen::FtraceEventBundle::event,
+            ElementsAre(Property(&protos::gen::FtraceEvent::print,
+                                 Property(&protos::gen::PrintFtraceEvent::buf,
+                                          "Hello, world!\n")),
+                        Property(&protos::gen::FtraceEvent::print,
+                                 Property(&protos::gen::PrintFtraceEvent::buf,
+                                          "Goodbye, world!\n"))))));
+  }
+}
+
 // clang-format off
 // # tracer: nop
 // #
@@ -862,10 +946,14 @@
   auto page = PageFromXxd(test_case->data);
 
   FtraceDataSourceConfig ds_config{EventFilter{},
+                                   EventFilter{},
                                    EnabledCompactSchedConfigForTesting(),
+                                   base::nullopt,
                                    {},
                                    {},
-                                   false /* symbolize_ksyms*/};
+                                   false /* symbolize_ksyms*/,
+                                   false /*preserve_ftrace_buffer*/,
+                                   {}};
   ds_config.event_filter.AddEnabledEvent(
       table->EventToFtraceId(GroupAndName("sched", "sched_switch")));
 
@@ -1086,6 +1174,7 @@
       &ftrace_, events, std::move(common_fields),
       ProtoTranslationTable::DefaultPageHeaderSpecForTesting(),
       InvalidCompactSchedEventFormatForTesting(), printk_formats);
+  FtraceDataSourceConfig ds_config = EmptyConfig();
 
   FakeEventProvider provider(base::kPageSize);
 
@@ -1122,7 +1211,7 @@
   FtraceMetadata metadata{};
 
   ASSERT_TRUE(CpuReader::ParseEvent(ftrace_event_id, input.get(),
-                                    input.get() + length, &table,
+                                    input.get() + length, &table, &ds_config,
                                     provider.writer(), &metadata));
 
   auto event = provider.ParseProto();
@@ -1152,11 +1241,93 @@
               Contains(Pair(99u, k64BitUserspaceBlockDeviceId)));
 }
 
+TEST(CpuReaderTest, SysEnterEvent) {
+  BinaryWriter writer;
+  ProtoTranslationTable* table = GetTable("synthetic");
+  FtraceDataSourceConfig ds_config = EmptyConfig();
+
+  const auto kSysEnterId = static_cast<uint16_t>(
+      table->EventToFtraceId(GroupAndName("raw_syscalls", "sys_enter")));
+  ASSERT_GT(kSysEnterId, 0ul);
+  constexpr uint32_t kPid = 23;
+  constexpr uint32_t kFd = 7;
+  constexpr auto kSyscall = SYS_close;
+
+  writer.Write<int32_t>(1001);      // Common field.
+  writer.Write<int32_t>(kPid);      // Common pid
+  writer.Write<int64_t>(kSyscall);  // id
+  for (uint32_t i = 0; i < 6; ++i) {
+    writer.Write<uint64_t>(kFd + i);  // args
+  }
+
+  auto input = writer.GetCopy();
+  auto length = writer.written();
+
+  BundleProvider bundle_provider(base::kPageSize);
+  FtraceMetadata metadata{};
+
+  ASSERT_TRUE(CpuReader::ParseEvent(
+      kSysEnterId, input.get(), input.get() + length, table, &ds_config,
+      bundle_provider.writer()->add_event(), &metadata));
+
+  std::unique_ptr<protos::gen::FtraceEventBundle> a =
+      bundle_provider.ParseProto();
+  ASSERT_NE(a, nullptr);
+  ASSERT_EQ(a->event().size(), 1u);
+  const auto& event = a->event()[0].sys_enter();
+  EXPECT_EQ(event.id(), kSyscall);
+  for (uint32_t i = 0; i < 6; ++i) {
+    EXPECT_EQ(event.args()[i], kFd + i);
+  }
+}
+
+TEST(CpuReaderTest, SysExitEvent) {
+  BinaryWriter writer;
+  ProtoTranslationTable* table = GetTable("synthetic");
+  FtraceDataSourceConfig ds_config = EmptyConfig();
+  const auto syscalls = SyscallTable::FromCurrentArch();
+
+  const auto kSysExitId = static_cast<uint16_t>(
+      table->EventToFtraceId(GroupAndName("raw_syscalls", "sys_exit")));
+  ASSERT_GT(kSysExitId, 0ul);
+  constexpr pid_t kPid = 23;
+  constexpr int64_t kFd = 2;
+
+  ds_config.syscalls_returning_fd =
+      FtraceConfigMuxer::GetSyscallsReturningFds(syscalls);
+  ASSERT_FALSE(ds_config.syscalls_returning_fd.empty());
+  const auto syscall_id = *ds_config.syscalls_returning_fd.begin();
+
+  writer.Write<int32_t>(1001);        // Common field.
+  writer.Write<int32_t>(kPid);        // Common pid
+  writer.Write<int64_t>(syscall_id);  // id
+  writer.Write<int64_t>(kFd);         // ret
+
+  auto input = writer.GetCopy();
+  auto length = writer.written();
+  BundleProvider bundle_provider(base::kPageSize);
+  FtraceMetadata metadata{};
+
+  ASSERT_TRUE(CpuReader::ParseEvent(
+      kSysExitId, input.get(), input.get() + length, table, &ds_config,
+      bundle_provider.writer()->add_event(), &metadata));
+
+  std::unique_ptr<protos::gen::FtraceEventBundle> a =
+      bundle_provider.ParseProto();
+  ASSERT_NE(a, nullptr);
+  ASSERT_EQ(a->event().size(), 1u);
+  const auto& event = a->event()[0].sys_exit();
+  EXPECT_EQ(event.id(), syscall_id);
+  EXPECT_EQ(event.ret(), kFd);
+  EXPECT_THAT(metadata.fds, Contains(std::make_pair(kPid, kFd)));
+}
+
 TEST(CpuReaderTest, TaskRenameEvent) {
   BundleProvider bundle_provider(base::kPageSize);
 
   BinaryWriter writer;
   ProtoTranslationTable* table = GetTable("android_seed_N2F62_3.10.49");
+  FtraceDataSourceConfig ds_config = EmptyConfig();
 
   constexpr uint32_t kTaskRenameId = 19;
 
@@ -1173,7 +1344,7 @@
   FtraceMetadata metadata{};
 
   ASSERT_TRUE(CpuReader::ParseEvent(kTaskRenameId, input.get(),
-                                    input.get() + length, table,
+                                    input.get() + length, table, &ds_config,
                                     bundle_provider.writer(), &metadata));
   EXPECT_THAT(metadata.rename_pids, Contains(9999));
   EXPECT_THAT(metadata.pids, Contains(9999));
@@ -1188,6 +1359,7 @@
 
   BinaryWriter writer;
   ProtoTranslationTable* table = GetTable("android_seed_N2F62_3.10.49");
+  FtraceDataSourceConfig ds_config = EmptyConfig();
 
   constexpr uint32_t kTaskRenameId = 19;
 
@@ -1206,7 +1378,7 @@
   FtraceMetadata metadata{};
 
   ASSERT_TRUE(CpuReader::ParseEvent(
-      kTaskRenameId, input.get(), input.get() + length, table,
+      kTaskRenameId, input.get(), input.get() + length, table, &ds_config,
       bundle_provider.writer()->add_event(), &metadata));
   std::unique_ptr<protos::gen::FtraceEventBundle> a =
       bundle_provider.ParseProto();
@@ -2890,7 +3062,7 @@
     00000CA0: 502D 7072 6564 6963 7469 6F6E 7C32 3137   P-prediction|217
     00000CB0: 3332 3932 3839 3739 3138 0A00 70FE 0600   3292897918..p...
     00000CC0: 0500 0000 EF02 0000 50AA 4C00 AEFF FFFF   ........P.L.....
-    00000CD0: 427C 3633 397C 6170 7020 616C 6172 6D20   B|639|app alarm 
+    00000CD0: 427C 3633 397C 6170 7020 616C 6172 6D20   B|639|app alarm
     00000CE0: 696E 2037 3837 3875 733B 2056 5359 4E43   in 7878us; VSYNC
     00000CF0: 2069 6E20 3338 3837 3875 730A 0000 0000    in 38878us.....
     00000D00: C7AD 0100 0500 0000 EF02 0000 50AA 4C00   ............P.L.
@@ -2980,4 +3152,5 @@
   ASSERT_TRUE(bundle);
 }
 
+}  // namespace
 }  // namespace perfetto
diff --git a/src/traced/probes/ftrace/discover_vendor_tracepoints.cc b/src/traced/probes/ftrace/discover_vendor_tracepoints.cc
deleted file mode 100644
index 68625c0..0000000
--- a/src/traced/probes/ftrace/discover_vendor_tracepoints.cc
+++ /dev/null
@@ -1,56 +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/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
deleted file mode 100644
index f5d791b..0000000
--- a/src/traced/probes/ftrace/discover_vendor_tracepoints.h
+++ /dev/null
@@ -1,44 +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_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
deleted file mode 100644
index a859c15..0000000
--- a/src/traced/probes/ftrace/discover_vendor_tracepoints_unittest.cc
+++ /dev/null
@@ -1,93 +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/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 9c88aad..ea53a44 100644
--- a/src/traced/probes/ftrace/event_info.cc
+++ b/src/traced/probes/ftrace/event_info.cc
@@ -30,6 +30,132 @@
   static constexpr uint16_t kUnsetSize = 0;
   static constexpr uint16_t kUnsetFtraceId = 0;
   return {
+      {"android_fs_dataread_end",
+       "android_fs",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "bytes", 1, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "ino", 2, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "offset", 3, ProtoSchemaType::kInt64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       431,
+       kUnsetSize},
+      {"android_fs_dataread_start",
+       "android_fs",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "bytes", 1, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "cmdline", 2, ProtoSchemaType::kString,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "i_size", 3, ProtoSchemaType::kInt64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "ino", 4, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "offset", 5, ProtoSchemaType::kInt64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "pathbuf", 6, ProtoSchemaType::kString,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "pid", 7, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       432,
+       kUnsetSize},
+      {"android_fs_datawrite_end",
+       "android_fs",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "bytes", 1, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "ino", 2, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "offset", 3, ProtoSchemaType::kInt64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       433,
+       kUnsetSize},
+      {"android_fs_datawrite_start",
+       "android_fs",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "bytes", 1, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "cmdline", 2, ProtoSchemaType::kString,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "i_size", 3, ProtoSchemaType::kInt64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "ino", 4, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "offset", 5, ProtoSchemaType::kInt64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "pathbuf", 6, ProtoSchemaType::kString,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "pid", 7, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       434,
+       kUnsetSize},
+      {"android_fs_fsync_end",
+       "android_fs",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "bytes", 1, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "ino", 2, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "offset", 3, ProtoSchemaType::kInt64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       435,
+       kUnsetSize},
+      {"android_fs_fsync_start",
+       "android_fs",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "cmdline", 1, ProtoSchemaType::kString,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "i_size", 2, ProtoSchemaType::kInt64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "ino", 3, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "pathbuf", 4, ProtoSchemaType::kString,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "pid", 5, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       436,
+       kUnsetSize},
       {"binder_transaction",
        "binder",
        {
@@ -782,6 +908,59 @@
        kUnsetFtraceId,
        322,
        kUnsetSize},
+      {"cma_alloc_start",
+       "cma",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "align", 1, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "count", 2, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "name", 3, ProtoSchemaType::kString,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       465,
+       kUnsetSize},
+      {"cma_alloc_info",
+       "cma",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "align", 1, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "count", 2, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "err_iso", 3, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "err_mig", 4, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "err_test", 5, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "name", 6, ProtoSchemaType::kString,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "nr_mapped", 7, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "nr_migrated", 8, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "nr_reclaimed", 9, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "pfn", 10, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       466,
+       kUnsetSize},
       {"mm_compaction_begin",
        "compaction",
        {
@@ -4541,6 +4720,41 @@
        kUnsetFtraceId,
        3,
        kUnsetSize},
+      {"funcgraph_entry",
+       "ftrace",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "depth", 1, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "func", 2, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       437,
+       kUnsetSize},
+      {"funcgraph_exit",
+       "ftrace",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "calltime", 1, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "depth", 2, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "func", 3, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "overrun", 4, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "rettime", 5, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       438,
+       kUnsetSize},
       {"tracing_mark_write",
        "g2d",
        {
@@ -6226,6 +6440,28 @@
        35,
        kUnsetSize},
       {"tracing_mark_write",
+       "lwis",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "lwis_name", 1, ProtoSchemaType::kString,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "type", 2, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "pid", 3, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "func_name", 4, ProtoSchemaType::kString,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "value", 5, ProtoSchemaType::kInt64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       467,
+       kUnsetSize},
+      {"tracing_mark_write",
        "mali",
        {
            {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
@@ -6244,6 +6480,138 @@
        kUnsetFtraceId,
        350,
        kUnsetSize},
+      {"mali_KCPU_CQS_SET",
+       "mali",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "id", 1, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "info_val1", 2, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "info_val2", 3, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "kctx_id", 4, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "kctx_tgid", 5, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       470,
+       kUnsetSize},
+      {"mali_KCPU_CQS_WAIT_START",
+       "mali",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "id", 1, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "info_val1", 2, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "info_val2", 3, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "kctx_id", 4, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "kctx_tgid", 5, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       471,
+       kUnsetSize},
+      {"mali_KCPU_CQS_WAIT_END",
+       "mali",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "id", 1, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "info_val1", 2, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "info_val2", 3, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "kctx_id", 4, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "kctx_tgid", 5, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       472,
+       kUnsetSize},
+      {"mali_KCPU_FENCE_SIGNAL",
+       "mali",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "info_val1", 1, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "info_val2", 2, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "kctx_tgid", 3, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "kctx_id", 4, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "id", 5, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       473,
+       kUnsetSize},
+      {"mali_KCPU_FENCE_WAIT_START",
+       "mali",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "info_val1", 1, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "info_val2", 2, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "kctx_tgid", 3, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "kctx_id", 4, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "id", 5, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       474,
+       kUnsetSize},
+      {"mali_KCPU_FENCE_WAIT_END",
+       "mali",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "info_val1", 1, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "info_val2", 2, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "kctx_tgid", 3, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "kctx_id", 4, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "id", 5, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       475,
+       kUnsetSize},
       {"mdp_cmd_kickoff",
        "mdss",
        {
@@ -6860,6 +7228,48 @@
        kUnsetFtraceId,
        334,
        kUnsetSize},
+      {"dsi_cmd_fifo_status",
+       "panel",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "header", 1, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "payload", 2, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       428,
+       kUnsetSize},
+      {"dsi_rx",
+       "panel",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "cmd", 1, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "rx_buf", 2, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       429,
+       kUnsetSize},
+      {"dsi_tx",
+       "panel",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "last", 1, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "tx_buf", 2, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "type", 3, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       430,
+       kUnsetSize},
       {"cpu_frequency",
        "power",
        {
@@ -7021,6 +7431,9 @@
            {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
             "id", 1, ProtoSchemaType::kInt64,
             TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "args", 2, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
        },
        kUnsetFtraceId,
        329,
@@ -7855,6 +8268,291 @@
        kUnsetFtraceId,
        342,
        kUnsetSize},
+      {"trusty_smc",
+       "trusty",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "r0", 1, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "r1", 2, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "r2", 3, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "r3", 4, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       445,
+       kUnsetSize},
+      {"trusty_smc_done",
+       "trusty",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "ret", 1, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       446,
+       kUnsetSize},
+      {"trusty_std_call32",
+       "trusty",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "r0", 1, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "r1", 2, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "r2", 3, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "r3", 4, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       447,
+       kUnsetSize},
+      {"trusty_std_call32_done",
+       "trusty",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "ret", 1, ProtoSchemaType::kInt64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       448,
+       kUnsetSize},
+      {"trusty_share_memory",
+       "trusty",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "len", 1, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "lend", 2, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "nents", 3, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       449,
+       kUnsetSize},
+      {"trusty_share_memory_done",
+       "trusty",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "handle", 1, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "len", 2, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "lend", 3, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "nents", 4, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "ret", 5, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       450,
+       kUnsetSize},
+      {"trusty_reclaim_memory",
+       "trusty",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "id", 1, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       451,
+       kUnsetSize},
+      {"trusty_reclaim_memory_done",
+       "trusty",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "id", 1, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "ret", 2, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       452,
+       kUnsetSize},
+      {"trusty_irq",
+       "trusty",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "irq", 1, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       453,
+       kUnsetSize},
+      {"trusty_ipc_handle_event",
+       "trusty",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "chan", 1, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "event_id", 2, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "srv_name", 3, ProtoSchemaType::kString,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       454,
+       kUnsetSize},
+      {"trusty_ipc_connect",
+       "trusty",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "chan", 1, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "port", 2, ProtoSchemaType::kString,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "state", 3, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       455,
+       kUnsetSize},
+      {"trusty_ipc_connect_end",
+       "trusty",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "chan", 1, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "err", 2, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "state", 3, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       456,
+       kUnsetSize},
+      {"trusty_ipc_write",
+       "trusty",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "buf_id", 1, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "chan", 2, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "kind_shm", 3, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "len_or_err", 4, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "shm_cnt", 5, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "srv_name", 6, ProtoSchemaType::kString,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       457,
+       kUnsetSize},
+      {"trusty_ipc_poll",
+       "trusty",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "chan", 1, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "poll_mask", 2, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "srv_name", 3, ProtoSchemaType::kString,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       458,
+       kUnsetSize},
+      {"trusty_ipc_read",
+       "trusty",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "chan", 1, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "srv_name", 2, ProtoSchemaType::kString,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       460,
+       kUnsetSize},
+      {"trusty_ipc_read_end",
+       "trusty",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "buf_id", 1, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "chan", 2, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "len_or_err", 3, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "shm_cnt", 4, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "srv_name", 5, ProtoSchemaType::kString,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       461,
+       kUnsetSize},
+      {"trusty_ipc_rx",
+       "trusty",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "buf_id", 1, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "chan", 2, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "srv_name", 3, ProtoSchemaType::kString,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       462,
+       kUnsetSize},
+      {"trusty_enqueue_nop",
+       "trusty",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "arg1", 1, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "arg2", 2, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "arg3", 3, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       464,
+       kUnsetSize},
       {"ufshcd_command",
        "ufs",
        {
@@ -8235,6 +8933,162 @@
        kUnsetFtraceId,
        427,
        kUnsetSize},
+      {"virtio_gpu_cmd_queue",
+       "virtio_gpu",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "ctx_id", 1, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "dev", 2, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "fence_id", 3, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "flags", 4, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "name", 5, ProtoSchemaType::kString,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "num_free", 6, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "seqno", 7, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "type", 8, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "vq", 9, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       468,
+       kUnsetSize},
+      {"virtio_gpu_cmd_response",
+       "virtio_gpu",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "ctx_id", 1, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "dev", 2, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "fence_id", 3, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "flags", 4, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "name", 5, ProtoSchemaType::kString,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "num_free", 6, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "seqno", 7, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "type", 8, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "vq", 9, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       469,
+       kUnsetSize},
+      {"virtio_video_cmd",
+       "virtio_video",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "stream_id", 1, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "type", 2, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       439,
+       kUnsetSize},
+      {"virtio_video_cmd_done",
+       "virtio_video",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "stream_id", 1, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "type", 2, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       440,
+       kUnsetSize},
+      {"virtio_video_resource_queue",
+       "virtio_video",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "data_size0", 1, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "data_size1", 2, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "data_size2", 3, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "data_size3", 4, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "queue_type", 5, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "resource_id", 6, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "stream_id", 7, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "timestamp", 8, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       441,
+       kUnsetSize},
+      {"virtio_video_resource_queue_done",
+       "virtio_video",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "data_size0", 1, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "data_size1", 2, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "data_size2", 3, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "data_size3", 4, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "queue_type", 5, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "resource_id", 6, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "stream_id", 7, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "timestamp", 8, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       442,
+       kUnsetSize},
       {"mm_vmscan_direct_reclaim_begin",
        "vmscan",
        {
@@ -8287,6 +9141,74 @@
        kUnsetFtraceId,
        49,
        kUnsetSize},
+      {"mm_shrink_slab_start",
+       "vmscan",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "cache_items", 1, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "delta", 2, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "gfp_flags", 3, ProtoSchemaType::kUint32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "lru_pgs", 4, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "nr_objects_to_shrink", 5, ProtoSchemaType::kInt64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "pgs_scanned", 6, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "shr", 7, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "shrink", 8, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "total_scan", 9, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "nid", 10, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "priority", 11, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       443,
+       kUnsetSize},
+      {"mm_shrink_slab_end",
+       "vmscan",
+       {
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "new_scan", 1, ProtoSchemaType::kInt64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "retval", 2, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "shr", 3, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "shrink", 4, ProtoSchemaType::kUint64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "total_scan", 5, ProtoSchemaType::kInt64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "unused_scan", 6, ProtoSchemaType::kInt64,
+            TranslationStrategy::kInvalidTranslationStrategy},
+           {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+            "nid", 7, ProtoSchemaType::kInt32,
+            TranslationStrategy::kInvalidTranslationStrategy},
+       },
+       kUnsetFtraceId,
+       444,
+       kUnsetSize},
       {"workqueue_activate_work",
        "workqueue",
        {
diff --git a/src/traced/probes/ftrace/format_parser/format_parser.cc b/src/traced/probes/ftrace/format_parser/format_parser.cc
index 7d5ab59..9ffc3ce 100644
--- a/src/traced/probes/ftrace/format_parser/format_parser.cc
+++ b/src/traced/probes/ftrace/format_parser/format_parser.cc
@@ -166,7 +166,7 @@
     return false;
   }
 
-  if (!has_id || !has_name || fields.empty()) {
+  if (!has_id || !has_name || common_fields.empty()) {
     if (output)
       PERFETTO_DLOG("Could not parse format file: %s.\n",
                     !has_id ? "no ID found"
diff --git a/src/traced/probes/ftrace/format_parser/format_parser_unittest.cc b/src/traced/probes/ftrace/format_parser/format_parser_unittest.cc
index f4e9523..a89ed2b 100644
--- a/src/traced/probes/ftrace/format_parser/format_parser_unittest.cc
+++ b/src/traced/probes/ftrace/format_parser/format_parser_unittest.cc
@@ -58,6 +58,25 @@
           Eq(FtraceEvent::Field{"int common_pid", 4, 4, true})));
 }
 
+TEST(FtraceEventParserTest, OnlyCommonFields) {
+  const std::string input = R"(name: another_name
+ID: 42
+format:
+	field:unsigned short common_type;	offset:0;	size:2;	signed:0;
+
+print fmt: " ",
+)";
+
+  FtraceEvent output;
+  EXPECT_TRUE(ParseFtraceEvent(input));
+  EXPECT_TRUE(ParseFtraceEvent(input, &output));
+  EXPECT_EQ(output.name, "another_name");
+  EXPECT_EQ(output.id, 42u);
+  EXPECT_THAT(output.common_fields,
+              ElementsAre(Eq(FtraceEvent::Field{"unsigned short common_type", 0,
+                                                2, false})));
+}
+
 TEST(FtraceEventParserTest, MissingName) {
   const std::string input = R"(ID: 42
 format:
diff --git a/src/traced/probes/ftrace/ftrace_config_muxer.cc b/src/traced/probes/ftrace/ftrace_config_muxer.cc
index 69c4176..b0d75e5 100644
--- a/src/traced/probes/ftrace/ftrace_config_muxer.cc
+++ b/src/traced/probes/ftrace/ftrace_config_muxer.cc
@@ -38,11 +38,21 @@
 constexpr int kDefaultPerCpuBufferSizeKb = 2 * 1024;  // 2mb
 constexpr int kMaxPerCpuBufferSizeKb = 64 * 1024;     // 64mb
 
+// A fake "syscall id" that indicates all syscalls should be recorded. This
+// allows us to distinguish between the case where `syscall_events` is empty
+// because raw_syscalls aren't enabled, or the case where it is and we want to
+// record all events.
+constexpr size_t kAllSyscallsId = kMaxSyscalls + 1;
+
 // trace_clocks in preference order.
 // If this list is changed, the FtraceClocks enum in ftrace_event_bundle.proto
 // and FtraceConfigMuxer::SetupClock() should be also changed accordingly.
 constexpr const char* kClocks[] = {"boot", "global", "local"};
 
+// optional monotonic raw clock.
+// Enabled by the "use_monotonic_raw_clock" option in the ftrace config.
+constexpr const char* kClockMonoRaw = "mono_raw";
+
 void AddEventGroup(const ProtoTranslationTable* table,
                    const std::string& group,
                    std::set<GroupAndName>* to) {
@@ -311,12 +321,15 @@
         InsertEvent("f2fs", "f2fs_sync_file_exit", &events);
         InsertEvent("f2fs", "f2fs_write_begin", &events);
         InsertEvent("f2fs", "f2fs_write_end", &events);
+        InsertEvent("f2fs", "f2fs_iostat", &events);
+        InsertEvent("f2fs", "f2fs_iostat_latency", &events);
         InsertEvent("ext4", "ext4_da_write_begin", &events);
         InsertEvent("ext4", "ext4_da_write_end", &events);
         InsertEvent("ext4", "ext4_sync_file_enter", &events);
         InsertEvent("ext4", "ext4_sync_file_exit", &events);
-        InsertEvent("block", "block_rq_issue", &events);
-        InsertEvent("block", "block_rq_complete", &events);
+        InsertEvent("block", "block_bio_queue", &events);
+        InsertEvent("block", "block_bio_complete", &events);
+        InsertEvent("ufs", "ufshcd_command", &events);
         continue;
       }
 
@@ -430,9 +443,21 @@
         InsertEvent("thermal", "cdev_update", &events);
         continue;
       }
+
+      if (category == "camera") {
+        AddEventGroup(table, "lwis", &events);
+        InsertEvent("lwis", "tracing_mark_write", &events);
+        continue;
+      }
     }
   }
 
+  // function_graph tracer emits two builtin ftrace events
+  if (request.enable_function_graph()) {
+    InsertEvent("ftrace", "funcgraph_entry", &events);
+    InsertEvent("ftrace", "funcgraph_exit", &events);
+  }
+
   // If throttle_rss_stat: true, use the rss_stat_throttled event if supported
   if (request.throttle_rss_stat() && ftrace_->SupportsRssStatThrottled()) {
     auto it = std::find_if(
@@ -449,6 +474,92 @@
   return events;
 }
 
+base::FlatSet<int64_t> FtraceConfigMuxer::GetSyscallsReturningFds(
+    const SyscallTable& syscalls) {
+  auto insertSyscallId = [&syscalls](base::FlatSet<int64_t>& set,
+                                     const char* syscall) {
+    auto syscall_id = syscalls.GetByName(syscall);
+    if (syscall_id)
+      set.insert(static_cast<int64_t>(*syscall_id));
+  };
+
+  base::FlatSet<int64_t> call_ids;
+  insertSyscallId(call_ids, "sys_open");
+  insertSyscallId(call_ids, "sys_openat");
+  insertSyscallId(call_ids, "sys_socket");
+  insertSyscallId(call_ids, "sys_dup");
+  insertSyscallId(call_ids, "sys_dup2");
+  insertSyscallId(call_ids, "sys_dup3");
+  return call_ids;
+}
+
+bool FtraceConfigMuxer::FilterHasGroup(const EventFilter& filter,
+                                       const std::string& group) {
+  const std::vector<const Event*>* events = table_->GetEventsByGroup(group);
+  if (!events) {
+    return false;
+  }
+
+  for (const Event* event : *events) {
+    if (filter.IsEventEnabled(event->ftrace_event_id)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+EventFilter FtraceConfigMuxer::BuildSyscallFilter(
+    const EventFilter& ftrace_filter,
+    const FtraceConfig& request) {
+  EventFilter output;
+
+  if (!FilterHasGroup(ftrace_filter, "raw_syscalls")) {
+    return output;
+  }
+
+  if (request.syscall_events().empty()) {
+    output.AddEnabledEvent(kAllSyscallsId);
+    return output;
+  }
+
+  for (const std::string& syscall : request.syscall_events()) {
+    base::Optional<size_t> id = syscalls_.GetByName(syscall);
+    if (!id.has_value()) {
+      PERFETTO_ELOG("Can't enable %s, syscall not known", syscall.c_str());
+      continue;
+    }
+    output.AddEnabledEvent(*id);
+  }
+
+  return output;
+}
+
+bool FtraceConfigMuxer::SetSyscallEventFilter(
+    const EventFilter& extra_syscalls) {
+  EventFilter syscall_filter;
+
+  syscall_filter.EnableEventsFrom(extra_syscalls);
+  for (const auto& id_config : ds_configs_) {
+    const perfetto::FtraceDataSourceConfig& config = id_config.second;
+    syscall_filter.EnableEventsFrom(config.syscall_filter);
+  }
+
+  std::set<size_t> filter_set = syscall_filter.GetEnabledEvents();
+  if (syscall_filter.IsEventEnabled(kAllSyscallsId)) {
+    filter_set.clear();
+  }
+
+  if (current_state_.syscall_filter != filter_set) {
+    if (!ftrace_->SetSyscallFilter(filter_set)) {
+      return false;
+    }
+
+    current_state_.syscall_filter = filter_set;
+  }
+
+  return true;
+}
+
 // Post-conditions:
 // 1. result >= 1 (should have at least one page per CPU)
 // 2. result * 4 < kMaxTotalBufferSizeKb
@@ -473,9 +584,11 @@
 FtraceConfigMuxer::FtraceConfigMuxer(
     FtraceProcfs* ftrace,
     ProtoTranslationTable* table,
+    SyscallTable syscalls,
     std::map<std::string, std::vector<GroupAndName>> vendor_events)
     : ftrace_(ftrace),
       table_(table),
+      syscalls_(std::move(syscalls)),
       current_state_(),
       ds_configs_(),
       vendor_events_(vendor_events) {}
@@ -484,26 +597,41 @@
 FtraceConfigId FtraceConfigMuxer::SetupConfig(const FtraceConfig& request,
                                               FtraceSetupErrors* errors) {
   EventFilter filter;
-  bool is_ftrace_enabled = ftrace_->IsTracingEnabled();
   if (ds_configs_.empty()) {
     PERFETTO_DCHECK(active_configs_.empty());
 
-    // If someone outside of perfetto is using ftrace give up now.
-    if (is_ftrace_enabled && !IsOldAtrace()) {
-      PERFETTO_ELOG("ftrace in use by non-Perfetto.");
+    // If someone outside of perfetto is using a non-nop tracer, yield. We can't
+    // realistically figure out all notions of "in use" even if we look at
+    // set_event or events/enable, so this is all we check for.
+    if (!request.preserve_ftrace_buffer() && !ftrace_->IsTracingAvailable()) {
+      PERFETTO_ELOG(
+          "ftrace in use by non-Perfetto. Check that %s current_tracer is nop.",
+          ftrace_->GetRootPath().c_str());
       return 0;
     }
 
-    // Setup ftrace, without starting it. Setting buffers can be quite slow
-    // (up to hundreds of ms).
-    SetupClock(request);
-    SetupBufferSize(request);
-  } else {
-    // Did someone turn ftrace off behind our back? If so give up.
-    if (!active_configs_.empty() && !is_ftrace_enabled && !IsOldAtrace()) {
-      PERFETTO_ELOG("ftrace disabled by non-Perfetto.");
-      return 0;
+    // Clear tracefs state, remembering which value of "tracing_on" to restore
+    // to after we're done, though we won't restore the rest of the tracefs
+    // state.
+    current_state_.saved_tracing_on = ftrace_->GetTracingOn();
+    if (!request.preserve_ftrace_buffer()) {
+      ftrace_->SetTracingOn(false);
+      // This will fail on release ("user") builds due to ACLs, but that's
+      // acceptable since the per-event enabling/disabling should still be
+      // balanced.
+      ftrace_->DisableAllEvents();
+      ftrace_->ClearTrace();
     }
+
+    // Set up the rest of the tracefs state, without starting it.
+    // Notes:
+    // * resizing buffers can be quite slow (up to hundreds of ms).
+    // * resizing buffers doesn't clear their existing contents, which matters
+    // to the preserve_ftrace_buffer option.
+    if (!request.preserve_ftrace_buffer()) {
+      SetupClock(request);
+    }
+    SetupBufferSize(request);
   }
 
   std::set<GroupAndName> events = GetFtraceEvents(request, table_);
@@ -568,16 +696,68 @@
     }
   }
 
+  EventFilter syscall_filter = BuildSyscallFilter(filter, request);
+  if (!SetSyscallEventFilter(syscall_filter)) {
+    PERFETTO_ELOG("Failed to set raw_syscall ftrace filter in SetupConfig");
+    return 0;
+  }
+
+  // Kernel function tracing (function_graph).
+  // Note 1: there is no cleanup in |RemoveConfig| because tracers cannot be
+  // changed while tracing pipes are opened. So we'll keep the current_tracer
+  // until all data sources are gone, at which point ftrace_controller will
+  // make an explicit call to |ResetCurrentTracer|.
+  // Note 2: we don't track the set of filters ourselves and instead let the
+  // kernel statefully collate them, hence the use of |AppendFunctionFilters|.
+  // This is because each concurrent data source that wants funcgraph will get
+  // all of the enabled functions (we don't go as far as doing per-DS event
+  // steering in the parser), and we don't want to remove functions midway
+  // through a trace (but some might get added).
+  if (request.enable_function_graph()) {
+    if (!current_state_.funcgraph_on && !ftrace_->ClearFunctionFilters())
+      return 0;
+    if (!current_state_.funcgraph_on && !ftrace_->ClearFunctionGraphFilters())
+      return 0;
+    if (!ftrace_->AppendFunctionFilters(request.function_filters()))
+      return 0;
+    if (!ftrace_->AppendFunctionGraphFilters(request.function_graph_roots()))
+      return 0;
+    if (!current_state_.funcgraph_on &&
+        !ftrace_->SetCurrentTracer("function_graph")) {
+      PERFETTO_LOG(
+          "Unable to enable function_graph tracing since a concurrent ftrace "
+          "data source is using a different tracer");
+      return 0;
+    }
+    current_state_.funcgraph_on = true;
+  }
+
   auto compact_sched =
       CreateCompactSchedConfig(request, table_->compact_sched_format());
 
+  base::Optional<FtracePrintFilterConfig> ftrace_print_filter;
+  if (request.has_print_filter()) {
+    ftrace_print_filter =
+        FtracePrintFilterConfig::Create(request.print_filter(), table_);
+    if (!ftrace_print_filter.has_value()) {
+      if (errors) {
+        errors->failed_ftrace_events.push_back(
+            "ftrace/print (unexpected format for filtering)");
+      }
+    }
+  }
+
   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), request.symbolize_ksyms()));
+      std::forward_as_tuple(std::move(filter), std::move(syscall_filter),
+                            compact_sched, std::move(ftrace_print_filter),
+                            std::move(apps), std::move(categories),
+                            request.symbolize_ksyms(),
+                            request.preserve_ftrace_buffer(),
+                            GetSyscallsReturningFds(syscalls_)));
   return id;
 }
 
@@ -587,13 +767,10 @@
     return false;
   }
 
+  // Enable tracing_on to activate ftrace ring buffer before activate the first
+  // config.
   if (active_configs_.empty()) {
-    if (ftrace_->IsTracingEnabled() && !IsOldAtrace()) {
-      // If someone outside of perfetto is using ftrace give up now.
-      PERFETTO_ELOG("ftrace in use by non-Perfetto.");
-      return false;
-    }
-    if (!ftrace_->EnableTracing()) {
+    if (!ftrace_->SetTracingOn(true)) {
       PERFETTO_ELOG("Failed to enable ftrace.");
       return false;
     }
@@ -631,6 +808,10 @@
       (current_state_.atrace_apps.size() != expected_apps.size()) ||
       (current_state_.atrace_categories.size() != expected_categories.size());
 
+  if (!SetSyscallEventFilter(/*extra_syscalls=*/{})) {
+    PERFETTO_ELOG("Failed to set raw_syscall ftrace filter in RemoveConfig");
+  }
+
   // Disable any events that are currently enabled, but are not in any configs
   // anymore.
   std::set<size_t> event_ids = current_state_.ftrace_events.GetEnabledEvents();
@@ -644,14 +825,14 @@
       current_state_.ftrace_events.DisableEvent(event->ftrace_event_id);
   }
 
-  // If there aren't any more active configs, disable ftrace.
   auto active_it = active_configs_.find(config_id);
   if (active_it != active_configs_.end()) {
     active_configs_.erase(active_it);
     if (active_configs_.empty()) {
-      // This was the last active config, disable ftrace.
-      if (!ftrace_->DisableTracing())
-        PERFETTO_ELOG("Failed to disable ftrace.");
+      // This was the last active config for now, but potentially more dormant
+      // configs need to be activated. We are not interested in reading while no
+      // active configs so diasble tracing_on here.
+      ftrace_->SetTracingOn(false);
     }
   }
 
@@ -663,6 +844,7 @@
       current_state_.cpu_buffer_size_pages = 1;
     ftrace_->DisableAllEvents();
     ftrace_->ClearTrace();
+    ftrace_->SetTracingOn(current_state_.saved_tracing_on);
   }
 
   if (current_state_.atrace_on) {
@@ -685,6 +867,25 @@
   return true;
 }
 
+bool FtraceConfigMuxer::ResetCurrentTracer() {
+  if (!current_state_.funcgraph_on)
+    return true;
+  if (!ftrace_->ResetCurrentTracer()) {
+    PERFETTO_PLOG("Failed to reset current_tracer to nop");
+    return false;
+  }
+  current_state_.funcgraph_on = false;
+  if (!ftrace_->ClearFunctionFilters()) {
+    PERFETTO_PLOG("Failed to reset set_ftrace_filter.");
+    return false;
+  }
+  if (!ftrace_->ClearFunctionGraphFilters()) {
+    PERFETTO_PLOG("Failed to reset set_function_graph.");
+    return false;
+  }
+  return true;
+}
+
 const FtraceDataSourceConfig* FtraceConfigMuxer::GetDataSourceConfig(
     FtraceConfigId id) {
   if (!ds_configs_.count(id))
@@ -692,19 +893,25 @@
   return &ds_configs_.at(id);
 }
 
-void FtraceConfigMuxer::SetupClock(const FtraceConfig&) {
+void FtraceConfigMuxer::SetupClock(const FtraceConfig& config) {
   std::string current_clock = ftrace_->GetClock();
   std::set<std::string> clocks = ftrace_->AvailableClocks();
 
-  for (size_t i = 0; i < base::ArraySize(kClocks); i++) {
-    std::string clock = std::string(kClocks[i]);
-    if (!clocks.count(clock))
-      continue;
-    if (current_clock == clock)
+  if (config.has_use_monotonic_raw_clock() &&
+      config.use_monotonic_raw_clock() && clocks.count(kClockMonoRaw)) {
+    ftrace_->SetClock(kClockMonoRaw);
+    current_clock = kClockMonoRaw;
+  } else {
+    for (size_t i = 0; i < base::ArraySize(kClocks); i++) {
+      std::string clock = std::string(kClocks[i]);
+      if (!clocks.count(clock))
+        continue;
+      if (current_clock == clock)
+        break;
+      ftrace_->SetClock(clock);
+      current_clock = clock;
       break;
-    ftrace_->SetClock(clock);
-    current_clock = clock;
-    break;
+    }
   }
 
   namespace pb0 = protos::pbzero;
@@ -717,6 +924,8 @@
     current_state_.ftrace_clock = pb0::FTRACE_CLOCK_GLOBAL;
   } else if (current_clock == "local") {
     current_state_.ftrace_clock = pb0::FTRACE_CLOCK_LOCAL;
+  } else if (current_clock == kClockMonoRaw) {
+    current_state_.ftrace_clock = pb0::FTRACE_CLOCK_MONO_RAW;
   } else {
     current_state_.ftrace_clock = pb0::FTRACE_CLOCK_UNKNOWN;
   }
diff --git a/src/traced/probes/ftrace/ftrace_config_muxer.h b/src/traced/probes/ftrace/ftrace_config_muxer.h
index c5a9323..fb5850a 100644
--- a/src/traced/probes/ftrace/ftrace_config_muxer.h
+++ b/src/traced/probes/ftrace/ftrace_config_muxer.h
@@ -20,9 +20,11 @@
 #include <map>
 #include <set>
 
+#include "perfetto/ext/base/optional.h"
+#include "src/kernel_utils/syscall_table.h"
 #include "src/traced/probes/ftrace/compact_sched.h"
 #include "src/traced/probes/ftrace/ftrace_config_utils.h"
-#include "src/traced/probes/ftrace/ftrace_controller.h"
+#include "src/traced/probes/ftrace/ftrace_print_filter.h"
 #include "src/traced/probes/ftrace/ftrace_procfs.h"
 #include "src/traced/probes/ftrace/proto_translation_table.h"
 
@@ -40,29 +42,50 @@
 // that data source's config.
 struct FtraceDataSourceConfig {
   FtraceDataSourceConfig(EventFilter _event_filter,
+                         EventFilter _syscall_filter,
                          CompactSchedConfig _compact_sched,
+                         base::Optional<FtracePrintFilterConfig> _print_filter,
                          std::vector<std::string> _atrace_apps,
                          std::vector<std::string> _atrace_categories,
-                         bool _symbolize_ksyms)
+                         bool _symbolize_ksyms,
+                         bool _preserve_ftrace_buffer,
+                         base::FlatSet<int64_t> _syscalls_returning_fd)
       : event_filter(std::move(_event_filter)),
+        syscall_filter(std::move(_syscall_filter)),
         compact_sched(_compact_sched),
+        print_filter(std::move(_print_filter)),
         atrace_apps(std::move(_atrace_apps)),
         atrace_categories(std::move(_atrace_categories)),
-        symbolize_ksyms(_symbolize_ksyms) {}
-
+        symbolize_ksyms(_symbolize_ksyms),
+        preserve_ftrace_buffer(_preserve_ftrace_buffer),
+        syscalls_returning_fd(std::move(_syscalls_returning_fd)) {}
   // The event filter allows to quickly check if a certain ftrace event with id
   // x is enabled for this data source.
   EventFilter event_filter;
 
+  // Specifies the syscalls (by id) that are enabled for this data source. An
+  // empty filter implies all events are enabled.
+  EventFilter syscall_filter;
+
   // Configuration of the optional compact encoding of scheduling events.
   const CompactSchedConfig compact_sched;
 
+  // Optional configuration that's used to filter "ftrace/print" events based on
+  // the content of their "buf" field.
+  base::Optional<FtracePrintFilterConfig> print_filter;
+
   // Used only in Android for ATRACE_EVENT/os.Trace() userspace annotations.
   std::vector<std::string> atrace_apps;
   std::vector<std::string> atrace_categories;
 
   // When enabled will turn on the kallsyms symbolizer in CpuReader.
   const bool symbolize_ksyms;
+
+  // Does not clear previous traces.
+  const bool preserve_ftrace_buffer;
+
+  // List of syscalls monitored to return a new filedescriptor upon success
+  base::FlatSet<int64_t> syscalls_returning_fd;
 };
 
 // Ftrace is a bunch of globally modifiable persistent state.
@@ -84,6 +107,7 @@
   FtraceConfigMuxer(
       FtraceProcfs* ftrace,
       ProtoTranslationTable* table,
+      SyscallTable syscalls,
       std::map<std::string, std::vector<GroupAndName>> vendor_events);
   virtual ~FtraceConfigMuxer();
 
@@ -107,21 +131,25 @@
 
   const FtraceDataSourceConfig* GetDataSourceConfig(FtraceConfigId id);
 
+  // Resets the current tracer to "nop" (the default). This cannot be handled
+  // by |RemoveConfig| because it requires all ftrace readers to be released
+  // beforehand, which is the reponsibility of ftrace_controller.
+  bool ResetCurrentTracer();
+
   // Returns the current per-cpu buffer size, as configured by this muxer
   // (without consulting debugfs). Constant for a given tracing session.
   // Note that if there are multiple concurrent tracing sessions, the first
   // session's buffer size is used for all of them.
   size_t GetPerCpuBufferSizePages();
 
-  // public for testing
-  void SetupClockForTesting(const FtraceConfig& request) {
-    SetupClock(request);
-  }
-
   protos::pbzero::FtraceClock ftrace_clock() const {
     return current_state_.ftrace_clock;
   }
 
+  void SetupClockForTesting(const FtraceConfig& request) {
+    SetupClock(request);
+  }
+
   std::set<GroupAndName> GetFtraceEventsForTesting(
       const FtraceConfig& request,
       const ProtoTranslationTable* table) {
@@ -132,6 +160,16 @@
     return &current_state_.ftrace_events;
   }
 
+  const std::set<size_t>& GetSyscallFilterForTesting() const {
+    return current_state_.syscall_filter;
+  }
+
+  // Returns the syscall ids for the current architecture
+  // matching the (subjectively) most commonly used syscalls
+  // producing a new file descriptor as their return value.
+  static base::FlatSet<int64_t> GetSyscallsReturningFds(
+      const SyscallTable& syscalls);
+
  private:
   static bool StartAtrace(const std::vector<std::string>& apps,
                           const std::vector<std::string>& categories,
@@ -139,12 +177,15 @@
 
   struct FtraceState {
     EventFilter ftrace_events;
-    // Used only in Android for ATRACE_EVENT/os.Trace() userspace
+    std::set<size_t> syscall_filter;  // syscall ids or kAllSyscallsId
+    bool funcgraph_on = false;        // current_tracer == "function_graph"
+    size_t cpu_buffer_size_pages = 0;
+    protos::pbzero::FtraceClock ftrace_clock{};
+    // Used only in Android for ATRACE_EVENT/os.Trace() userspace:
+    bool atrace_on = false;
     std::vector<std::string> atrace_apps;
     std::vector<std::string> atrace_categories;
-    size_t cpu_buffer_size_pages = 0;
-    bool atrace_on = false;
-    protos::pbzero::FtraceClock ftrace_clock{};
+    bool saved_tracing_on;  // Backup for the original tracing_on.
   };
 
   FtraceConfigMuxer(const FtraceConfigMuxer&) = delete;
@@ -162,11 +203,30 @@
   std::set<GroupAndName> GetFtraceEvents(const FtraceConfig& request,
                                          const ProtoTranslationTable*);
 
+  // Returns true if the event filter has at least one event from group.
+  bool FilterHasGroup(const EventFilter& filter, const std::string& group);
+
+  // Configs have three states:
+  // 1. The config does not include raw_syscall ftrace events (empty filter).
+  // 2. The config has at least one raw_syscall ftrace events, then either:
+  //   a. The syscall_events is left empty (match all events).
+  //   b. The syscall_events is non-empty (match only those events).
+  EventFilter BuildSyscallFilter(const EventFilter& ftrace_filter,
+                                 const FtraceConfig& request);
+
+  // Updates the ftrace syscall filters such that they satisfy all ds_configs_
+  // and the extra_syscalls provided here. The filter is set to be the union of
+  // all configs meaning no config will lose events, but concurrent configs can
+  // see additional events. You may provide a syscall filter during SetUpConfig
+  // so the filter can be updated before ds_configs_.
+  bool SetSyscallEventFilter(const EventFilter& extra_syscalls);
+
   FtraceConfigId GetNextId();
 
   FtraceConfigId last_id_ = 1;
   FtraceProcfs* ftrace_;
   ProtoTranslationTable* table_;
+  SyscallTable syscalls_;
 
   FtraceState current_state_;
 
diff --git a/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc b/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
index 4012763..5be1f1f 100644
--- a/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
+++ b/src/traced/probes/ftrace/ftrace_config_muxer_unittest.cc
@@ -45,6 +45,13 @@
 constexpr int kFakeSchedSwitchEventId = 1;
 constexpr int kCgroupMkdirEventId = 12;
 constexpr int kFakePrintEventId = 20;
+constexpr int kSysEnterId = 329;
+
+constexpr size_t kFakeSyscallCount = 2;
+constexpr const char* kFakeSyscalls[] = {
+    "sys_open",
+    "sys_read",
+};
 
 class MockFtraceProcfs : public FtraceProcfs {
  public:
@@ -123,6 +130,10 @@
             InvalidCompactSchedEventFormatForTesting()));
   }
 
+  SyscallTable GetSyscallTable() {
+    return SyscallTable(kFakeSyscalls, kFakeSyscallCount);
+  }
+
   std::unique_ptr<ProtoTranslationTable> CreateFakeTable(
       CompactSchedEventFormat compact_format =
           InvalidCompactSchedEventFormatForTesting()) {
@@ -184,6 +195,14 @@
       events.push_back(event);
     }
 
+    {
+      Event event = {};
+      event.name = "sys_enter";
+      event.group = "raw_syscalls";
+      event.ftrace_event_id = kSysEnterId;
+      events.push_back(event);
+    }
+
     return std::unique_ptr<ProtoTranslationTable>(new ProtoTranslationTable(
         &table_procfs_, events, std::move(common_fields),
         ProtoTranslationTable::DefaultPageHeaderSpecForTesting(),
@@ -208,25 +227,127 @@
   EXPECT_EQ(ComputeCpuBufferSizeInPages(42), 10u);
 }
 
+TEST_F(FtraceConfigMuxerTest, GenericSyscallFiltering) {
+  auto fake_table = CreateFakeTable();
+  NiceMock<MockFtraceProcfs> ftrace;
+
+  FtraceConfig config = CreateFtraceConfig({"raw_syscalls/sys_enter"});
+  *config.add_syscall_events() = "sys_open";
+  *config.add_syscall_events() = "sys_read";
+
+  FtraceConfigMuxer model(&ftrace, fake_table.get(), GetSyscallTable(), {});
+
+  ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
+      .WillByDefault(Return("[local] global boot"));
+  EXPECT_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
+      .Times(AnyNumber());
+  EXPECT_CALL(ftrace, ReadFileIntoString("/root/current_tracer"))
+      .WillOnce(Return("nop"));
+  EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
+      .WillOnce(Return('1'));
+  EXPECT_CALL(ftrace, WriteToFile(_, _)).WillRepeatedly(Return(true));
+  EXPECT_CALL(ftrace, WriteToFile("/root/events/raw_syscalls/sys_enter/filter",
+                                  "id == 0 || id == 1"));
+  EXPECT_CALL(ftrace, WriteToFile("/root/events/raw_syscalls/sys_exit/filter",
+                                  "id == 0 || id == 1"));
+
+  FtraceConfigId id = model.SetupConfig(config);
+  ASSERT_TRUE(model.ActivateConfig(id));
+
+  const std::set<size_t>& filter = model.GetSyscallFilterForTesting();
+  ASSERT_THAT(filter, UnorderedElementsAre(0, 1));
+}
+
+TEST_F(FtraceConfigMuxerTest, UnknownSyscallFilter) {
+  auto fake_table = CreateFakeTable();
+  NiceMock<MockFtraceProcfs> ftrace;
+  FtraceConfigMuxer model(&ftrace, fake_table.get(), GetSyscallTable(), {});
+
+  FtraceConfig config = CreateFtraceConfig({"raw_syscalls/sys_enter"});
+  config.add_syscall_events("sys_open");
+  config.add_syscall_events("sys_not_a_call");
+
+  ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
+      .WillByDefault(Return("[local] global boot"));
+  EXPECT_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
+      .Times(AnyNumber());
+  EXPECT_CALL(ftrace, ReadFileIntoString("/root/current_tracer"))
+      .WillOnce(Return("nop"));
+  EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
+      .WillOnce(Return('1'));
+
+  // Unknown syscall is ignored.
+  ASSERT_TRUE(model.SetupConfig(config));
+  ASSERT_THAT(model.GetSyscallFilterForTesting(), UnorderedElementsAre(0));
+}
+
+TEST_F(FtraceConfigMuxerTest, SyscallFilterMuxing) {
+  auto fake_table = CreateFakeTable();
+  NiceMock<MockFtraceProcfs> ftrace;
+  FtraceConfigMuxer model(&ftrace, fake_table.get(), GetSyscallTable(), {});
+
+  FtraceConfig empty_config = CreateFtraceConfig({});
+
+  FtraceConfig syscall_config = empty_config;
+  syscall_config.add_ftrace_events("raw_syscalls/sys_enter");
+
+  FtraceConfig syscall_open_config = syscall_config;
+  syscall_open_config.add_syscall_events("sys_open");
+
+  FtraceConfig syscall_read_config = syscall_config;
+  syscall_read_config.add_syscall_events("sys_read");
+
+  ON_CALL(ftrace, ReadFileIntoString("/root/current_tracer"))
+      .WillByDefault(Return("nop"));
+
+  // Expect no filter for non-syscall config.
+  model.SetupConfig(empty_config);
+  ASSERT_THAT(model.GetSyscallFilterForTesting(), UnorderedElementsAre());
+
+  // Expect no filter for syscall config with no specified events.
+  FtraceConfigId syscall_id = model.SetupConfig(syscall_config);
+  ASSERT_THAT(model.GetSyscallFilterForTesting(), UnorderedElementsAre());
+
+  // Still expect no filter to satisfy this and the above.
+  FtraceConfigId syscall_open_id = model.SetupConfig(syscall_open_config);
+  ASSERT_THAT(model.GetSyscallFilterForTesting(), UnorderedElementsAre());
+
+  // After removing the generic syscall trace, only the one with filter is left.
+  ASSERT_TRUE(model.RemoveConfig(syscall_id));
+  ASSERT_THAT(model.GetSyscallFilterForTesting(), UnorderedElementsAre(0));
+
+  // With sys_read and sys_open traced separately, filter includes both.
+  FtraceConfigId syscall_read_id = model.SetupConfig(syscall_read_config);
+  ASSERT_THAT(model.GetSyscallFilterForTesting(), UnorderedElementsAre(0, 1));
+
+  // After removing configs with filters, filter is reset to empty.
+  ASSERT_TRUE(model.RemoveConfig(syscall_open_id));
+  ASSERT_TRUE(model.RemoveConfig(syscall_read_id));
+  ASSERT_THAT(model.GetSyscallFilterForTesting(), UnorderedElementsAre());
+}
+
 TEST_F(FtraceConfigMuxerTest, AddGenericEvent) {
   auto mock_table = GetMockTable();
   MockFtraceProcfs ftrace;
 
   FtraceConfig config = CreateFtraceConfig({"power/cpu_frequency"});
 
-  FtraceConfigMuxer model(&ftrace, mock_table.get(), {});
+  FtraceConfigMuxer model(&ftrace, mock_table.get(), GetSyscallTable(), {});
 
+  EXPECT_CALL(ftrace, ReadFileIntoString("/root/current_tracer"))
+      .WillOnce(Return("nop"));
+  EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
+      .WillOnce(Return('1'));
+  EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "0"));
+  EXPECT_CALL(ftrace, WriteToFile("/root/events/enable", "0"));
+  EXPECT_CALL(ftrace, ClearFile("/root/trace"));
+  EXPECT_CALL(ftrace, ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")));
   ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
       .WillByDefault(Return("[local] global boot"));
   EXPECT_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
       .Times(AnyNumber());
-
-  EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
-      .Times(2)
-      .WillRepeatedly(Return('0'));
   EXPECT_CALL(ftrace, WriteToFile("/root/buffer_size_kb", _));
   EXPECT_CALL(ftrace, WriteToFile("/root/trace_clock", "boot"));
-  EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "1"));
   EXPECT_CALL(ftrace,
               WriteToFile("/root/events/power/cpu_frequency/enable", "1"));
   EXPECT_CALL(*mock_table, GetEvent(GroupAndName("power", "cpu_frequency")))
@@ -243,6 +364,8 @@
               GetOrCreateEvent(GroupAndName("power", "cpu_frequency")));
 
   FtraceConfigId id = model.SetupConfig(config);
+
+  EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "1"));
   ASSERT_TRUE(model.ActivateConfig(id));
 
   const FtraceDataSourceConfig* ds_config = model.GetDataSourceConfig(id);
@@ -261,7 +384,7 @@
 
   FtraceConfig config = CreateFtraceConfig({"group_one/foo", "group_two/foo"});
 
-  FtraceConfigMuxer model(&ftrace, mock_table.get(), {});
+  FtraceConfigMuxer model(&ftrace, mock_table.get(), GetSyscallTable(), {});
 
   static constexpr int kEventId1 = 1;
   Event event1;
@@ -281,6 +404,11 @@
       .WillByDefault(Return(&event2));
   EXPECT_CALL(*mock_table, GetOrCreateEvent(GroupAndName("group_two", "foo")));
 
+  ON_CALL(ftrace, ReadFileIntoString("/root/current_tracer"))
+      .WillByDefault(Return("nop"));
+  ON_CALL(ftrace, ReadFileIntoString("/root/events/enable"))
+      .WillByDefault(Return("0"));
+
   FtraceConfigId id = model.SetupConfig(config);
   ASSERT_TRUE(model.ActivateConfig(id));
 
@@ -299,23 +427,26 @@
 
   FtraceConfig config = CreateFtraceConfig({"sched/*"});
 
+  EXPECT_CALL(ftrace, ReadFileIntoString("/root/current_tracer"))
+      .WillOnce(Return("nop"));
+  EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
+      .WillOnce(Return('1'));
+  EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "0"));
+  EXPECT_CALL(ftrace, WriteToFile("/root/events/enable", "0"));
+  EXPECT_CALL(ftrace, ClearFile("/root/trace"));
+  EXPECT_CALL(ftrace, ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")));
   ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
       .WillByDefault(Return("[local] global boot"));
   EXPECT_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
       .Times(AnyNumber());
-
-  EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
-      .Times(2)
-      .WillRepeatedly(Return('0'));
   EXPECT_CALL(ftrace, WriteToFile("/root/buffer_size_kb", _));
   EXPECT_CALL(ftrace, WriteToFile("/root/trace_clock", "boot"));
-  EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "1"));
   EXPECT_CALL(ftrace,
               WriteToFile("/root/events/sched/sched_switch/enable", "1"));
   EXPECT_CALL(ftrace,
               WriteToFile("/root/events/sched/sched_new_event/enable", "1"));
 
-  FtraceConfigMuxer model(&ftrace, mock_table.get(), {});
+  FtraceConfigMuxer model(&ftrace, mock_table.get(), GetSyscallTable(), {});
   std::set<std::string> n = {"sched_switch", "sched_new_event"};
   ON_CALL(ftrace, GetEventNamesForGroup("events/sched"))
       .WillByDefault(Return(n));
@@ -345,6 +476,8 @@
 
   FtraceConfigId id = model.SetupConfig(config);
   ASSERT_TRUE(id);
+
+  EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "1"));
   ASSERT_TRUE(model.ActivateConfig(id));
 
   const FtraceDataSourceConfig* ds_config = model.GetDataSourceConfig(id);
@@ -363,7 +496,7 @@
 
   FtraceConfig config = CreateFtraceConfig({"group_one/*", "group_two/*"});
 
-  FtraceConfigMuxer model(&ftrace, mock_table.get(), {});
+  FtraceConfigMuxer model(&ftrace, mock_table.get(), GetSyscallTable(), {});
 
   std::set<std::string> event_names = {"foo"};
   ON_CALL(ftrace, GetEventNamesForGroup("events/group_one"))
@@ -394,6 +527,11 @@
       .WillByDefault(Return(&event2));
   EXPECT_CALL(*mock_table, GetOrCreateEvent(GroupAndName("group_two", "foo")));
 
+  ON_CALL(ftrace, ReadFileIntoString("/root/current_tracer"))
+      .WillByDefault(Return("nop"));
+  ON_CALL(ftrace, ReadFileIntoString("/root/events/enable"))
+      .WillByDefault(Return("0"));
+
   FtraceConfigId id = model.SetupConfig(config);
   ASSERT_TRUE(model.ActivateConfig(id));
 
@@ -412,24 +550,29 @@
 
   FtraceConfig config = CreateFtraceConfig({"sched_switch", "foo"});
 
-  FtraceConfigMuxer model(&ftrace, table_.get(), {});
+  FtraceConfigMuxer model(&ftrace, table_.get(), GetSyscallTable(), {});
 
+  EXPECT_CALL(ftrace, ReadFileIntoString("/root/current_tracer"))
+      .WillOnce(Return("nop"));
+  EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
+      .WillOnce(Return('1'));
+  EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "0"));
+  EXPECT_CALL(ftrace, WriteToFile("/root/events/enable", "0"));
+  EXPECT_CALL(ftrace, ClearFile("/root/trace"));
+  EXPECT_CALL(ftrace, ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")));
   ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
       .WillByDefault(Return("[local] global boot"));
   EXPECT_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
       .Times(AnyNumber());
-
-  EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
-      .Times(2)
-      .WillRepeatedly(Return('0'));
   EXPECT_CALL(ftrace, WriteToFile("/root/buffer_size_kb", _));
   EXPECT_CALL(ftrace, WriteToFile("/root/trace_clock", "boot"));
-  EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "1"));
   EXPECT_CALL(ftrace,
               WriteToFile("/root/events/sched/sched_switch/enable", "1"));
 
   FtraceConfigId id = model.SetupConfig(config);
   ASSERT_TRUE(id);
+
+  EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "1"));
   ASSERT_TRUE(model.ActivateConfig(id));
 
   const FtraceDataSourceConfig* ds_config = model.GetDataSourceConfig(id);
@@ -444,13 +587,14 @@
   ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&ftrace));
   EXPECT_CALL(ftrace, NumberOfCpus()).Times(AnyNumber());
 
+  EXPECT_CALL(ftrace,
+              WriteToFile("/root/events/sched/sched_switch/enable", "0"));
   EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "0"));
   EXPECT_CALL(ftrace, WriteToFile("/root/buffer_size_kb", "4"));
   EXPECT_CALL(ftrace, WriteToFile("/root/events/enable", "0"));
-  EXPECT_CALL(ftrace,
-              WriteToFile("/root/events/sched/sched_switch/enable", "0"));
   EXPECT_CALL(ftrace, ClearFile("/root/trace"));
   EXPECT_CALL(ftrace, ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")));
+  EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "1"));
 
   ASSERT_TRUE(model.RemoveConfig(id));
 }
@@ -460,11 +604,11 @@
 
   FtraceConfig config = CreateFtraceConfig({"sched/sched_switch"});
 
-  FtraceConfigMuxer model(&ftrace, table_.get(), {});
+  FtraceConfigMuxer model(&ftrace, table_.get(), GetSyscallTable(), {});
 
   // If someone is using ftrace already don't stomp on what they are doing.
-  EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
-      .WillOnce(Return('1'));
+  EXPECT_CALL(ftrace, ReadFileIntoString("/root/current_tracer"))
+      .WillOnce(Return("function"));
   FtraceConfigId id = model.SetupConfig(config);
   ASSERT_FALSE(id);
 }
@@ -476,10 +620,12 @@
   FtraceConfig config = CreateFtraceConfig({"sched/sched_switch"});
   *config.add_atrace_categories() = "sched";
 
-  FtraceConfigMuxer model(&ftrace, table_.get(), {});
+  FtraceConfigMuxer model(&ftrace, table_.get(), GetSyscallTable(), {});
 
-  EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
-      .WillOnce(Return('0'));
+  ON_CALL(ftrace, ReadFileIntoString("/root/current_tracer"))
+      .WillByDefault(Return("nop"));
+  ON_CALL(ftrace, ReadFileIntoString("/root/events/enable"))
+      .WillByDefault(Return("0"));
   EXPECT_CALL(atrace, RunAtrace(ElementsAreArray({"atrace", "--async_start",
                                                   "--only_userspace", "sched"}),
                                 _))
@@ -518,10 +664,12 @@
   *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(), GetSyscallTable(), {});
 
-  EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
-      .WillOnce(Return('0'));
+  ON_CALL(ftrace, ReadFileIntoString("/root/current_tracer"))
+      .WillByDefault(Return("nop"));
+  ON_CALL(ftrace, ReadFileIntoString("/root/events/enable"))
+      .WillByDefault(Return("0"));
   EXPECT_CALL(
       atrace,
       RunAtrace(
@@ -563,8 +711,12 @@
   *config_c.add_atrace_apps() = "app_c";
   *config_c.add_atrace_categories() = "cat_c";
 
-  FtraceConfigMuxer model(&ftrace, table_.get(), {});
+  FtraceConfigMuxer model(&ftrace, table_.get(), GetSyscallTable(), {});
 
+  ON_CALL(ftrace, ReadFileIntoString("/root/current_tracer"))
+      .WillByDefault(Return("nop"));
+  ON_CALL(ftrace, ReadFileIntoString("/root/events/enable"))
+      .WillByDefault(Return("0"));
   EXPECT_CALL(atrace, RunAtrace(ElementsAreArray({"atrace", "--async_start",
                                                   "--only_userspace", "cat_a",
                                                   "-a", "app_a"}),
@@ -634,8 +786,12 @@
   *config_c.add_atrace_categories() = "cat_1";
   *config_c.add_atrace_categories() = "cat_3";
 
-  FtraceConfigMuxer model(&ftrace, table_.get(), {});
+  FtraceConfigMuxer model(&ftrace, table_.get(), GetSyscallTable(), {});
 
+  ON_CALL(ftrace, ReadFileIntoString("/root/current_tracer"))
+      .WillByDefault(Return("nop"));
+  ON_CALL(ftrace, ReadFileIntoString("/root/events/enable"))
+      .WillByDefault(Return("0"));
   EXPECT_CALL(
       atrace,
       RunAtrace(ElementsAreArray({"atrace", "--async_start", "--only_userspace",
@@ -695,8 +851,12 @@
   *config_b.add_atrace_apps() = "app_1";
   *config_b.add_atrace_categories() = "cat_1";
 
-  FtraceConfigMuxer model(&ftrace, table_.get(), {});
+  FtraceConfigMuxer model(&ftrace, table_.get(), GetSyscallTable(), {});
 
+  ON_CALL(ftrace, ReadFileIntoString("/root/current_tracer"))
+      .WillByDefault(Return("nop"));
+  ON_CALL(ftrace, ReadFileIntoString("/root/events/enable"))
+      .WillByDefault(Return("0"));
   EXPECT_CALL(atrace, RunAtrace(ElementsAreArray({"atrace", "--async_start",
                                                   "--only_userspace", "cat_1",
                                                   "-a", "app_1"}),
@@ -732,8 +892,12 @@
   FtraceConfig config_d = CreateFtraceConfig({"sched/sched_cpu_hotplug"});
   *config_d.add_atrace_categories() = "d";
 
-  FtraceConfigMuxer model(&ftrace, table_.get(), {});
+  FtraceConfigMuxer model(&ftrace, table_.get(), GetSyscallTable(), {});
 
+  ON_CALL(ftrace, ReadFileIntoString("/root/current_tracer"))
+      .WillByDefault(Return("nop"));
+  ON_CALL(ftrace, ReadFileIntoString("/root/events/enable"))
+      .WillByDefault(Return("0"));
   FtraceConfigId id_a = model.SetupConfig(config_a);
   ASSERT_TRUE(id_a);
 
@@ -781,10 +945,12 @@
   *config.add_atrace_categories() = "cat_1";
   *config.add_atrace_categories() = "cat_2";
 
-  EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
-      .WillRepeatedly(Return('0'));
+  ON_CALL(ftrace, ReadFileIntoString("/root/current_tracer"))
+      .WillByDefault(Return("nop"));
+  ON_CALL(ftrace, ReadFileIntoString("/root/events/enable"))
+      .WillByDefault(Return("0"));
 
-  FtraceConfigMuxer model(&ftrace, table_.get(), {});
+  FtraceConfigMuxer model(&ftrace, table_.get(), GetSyscallTable(), {});
 
   EXPECT_CALL(atrace, RunAtrace(ElementsAreArray({"atrace", "--async_start",
                                                   "--only_userspace", "cat_1",
@@ -807,7 +973,7 @@
   MockFtraceProcfs ftrace;
   FtraceConfig config;
 
-  FtraceConfigMuxer model(&ftrace, table_.get(), {});
+  FtraceConfigMuxer model(&ftrace, table_.get(), GetSyscallTable(), {});
   namespace pb0 = protos::pbzero;
 
   EXPECT_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
@@ -840,7 +1006,7 @@
 
 TEST_F(FtraceConfigMuxerTest, GetFtraceEvents) {
   MockFtraceProcfs ftrace;
-  FtraceConfigMuxer model(&ftrace, table_.get(), {});
+  FtraceConfigMuxer model(&ftrace, table_.get(), GetSyscallTable(), {});
 
   FtraceConfig config = CreateFtraceConfig({"sched/sched_switch"});
   std::set<GroupAndName> events =
@@ -852,7 +1018,7 @@
 
 TEST_F(FtraceConfigMuxerTest, GetFtraceEventsAtrace) {
   MockFtraceProcfs ftrace;
-  FtraceConfigMuxer model(&ftrace, table_.get(), {});
+  FtraceConfigMuxer model(&ftrace, table_.get(), GetSyscallTable(), {});
 
   FtraceConfig config = CreateFtraceConfig({});
   *config.add_atrace_categories() = "sched";
@@ -866,7 +1032,7 @@
 
 TEST_F(FtraceConfigMuxerTest, GetFtraceEventsAtraceCategories) {
   MockFtraceProcfs ftrace;
-  FtraceConfigMuxer model(&ftrace, table_.get(), {});
+  FtraceConfigMuxer model(&ftrace, table_.get(), GetSyscallTable(), {});
 
   FtraceConfig config = CreateFtraceConfig({});
   *config.add_atrace_categories() = "sched";
@@ -890,19 +1056,22 @@
   MockFtraceProcfs ftrace;
   FtraceConfig config =
       CreateFtraceConfig({"sched/sched_switch", "cgroup/cgroup_mkdir"});
-  FtraceConfigMuxer model(&ftrace, table_.get(), {});
+  FtraceConfigMuxer model(&ftrace, table_.get(), GetSyscallTable(), {});
 
+  EXPECT_CALL(ftrace, ReadFileIntoString("/root/current_tracer"))
+      .WillOnce(Return("nop"));
+  EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
+      .WillOnce(Return('1'));
+  EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "0"));
+  EXPECT_CALL(ftrace, WriteToFile("/root/events/enable", "0"));
+  EXPECT_CALL(ftrace, ClearFile("/root/trace"));
+  EXPECT_CALL(ftrace, ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")));
   ON_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
       .WillByDefault(Return("[local] global boot"));
   EXPECT_CALL(ftrace, ReadFileIntoString("/root/trace_clock"))
       .Times(AnyNumber());
-
-  EXPECT_CALL(ftrace, ReadOneCharFromFile("/root/tracing_on"))
-      .Times(2)
-      .WillRepeatedly(Return('0'));
   EXPECT_CALL(ftrace, WriteToFile("/root/buffer_size_kb", _));
   EXPECT_CALL(ftrace, WriteToFile("/root/trace_clock", "boot"));
-  EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "1"));
   EXPECT_CALL(ftrace,
               WriteToFile("/root/events/sched/sched_switch/enable", "1"));
   EXPECT_CALL(ftrace,
@@ -912,6 +1081,8 @@
       .WillOnce(Return(true));
   FtraceConfigId id = model.SetupConfig(config);
   ASSERT_TRUE(id);
+
+  EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "1"));
   ASSERT_TRUE(model.ActivateConfig(id));
 
   const FtraceDataSourceConfig* ds_config = model.GetDataSourceConfig(id);
@@ -927,9 +1098,6 @@
   EXPECT_THAT(central_filter->GetEnabledEvents(),
               Contains(kCgroupMkdirEventId));
 
-  EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "0"));
-  EXPECT_CALL(ftrace, WriteToFile("/root/buffer_size_kb", "4"));
-  EXPECT_CALL(ftrace, WriteToFile("/root/events/enable", "0"));
   EXPECT_CALL(ftrace,
               WriteToFile("/root/events/sched/sched_switch/enable", "0"));
   EXPECT_CALL(ftrace,
@@ -937,8 +1105,12 @@
       .WillOnce(Return(false));
   EXPECT_CALL(ftrace, AppendToFile("/root/set_event", "!cgroup:cgroup_mkdir"))
       .WillOnce(Return(true));
+  EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "0"));
+  EXPECT_CALL(ftrace, WriteToFile("/root/buffer_size_kb", "4"));
+  EXPECT_CALL(ftrace, WriteToFile("/root/events/enable", "0"));
   EXPECT_CALL(ftrace, ClearFile("/root/trace"));
   EXPECT_CALL(ftrace, ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")));
+  EXPECT_CALL(ftrace, WriteToFile("/root/tracing_on", "1"));
   ASSERT_TRUE(model.RemoveConfig(id));
 }
 
@@ -951,7 +1123,7 @@
 
   NiceMock<MockFtraceProcfs> ftrace;
   table_ = CreateFakeTable(valid_compact_format);
-  FtraceConfigMuxer model(&ftrace, table_.get(), {});
+  FtraceConfigMuxer model(&ftrace, table_.get(), GetSyscallTable(), {});
 
   // First data source - request compact encoding.
   FtraceConfig config_enabled = CreateFtraceConfig({"sched/sched_switch"});
@@ -960,6 +1132,11 @@
   // Second data source - no compact encoding (default).
   FtraceConfig config_disabled = CreateFtraceConfig({"sched/sched_switch"});
 
+  ON_CALL(ftrace, ReadFileIntoString("/root/current_tracer"))
+      .WillByDefault(Return("nop"));
+  ON_CALL(ftrace, ReadFileIntoString("/root/events/enable"))
+      .WillByDefault(Return("0"));
+
   {
     FtraceConfigId id = model.SetupConfig(config_enabled);
     ASSERT_TRUE(id);
@@ -982,12 +1159,17 @@
 
 TEST_F(FtraceConfigMuxerTest, CompactSchedConfigWithInvalidFormat) {
   NiceMock<MockFtraceProcfs> ftrace;
-  FtraceConfigMuxer model(&ftrace, table_.get(), {});
+  FtraceConfigMuxer model(&ftrace, table_.get(), GetSyscallTable(), {});
 
   // Request compact encoding.
   FtraceConfig config = CreateFtraceConfig({"sched/sched_switch"});
   config.mutable_compact_sched()->set_enabled(true);
 
+  ON_CALL(ftrace, ReadFileIntoString("/root/current_tracer"))
+      .WillByDefault(Return("nop"));
+  ON_CALL(ftrace, ReadFileIntoString("/root/events/enable"))
+      .WillByDefault(Return("0"));
+
   FtraceConfigId id = model.SetupConfig(config);
   ASSERT_TRUE(id);
 
@@ -1003,7 +1185,7 @@
 
 TEST_F(FtraceConfigMuxerTest, SkipGenericEventsOption) {
   NiceMock<MockFtraceProcfs> ftrace;
-  FtraceConfigMuxer model(&ftrace, table_.get(), {});
+  FtraceConfigMuxer model(&ftrace, table_.get(), GetSyscallTable(), {});
 
   static constexpr int kFtraceGenericEventId = 42;
   ON_CALL(table_procfs_, ReadEventFormat("sched", "generic"))
@@ -1026,6 +1208,11 @@
       CreateFtraceConfig({"sched/sched_switch", "sched/generic"});
   config_with_disable.set_disable_generic_events(true);
 
+  ON_CALL(ftrace, ReadFileIntoString("/root/current_tracer"))
+      .WillByDefault(Return("nop"));
+  ON_CALL(ftrace, ReadFileIntoString("/root/events/enable"))
+      .WillByDefault(Return("0"));
+
   {
     FtraceConfigId id = model.SetupConfig(config_default);
     ASSERT_TRUE(id);
@@ -1047,5 +1234,54 @@
   }
 }
 
+TEST_F(FtraceConfigMuxerTest, Funcgraph) {
+  auto fake_table = CreateFakeTable();
+  NiceMock<MockFtraceProcfs> ftrace;
+  FtraceConfigMuxer model(&ftrace, fake_table.get(), GetSyscallTable(), {});
+
+  FtraceConfig config;
+  config.set_enable_function_graph(true);
+  *config.add_function_filters() = "sched*";
+  *config.add_function_filters() = "handle_mm_fault";
+
+  *config.add_function_graph_roots() = "sched*";
+  *config.add_function_graph_roots() = "*mm_fault";
+
+  ON_CALL(ftrace, ReadFileIntoString("/root/current_tracer"))
+      .WillByDefault(Return("nop"));
+
+  EXPECT_CALL(ftrace, WriteToFile(_, _)).WillRepeatedly(Return(true));
+
+  EXPECT_CALL(ftrace, ClearFile("/root/trace"));
+  EXPECT_CALL(ftrace, ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")));
+
+  // Set up config, assert that the tracefs writes happened:
+  EXPECT_CALL(ftrace, ClearFile("/root/set_ftrace_filter"));
+  EXPECT_CALL(ftrace, ClearFile("/root/set_graph_function"));
+  EXPECT_CALL(ftrace, AppendToFile("/root/set_ftrace_filter",
+                                   "sched*\nhandle_mm_fault"))
+      .WillOnce(Return(true));
+  EXPECT_CALL(ftrace,
+              AppendToFile("/root/set_graph_function", "sched*\n*mm_fault"))
+      .WillOnce(Return(true));
+  EXPECT_CALL(ftrace, WriteToFile("/root/current_tracer", "function_graph"))
+      .WillOnce(Return(true));
+  FtraceConfigId id = model.SetupConfig(config);
+  ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&ftrace));
+  // Toggle config on and off, tracer won't be reset yet:
+  ASSERT_TRUE(model.ActivateConfig(id));
+  ASSERT_TRUE(model.RemoveConfig(id));
+  ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&ftrace));
+
+  // Emulate ftrace_controller's call to ResetCurrentTracer (see impl comments
+  // for why RemoveConfig is insufficient).
+  EXPECT_CALL(ftrace, ClearFile("/root/set_ftrace_filter"));
+  EXPECT_CALL(ftrace, ClearFile("/root/set_graph_function"));
+  EXPECT_CALL(ftrace, WriteToFile("/root/current_tracer", "nop"))
+      .WillOnce(Return(true));
+  ASSERT_TRUE(model.ResetCurrentTracer());
+  ASSERT_TRUE(testing::Mock::VerifyAndClearExpectations(&ftrace));
+}
+
 }  // namespace
 }  // namespace perfetto
diff --git a/src/traced/probes/ftrace/ftrace_controller.cc b/src/traced/probes/ftrace/ftrace_controller.cc
index 5cb1de1..d82d115 100644
--- a/src/traced/probes/ftrace/ftrace_controller.cc
+++ b/src/traced/probes/ftrace/ftrace_controller.cc
@@ -40,7 +40,7 @@
 #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/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"
@@ -124,9 +124,11 @@
     bool res = true;
     res &= WriteToFile((prefix + "tracing_on").c_str(), "0");
     res &= WriteToFile((prefix + "buffer_size_kb").c_str(), "4");
-    // We deliberately don't check for this as on some older versions of Android
-    // events/enable was not writable by the shell user.
+    // Not checking success because these files might not be accessible on
+    // older or release builds of Android:
     WriteToFile((prefix + "events/enable").c_str(), "0");
+    WriteToFile((prefix + "events/raw_syscalls/filter").c_str(), "0");
+    WriteToFile((prefix + "current_tracer").c_str(), "nop");
     res &= ClearFile((prefix + "trace").c_str());
     if (res)
       return true;
@@ -137,9 +139,10 @@
 // static
 std::unique_ptr<FtraceController> FtraceController::Create(
     base::TaskRunner* runner,
-    Observer* observer) {
+    Observer* observer,
+    bool preserve_ftrace_buffer) {
   std::unique_ptr<FtraceProcfs> ftrace_procfs =
-      FtraceProcfs::CreateGuessingMountPoint();
+      FtraceProcfs::CreateGuessingMountPoint("", preserve_ftrace_buffer);
 
   if (!ftrace_procfs)
     return nullptr;
@@ -150,22 +153,39 @@
   if (!table)
     return nullptr;
 
-  AtraceHalWrapper hal;
-  auto vendor_evts =
-      vendor_tracepoints::DiscoverVendorTracepoints(&hal, ftrace_procfs.get());
+  std::map<std::string, std::vector<GroupAndName>> vendor_evts;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+  if (base::FileExists(vendor_tracepoints::kCategoriesFile)) {
+    base::Status status =
+        vendor_tracepoints::DiscoverAccessibleVendorTracepointsWithFile(
+            vendor_tracepoints::kCategoriesFile, &vendor_evts,
+            ftrace_procfs.get());
+    if (!status.ok()) {
+      PERFETTO_ELOG("Cannot load vendor categories: %s", status.c_message());
+    }
+  } else {
+    AtraceHalWrapper hal;
+    vendor_evts = vendor_tracepoints::DiscoverVendorTracepointsWithHal(
+        &hal, ftrace_procfs.get());
+  }
+#endif
 
-  std::unique_ptr<FtraceConfigMuxer> model = std::unique_ptr<FtraceConfigMuxer>(
-      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));
+  auto syscalls = SyscallTable::FromCurrentArch();
+
+  std::unique_ptr<FtraceConfigMuxer> model =
+      std::unique_ptr<FtraceConfigMuxer>(new FtraceConfigMuxer(
+          ftrace_procfs.get(), table.get(), std::move(syscalls), vendor_evts));
+  return std::unique_ptr<FtraceController>(new FtraceController(
+      std::move(ftrace_procfs), std::move(table), std::move(model), runner,
+      observer, preserve_ftrace_buffer));
 }
 
 FtraceController::FtraceController(std::unique_ptr<FtraceProcfs> ftrace_procfs,
                                    std::unique_ptr<ProtoTranslationTable> table,
                                    std::unique_ptr<FtraceConfigMuxer> model,
                                    base::TaskRunner* task_runner,
-                                   Observer* observer)
+                                   Observer* observer,
+                                   bool preserve_ftrace_buffer)
     : task_runner_(task_runner),
       observer_(observer),
       symbolizer_(new LazyKernelSymbolizer()),
@@ -173,6 +193,7 @@
       table_(std::move(table)),
       ftrace_config_muxer_(std::move(model)),
       ftrace_clock_snapshot_(new FtraceClockSnapshot()),
+      preserve_ftrace_buffer_(preserve_ftrace_buffer),
       weak_factory_(this) {}
 
 FtraceController::~FtraceController() {
@@ -207,10 +228,11 @@
     MaybeSnapshotFtraceClock();
   }
 
+  size_t num_cpus = ftrace_procfs_->NumberOfCpus();
   per_cpu_.clear();
-  per_cpu_.reserve(ftrace_procfs_->NumberOfCpus());
+  per_cpu_.reserve(num_cpus);
   size_t period_page_quota = ftrace_config_muxer_->GetPerCpuBufferSizePages();
-  for (size_t cpu = 0; cpu < ftrace_procfs_->NumberOfCpus(); cpu++) {
+  for (size_t cpu = 0; cpu < num_cpus; cpu++) {
     auto reader = std::unique_ptr<CpuReader>(new CpuReader(
         cpu, table_.get(), symbolizer_.get(), ftrace_clock_snapshot_.get(),
         ftrace_procfs_->OpenPipeForCpu(cpu)));
@@ -343,12 +365,8 @@
   ftrace_procfs_->ClearTrace();
 }
 
-void FtraceController::DisableAllEvents() {
-  ftrace_procfs_->DisableAllEvents();
-}
-
-void FtraceController::WriteTraceMarker(const std::string& s) {
-  ftrace_procfs_->WriteTraceMarker(s);
+bool FtraceController::IsTracingAvailable() {
+  return ftrace_procfs_->IsTracingAvailable();
 }
 
 void FtraceController::Flush(FlushRequestID flush_id) {
@@ -382,6 +400,11 @@
 
   per_cpu_.clear();
   cpu_zero_stats_fd_.reset();
+
+  // Muxer cannot change the current_tracer until we close the trace pipe fds
+  // (i.e. per_cpu_). Hence an explicit request here.
+  ftrace_config_muxer_->ResetCurrentTracer();
+
   if (!retain_ksyms_on_stop_) {
     symbolizer_->Destroy();
   }
diff --git a/src/traced/probes/ftrace/ftrace_controller.h b/src/traced/probes/ftrace/ftrace_controller.h
index e2b5990..fd8cdee 100644
--- a/src/traced/probes/ftrace/ftrace_controller.h
+++ b/src/traced/probes/ftrace/ftrace_controller.h
@@ -72,12 +72,13 @@
   };
 
   // The passed Observer must outlive the returned FtraceController instance.
-  static std::unique_ptr<FtraceController> Create(base::TaskRunner*, Observer*);
+  static std::unique_ptr<FtraceController> Create(base::TaskRunner*,
+                                                  Observer*,
+                                                  bool preserve_ftrace_buffer);
   virtual ~FtraceController();
 
-  void DisableAllEvents();
-  void WriteTraceMarker(const std::string& s);
   void ClearTrace();
+  bool IsTracingAvailable();
 
   bool AddDataSource(FtraceDataSource*) PERFETTO_WARN_UNUSED_RESULT;
   bool StartDataSource(FtraceDataSource*);
@@ -99,7 +100,8 @@
                    std::unique_ptr<ProtoTranslationTable>,
                    std::unique_ptr<FtraceConfigMuxer>,
                    base::TaskRunner*,
-                   Observer*);
+                   Observer*,
+                   bool);
 
   // Protected and virtual for testing.
   virtual uint64_t NowMs() const;
@@ -139,6 +141,7 @@
   int generation_ = 0;
   bool atrace_running_ = false;
   bool retain_ksyms_on_stop_ = false;
+  bool preserve_ftrace_buffer_ = false;
   std::vector<PerCpuState> per_cpu_;  // empty if tracing isn't active
   std::set<FtraceDataSource*> data_sources_;
   std::set<FtraceDataSource*> started_data_sources_;
diff --git a/src/traced/probes/ftrace/ftrace_controller_unittest.cc b/src/traced/probes/ftrace/ftrace_controller_unittest.cc
index a3a02cc..ba86ca7 100644
--- a/src/traced/probes/ftrace/ftrace_controller_unittest.cc
+++ b/src/traced/probes/ftrace/ftrace_controller_unittest.cc
@@ -94,7 +94,7 @@
 std::unique_ptr<FtraceConfigMuxer> FakeModel(FtraceProcfs* ftrace,
                                              ProtoTranslationTable* table) {
   return std::unique_ptr<FtraceConfigMuxer>(
-      new FtraceConfigMuxer(ftrace, table, {}));
+      new FtraceConfigMuxer(ftrace, table, SyscallTable(kUnknown), {}));
 }
 
 class MockFtraceProcfs : public FtraceProcfs {
@@ -132,6 +132,13 @@
         .WillByDefault(Invoke(this, &MockFtraceProcfs::ReadTracingOn));
     EXPECT_CALL(*this, ReadOneCharFromFile("/root/tracing_on"))
         .Times(AnyNumber());
+
+    ON_CALL(*this, WriteToFile("/root/current_tracer", _))
+        .WillByDefault(Invoke(this, &MockFtraceProcfs::WriteCurrentTracer));
+    ON_CALL(*this, ReadFileIntoString("/root/current_tracer"))
+        .WillByDefault(Invoke(this, &MockFtraceProcfs::ReadCurrentTracer));
+    EXPECT_CALL(*this, ReadFileIntoString("/root/current_tracer"))
+        .Times(AnyNumber());
   }
 
   bool WriteTracingOn(const std::string& /*path*/, const std::string& value) {
@@ -144,6 +151,16 @@
     return tracing_on_ ? '1' : '0';
   }
 
+  bool WriteCurrentTracer(const std::string& /*path*/,
+                          const std::string& value) {
+    current_tracer_ = value;
+    return true;
+  }
+
+  std::string ReadCurrentTracer(const std::string& /*path*/) {
+    return current_tracer_;
+  }
+
   base::ScopedFile OpenPipeForCpu(size_t /*cpu*/) override {
     return base::ScopedFile(base::OpenFile("/dev/null", O_RDONLY));
   }
@@ -153,12 +170,14 @@
   MOCK_CONST_METHOD0(NumberOfCpus, size_t());
   MOCK_METHOD1(ReadOneCharFromFile, char(const std::string& path));
   MOCK_METHOD1(ClearFile, bool(const std::string& path));
+  MOCK_METHOD1(IsFileWriteable, bool(const std::string& path));
   MOCK_CONST_METHOD1(ReadFileIntoString, std::string(const std::string& path));
 
   bool is_tracing_on() { return tracing_on_; }
 
  private:
-  bool tracing_on_ = false;
+  bool tracing_on_ = true;
+  std::string current_tracer_ = "nop";
 };
 
 }  // namespace
@@ -175,7 +194,8 @@
                          std::move(table),
                          std::move(model),
                          runner.get(),
-                         /*observer=*/this),
+                         /*observer=*/this,
+                         false),
         runner_(std::move(runner)),
         procfs_(raw_procfs) {}
 
@@ -259,8 +279,16 @@
 
   FtraceConfig config = CreateFtraceConfig({"group/foo"});
 
-  EXPECT_CALL(*controller->procfs(), WriteToFile(kFooEnablePath, "1"));
+  EXPECT_CALL(*controller->procfs(), WriteToFile("/root/tracing_on", "0"));
+  EXPECT_CALL(*controller->procfs(), WriteToFile("/root/events/enable", "0"));
+  EXPECT_CALL(*controller->procfs(), ClearFile("/root/trace"))
+      .WillOnce(Return(true));
+  EXPECT_CALL(*controller->procfs(),
+              ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")))
+      .WillRepeatedly(Return(true));
   EXPECT_CALL(*controller->procfs(), WriteToFile("/root/buffer_size_kb", _));
+  EXPECT_CALL(*controller->procfs(), WriteToFile(kFooEnablePath, "1"));
+
   auto data_source = controller->AddFakeDataSource(config);
   ASSERT_TRUE(data_source);
 
@@ -277,19 +305,19 @@
   Mock::VerifyAndClearExpectations(controller->runner());
 
   // State clearing on tracing teardown.
+  EXPECT_CALL(*controller->procfs(), WriteToFile(kFooEnablePath, "0"));
+  EXPECT_CALL(*controller->procfs(), WriteToFile("/root/tracing_on", "0"));
   EXPECT_CALL(*controller->procfs(), WriteToFile("/root/buffer_size_kb", "4"));
+  EXPECT_CALL(*controller->procfs(), WriteToFile("/root/events/enable", "0"));
   EXPECT_CALL(*controller->procfs(), ClearFile("/root/trace"))
       .WillOnce(Return(true));
   EXPECT_CALL(*controller->procfs(),
               ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")))
       .WillRepeatedly(Return(true));
-  EXPECT_CALL(*controller->procfs(), WriteToFile(kFooEnablePath, "0"));
-  EXPECT_CALL(*controller->procfs(), WriteToFile("/root/tracing_on", "0"));
-  EXPECT_CALL(*controller->procfs(), WriteToFile("/root/events/enable", "0"));
-  EXPECT_TRUE(controller->procfs()->is_tracing_on());
+  EXPECT_CALL(*controller->procfs(), WriteToFile("/root/tracing_on", "1"));
 
   data_source.reset();
-  EXPECT_FALSE(controller->procfs()->is_tracing_on());
+  EXPECT_TRUE(controller->procfs()->is_tracing_on());
 }
 
 TEST(FtraceControllerTest, MultipleSinks) {
@@ -301,6 +329,13 @@
   // No read tasks posted as part of adding the data sources.
   EXPECT_CALL(*controller->runner(), PostDelayedTask(_, _)).Times(0);
 
+  EXPECT_CALL(*controller->procfs(), WriteToFile("/root/tracing_on", "0"));
+  EXPECT_CALL(*controller->procfs(), WriteToFile("/root/events/enable", "0"));
+  EXPECT_CALL(*controller->procfs(), ClearFile("/root/trace"))
+      .WillOnce(Return(true));
+  EXPECT_CALL(*controller->procfs(),
+              ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")))
+      .WillRepeatedly(Return(true));
   EXPECT_CALL(*controller->procfs(), WriteToFile("/root/buffer_size_kb", _));
   EXPECT_CALL(*controller->procfs(), WriteToFile(kFooEnablePath, "1"));
   auto data_sourceA = controller->AddFakeDataSource(configA);
@@ -321,17 +356,22 @@
   Mock::VerifyAndClearExpectations(controller->runner());
 
   data_sourceA.reset();
+  EXPECT_TRUE(controller->procfs()->is_tracing_on());
 
   // State clearing on tracing teardown.
   EXPECT_CALL(*controller->procfs(), WriteToFile(kFooEnablePath, "0"));
   EXPECT_CALL(*controller->procfs(), WriteToFile(kBarEnablePath, "0"));
-  EXPECT_CALL(*controller->procfs(), WriteToFile("/root/buffer_size_kb", "4"));
   EXPECT_CALL(*controller->procfs(), WriteToFile("/root/tracing_on", "0"));
+  EXPECT_CALL(*controller->procfs(), WriteToFile("/root/buffer_size_kb", "4"));
   EXPECT_CALL(*controller->procfs(), WriteToFile("/root/events/enable", "0"));
-  EXPECT_CALL(*controller->procfs(), ClearFile("/root/trace"));
+  EXPECT_CALL(*controller->procfs(), ClearFile("/root/trace"))
+      .WillOnce(Return(true));
   EXPECT_CALL(*controller->procfs(),
-              ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")));
+              ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(*controller->procfs(), WriteToFile("/root/tracing_on", "1"));
   data_sourceB.reset();
+  EXPECT_TRUE(controller->procfs()->is_tracing_on());
 }
 
 TEST(FtraceControllerTest, ControllerMayDieFirst) {
@@ -339,6 +379,13 @@
 
   FtraceConfig config = CreateFtraceConfig({"group/foo"});
 
+  EXPECT_CALL(*controller->procfs(), WriteToFile("/root/tracing_on", "0"));
+  EXPECT_CALL(*controller->procfs(), WriteToFile("/root/events/enable", "0"));
+  EXPECT_CALL(*controller->procfs(), ClearFile("/root/trace"))
+      .WillOnce(Return(true));
+  EXPECT_CALL(*controller->procfs(),
+              ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")))
+      .WillRepeatedly(Return(true));
   EXPECT_CALL(*controller->procfs(), WriteToFile("/root/buffer_size_kb", _));
   EXPECT_CALL(*controller->procfs(), WriteToFile(kFooEnablePath, "1"));
   auto data_source = controller->AddFakeDataSource(config);
@@ -348,14 +395,15 @@
 
   // State clearing on tracing teardown.
   EXPECT_CALL(*controller->procfs(), WriteToFile(kFooEnablePath, "0"));
+  EXPECT_CALL(*controller->procfs(), WriteToFile("/root/tracing_on", "0"));
+  EXPECT_CALL(*controller->procfs(), WriteToFile("/root/buffer_size_kb", "4"));
+  EXPECT_CALL(*controller->procfs(), WriteToFile("/root/events/enable", "0"));
   EXPECT_CALL(*controller->procfs(), ClearFile("/root/trace"))
       .WillOnce(Return(true));
   EXPECT_CALL(*controller->procfs(),
               ClearFile(MatchesRegex("/root/per_cpu/cpu[0-9]/trace")))
       .WillRepeatedly(Return(true));
-  EXPECT_CALL(*controller->procfs(), WriteToFile("/root/tracing_on", "0"));
-  EXPECT_CALL(*controller->procfs(), WriteToFile("/root/buffer_size_kb", "4"));
-  EXPECT_CALL(*controller->procfs(), WriteToFile("/root/events/enable", "0"));
+  EXPECT_CALL(*controller->procfs(), WriteToFile("/root/tracing_on", "1"));
   controller.reset();
   data_source.reset();
 }
diff --git a/src/traced/probes/ftrace/ftrace_data_source.cc b/src/traced/probes/ftrace/ftrace_data_source.cc
index 32f945e..7c051fb 100644
--- a/src/traced/probes/ftrace/ftrace_data_source.cc
+++ b/src/traced/probes/ftrace/ftrace_data_source.cc
@@ -112,6 +112,13 @@
     return;
   DumpFtraceStats(&stats_before_);
   setup_errors_ = FtraceSetupErrors();  // Dump only on START_OF_TRACE.
+
+  if (config_.preserve_ftrace_buffer()) {
+    auto stats_packet = writer_->NewTracePacket();
+    auto* stats = stats_packet->set_ftrace_stats();
+    stats->set_phase(protos::pbzero::FtraceStats::Phase::START_OF_TRACE);
+    stats->set_preserve_ftrace_buffer(true);
+  }
 }
 
 void FtraceDataSource::DumpFtraceStats(FtraceStats* stats) {
diff --git a/src/traced/probes/ftrace/ftrace_metadata.h b/src/traced/probes/ftrace/ftrace_metadata.h
index 21b0530..0a741b1 100644
--- a/src/traced/probes/ftrace/ftrace_metadata.h
+++ b/src/traced/probes/ftrace/ftrace_metadata.h
@@ -22,6 +22,7 @@
 #include <unistd.h>
 
 #include <bitset>
+#include <unordered_map>
 
 #include "perfetto/base/flat_set.h"
 #include "perfetto/base/logging.h"
@@ -125,6 +126,7 @@
     pids.clear();
     pids_cache.reset();
     kernel_addrs.clear();
+    fds.clear();
     last_kernel_addr_index_written = 0;
     FinishEvent();
   }
@@ -148,6 +150,7 @@
   base::FlatSet<int32_t> rename_pids;
   base::FlatSet<int32_t> pids;
   base::FlatSet<KernelAddr> kernel_addrs;
+  base::FlatSet<std::pair<pid_t, uint64_t>> fds;
 
   // This bitmap is a cache for |pids|. It speculates on the fact that on most
   // Android kernels, PID_MAX=32768. It saves ~1-2% cpu time on high load
diff --git a/src/traced/probes/ftrace/ftrace_print_filter.cc b/src/traced/probes/ftrace/ftrace_print_filter.cc
new file mode 100644
index 0000000..5080fca
--- /dev/null
+++ b/src/traced/probes/ftrace/ftrace_print_filter.cc
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2022 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/ftrace_print_filter.h"
+
+#include <string.h>
+
+#include "protos/perfetto/config/ftrace/ftrace_config.gen.h"
+#include "src/traced/probes/ftrace/event_info_constants.h"
+
+namespace perfetto {
+namespace {
+using ::perfetto::protos::gen::FtraceConfig;
+
+bool PrefixMatches(const std::string& prefix, const char* start, size_t size) {
+  if (prefix.size() > size) {
+    return false;
+  }
+  return strncmp(prefix.c_str(), start, prefix.size()) == 0;
+}
+
+bool AtraceMessageMatches(const std::string& before_pid_part,
+                          const std::string& after_pid_prefix,
+                          const char* start,
+                          size_t size) {
+  base::StringView s(start, size);
+  if (!s.StartsWith(base::StringView(before_pid_part))) {
+    return false;
+  }
+  s = s.substr(before_pid_part.size());
+
+  if (!s.StartsWith("|")) {
+    return false;
+  }
+  s = s.substr(1);
+
+  size_t skip_pid_count = 0;
+  for (;; skip_pid_count++) {
+    if (skip_pid_count == s.size()) {
+      return false;
+    }
+    if (s.at(skip_pid_count) == '|') {
+      break;
+    }
+    if (!isdigit(s.at(skip_pid_count))) {
+      return false;
+    }
+  }
+  skip_pid_count++;
+  s = s.substr(skip_pid_count);
+
+  return PrefixMatches(after_pid_prefix, s.data(), s.size());
+}
+
+}  // namespace
+
+// static
+bool FtracePrintFilter::RuleMatches(const Rule& rule,
+                                    const char* start,
+                                    size_t size) {
+  switch (rule.type) {
+    case Rule::Type::kAtraceMessage:
+      return AtraceMessageMatches(rule.before_pid_part, rule.prefix, start,
+                                  size);
+    case Rule::Type::kPrefixMatch:
+      break;
+  }
+  return PrefixMatches(rule.prefix, start, size);
+}
+
+FtracePrintFilter::FtracePrintFilter(const FtraceConfig::PrintFilter& conf) {
+  rules_.reserve(conf.rules().size());
+  for (const FtraceConfig::PrintFilter::Rule& conf_rule : conf.rules()) {
+    Rule rule;
+    rule.allow = conf_rule.allow();
+    if (conf_rule.has_atrace_msg()) {
+      rule.type = Rule::Type::kAtraceMessage;
+      rule.before_pid_part = conf_rule.atrace_msg().type();
+      rule.prefix = conf_rule.atrace_msg().prefix();
+    } else {
+      rule.type = Rule::Type::kPrefixMatch;
+      rule.prefix = conf_rule.prefix();
+    }
+    rules_.push_back(std::move(rule));
+  }
+}
+
+bool FtracePrintFilter::IsAllowed(const char* start, size_t size) const {
+  for (const Rule& rule : rules_) {
+    if (RuleMatches(rule, start, size)) {
+      return rule.allow;
+    }
+  }
+  return true;
+}
+
+// static
+base::Optional<FtracePrintFilterConfig> FtracePrintFilterConfig::Create(
+    const protos::gen::FtraceConfig::PrintFilter& config,
+    ProtoTranslationTable* table) {
+  const Event* print_event = table->GetEvent(GroupAndName("ftrace", "print"));
+  if (!print_event) {
+    return base::nullopt;
+  }
+  const Field* buf_field = nullptr;
+  for (const Field& field : print_event->fields) {
+    if (strcmp(field.ftrace_name, "buf") == 0) {
+      buf_field = &field;
+      break;
+    }
+  }
+  if (!buf_field) {
+    return base::nullopt;
+  }
+
+  if (buf_field->strategy != kCStringToString) {
+    return base::nullopt;
+  }
+  FtracePrintFilterConfig ret{FtracePrintFilter{config}};
+  ret.event_id_ = print_event->ftrace_event_id;
+  ret.event_size_ = print_event->size;
+  ret.buf_field_offset_ = buf_field->ftrace_offset;
+  return std::move(ret);
+}
+
+FtracePrintFilterConfig::FtracePrintFilterConfig(FtracePrintFilter filter)
+    : filter_(filter) {}
+
+bool FtracePrintFilterConfig::IsEventInteresting(const uint8_t* start,
+                                                 const uint8_t* end) const {
+  PERFETTO_DCHECK(start < end);
+  const size_t length = static_cast<size_t>(end - start);
+
+  // If the end of the buffer is before the end of the event, give up.
+  if (event_size_ >= length) {
+    PERFETTO_DFATAL("Buffer overflowed.");
+    return true;
+  }
+
+  const uint8_t* field_start = start + buf_field_offset_;
+  return filter_.IsAllowed(reinterpret_cast<const char*>(field_start),
+                           static_cast<size_t>(end - field_start));
+}
+
+}  // namespace perfetto
diff --git a/src/traced/probes/ftrace/ftrace_print_filter.h b/src/traced/probes/ftrace/ftrace_print_filter.h
new file mode 100644
index 0000000..707bb3e
--- /dev/null
+++ b/src/traced/probes/ftrace/ftrace_print_filter.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2022 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_FTRACE_PRINT_FILTER_H_
+#define SRC_TRACED_PROBES_FTRACE_FTRACE_PRINT_FILTER_H_
+
+#include <string>
+#include <vector>
+
+#include "perfetto/ext/base/optional.h"
+#include "src/traced/probes/ftrace/proto_translation_table.h"
+
+namespace perfetto {
+
+struct Event;
+
+namespace protos {
+namespace gen {
+class FtraceConfig_PrintFilter;
+}  // namespace gen
+}  // namespace protos
+
+class FtracePrintFilter {
+ public:
+  // Builds a filter from a proto config.
+  explicit FtracePrintFilter(const protos::gen::FtraceConfig::PrintFilter&);
+
+  // Returns true if a string is allowed by this filter, false otherwise.
+  // The string begins at `start` and terminates after `size` bytes, or at the
+  // first '\0' byte, whichever comes first.
+  bool IsAllowed(const char* start, size_t size) const;
+
+ private:
+  struct Rule {
+    enum class Type {
+      kPrefixMatch,
+      kAtraceMessage,
+    };
+    Type type;
+    std::string before_pid_part;
+    std::string prefix;
+    bool allow;
+  };
+
+  static bool RuleMatches(const Rule&, const char* start, size_t size);
+
+  std::vector<Rule> rules_;
+};
+
+class FtracePrintFilterConfig {
+ public:
+  static base::Optional<FtracePrintFilterConfig> Create(
+      const protos::gen::FtraceConfig_PrintFilter&,
+      ProtoTranslationTable* table);
+
+  uint32_t event_id() const { return event_id_; }
+
+  // Returns true if the "ftrace/print" event (encoded from `start` to `end`)
+  // should be allowed.
+  //
+  // If the event should be allowed, or **if there was a problem parsing it**
+  // returns true. If the event should be disallowed (i.e. ignored), returns
+  // false.
+  bool IsEventInteresting(const uint8_t* start, const uint8_t* end) const;
+
+ private:
+  explicit FtracePrintFilterConfig(FtracePrintFilter filter);
+  FtracePrintFilter filter_;
+  uint32_t event_id_;
+  uint16_t event_size_;
+  uint16_t buf_field_offset_;
+};
+
+}  // namespace perfetto
+
+#endif  // SRC_TRACED_PROBES_FTRACE_FTRACE_PRINT_FILTER_H_
diff --git a/src/traced/probes/ftrace/ftrace_print_filter_unittest.cc b/src/traced/probes/ftrace/ftrace_print_filter_unittest.cc
new file mode 100644
index 0000000..803987e
--- /dev/null
+++ b/src/traced/probes/ftrace/ftrace_print_filter_unittest.cc
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2022 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/ftrace_print_filter.h"
+
+#include "protos/perfetto/config/ftrace/ftrace_config.gen.h"
+
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace {
+
+using perfetto::protos::gen::FtraceConfig;
+
+TEST(FtracePrintFilterTest, EmptyConfigDefaultAllows) {
+  FtraceConfig::PrintFilter conf;
+  FtracePrintFilter filter(conf);
+
+  EXPECT_TRUE(filter.IsAllowed("word", 4));
+}
+
+TEST(FtracePrintFilterTest, OneRuleMatchesAllows) {
+  FtraceConfig::PrintFilter conf;
+  auto* rule = conf.add_rules();
+  rule->set_prefix("w");
+  rule->set_allow(true);
+  FtracePrintFilter filter(conf);
+
+  EXPECT_TRUE(filter.IsAllowed("word", 4));
+}
+
+TEST(FtracePrintFilterTest, OneRuleMatchesDenies) {
+  FtraceConfig::PrintFilter conf;
+  auto* rule = conf.add_rules();
+  rule->set_prefix("w");
+  rule->set_allow(false);
+  FtracePrintFilter filter(conf);
+
+  EXPECT_FALSE(filter.IsAllowed("word", 4));
+}
+
+TEST(FtracePrintFilterTest, OneRuleMatchesLongSize) {
+  FtraceConfig::PrintFilter conf;
+  auto* rule = conf.add_rules();
+  rule->set_prefix("w");
+  rule->set_allow(false);
+  FtracePrintFilter filter(conf);
+
+  EXPECT_FALSE(filter.IsAllowed("word", 120));
+}
+
+TEST(FtracePrintFilterTest, OneRuleMatchesShortSize) {
+  FtraceConfig::PrintFilter conf;
+  auto* rule = conf.add_rules();
+  rule->set_prefix("w");
+  rule->set_allow(false);
+  FtracePrintFilter filter(conf);
+
+  EXPECT_FALSE(filter.IsAllowed("word", 1));
+}
+
+TEST(FtracePrintFilterTest, OneRuleDoesntMatchLongSize) {
+  FtraceConfig::PrintFilter conf;
+  auto* rule = conf.add_rules();
+  rule->set_prefix("verylongprefix");
+  rule->set_allow(false);
+  FtracePrintFilter filter(conf);
+
+  EXPECT_TRUE(filter.IsAllowed("short", 120));
+}
+
+TEST(FtracePrintFilterTest, OneRuleWildcard) {
+  FtraceConfig::PrintFilter conf;
+  auto* rule = conf.add_rules();
+  rule->set_prefix("");
+  rule->set_allow(false);
+  FtracePrintFilter filter(conf);
+
+  EXPECT_FALSE(filter.IsAllowed("anything", 8));
+}
+
+TEST(FtracePrintFilterTest, TwoRulesMatchFirst) {
+  FtraceConfig::PrintFilter conf;
+  {
+    auto* rule = conf.add_rules();
+    rule->set_prefix("word");
+    rule->set_allow(false);
+  }
+  {
+    auto* rule = conf.add_rules();
+    rule->set_prefix("doesntmatch");
+    rule->set_allow(true);
+  }
+  FtracePrintFilter filter(conf);
+
+  EXPECT_FALSE(filter.IsAllowed("word", 120));
+}
+
+TEST(FtracePrintFilterTest, TwoRulesMatchesSecond) {
+  FtraceConfig::PrintFilter conf;
+  {
+    auto* rule = conf.add_rules();
+    rule->set_prefix("doesntmatch");
+    rule->set_allow(true);
+  }
+  {
+    auto* rule = conf.add_rules();
+    rule->set_prefix("word");
+    rule->set_allow(false);
+  }
+  FtracePrintFilter filter(conf);
+
+  EXPECT_FALSE(filter.IsAllowed("word", 120));
+}
+
+TEST(FtracePrintFilterTest, AtraceRuleTypeDoesntMatch) {
+  FtraceConfig::PrintFilter conf;
+  auto* rule = conf.add_rules();
+  auto* atrace = rule->mutable_atrace_msg();
+  atrace->set_type("C");
+  atrace->set_type("mycounter");
+  rule->set_allow(false);
+  FtracePrintFilter filter(conf);
+
+  EXPECT_TRUE(filter.IsAllowed("B", 1));
+}
+
+TEST(FtracePrintFilterTest, AtraceRuleNoFirstSlash) {
+  FtraceConfig::PrintFilter conf;
+  auto* rule = conf.add_rules();
+  auto* atrace = rule->mutable_atrace_msg();
+  atrace->set_type("C");
+  atrace->set_prefix("mycounter");
+  rule->set_allow(false);
+  FtracePrintFilter filter(conf);
+
+  EXPECT_TRUE(filter.IsAllowed("Cnopipemycounter", 16));
+}
+
+TEST(FtracePrintFilterTest, AtraceRuleNoFirstSlashEnd) {
+  FtraceConfig::PrintFilter conf;
+  auto* rule = conf.add_rules();
+  auto* atrace = rule->mutable_atrace_msg();
+  atrace->set_type("C");
+  atrace->set_prefix("mycounter");
+  rule->set_allow(false);
+  FtracePrintFilter filter(conf);
+
+  EXPECT_TRUE(filter.IsAllowed("C", 1));
+}
+
+TEST(FtracePrintFilterTest, AtraceRuleNonIntPid) {
+  FtraceConfig::PrintFilter conf;
+  auto* rule = conf.add_rules();
+  auto* atrace = rule->mutable_atrace_msg();
+  atrace->set_type("C");
+  atrace->set_prefix("mycounter");
+  rule->set_allow(false);
+  FtracePrintFilter filter(conf);
+
+  EXPECT_TRUE(filter.IsAllowed("C|badpid|mycounter", 18));
+}
+
+TEST(FtracePrintFilterTest, AtraceRuleEndAfterPid) {
+  FtraceConfig::PrintFilter conf;
+  auto* rule = conf.add_rules();
+  auto* atrace = rule->mutable_atrace_msg();
+  atrace->set_type("C");
+  atrace->set_prefix("mycounter");
+  rule->set_allow(false);
+  FtracePrintFilter filter(conf);
+
+  EXPECT_TRUE(filter.IsAllowed("C|111111", 8));
+}
+
+TEST(FtracePrintFilterTest, AtraceRuleNoSecondSlash) {
+  FtraceConfig::PrintFilter conf;
+  auto* rule = conf.add_rules();
+  auto* atrace = rule->mutable_atrace_msg();
+  atrace->set_type("C");
+  atrace->set_prefix("mycounter");
+  rule->set_allow(false);
+  FtracePrintFilter filter(conf);
+
+  EXPECT_TRUE(filter.IsAllowed("C|111111Xmycounter", 18));
+}
+
+TEST(FtracePrintFilterTest, AtraceRuleAfterPrefixDoesntMatch) {
+  FtraceConfig::PrintFilter conf;
+  auto* rule = conf.add_rules();
+  auto* atrace = rule->mutable_atrace_msg();
+  atrace->set_type("C");
+  atrace->set_prefix("mycounter");
+  rule->set_allow(false);
+  FtracePrintFilter filter(conf);
+
+  EXPECT_TRUE(filter.IsAllowed("C|111111|nomatch", 16));
+}
+
+TEST(FtracePrintFilterTest, AtraceRuleMatches) {
+  FtraceConfig::PrintFilter conf;
+  auto* rule = conf.add_rules();
+  auto* atrace = rule->mutable_atrace_msg();
+  atrace->set_type("C");
+  atrace->set_prefix("mycounter");
+  rule->set_allow(false);
+  FtracePrintFilter filter(conf);
+
+  EXPECT_FALSE(filter.IsAllowed("C|111111|mycounter...", 21));
+}
+
+}  // namespace
+}  // namespace perfetto
diff --git a/src/traced/probes/ftrace/ftrace_procfs.cc b/src/traced/probes/ftrace/ftrace_procfs.cc
index 55b81b4..bd1c60c 100644
--- a/src/traced/probes/ftrace/ftrace_procfs.cc
+++ b/src/traced/probes/ftrace/ftrace_procfs.cc
@@ -83,7 +83,8 @@
 
 // static
 std::unique_ptr<FtraceProcfs> FtraceProcfs::CreateGuessingMountPoint(
-    const std::string& instance_path) {
+    const std::string& instance_path,
+    bool preserve_ftrace_buffer) {
   std::unique_ptr<FtraceProcfs> ftrace_procfs;
   size_t index = 0;
   while (!ftrace_procfs && kTracingPaths[index]) {
@@ -91,14 +92,16 @@
     if (!instance_path.empty())
       path += instance_path;
 
-    ftrace_procfs = Create(path);
+    ftrace_procfs = Create(path, preserve_ftrace_buffer);
   }
   return ftrace_procfs;
 }
 
 // static
-std::unique_ptr<FtraceProcfs> FtraceProcfs::Create(const std::string& root) {
-  if (!CheckRootPath(root)) {
+std::unique_ptr<FtraceProcfs> FtraceProcfs::Create(
+    const std::string& root,
+    bool preserve_ftrace_buffer) {
+  if (!preserve_ftrace_buffer && !CheckRootPath(root)) {
     return nullptr;
   }
   return std::unique_ptr<FtraceProcfs>(new FtraceProcfs(root));
@@ -107,6 +110,28 @@
 FtraceProcfs::FtraceProcfs(const std::string& root) : root_(root) {}
 FtraceProcfs::~FtraceProcfs() = default;
 
+bool FtraceProcfs::SetSyscallFilter(const std::set<size_t>& filter) {
+  std::vector<std::string> parts;
+  for (size_t id : filter) {
+    base::StackString<16> m("id == %zu", id);
+    parts.push_back(m.ToStdString());
+  }
+
+  std::string filter_str = "0";
+  if (!parts.empty()) {
+    filter_str = base::Join(parts, " || ");
+  }
+
+  for (const char* event : {"sys_enter", "sys_exit"}) {
+    std::string path = root_ + "events/raw_syscalls/" + event + "/filter";
+    if (!WriteToFile(path, filter_str)) {
+      PERFETTO_ELOG("Failed to write file: %s", path.c_str());
+      return false;
+    }
+  }
+  return true;
+}
+
 bool FtraceProcfs::EnableEvent(const std::string& group,
                                const std::string& name) {
   std::string path = root_ + "events/" + group + "/" + name + "/enable";
@@ -137,6 +162,13 @@
   return ret;
 }
 
+bool FtraceProcfs::IsEventAccessible(const std::string& group,
+                                     const std::string& name) {
+  std::string path = root_ + "events/" + group + "/" + name + "/enable";
+
+  return IsFileWriteable(path);
+}
+
 bool FtraceProcfs::DisableAllEvents() {
   std::string path = root_ + "events/enable";
   return WriteToFile(path, "0");
@@ -148,6 +180,57 @@
   return ReadFileIntoString(path);
 }
 
+std::string FtraceProcfs::GetCurrentTracer() {
+  std::string path = root_ + "current_tracer";
+  std::string current_tracer = ReadFileIntoString(path);
+  return base::StripSuffix(current_tracer, "\n");
+}
+
+bool FtraceProcfs::SetCurrentTracer(const std::string& tracer) {
+  std::string path = root_ + "current_tracer";
+  return WriteToFile(path, tracer);
+}
+
+bool FtraceProcfs::ResetCurrentTracer() {
+  return SetCurrentTracer("nop");
+}
+
+bool FtraceProcfs::AppendFunctionFilters(
+    const std::vector<std::string>& filters) {
+  std::string path = root_ + "set_ftrace_filter";
+  std::string filter = base::Join(filters, "\n");
+
+  // The same file accepts special actions to perform when a corresponding
+  // kernel function is hit (regardless of active tracer). For example
+  // "__schedule_bug:traceoff" would disable tracing once __schedule_bug is
+  // called.
+  // We disallow these commands as most of them break the isolation of
+  // concurrent ftrace data sources (as the underlying ftrace instance is
+  // shared).
+  if (base::Contains(filter, ':')) {
+    PERFETTO_ELOG("Filter commands are disallowed.");
+    return false;
+  }
+  return AppendToFile(path, filter);
+}
+
+bool FtraceProcfs::ClearFunctionFilters() {
+  std::string path = root_ + "set_ftrace_filter";
+  return ClearFile(path);
+}
+
+bool FtraceProcfs::AppendFunctionGraphFilters(
+    const std::vector<std::string>& filters) {
+  std::string path = root_ + "set_graph_function";
+  std::string filter = base::Join(filters, "\n");
+  return AppendToFile(path, filter);
+}
+
+bool FtraceProcfs::ClearFunctionGraphFilters() {
+  std::string path = root_ + "set_graph_function";
+  return ClearFile(path);
+}
+
 std::vector<std::string> FtraceProcfs::ReadEventTriggers(
     const std::string& group,
     const std::string& name) const {
@@ -330,25 +413,7 @@
   return WriteNumberToFile(path, pages * (base::kPageSize / 1024ul));
 }
 
-bool FtraceProcfs::EnableTracing() {
-  KernelLogWrite("perfetto: enabled ftrace\n");
-  PERFETTO_LOG("enabled ftrace in %s", root_.c_str());
-  std::string path = root_ + "tracing_on";
-  return WriteToFile(path, "1");
-}
-
-bool FtraceProcfs::DisableTracing() {
-  KernelLogWrite("perfetto: disabled ftrace\n");
-  PERFETTO_LOG("disabled ftrace in %s", root_.c_str());
-  std::string path = root_ + "tracing_on";
-  return WriteToFile(path, "0");
-}
-
-bool FtraceProcfs::SetTracingOn(bool enable) {
-  return enable ? EnableTracing() : DisableTracing();
-}
-
-bool FtraceProcfs::IsTracingEnabled() {
+bool FtraceProcfs::GetTracingOn() {
   std::string path = root_ + "tracing_on";
   char tracing_on = ReadOneCharFromFile(path);
   if (tracing_on == '\0')
@@ -356,6 +421,37 @@
   return tracing_on == '1';
 }
 
+bool FtraceProcfs::SetTracingOn(bool on) {
+  std::string path = root_ + "tracing_on";
+  if (!WriteToFile(path, on ? "1" : "0")) {
+    PERFETTO_PLOG("Failed to write %s", path.c_str());
+    return false;
+  }
+  if (on) {
+    KernelLogWrite("perfetto: enabled ftrace\n");
+    PERFETTO_LOG("enabled ftrace in %s", root_.c_str());
+  } else {
+    KernelLogWrite("perfetto: disabled ftrace\n");
+    PERFETTO_LOG("disabled ftrace in %s", root_.c_str());
+  }
+
+  return true;
+}
+
+bool FtraceProcfs::IsTracingAvailable() {
+  std::string current_tracer = GetCurrentTracer();
+
+  // Ftrace tracing is available if current_tracer == "nop".
+  // events/enable could be 0, 1, X or 0*. 0* means events would be
+  // dynamically enabled so we need to treat as event tracing is in use.
+  // However based on the discussion in asop/2328817, on Android events/enable
+  // is "X" after boot up. To avoid causing more problem, the decision is just
+  // look at current_tracer.
+  // As the discussion in asop/2328817, if GetCurrentTracer failed to
+  // read file and return "", we treat it as tracing is available.
+  return current_tracer == "nop" || current_tracer == "";
+}
+
 bool FtraceProcfs::SetClock(const std::string& clock_name) {
   std::string path = root_ + "trace_clock";
   return WriteToFile(path, clock_name);
@@ -446,6 +542,10 @@
   return !!fd;
 }
 
+bool FtraceProcfs::IsFileWriteable(const std::string& path) {
+  return access(path.c_str(), W_OK) == 0;
+}
+
 std::string FtraceProcfs::ReadFileIntoString(const std::string& path) const {
   // You can't seek or stat the procfs files on Android.
   // The vast majority (884/886) of format files are under 4k.
diff --git a/src/traced/probes/ftrace/ftrace_procfs.h b/src/traced/probes/ftrace/ftrace_procfs.h
index b7ddf99..40a7da9 100644
--- a/src/traced/probes/ftrace/ftrace_procfs.h
+++ b/src/traced/probes/ftrace/ftrace_procfs.h
@@ -34,20 +34,30 @@
   // Takes an optional |instance_path| such as "instances/wifi/", in which case
   // the returned object will be for that ftrace instance path.
   static std::unique_ptr<FtraceProcfs> CreateGuessingMountPoint(
-      const std::string& instance_path = "");
+      const std::string& instance_path = "",
+      bool preserve_ftrace_buffer = false);
 
-  static std::unique_ptr<FtraceProcfs> Create(const std::string& root);
+  static std::unique_ptr<FtraceProcfs> Create(
+      const std::string& root,
+      bool preserve_ftrace_buffer = false);
   static int g_kmesg_fd;
 
   explicit FtraceProcfs(const std::string& root);
   virtual ~FtraceProcfs();
 
+  // Set the filter for syscall events. If empty, clear the filter.
+  bool SetSyscallFilter(const std::set<size_t>& filter);
+
   // Enable the event under with the given |group| and |name|.
   bool EnableEvent(const std::string& group, const std::string& name);
 
   // Disable the event under with the given |group| and |name|.
   bool DisableEvent(const std::string& group, const std::string& name);
 
+  // Returns true if the event under the given |group| and |name| exists and its
+  // enable file is writeable.
+  bool IsEventAccessible(const std::string& group, const std::string& name);
+
   // Disable all events by writing to the global enable file.
   bool DisableAllEvents();
 
@@ -58,6 +68,17 @@
 
   virtual std::string ReadPageHeaderFormat() const;
 
+  std::string GetCurrentTracer();
+  // Sets the "current_tracer". Might fail with EBUSY if tracing pipes have
+  // already been opened for reading.
+  bool SetCurrentTracer(const std::string& tracer);
+  // Resets the "current_tracer" to "nop".
+  bool ResetCurrentTracer();
+  bool AppendFunctionFilters(const std::vector<std::string>& filters);
+  bool ClearFunctionFilters();
+  bool AppendFunctionGraphFilters(const std::vector<std::string>& filters);
+  bool ClearFunctionGraphFilters();
+
   // Get all triggers for event with the given |group| and |name|.
   std::vector<std::string> ReadEventTriggers(const std::string& group,
                                              const std::string& name) const;
@@ -114,19 +135,17 @@
   // Writes the string |str| as an event into the trace buffer.
   bool WriteTraceMarker(const std::string& str);
 
-  // Enable tracing.
-  bool EnableTracing();
+  // Read tracing_on and return true if tracing_on is 1, otherwise return false.
+  bool GetTracingOn();
 
-  // Disables tracing, does not clear the buffer.
-  bool DisableTracing();
+  // Write 1 to tracing_on if |on| is true, otherwise write 0.
+  bool SetTracingOn(bool on);
 
-  // Enables/disables tracing, does not clear the buffer.
-  bool SetTracingOn(bool enable);
-
-  // Returns true iff tracing is enabled.
-  // Necessarily racy: another program could enable/disable tracing at any
-  // point.
-  bool IsTracingEnabled();
+  // Returns true if ftrace tracing is available.
+  // Ftrace tracing is available iff "/current_tracer" is "nop", indicates
+  // function tracing is not in use. Necessarily
+  // racy: another program could enable/disable tracing at any point.
+  bool IsTracingAvailable();
 
   // Set the clock. |clock_name| should be one of the names returned by
   // AvailableClocks. Setting the clock clears the buffer.
@@ -159,6 +178,7 @@
   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);
+  virtual bool IsFileWriteable(const std::string& path);
   virtual char ReadOneCharFromFile(const std::string& path);
   virtual std::string ReadFileIntoString(const std::string& path) const;
 
diff --git a/src/traced/probes/ftrace/ftrace_procfs_integrationtest.cc b/src/traced/probes/ftrace/ftrace_procfs_integrationtest.cc
index 25e1aa7..24ad32a 100644
--- a/src/traced/probes/ftrace/ftrace_procfs_integrationtest.cc
+++ b/src/traced/probes/ftrace/ftrace_procfs_integrationtest.cc
@@ -81,19 +81,18 @@
 void FtraceProcfsIntegrationTest::SetUp() {
   ftrace_ = FtraceProcfs::Create(GetFtracePath());
   ASSERT_TRUE(ftrace_);
-  if (ftrace_->IsTracingEnabled()) {
+  if (!ftrace_->IsTracingAvailable()) {
     GTEST_SKIP() << "Something else is using ftrace, skipping";
   }
 
-  ftrace_->DisableAllEvents();
   ftrace_->ClearTrace();
-  ftrace_->EnableTracing();
+  ftrace_->SetTracingOn(true);
 }
 
 void FtraceProcfsIntegrationTest::TearDown() {
   ftrace_->DisableAllEvents();
   ftrace_->ClearTrace();
-  ftrace_->DisableTracing();
+  ftrace_->SetTracingOn(false);
 }
 
 TEST_F(FtraceProcfsIntegrationTest, ANDROID_ONLY_TEST(CreateWithBadPath)) {
@@ -123,20 +122,30 @@
   EXPECT_THAT(GetTraceOutput(), Not(HasSubstr("sched_switch")));
 }
 
-TEST_F(FtraceProcfsIntegrationTest, ANDROID_ONLY_TEST(EnableDisableTracing)) {
-  EXPECT_TRUE(ftrace_->IsTracingEnabled());
+TEST_F(FtraceProcfsIntegrationTest,
+       ANDROID_ONLY_TEST(EnableDisableTraceBuffer)) {
   ftrace_->WriteTraceMarker("Before");
-  ftrace_->DisableTracing();
-  EXPECT_FALSE(ftrace_->IsTracingEnabled());
+  ftrace_->SetTracingOn(false);
   ftrace_->WriteTraceMarker("During");
-  ftrace_->EnableTracing();
-  EXPECT_TRUE(ftrace_->IsTracingEnabled());
+  ftrace_->SetTracingOn(true);
   ftrace_->WriteTraceMarker("After");
   EXPECT_THAT(GetTraceOutput(), HasSubstr("Before"));
   EXPECT_THAT(GetTraceOutput(), Not(HasSubstr("During")));
   EXPECT_THAT(GetTraceOutput(), HasSubstr("After"));
 }
 
+TEST_F(FtraceProcfsIntegrationTest, ANDROID_ONLY_TEST(IsTracingAvailable)) {
+  EXPECT_TRUE(ftrace_->IsTracingAvailable());
+  ftrace_->SetCurrentTracer("function");
+  EXPECT_FALSE(ftrace_->IsTracingAvailable());
+  ftrace_->SetCurrentTracer("nop");
+  EXPECT_TRUE(ftrace_->IsTracingAvailable());
+  ASSERT_TRUE(ftrace_->EnableEvent("sched", "sched_switch"));
+  EXPECT_FALSE(ftrace_->IsTracingAvailable());
+  ftrace_->DisableAllEvents();
+  EXPECT_TRUE(ftrace_->IsTracingAvailable());
+}
+
 TEST_F(FtraceProcfsIntegrationTest, ANDROID_ONLY_TEST(ReadFormatFile)) {
   std::string format = ftrace_->ReadEventFormat("ftrace", "print");
   EXPECT_THAT(format, HasSubstr("name: print"));
@@ -168,7 +177,6 @@
 TEST_F(FtraceProcfsIntegrationTest,
        ANDROID_ONLY_TEST(FtraceControllerHardReset)) {
   ftrace_->SetCpuBufferSizeInPages(4ul);
-  ftrace_->EnableTracing();
   ftrace_->EnableEvent("sched", "sched_switch");
   ftrace_->WriteTraceMarker("Hello, World!");
 
diff --git a/src/traced/probes/ftrace/proto_translation_table.cc b/src/traced/probes/ftrace/proto_translation_table.cc
index a4a7a31..b3fc5a2 100644
--- a/src/traced/probes/ftrace/proto_translation_table.cc
+++ b/src/traced/probes/ftrace/proto_translation_table.cc
@@ -287,6 +287,20 @@
     return true;
   }
 
+  // Parsing of sys_enter argument field declared as
+  //    field:unsigned long args[6];
+  if (type_and_name == "unsigned long args[6]") {
+    if (size == 24) {
+      // 24 / 6 = 4 -> 32bit system
+      *out = kFtraceUint32;
+      return true;
+    } else if (size == 48) {
+      // 48 / 6 = 8 -> 64bit system
+      *out = kFtraceUint64;
+      return true;
+    }
+  }
+
   if (Contains(type_and_name, "char[] ")) {
     *out = kFtraceStringPtr;
     return true;
@@ -446,6 +460,20 @@
       }
     }
 
+    // Special case function_graph events as they use a u64 field for kernel
+    // function pointers. Fudge the type so that |MergeFields| correctly tags
+    // the fields for kernel address symbolization (kFtraceSymAddr64).
+    if (!strcmp(event.group, "ftrace") &&
+        (!strcmp(event.name, "funcgraph_entry") ||
+         !strcmp(event.name, "funcgraph_exit"))) {
+      for (auto& field : ftrace_event.fields) {
+        if (GetNameFromTypeAndName(field.type_and_name) == "func") {
+          field.type_and_name = "void * func";
+          break;
+        }
+      }
+    }
+
     event.ftrace_event_id = ftrace_event.id;
 
     if (!common_fields_processed) {
diff --git a/src/traced/probes/ftrace/proto_translation_table_unittest.cc b/src/traced/probes/ftrace/proto_translation_table_unittest.cc
index addcad9..6590115 100644
--- a/src/traced/probes/ftrace/proto_translation_table_unittest.cc
+++ b/src/traced/probes/ftrace/proto_translation_table_unittest.cc
@@ -27,12 +27,14 @@
 #include "protos/perfetto/trace/ftrace/generic.pbzero.h"
 
 using testing::_;
+using testing::AllOf;
 using testing::AnyNumber;
 using testing::Contains;
 using testing::Eq;
 using testing::IsNull;
 using testing::Pointee;
 using testing::Return;
+using testing::StrEq;
 using testing::TestWithParam;
 using testing::Values;
 using testing::ValuesIn;
@@ -371,6 +373,12 @@
   ASSERT_EQ(type, kFtraceDataLoc);
   ASSERT_FALSE(InferFtraceType("__data_loc char[] foo", 8, false, &type));
 
+  ASSERT_TRUE(InferFtraceType("unsigned long args[6]", 24, true, &type));
+  ASSERT_EQ(type, kFtraceUint32);
+  ASSERT_TRUE(InferFtraceType("unsigned long args[6]", 48, true, &type));
+  ASSERT_EQ(type, kFtraceUint64);
+  ASSERT_FALSE(InferFtraceType("unsigned long args[6]", 96, true, &type));
+
   EXPECT_FALSE(InferFtraceType("foo", 64, false, &type));
 }
 
@@ -544,5 +552,53 @@
   EXPECT_TRUE(empty_filter.IsEventEnabled(1));
 }
 
+TEST(TranslationTableTest, FuncgraphEvents) {
+  std::string path =
+      base::GetTestDataPath("src/traced/probes/ftrace/test/data/synthetic/");
+  FtraceProcfs ftrace_procfs(path);
+  auto table = ProtoTranslationTable::Create(
+      &ftrace_procfs, GetStaticEventInfo(), GetStaticCommonFieldsInfo());
+  PERFETTO_CHECK(table);
+
+  {
+    auto* event = table->GetEvent(GroupAndName("ftrace", "funcgraph_entry"));
+    EXPECT_EQ(std::string(event->name), "funcgraph_entry");
+    EXPECT_EQ(std::string(event->group), "ftrace");
+
+    // field:unsigned long func;  offset:8;   size:8;  signed:0;
+    // field:int depth;           offset:16;  size:4;  signed:1;
+    ASSERT_EQ(event->fields.size(), 2u);
+
+    // note: fields in struct are ordered as in the proto, not the format file
+    EXPECT_THAT(
+        event->fields,
+        Contains(
+            AllOf(testing::Field(&Field::ftrace_name, StrEq("func")),
+                  testing::Field(&Field::ftrace_offset, Eq(8u)),
+                  testing::Field(&Field::ftrace_type, kFtraceSymAddr64),
+                  testing::Field(&Field::strategy, kFtraceSymAddr64ToUint64))));
+  }
+  {
+    auto* event = table->GetEvent(GroupAndName("ftrace", "funcgraph_exit"));
+    EXPECT_EQ(std::string(event->name), "funcgraph_exit");
+    EXPECT_EQ(std::string(event->group), "ftrace");
+
+    // field:unsigned long func;           offset:8;   size:8;  signed:0;
+    // field:int depth;                    offset:16;  size:4;  signed:1;
+    // field:unsigned int overrun;         offset:20;  size:4;  signed:0;
+    // field:unsigned long long calltime;  offset:24;  size:8;  signed:0;
+    // field:unsigned long long rettime;   offset:32;  size:8;  signed:0;
+    ASSERT_EQ(event->fields.size(), 5u);
+    // note: fields in struct are ordered as in the proto, not the format file
+    EXPECT_THAT(
+        event->fields,
+        Contains(
+            AllOf(testing::Field(&Field::ftrace_name, StrEq("func")),
+                  testing::Field(&Field::ftrace_offset, Eq(8u)),
+                  testing::Field(&Field::ftrace_type, kFtraceSymAddr64),
+                  testing::Field(&Field::strategy, kFtraceSymAddr64ToUint64))));
+  }
+}
+
 }  // namespace
 }  // namespace perfetto
diff --git a/src/traced/probes/ftrace/test/data/synthetic/available_events b/src/traced/probes/ftrace/test/data/synthetic/available_events
index 2ee225a..c57e8f9 100644
--- a/src/traced/probes/ftrace/test/data/synthetic/available_events
+++ b/src/traced/probes/ftrace/test/data/synthetic/available_events
@@ -12,3 +12,4 @@
 g2d:tracing_mark_write
 power:suspend_resume
 cpuhp:cpuhp_pause
+lwis:tracing_mark_write
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/android_fs/android_fs_dataread_end/format b/src/traced/probes/ftrace/test/data/synthetic/events/android_fs/android_fs_dataread_end/format
new file mode 100644
index 0000000..797a61f
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/android_fs/android_fs_dataread_end/format
@@ -0,0 +1,13 @@
+name: android_fs_dataread_end
+ID: 332
+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:ino_t ino; offset:8; size:4; signed:0;
+field:loff_t offset; offset:16; size:8; signed:1;
+field:int bytes; offset:24; size:4; signed:1;
+
+print fmt: "ino %lu, offset %llu, bytes %d", (unsigned long) REC->ino, REC->offset, REC->bytes
\ No newline at end of file
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/android_fs/android_fs_dataread_start/format b/src/traced/probes/ftrace/test/data/synthetic/events/android_fs/android_fs_dataread_start/format
new file mode 100644
index 0000000..81ad7f5
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/android_fs/android_fs_dataread_start/format
@@ -0,0 +1,17 @@
+name: android_fs_dataread_start
+ID: 331
+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:__data_loc char[] pathbuf; offset:8; size:4; signed:0;
+field:loff_t offset; offset:16; size:8; signed:1;
+field:int bytes; offset:24; size:4; signed:1;
+field:loff_t i_size; offset:32; size:8; signed:1;
+field:__data_loc char[] cmdline; offset:40; size:4; signed:0;
+field:pid_t pid; offset:44; size:4; signed:1;
+field:ino_t ino; offset:48; size:4; signed:0;
+
+print fmt: "entry_name %s, offset %llu, bytes %d, cmdline %s, pid %d, i_size %llu, ino %lu", __get_str(pathbuf), REC->offset, REC->bytes, __get_str(cmdline), REC->pid, REC->i_size, (unsigned long) REC->ino
\ No newline at end of file
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/android_fs/android_fs_datawrite_end/format b/src/traced/probes/ftrace/test/data/synthetic/events/android_fs/android_fs_datawrite_end/format
new file mode 100644
index 0000000..8ee014a
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/android_fs/android_fs_datawrite_end/format
@@ -0,0 +1,13 @@
+name: android_fs_datawrite_end
+ID: 334
+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:ino_t ino; offset:8; size:4; signed:0;
+field:loff_t offset; offset:16; size:8; signed:1;
+field:int bytes; offset:24; size:4; signed:1;
+
+print fmt: "ino %lu, offset %llu, bytes %d", (unsigned long) REC->ino, REC->offset, REC->bytes
\ No newline at end of file
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/android_fs/android_fs_datawrite_start/format b/src/traced/probes/ftrace/test/data/synthetic/events/android_fs/android_fs_datawrite_start/format
new file mode 100644
index 0000000..2f4d892
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/android_fs/android_fs_datawrite_start/format
@@ -0,0 +1,17 @@
+name: android_fs_datawrite_start
+ID: 333
+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:__data_loc char[] pathbuf; offset:8; size:4; signed:0;
+field:loff_t offset; offset:16; size:8; signed:1;
+field:int bytes; offset:24; size:4; signed:1;
+field:loff_t i_size; offset:32; size:8; signed:1;
+field:__data_loc char[] cmdline; offset:40; size:4; signed:0;
+field:pid_t pid; offset:44; size:4; signed:1;
+field:ino_t ino; offset:48; size:4; signed:0;
+
+print fmt: "entry_name %s, offset %llu, bytes %d, cmdline %s, pid %d, i_size %llu, ino %lu", __get_str(pathbuf), REC->offset, REC->bytes, __get_str(cmdline), REC->pid, REC->i_size, (unsigned long) REC->ino
\ No newline at end of file
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/android_fs/android_fs_fsync_end/format b/src/traced/probes/ftrace/test/data/synthetic/events/android_fs/android_fs_fsync_end/format
new file mode 100644
index 0000000..9cc6a7c
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/android_fs/android_fs_fsync_end/format
@@ -0,0 +1,13 @@
+name: android_fs_fsync_end
+ID: 336
+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:ino_t ino; offset:8; size:4; signed:0;
+field:loff_t offset; offset:16; size:8; signed:1;
+field:int bytes; offset:24; size:4; signed:1;
+
+print fmt: "ino %lu, offset %llu, bytes %d", (unsigned long) REC->ino, REC->offset, REC->bytes
\ No newline at end of file
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/android_fs/android_fs_fsync_start/format b/src/traced/probes/ftrace/test/data/synthetic/events/android_fs/android_fs_fsync_start/format
new file mode 100644
index 0000000..fb5f033
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/android_fs/android_fs_fsync_start/format
@@ -0,0 +1,15 @@
+name: android_fs_fsync_start
+ID: 335
+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:__data_loc char[] pathbuf; offset:8; size:4; signed:0;
+field:loff_t i_size; offset:16; size:8; signed:1;
+field:__data_loc char[] cmdline; offset:24; size:4; signed:0;
+field:pid_t pid; offset:28; size:4; signed:1;
+field:ino_t ino; offset:32; size:4; signed:0;
+
+print fmt: "entry_name %s, cmdline %s, pid %d, i_size %llu, ino %lu", __get_str(pathbuf), __get_str(cmdline), REC->pid, REC->i_size, (unsigned long) REC->ino
\ No newline at end of file
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/cma/cma_alloc_info/format b/src/traced/probes/ftrace/test/data/synthetic/events/cma/cma_alloc_info/format
new file mode 100644
index 0000000..038d756
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/cma/cma_alloc_info/format
@@ -0,0 +1,20 @@
+name: cma_alloc_info
+ID: 292
+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:__data_loc char[] name;	offset:8;	size:4;	signed:0;
+	field:unsigned long pfn;	offset:16;	size:8;	signed:0;
+	field:unsigned int count;	offset:24;	size:4;	signed:0;
+	field:unsigned int align;	offset:28;	size:4;	signed:0;
+	field:unsigned long nr_migrated;	offset:32;	size:8;	signed:0;
+	field:unsigned long nr_reclaimed;	offset:40;	size:8;	signed:0;
+	field:unsigned long nr_mapped;	offset:48;	size:8;	signed:0;
+	field:unsigned int err_iso;	offset:56;	size:4;	signed:0;
+	field:unsigned int err_mig;	offset:60;	size:4;	signed:0;
+	field:unsigned int err_test;	offset:64;	size:4;	signed:0;
+
+print fmt: "name=%s pfn=0x%lx count=%u align=%u nr_migrated=%lu nr_reclaimed=%lu nr_mapped=%lu err_iso=%u err_mig=%u err_test=%u", __get_str(name), REC->pfn, REC->count, REC->align, REC->nr_migrated, REC->nr_reclaimed, REC->nr_mapped, REC->err_iso, REC->err_mig, REC->err_test
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/cma/cma_alloc_start/format b/src/traced/probes/ftrace/test/data/synthetic/events/cma/cma_alloc_start/format
new file mode 100644
index 0000000..2d3695f
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/cma/cma_alloc_start/format
@@ -0,0 +1,13 @@
+name: cma_alloc_start
+ID: 291
+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:__data_loc char[] name;	offset:8;	size:4;	signed:0;
+	field:unsigned int count;	offset:12;	size:4;	signed:0;
+	field:unsigned int align;	offset:16;	size:4;	signed:0;
+
+print fmt: "name=%s count=%u align=%u", __get_str(name), REC->count, REC->align
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/ftrace/funcgraph_entry/format b/src/traced/probes/ftrace/test/data/synthetic/events/ftrace/funcgraph_entry/format
new file mode 100644
index 0000000..6c17b8d
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/ftrace/funcgraph_entry/format
@@ -0,0 +1,12 @@
+name: funcgraph_entry
+ID: 11
+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 long func;	offset:8;	size:8;	signed:0;
+	field:int depth;	offset:16;	size:4;	signed:1;
+
+print fmt: "--> %ps (%d)", (void *)REC->func, REC->depth
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/ftrace/funcgraph_exit/format b/src/traced/probes/ftrace/test/data/synthetic/events/ftrace/funcgraph_exit/format
new file mode 100644
index 0000000..dfa1ca2
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/ftrace/funcgraph_exit/format
@@ -0,0 +1,15 @@
+name: funcgraph_exit
+ID: 10
+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 long func;	offset:8;	size:8;	signed:0;
+	field:int depth;	offset:16;	size:4;	signed:1;
+	field:unsigned int overrun;	offset:20;	size:4;	signed:0;
+	field:unsigned long long calltime;	offset:24;	size:8;	signed:0;
+	field:unsigned long long rettime;	offset:32;	size:8;	signed:0;
+
+print fmt: "<-- %ps (%d) (start: %llx  end: %llx) over: %d", (void *)REC->func, REC->depth, REC->calltime, REC->rettime, REC->depth
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/lwis/tracing_mark_write/format b/src/traced/probes/ftrace/test/data/synthetic/events/lwis/tracing_mark_write/format
new file mode 100644
index 0000000..95c7e3f
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/lwis/tracing_mark_write/format
@@ -0,0 +1,15 @@
+name: tracing_mark_write
+ID: 1473
+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:char lwis_name[32];	offset:8;	size:32;	signed:0;
+	field:char type;	offset:40;	size:1;	signed:0;
+	field:int pid;	offset:44;	size:4;	signed:1;
+	field:__data_loc char[] func_name;	offset:48;	size:4;	signed:0;
+	field:int64_t value;	offset:56;	size:8;	signed:1;
+
+print fmt: "%c|%d|lwis-%s:%s|%lld", REC->type, REC->pid, REC->lwis_name, __get_str(func_name), REC->value
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/mali/mali_KCPU_CQS_SET/format b/src/traced/probes/ftrace/test/data/synthetic/events/mali/mali_KCPU_CQS_SET/format
new file mode 100644
index 0000000..63bf7bd
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/mali/mali_KCPU_CQS_SET/format
@@ -0,0 +1,16 @@
+name: mali_KCPU_CQS_SET
+ID: 1738
+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:u64 info_val1;	offset:8;	size:8;	signed:0;
+	field:u64 info_val2;	offset:16;	size:8;	signed:0;
+	field:pid_t kctx_tgid;	offset:24;	size:4;	signed:1;
+	field:u32 kctx_id;	offset:28;	size:4;	signed:0;
+	field:u8 id;	offset:32;	size:1;	signed:0;
+
+print fmt: "kctx=%d_%u id=%u info_val1=0x%llx info_val2=0x%llx", REC->kctx_tgid, REC->kctx_id, REC->id, REC->info_val1, REC->info_val2
+
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/mali/mali_KCPU_CQS_WAIT_END/format b/src/traced/probes/ftrace/test/data/synthetic/events/mali/mali_KCPU_CQS_WAIT_END/format
new file mode 100644
index 0000000..43f4204
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/mali/mali_KCPU_CQS_WAIT_END/format
@@ -0,0 +1,16 @@
+name: mali_KCPU_CQS_WAIT_END
+ID: 1740
+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:u64 info_val1;	offset:8;	size:8;	signed:0;
+	field:u64 info_val2;	offset:16;	size:8;	signed:0;
+	field:pid_t kctx_tgid;	offset:24;	size:4;	signed:1;
+	field:u32 kctx_id;	offset:28;	size:4;	signed:0;
+	field:u8 id;	offset:32;	size:1;	signed:0;
+
+print fmt: "kctx=%d_%u id=%u info_val1=0x%llx info_val2=0x%llx", REC->kctx_tgid, REC->kctx_id, REC->id, REC->info_val1, REC->info_val2
+
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/mali/mali_KCPU_CQS_WAIT_START/format b/src/traced/probes/ftrace/test/data/synthetic/events/mali/mali_KCPU_CQS_WAIT_START/format
new file mode 100644
index 0000000..7462751
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/mali/mali_KCPU_CQS_WAIT_START/format
@@ -0,0 +1,15 @@
+name: mali_KCPU_CQS_WAIT_START
+ID: 1739
+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:u64 info_val1;	offset:8;	size:8;	signed:0;
+	field:u64 info_val2;	offset:16;	size:8;	signed:0;
+	field:pid_t kctx_tgid;	offset:24;	size:4;	signed:1;
+	field:u32 kctx_id;	offset:28;	size:4;	signed:0;
+	field:u8 id;	offset:32;	size:1;	signed:0;
+
+print fmt: "kctx=%d_%u id=%u info_val1=0x%llx info_val2=0x%llx", REC->kctx_tgid, REC->kctx_id, REC->id, REC->info_val1, REC->info_val2
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/mali/mali_KCPU_FENCE_SIGNAL/format b/src/traced/probes/ftrace/test/data/synthetic/events/mali/mali_KCPU_FENCE_SIGNAL/format
new file mode 100644
index 0000000..d6c8e39
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/mali/mali_KCPU_FENCE_SIGNAL/format
@@ -0,0 +1,15 @@
+name: mali_KCPU_FENCE_SIGNAL
+ID: 1741
+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:u64 info_val1;	offset:8;	size:8;	signed:0;
+	field:u64 info_val2;	offset:16;	size:8;	signed:0;
+	field:pid_t kctx_tgid;	offset:24;	size:4;	signed:1;
+	field:u32 kctx_id;	offset:28;	size:4;	signed:0;
+	field:u8 id;	offset:32;	size:1;	signed:0;
+
+print fmt: "kctx=%d_%u id=%u info_val1=0x%llx info_val2=0x%llx", REC->kctx_tgid, REC->kctx_id, REC->id, REC->info_val1, REC->info_val2
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/mali/mali_KCPU_FENCE_WAIT_END/format b/src/traced/probes/ftrace/test/data/synthetic/events/mali/mali_KCPU_FENCE_WAIT_END/format
new file mode 100644
index 0000000..139546c
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/mali/mali_KCPU_FENCE_WAIT_END/format
@@ -0,0 +1,15 @@
+name: mali_KCPU_FENCE_WAIT_END
+ID: 1743
+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:u64 info_val1;	offset:8;	size:8;	signed:0;
+	field:u64 info_val2;	offset:16;	size:8;	signed:0;
+	field:pid_t kctx_tgid;	offset:24;	size:4;	signed:1;
+	field:u32 kctx_id;	offset:28;	size:4;	signed:0;
+	field:u8 id;	offset:32;	size:1;	signed:0;
+
+print fmt: "kctx=%d_%u id=%u info_val1=0x%llx info_val2=0x%llx", REC->kctx_tgid, REC->kctx_id, REC->id, REC->info_val1, REC->info_val2
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/mali/mali_KCPU_FENCE_WAIT_START/format b/src/traced/probes/ftrace/test/data/synthetic/events/mali/mali_KCPU_FENCE_WAIT_START/format
new file mode 100644
index 0000000..4b3152e
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/mali/mali_KCPU_FENCE_WAIT_START/format
@@ -0,0 +1,15 @@
+name: mali_KCPU_FENCE_WAIT_START
+ID: 1742
+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:u64 info_val1;	offset:8;	size:8;	signed:0;
+	field:u64 info_val2;	offset:16;	size:8;	signed:0;
+	field:pid_t kctx_tgid;	offset:24;	size:4;	signed:1;
+	field:u32 kctx_id;	offset:28;	size:4;	signed:0;
+	field:u8 id;	offset:32;	size:1;	signed:0;
+
+print fmt: "kctx=%d_%u id=%u info_val1=0x%llx info_val2=0x%llx", REC->kctx_tgid, REC->kctx_id, REC->id, REC->info_val1, REC->info_val2
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/panel/dsi_cmd_fifo_status/format b/src/traced/probes/ftrace/test/data/synthetic/events/panel/dsi_cmd_fifo_status/format
new file mode 100644
index 0000000..e761ff8
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/panel/dsi_cmd_fifo_status/format
@@ -0,0 +1,12 @@
+name: dsi_cmd_fifo_status
+ID: 1548
+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:u8 header;	offset:8;	size:1;	signed:0;
+	field:u16 payload;	offset:10;	size:2;	signed:0;
+
+print fmt: "header=%d payload=%d", REC->header, REC->payload
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/panel/dsi_rx/format b/src/traced/probes/ftrace/test/data/synthetic/events/panel/dsi_rx/format
new file mode 100644
index 0000000..cdacbcb
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/panel/dsi_rx/format
@@ -0,0 +1,12 @@
+name: dsi_rx
+ID: 1547
+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:u8 cmd;	offset:8;	size:1;	signed:0;
+	field:__data_loc u8[] rx_buf;	offset:12;	size:4;	signed:0;
+
+print fmt: "cmd=0x%02x length=%u rx=[%s]", REC->cmd, __get_dynamic_array_len(rx_buf), __print_hex(__get_dynamic_array(rx_buf), __get_dynamic_array_len(rx_buf))
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/panel/dsi_tx/format b/src/traced/probes/ftrace/test/data/synthetic/events/panel/dsi_tx/format
new file mode 100644
index 0000000..b22c493
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/panel/dsi_tx/format
@@ -0,0 +1,13 @@
+name: dsi_tx
+ID: 1546
+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:u8 type;	offset:8;	size:1;	signed:0;
+	field:__data_loc u8[] tx_buf;	offset:12;	size:4;	signed:0;
+	field:bool last;	offset:16;	size:1;	signed:0;
+
+print fmt: "type=0x%02x length=%u last=%d tx=[%s]", REC->type, __get_dynamic_array_len(tx_buf), REC->last, __print_hex(__get_dynamic_array(tx_buf), __get_dynamic_array_len(tx_buf))
diff --git a/test/trace_processor/common/empty.textproto b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/format
similarity index 100%
rename from test/trace_processor/common/empty.textproto
rename to src/traced/probes/ftrace/test/data/synthetic/events/trusty/format
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_enqueue_nop/format b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_enqueue_nop/format
new file mode 100644
index 0000000..a9dd874
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_enqueue_nop/format
@@ -0,0 +1,13 @@
+name: trusty_enqueue_nop
+ID: 844
+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:u32 arg1;	offset:8;	size:4;	signed:0;
+	field:u32 arg2;	offset:12;	size:4;	signed:0;
+	field:u32 arg3;	offset:16;	size:4;	signed:0;
+
+print fmt: "arg1=0x%x, arg2=0x%x, arg3=0x%x", REC->arg1, REC->arg2, REC->arg3
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_ipc_connect/format b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_ipc_connect/format
new file mode 100644
index 0000000..6305bb5
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_ipc_connect/format
@@ -0,0 +1,13 @@
+name: trusty_ipc_connect
+ID: 848
+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:u32 chan;	offset:8;	size:4;	signed:0;
+	field:__data_loc char[] port;	offset:12;	size:4;	signed:0;
+	field:int state;	offset:16;	size:4;	signed:1;
+
+print fmt: "chan=%u port=%s state=%d", REC->chan, __get_str(port), REC->state
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_ipc_connect_end/format b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_ipc_connect_end/format
new file mode 100644
index 0000000..abd2217
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_ipc_connect_end/format
@@ -0,0 +1,13 @@
+name: trusty_ipc_connect_end
+ID: 849
+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:u32 chan;	offset:8;	size:4;	signed:0;
+	field:int err;	offset:12;	size:4;	signed:1;
+	field:int state;	offset:16;	size:4;	signed:1;
+
+print fmt: "chan=%u err=%d state=%d", REC->chan, REC->err, REC->state
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_ipc_handle_event/format b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_ipc_handle_event/format
new file mode 100644
index 0000000..9c1d164
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_ipc_handle_event/format
@@ -0,0 +1,13 @@
+name: trusty_ipc_handle_event
+ID: 850
+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:u32 chan;	offset:8;	size:4;	signed:0;
+	field:char srv_name[256];	offset:12;	size:256;	signed:0;
+	field:u32 event_id;	offset:268;	size:4;	signed:0;
+
+print fmt: "chan=%u srv_name=%s event=%s", REC->chan, REC->srv_name, __print_symbolic(REC->event_id, { 1, "CONNECTED" }, { 2, "DISCONNECTED" }, { 3, "SHUTDOWN" })
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_ipc_poll/format b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_ipc_poll/format
new file mode 100644
index 0000000..0034ae3
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_ipc_poll/format
@@ -0,0 +1,13 @@
+name: trusty_ipc_poll
+ID: 854
+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 poll_mask;	offset:8;	size:4;	signed:0;
+	field:u32 chan;	offset:12;	size:4;	signed:0;
+	field:char srv_name[256];	offset:16;	size:256;	signed:0;
+
+print fmt: "poll_mask=%u chan=%u srv_name=%s", REC->poll_mask, REC->chan, REC->srv_name
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_ipc_read/format b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_ipc_read/format
new file mode 100644
index 0000000..a3b41c5
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_ipc_read/format
@@ -0,0 +1,12 @@
+name: trusty_ipc_read
+ID: 852
+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:u32 chan;	offset:8;	size:4;	signed:0;
+	field:char srv_name[256];	offset:12;	size:256;	signed:0;
+
+print fmt: "chan=%u srv_name=%s", REC->chan, REC->srv_name
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_ipc_read_end/format b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_ipc_read_end/format
new file mode 100644
index 0000000..89b3707
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_ipc_read_end/format
@@ -0,0 +1,15 @@
+name: trusty_ipc_read_end
+ID: 853
+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 len_or_err;	offset:8;	size:4;	signed:1;
+	field:u32 chan;	offset:12;	size:4;	signed:0;
+	field:char srv_name[256];	offset:16;	size:256;	signed:0;
+	field:u64 buf_id;	offset:272;	size:8;	signed:0;
+	field:size_t shm_cnt;	offset:280;	size:8;	signed:0;
+
+print fmt: "len_or_err=%d chan=%u srv_name=%s buf_id=0x%llx shm_cnt=%zu", REC->len_or_err, REC->chan, REC->srv_name, REC->buf_id, REC->shm_cnt
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_ipc_rx/format b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_ipc_rx/format
new file mode 100644
index 0000000..8081d2f
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_ipc_rx/format
@@ -0,0 +1,13 @@
+name: trusty_ipc_rx
+ID: 855
+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:u32 chan;	offset:8;	size:4;	signed:0;
+	field:char srv_name[256];	offset:12;	size:256;	signed:0;
+	field:u64 buf_id;	offset:272;	size:8;	signed:0;
+
+print fmt: "chan=%u srv_name=%s buf_id=0x%llx", REC->chan, REC->srv_name, REC->buf_id
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_ipc_write/format b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_ipc_write/format
new file mode 100644
index 0000000..c2082d1
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_ipc_write/format
@@ -0,0 +1,16 @@
+name: trusty_ipc_write
+ID: 851
+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 len_or_err;	offset:8;	size:4;	signed:1;
+	field:u32 chan;	offset:12;	size:4;	signed:0;
+	field:char srv_name[256];	offset:16;	size:256;	signed:0;
+	field:u64 buf_id;	offset:272;	size:8;	signed:0;
+	field:size_t shm_cnt;	offset:280;	size:8;	signed:0;
+	field:__data_loc int[] kind_shm;	offset:288;	size:4;	signed:1;
+
+print fmt: "len_or_err=%d chan=%u srv_name=%s buf_id=0x%llx shm_cnt=%zu kind_shm=%s", REC->len_or_err, REC->chan, REC->srv_name, REC->buf_id, REC->shm_cnt, __print_array(__get_dynamic_array(kind_shm), __get_dynamic_array_len(kind_shm) / sizeof(int), sizeof(int))
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_irq/format b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_irq/format
new file mode 100644
index 0000000..d9b5950
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_irq/format
@@ -0,0 +1,11 @@
+name: trusty_irq
+ID: 847
+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 irq;	offset:8;	size:4;	signed:1;
+
+print fmt: "irq=%d", REC->irq
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_reclaim_memory/format b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_reclaim_memory/format
new file mode 100644
index 0000000..06247f2
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_reclaim_memory/format
@@ -0,0 +1,11 @@
+name: trusty_reclaim_memory
+ID: 845
+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:u64 id;	offset:8;	size:8;	signed:0;
+
+print fmt: "id=%llu", REC->id
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_reclaim_memory_done/format b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_reclaim_memory_done/format
new file mode 100644
index 0000000..149c79f
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_reclaim_memory_done/format
@@ -0,0 +1,12 @@
+name: trusty_reclaim_memory_done
+ID: 846
+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:u64 id;	offset:8;	size:8;	signed:0;
+	field:int ret;	offset:16;	size:4;	signed:1;
+
+print fmt: "id=%llu ret=%d (0x%x)", REC->id, REC->ret, REC->ret
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_share_memory/format b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_share_memory/format
new file mode 100644
index 0000000..2e1c233
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_share_memory/format
@@ -0,0 +1,13 @@
+name: trusty_share_memory
+ID: 842
+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:size_t len;	offset:8;	size:8;	signed:0;
+	field:unsigned int nents;	offset:16;	size:4;	signed:0;
+	field:bool lend;	offset:20;	size:1;	signed:0;
+
+print fmt: "len=%zu, nents=%u, lend=%u", REC->len, REC->nents, REC->lend
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_share_memory_done/format b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_share_memory_done/format
new file mode 100644
index 0000000..3ed0469
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_share_memory_done/format
@@ -0,0 +1,15 @@
+name: trusty_share_memory_done
+ID: 843
+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:size_t len;	offset:8;	size:8;	signed:0;
+	field:unsigned int nents;	offset:16;	size:4;	signed:0;
+	field:bool lend;	offset:20;	size:1;	signed:0;
+	field:u64 handle;	offset:24;	size:8;	signed:0;
+	field:int ret;	offset:32;	size:4;	signed:1;
+
+print fmt: "len=%zu, nents=%u, lend=%u, ffa_handle=0x%llx, ret=%d", REC->len, REC->nents, REC->lend, REC->handle, REC->ret
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_smc/format b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_smc/format
new file mode 100644
index 0000000..4f6bf88
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_smc/format
@@ -0,0 +1,14 @@
+name: trusty_smc
+ID: 839
+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 long r0;	offset:8;	size:8;	signed:0;
+	field:unsigned long r1;	offset:16;	size:8;	signed:0;
+	field:unsigned long r2;	offset:24;	size:8;	signed:0;
+	field:unsigned long r3;	offset:32;	size:8;	signed:0;
+
+print fmt: "smcnr=%s r0=0x%lx r1=0x%lx r2=0x%lx r3=0x%lx", __print_symbolic(REC->r0, { ((((0) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((60)) & 0x3FU) << 24) | (((0)) & 0xFFFFU) ), "SC_RESTART_LAST" }, { ((((0) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((60)) & 0x3FU) << 24) | (((1)) & 0xFFFFU) ), "SC_LOCKED_NOP" }, { ((((0) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((60)) & 0x3FU) << 24) | (((2)) & 0xFFFFU) ), "SC_RESTART_FIQ" }, { ((((0) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((60)) & 0x3FU) << 24) | (((3)) & 0xFFFFU) ), "SC_NOP" }, { ((((1) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((60)) & 0x3FU) << 24) | (((0)) & 0xFFFFU) ), "FC_RESERVED" }, { ((((1) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((60)) & 0x3FU) << 24) | (((1)) & 0xFFFFU) ), "FC_FIQ_EXIT" }, { ((((1) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((60)) & 0x3FU) << 24) | (((2)) & 0xFFFFU) ), "FC_REQUEST_FIQ" }, { ((((1) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((60)) & 0x3FU) << 24) | (((3)) & 0xFFFFU) ), "FC_GET_NEXT_IRQ" }, { ((((1) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((60)) & 0x3FU) << 24) | (((7)) & 0xFFFFU) ), "FC_CPU_SUSPEND" }, { ((((1) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((60)) & 0x3FU) << 24) | (((8)) & 0xFFFFU) ), "FC_CPU_RESUME" }, { ((((1) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((60)) & 0x3FU) << 24) | (((9)) & 0xFFFFU) ), "FC_AARCH_SWITCH" }, { ((((1) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((60)) & 0x3FU) << 24) | (((10)) & 0xFFFFU) ), "FC_GET_VERSION_STR" }, { ((((1) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((60)) & 0x3FU) << 24) | (((11)) & 0xFFFFU) ), "FC_API_VERSION" }, { ((((0) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((50)) & 0x3FU) << 24) | (((20)) & 0xFFFFU) ), "SC_VIRTIO_GET_DESCR" }, { ((((0) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((50)) & 0x3FU) << 24) | (((21)) & 0xFFFFU) ), "SC_VIRTIO_START" }, { ((((0) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((50)) & 0x3FU) << 24) | (((22)) & 0xFFFFU) ), "SC_VIRTIO_STOP" }, { ((((0) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((50)) & 0x3FU) << 24) | (((23)) & 0xFFFFU) ), "SC_VDEV_RESET" }, { ((((0) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((50)) & 0x3FU) << 24) | (((24)) & 0xFFFFU) ), "SC_VDEV_KICK_VQ" }, { ((((0) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((50)) & 0x3FU) << 24) | (((25)) & 0xFFFFU) ), "NC_VDEV_KICK_VQ" }), REC->r0, REC->r1, REC->r2, REC->r3
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_smc_done/format b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_smc_done/format
new file mode 100644
index 0000000..026d17f
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_smc_done/format
@@ -0,0 +1,11 @@
+name: trusty_smc_done
+ID: 841
+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 long ret;	offset:8;	size:8;	signed:0;
+
+print fmt: "ret=%d (0x%x)", REC->ret, REC->ret
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_std_call32/format b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_std_call32/format
new file mode 100644
index 0000000..03b5932
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_std_call32/format
@@ -0,0 +1,14 @@
+name: trusty_std_call32
+ID: 838
+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 long r0;	offset:8;	size:8;	signed:0;
+	field:unsigned long r1;	offset:16;	size:8;	signed:0;
+	field:unsigned long r2;	offset:24;	size:8;	signed:0;
+	field:unsigned long r3;	offset:32;	size:8;	signed:0;
+
+print fmt: "smcnr=%s r0=0x%lx r1=0x%lx r2=0x%lx r3=0x%lx", __print_symbolic(REC->r0, { ((((0) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((60)) & 0x3FU) << 24) | (((0)) & 0xFFFFU) ), "SC_RESTART_LAST" }, { ((((0) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((60)) & 0x3FU) << 24) | (((1)) & 0xFFFFU) ), "SC_LOCKED_NOP" }, { ((((0) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((60)) & 0x3FU) << 24) | (((2)) & 0xFFFFU) ), "SC_RESTART_FIQ" }, { ((((0) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((60)) & 0x3FU) << 24) | (((3)) & 0xFFFFU) ), "SC_NOP" }, { ((((1) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((60)) & 0x3FU) << 24) | (((0)) & 0xFFFFU) ), "FC_RESERVED" }, { ((((1) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((60)) & 0x3FU) << 24) | (((1)) & 0xFFFFU) ), "FC_FIQ_EXIT" }, { ((((1) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((60)) & 0x3FU) << 24) | (((2)) & 0xFFFFU) ), "FC_REQUEST_FIQ" }, { ((((1) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((60)) & 0x3FU) << 24) | (((3)) & 0xFFFFU) ), "FC_GET_NEXT_IRQ" }, { ((((1) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((60)) & 0x3FU) << 24) | (((7)) & 0xFFFFU) ), "FC_CPU_SUSPEND" }, { ((((1) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((60)) & 0x3FU) << 24) | (((8)) & 0xFFFFU) ), "FC_CPU_RESUME" }, { ((((1) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((60)) & 0x3FU) << 24) | (((9)) & 0xFFFFU) ), "FC_AARCH_SWITCH" }, { ((((1) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((60)) & 0x3FU) << 24) | (((10)) & 0xFFFFU) ), "FC_GET_VERSION_STR" }, { ((((1) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((60)) & 0x3FU) << 24) | (((11)) & 0xFFFFU) ), "FC_API_VERSION" }, { ((((0) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((50)) & 0x3FU) << 24) | (((20)) & 0xFFFFU) ), "SC_VIRTIO_GET_DESCR" }, { ((((0) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((50)) & 0x3FU) << 24) | (((21)) & 0xFFFFU) ), "SC_VIRTIO_START" }, { ((((0) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((50)) & 0x3FU) << 24) | (((22)) & 0xFFFFU) ), "SC_VIRTIO_STOP" }, { ((((0) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((50)) & 0x3FU) << 24) | (((23)) & 0xFFFFU) ), "SC_VDEV_RESET" }, { ((((0) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((50)) & 0x3FU) << 24) | (((24)) & 0xFFFFU) ), "SC_VDEV_KICK_VQ" }, { ((((0) & 0x1U) << 31) | (((0) & 0x1U) << 30) | ((((50)) & 0x3FU) << 24) | (((25)) & 0xFFFFU) ), "NC_VDEV_KICK_VQ" }), REC->r0, REC->r1, REC->r2, REC->r3
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_std_call32_done/format b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_std_call32_done/format
new file mode 100644
index 0000000..866da41
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/trusty/trusty_std_call32_done/format
@@ -0,0 +1,11 @@
+name: trusty_std_call32_done
+ID: 840
+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:long ret;	offset:8;	size:8;	signed:1;
+
+print fmt: "ret=%d (0x%x)", REC->ret, REC->ret
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/virtio_gpu/virtio_gpu_cmd_queue/format b/src/traced/probes/ftrace/test/data/synthetic/events/virtio_gpu/virtio_gpu_cmd_queue/format
new file mode 100644
index 0000000..219457a
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/virtio_gpu/virtio_gpu_cmd_queue/format
@@ -0,0 +1,19 @@
+name: virtio_gpu_cmd_queue
+ID: 664
+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 dev;	offset:8;	size:4;	signed:1;
+	field:unsigned int vq;	offset:12;	size:4;	signed:0;
+	field:__data_loc char[] name;	offset:16;	size:4;	signed:0;
+	field:u32 type;	offset:20;	size:4;	signed:0;
+	field:u32 flags;	offset:24;	size:4;	signed:0;
+	field:u64 fence_id;	offset:32;	size:8;	signed:0;
+	field:u32 ctx_id;	offset:40;	size:4;	signed:0;
+	field:u32 num_free;	offset:44;	size:4;	signed:0;
+	field:u32 seqno;	offset:48;	size:4;	signed:0;
+
+print fmt: "vdev=%d vq=%u name=%s type=0x%x flags=0x%x fence_id=%llu ctx_id=%u num_free=%u seqno=%u", REC->dev, REC->vq, __get_str(name), REC->type, REC->flags, REC->fence_id, REC->ctx_id, REC->num_free, REC->seqno
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/virtio_gpu/virtio_gpu_cmd_response/format b/src/traced/probes/ftrace/test/data/synthetic/events/virtio_gpu/virtio_gpu_cmd_response/format
new file mode 100644
index 0000000..bf2db30
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/virtio_gpu/virtio_gpu_cmd_response/format
@@ -0,0 +1,19 @@
+name: virtio_gpu_cmd_response
+ID: 665
+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 dev;	offset:8;	size:4;	signed:1;
+	field:unsigned int vq;	offset:12;	size:4;	signed:0;
+	field:__data_loc char[] name;	offset:16;	size:4;	signed:0;
+	field:u32 type;	offset:20;	size:4;	signed:0;
+	field:u32 flags;	offset:24;	size:4;	signed:0;
+	field:u64 fence_id;	offset:32;	size:8;	signed:0;
+	field:u32 ctx_id;	offset:40;	size:4;	signed:0;
+	field:u32 num_free;	offset:44;	size:4;	signed:0;
+	field:u32 seqno;	offset:48;	size:4;	signed:0;
+
+print fmt: "vdev=%d vq=%u name=%s type=0x%x flags=0x%x fence_id=%llu ctx_id=%u num_free=%u seqno=%u", REC->dev, REC->vq, __get_str(name), REC->type, REC->flags, REC->fence_id, REC->ctx_id, REC->num_free, REC->seqno
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/virtio_video/virtio_video_cmd/format b/src/traced/probes/ftrace/test/data/synthetic/events/virtio_video/virtio_video_cmd/format
new file mode 100644
index 0000000..c0d3cf6
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/virtio_video/virtio_video_cmd/format
@@ -0,0 +1,12 @@
+name: virtio_video_cmd
+ID: 1407
+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:uint32_t type;	offset:8;	size:4;	signed:0;
+	field:uint32_t stream_id;	offset:12;	size:4;	signed:0;
+
+print fmt: "type=%s stream_id=%u", __print_symbolic(REC->type, { VIRTIO_VIDEO_CMD_QUERY_CAPABILITY, "QUERY_CAPABILITY" }, { VIRTIO_VIDEO_CMD_STREAM_CREATE, "STREAM_CREATE" }, { VIRTIO_VIDEO_CMD_STREAM_DESTROY, "STREAM_DESTROY" }, { VIRTIO_VIDEO_CMD_STREAM_DRAIN, "STREAM_DRAIN" }, { VIRTIO_VIDEO_CMD_RESOURCE_CREATE, "RESOURCE_CREATE" }, { VIRTIO_VIDEO_CMD_RESOURCE_QUEUE, "RESOURCE_QUEUE" }, { VIRTIO_VIDEO_CMD_RESOURCE_DESTROY_ALL, "RESOURCE_DESTROY_ALL" }, { VIRTIO_VIDEO_CMD_QUEUE_CLEAR, "QUEUE_CLEAR" }, { VIRTIO_VIDEO_CMD_QUERY_CONTROL, "QUERY_CONTROL" }, { VIRTIO_VIDEO_CMD_GET_CONTROL, "GET_CONTROL" }, { VIRTIO_VIDEO_CMD_SET_CONTROL, "SET_CONTROL" }, { VIRTIO_VIDEO_CMD_GET_PARAMS_EXT, "GET_PARAMS_EXT" }, { VIRTIO_VIDEO_CMD_SET_PARAMS_EXT, "SET_PARAMS_EXT" } ), REC->stream_id
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/virtio_video/virtio_video_cmd_done/format b/src/traced/probes/ftrace/test/data/synthetic/events/virtio_video/virtio_video_cmd_done/format
new file mode 100644
index 0000000..6b4f199
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/virtio_video/virtio_video_cmd_done/format
@@ -0,0 +1,12 @@
+name: virtio_video_cmd_done
+ID: 1408
+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:uint32_t type;	offset:8;	size:4;	signed:0;
+	field:uint32_t stream_id;	offset:12;	size:4;	signed:0;
+
+print fmt: "type=%s stream_id=%u", __print_symbolic(REC->type, { VIRTIO_VIDEO_CMD_QUERY_CAPABILITY, "QUERY_CAPABILITY" }, { VIRTIO_VIDEO_CMD_STREAM_CREATE, "STREAM_CREATE" }, { VIRTIO_VIDEO_CMD_STREAM_DESTROY, "STREAM_DESTROY" }, { VIRTIO_VIDEO_CMD_STREAM_DRAIN, "STREAM_DRAIN" }, { VIRTIO_VIDEO_CMD_RESOURCE_CREATE, "RESOURCE_CREATE" }, { VIRTIO_VIDEO_CMD_RESOURCE_QUEUE, "RESOURCE_QUEUE" }, { VIRTIO_VIDEO_CMD_RESOURCE_DESTROY_ALL, "RESOURCE_DESTROY_ALL" }, { VIRTIO_VIDEO_CMD_QUEUE_CLEAR, "QUEUE_CLEAR" }, { VIRTIO_VIDEO_CMD_QUERY_CONTROL, "QUERY_CONTROL" }, { VIRTIO_VIDEO_CMD_GET_CONTROL, "GET_CONTROL" }, { VIRTIO_VIDEO_CMD_SET_CONTROL, "SET_CONTROL" }, { VIRTIO_VIDEO_CMD_GET_PARAMS_EXT, "GET_PARAMS_EXT" }, { VIRTIO_VIDEO_CMD_SET_PARAMS_EXT, "SET_PARAMS_EXT" } ), REC->stream_id
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/virtio_video/virtio_video_resource_queue/format b/src/traced/probes/ftrace/test/data/synthetic/events/virtio_video/virtio_video_resource_queue/format
new file mode 100644
index 0000000..b93c2ad
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/virtio_video/virtio_video_resource_queue/format
@@ -0,0 +1,18 @@
+name: virtio_video_resource_queue
+ID: 1405
+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 stream_id;	offset:8;	size:4;	signed:1;
+	field:int resource_id;	offset:12;	size:4;	signed:1;
+	field:uint32_t queue_type;	offset:16;	size:4;	signed:0;
+	field:uint32_t data_size0;	offset:20;	size:4;	signed:0;
+	field:uint32_t data_size1;	offset:24;	size:4;	signed:0;
+	field:uint32_t data_size2;	offset:28;	size:4;	signed:0;
+	field:uint32_t data_size3;	offset:32;	size:4;	signed:0;
+	field:uint64_t timestamp;	offset:40;	size:8;	signed:0;
+
+print fmt: "stream_id=%d resource_id=%d queue_type=%s data_size0=%u data_size1=%u data_size2=%u data_size3=%u timestamp=%llu", REC->stream_id, REC->resource_id, __print_symbolic(REC->queue_type, { VIRTIO_VIDEO_QUEUE_TYPE_INPUT, "INPUT" }, { VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT, "OUTPUT" }), REC->data_size0, REC->data_size1, REC->data_size2, REC->data_size3, REC->timestamp
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/virtio_video/virtio_video_resource_queue_done/format b/src/traced/probes/ftrace/test/data/synthetic/events/virtio_video/virtio_video_resource_queue_done/format
new file mode 100644
index 0000000..cbe5511
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/virtio_video/virtio_video_resource_queue_done/format
@@ -0,0 +1,18 @@
+name: virtio_video_resource_queue_done
+ID: 1406
+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 stream_id;	offset:8;	size:4;	signed:1;
+	field:int resource_id;	offset:12;	size:4;	signed:1;
+	field:uint32_t queue_type;	offset:16;	size:4;	signed:0;
+	field:uint32_t data_size0;	offset:20;	size:4;	signed:0;
+	field:uint32_t data_size1;	offset:24;	size:4;	signed:0;
+	field:uint32_t data_size2;	offset:28;	size:4;	signed:0;
+	field:uint32_t data_size3;	offset:32;	size:4;	signed:0;
+	field:uint64_t timestamp;	offset:40;	size:8;	signed:0;
+
+print fmt: "stream_id=%d resource_id=%d queue_type=%s data_size0=%u data_size1=%u data_size2=%u data_size3=%u timestamp=%llu", REC->stream_id, REC->resource_id, __print_symbolic(REC->queue_type, { VIRTIO_VIDEO_QUEUE_TYPE_INPUT, "INPUT" }, { VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT, "OUTPUT" }), REC->data_size0, REC->data_size1, REC->data_size2, REC->data_size3, REC->timestamp
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/vmscan/mm_shrink_slab_end/format b/src/traced/probes/ftrace/test/data/synthetic/events/vmscan/mm_shrink_slab_end/format
new file mode 100644
index 0000000..3f2bb2f
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/vmscan/mm_shrink_slab_end/format
@@ -0,0 +1,17 @@
+name: mm_shrink_slab_end
+ID: 236
+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:struct shrinker * shr;	offset:8;	size:8;	signed:0;
+	field:int nid;	offset:16;	size:4;	signed:1;
+	field:void * shrink;	offset:24;	size:8;	signed:0;
+	field:long unused_scan;	offset:32;	size:8;	signed:1;
+	field:long new_scan;	offset:40;	size:8;	signed:1;
+	field:int retval;	offset:48;	size:4;	signed:1;
+	field:long total_scan;	offset:56;	size:8;	signed:1;
+
+print fmt: "%pS %p: nid: %d unused scan count %ld new scan count %ld total_scan %ld last shrinker return val %d", REC->shrink, REC->shr, REC->nid, REC->unused_scan, REC->new_scan, REC->total_scan, REC->retval
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/vmscan/mm_shrink_slab_start/format b/src/traced/probes/ftrace/test/data/synthetic/events/vmscan/mm_shrink_slab_start/format
new file mode 100644
index 0000000..5a3cde4
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/vmscan/mm_shrink_slab_start/format
@@ -0,0 +1,19 @@
+name: mm_shrink_slab_start
+ID: 235
+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:struct shrinker * shr;	offset:8;	size:8;	signed:0;
+	field:void * shrink;	offset:16;	size:8;	signed:0;
+	field:int nid;	offset:24;	size:4;	signed:1;
+	field:long nr_objects_to_shrink;	offset:32;	size:8;	signed:1;
+	field:gfp_t gfp_flags;	offset:40;	size:4;	signed:0;
+	field:unsigned long cache_items;	offset:48;	size:8;	signed:0;
+	field:unsigned long long delta;	offset:56;	size:8;	signed:0;
+	field:unsigned long total_scan;	offset:64;	size:8;	signed:0;
+	field:int priority;	offset:72;	size:4;	signed:1;
+
+print fmt: "%pS %p: nid: %d objects to shrink %ld gfp_flags %s cache items %ld delta %lld total_scan %ld priority %d", REC->shrink, REC->shr, REC->nid, REC->nr_objects_to_shrink, (REC->gfp_flags) ? __print_flags(REC->gfp_flags, "|", {(unsigned long)(((((((( gfp_t)(0x400u|0x800u)) | (( gfp_t)0x40u) | (( gfp_t)0x80u) | (( gfp_t)0x100000u)) | (( gfp_t)0x02u)) | (( gfp_t)0x08u) | (( gfp_t)0x4000000u)) | (( gfp_t)0x40000u) | (( gfp_t)0x80000u) | (( gfp_t)0x2000u)) & ~(( gfp_t)(0x400u|0x800u))) | (( gfp_t)0x400u)), "GFP_TRANSHUGE"}, {(unsigned long)((((((( gfp_t)(0x400u|0x800u)) | (( gfp_t)0x40u) | (( gfp_t)0x80u) | (( gfp_t)0x100000u)) | (( gfp_t)0x02u)) | (( gfp_t)0x08u) | (( gfp_t)0x4000000u)) | (( gfp_t)0x40000u) | (( gfp_t)0x80000u) | (( gfp_t)0x2000u)) & ~(( gfp_t)(0x400u|0x800u))), "GFP_TRANSHUGE_LIGHT"}, {(unsigned long)((((( gfp_t)(0x400u|0x800u)) | (( gfp_t)0x40u) | (( gfp_t)0x80u) | (( gfp_t)0x100000u)) | (( gfp_t)0x02u)) | (( gfp_t)0x08u) | (( gfp_t)0x4000000u)), "GFP_HIGHUSER_MOVABLE"}, {(unsigned long)(((( gfp_t)(0x400u|0x800u)) | (( gfp_t)0x40u) | (( gfp_t)0x80u) | (( gfp_t)0x100000u)) | (( gfp_t)0x02u)), "GFP_HIGHUSER"}, {(unsigned long)((( gfp_t)(0x400u|0x800u)) | (( gfp_t)0x40u) | (( gfp_t)0x80u) | (( gfp_t)0x100000u)), "GFP_USER"}, {(unsigned long)(((( gfp_t)(0x400u|0x800u)) | (( gfp_t)0x40u) | (( gfp_t)0x80u)) | (( gfp_t)0x400000u)), "GFP_KERNEL_ACCOUNT"}, {(unsigned long)((( gfp_t)(0x400u|0x800u)) | (( gfp_t)0x40u) | (( gfp_t)0x80u)), "GFP_KERNEL"}, {(unsigned long)((( gfp_t)(0x400u|0x800u)) | (( gfp_t)0x40u)), "GFP_NOFS"}, {(unsigned long)((( gfp_t)0x20u)|(( gfp_t)0x200u)|(( gfp_t)0x800u)), "GFP_ATOMIC"}, {(unsigned long)((( gfp_t)(0x400u|0x800u))), "GFP_NOIO"}, {(unsigned long)((( gfp_t)0x800u)), "GFP_NOWAIT"}, {(unsigned long)(( gfp_t)0x01u), "GFP_DMA"}, {(unsigned long)(( gfp_t)0x02u), "__GFP_HIGHMEM"}, {(unsigned long)(( gfp_t)0x04u), "GFP_DMA32"}, {(unsigned long)(( gfp_t)0x20u), "__GFP_HIGH"}, {(unsigned long)(( gfp_t)0x200u), "__GFP_ATOMIC"}, {(unsigned long)(( gfp_t)0x40u), "__GFP_IO"}, {(unsigned long)(( gfp_t)0x80u), "__GFP_FS"}, {(unsigned long)(( gfp_t)0x2000u), "__GFP_NOWARN"}, {(unsigned long)(( gfp_t)0x4000u), "__GFP_RETRY_MAYFAIL"}, {(unsigned long)(( gfp_t)0x8000u), "__GFP_NOFAIL"}, {(unsigned long)(( gfp_t)0x10000u), "__GFP_NORETRY"}, {(unsigned long)(( gfp_t)0x40000u), "__GFP_COMP"}, {(unsigned long)(( gfp_t)0x100u), "__GFP_ZERO"}, {(unsigned long)(( gfp_t)0x80000u), "__GFP_NOMEMALLOC"}, {(unsigned long)(( gfp_t)0x20000u), "__GFP_MEMALLOC"}, {(unsigned long)(( gfp_t)0x100000u), "__GFP_HARDWALL"}, {(unsigned long)(( gfp_t)0x200000u), "__GFP_THISNODE"}, {(unsigned long)(( gfp_t)0x10u), "__GFP_RECLAIMABLE"}, {(unsigned long)(( gfp_t)0x08u), "__GFP_MOVABLE"}, {(unsigned long)(( gfp_t)0x400000u), "__GFP_ACCOUNT"}, {(unsigned long)(( gfp_t)0x1000u), "__GFP_WRITE"}, {(unsigned long)(( gfp_t)(0x400u|0x800u)), "__GFP_RECLAIM"}, {(unsigned long)(( gfp_t)0x400u), "__GFP_DIRECT_RECLAIM"}, {(unsigned long)(( gfp_t)0x800u), "__GFP_KSWAPD_RECLAIM"}, {(unsigned long)(( gfp_t)0x800000u), "__GFP_ZEROTAGS"} , {(unsigned long)(( gfp_t)0x1000000u), "__GFP_SKIP_ZERO"}, {(unsigned long)(( gfp_t)0x4000000u), "__GFP_SKIP_KASAN_POISON"}, {(unsigned long)(( gfp_t)0x2000000u), "__GFP_SKIP_KASAN_UNPOISON"} ) : "none", REC->cache_items, REC->delta, REC->total_scan, REC->priority
diff --git a/src/traced/probes/ftrace/vendor_tracepoints.cc b/src/traced/probes/ftrace/vendor_tracepoints.cc
new file mode 100644
index 0000000..27c228b
--- /dev/null
+++ b/src/traced/probes/ftrace/vendor_tracepoints.cc
@@ -0,0 +1,173 @@
+/*
+ * 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/vendor_tracepoints.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "perfetto/base/status.h"
+#include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/string_splitter.h"
+#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 {
+namespace {
+
+using EmptyTokenMode = ::perfetto::base::StringSplitter::EmptyTokenMode;
+
+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;
+}
+
+base::Status ParseEventLine(base::StringView line,
+                            std::vector<GroupAndName>* category) {
+  // `line` is a line in the vendor file that starts with one or more whitespace
+  // and is expected to contain the path to an ftrace event like:
+  // ```
+  //  cma/cma_alloc_start
+  // ```
+  while (!line.empty() && (line.at(0) == ' ' || line.at(0) == '\t')) {
+    line = line.substr(1);
+  }
+  if (line.empty()) {
+    return base::OkStatus();
+  }
+  size_t pos = line.find('/');
+  if (pos == line.npos) {
+    return base::ErrStatus("Ftrace event path not in group/event format");
+  }
+  base::StringView group = line.substr(0, pos);
+  if (group.empty()) {
+    return base::ErrStatus("Ftrace event path group is empty");
+  }
+  base::StringView name = line.substr(pos + 1);
+  if (name.find('/') != name.npos) {
+    return base::ErrStatus("Ftrace event path has extra / in event name");
+  }
+  if (name.empty()) {
+    return base::ErrStatus("Ftrace event name empty");
+  }
+  category->push_back(GroupAndName(group.ToStdString(), name.ToStdString()));
+  return base::OkStatus();
+}
+
+}  // namespace
+
+std::map<std::string, std::vector<GroupAndName>>
+DiscoverVendorTracepointsWithHal(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;
+}
+
+base::Status DiscoverVendorTracepointsWithFile(
+    const std::string& vendor_atrace_categories_path,
+    std::map<std::string, std::vector<GroupAndName>>* categories_map) {
+  std::string content;
+  if (!base::ReadFile(vendor_atrace_categories_path, &content)) {
+    return base::ErrStatus("Cannot read vendor atrace file: %s (errno: %d, %s)",
+                           vendor_atrace_categories_path.c_str(), errno,
+                           strerror(errno));
+  }
+  // The file should contain a list of categories (one per line) and, for each
+  // category, a list of ftrace events (one per line, nested):
+  // ```
+  // gfx
+  //  mali/gpu_power_state
+  //  mali/mali_pm_status
+  // thermal_tj
+  //  thermal_exynos/thermal_cpu_pressure
+  //  thermal_exynos/thermal_exynos_arm_update
+  // ```
+  std::vector<GroupAndName>* category = nullptr;
+  for (base::StringSplitter lines(std::move(content), '\n',
+                                  EmptyTokenMode::DISALLOW_EMPTY_TOKENS);
+       lines.Next();) {
+    base::StringView line(lines.cur_token());
+    if (line.empty()) {
+      continue;
+    }
+    char firstchar = line.at(0);
+    if (firstchar == '\t' || firstchar == ' ') {
+      // The line begins with a whitespace. It should contain an ftrace event
+      // path, part of a previously defined category.
+      if (category == nullptr) {
+        return base::ErrStatus(
+            "Ftrace event path before category. Malformed vendor atrace file");
+      }
+      base::Status status = ParseEventLine(line, category);
+      if (!status.ok()) {
+        return status;
+      }
+    } else {
+      // The line doesn't begin with a whitespace. Start a new category.
+      category = &(*categories_map)[line.ToStdString()];
+    }
+  }
+  return base::OkStatus();
+}
+
+base::Status DiscoverAccessibleVendorTracepointsWithFile(
+    const std::string& vendor_atrace_categories_path,
+    std::map<std::string, std::vector<GroupAndName>>* categories_map,
+    FtraceProcfs* ftrace) {
+  categories_map->clear();
+  base::Status status = DiscoverVendorTracepointsWithFile(
+      vendor_atrace_categories_path, categories_map);
+  if (!status.ok()) {
+    return status;
+  }
+
+  for (auto& it : *categories_map) {
+    std::vector<GroupAndName>& events = it.second;
+    events.erase(std::remove_if(events.begin(), events.end(),
+                                [ftrace](const GroupAndName& event) {
+                                  return !ftrace->IsEventAccessible(
+                                      event.group(), event.name());
+                                }),
+                 events.end());
+  }
+
+  return base::OkStatus();
+}
+
+}  // namespace vendor_tracepoints
+}  // namespace perfetto
diff --git a/src/traced/probes/ftrace/vendor_tracepoints.h b/src/traced/probes/ftrace/vendor_tracepoints.h
new file mode 100644
index 0000000..df449ca
--- /dev/null
+++ b/src/traced/probes/ftrace/vendor_tracepoints.h
@@ -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.
+ */
+
+#ifndef SRC_TRACED_PROBES_FTRACE_VENDOR_TRACEPOINTS_H_
+#define SRC_TRACED_PROBES_FTRACE_VENDOR_TRACEPOINTS_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "perfetto/base/status.h"
+#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 {
+
+// Path to the vendor categories file in Android (since Android 14).
+constexpr const char* kCategoriesFile =
+    "/vendor/etc/atrace/atrace_categories.txt";
+
+// Returns a map from vendor category to events we should enable. Queries the
+// atrace HAL.
+std::map<std::string, std::vector<GroupAndName>>
+DiscoverVendorTracepointsWithHal(AtraceHalWrapper* hal, FtraceProcfs* ftrace);
+
+// Fills `*categories_map` with a map from vendor category to events we should
+// enable. Queries the vendor categories file at
+// `vendor_atrace_categories_path` (which should always be `kCategoriesFile`
+// except in tests).
+base::Status DiscoverVendorTracepointsWithFile(
+    const std::string& vendor_atrace_categories_path,
+    std::map<std::string, std::vector<GroupAndName>>* categories_map);
+
+// Like `DiscoverVendorTracepointsWithFile`, but does not return events that are
+// not accessible or do not actually exist on the tracing file system.
+base::Status DiscoverAccessibleVendorTracepointsWithFile(
+    const std::string& vendor_atrace_categories_path,
+    std::map<std::string, std::vector<GroupAndName>>* categories_map,
+    FtraceProcfs* ftrace);
+
+}  // namespace vendor_tracepoints
+}  // namespace perfetto
+
+#endif  // SRC_TRACED_PROBES_FTRACE_VENDOR_TRACEPOINTS_H_
diff --git a/src/traced/probes/ftrace/vendor_tracepoints_unittest.cc b/src/traced/probes/ftrace/vendor_tracepoints_unittest.cc
new file mode 100644
index 0000000..908c6ff
--- /dev/null
+++ b/src/traced/probes/ftrace/vendor_tracepoints_unittest.cc
@@ -0,0 +1,272 @@
+/*
+ * 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/vendor_tracepoints.h"
+
+#include <vector>
+
+#include "test/gtest_and_gmock.h"
+
+#include "src/base/test/tmp_dir_tree.h"
+#include "src/traced/probes/ftrace/atrace_hal_wrapper.h"
+#include "src/traced/probes/ftrace/ftrace_procfs.h"
+
+using testing::_;
+using testing::AnyNumber;
+using testing::ElementsAre;
+using testing::HasSubstr;
+using testing::NiceMock;
+using testing::Pair;
+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_METHOD1(IsFileWriteable, 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, DiscoverVendorTracepointsWithHal) {
+  MockHal hal;
+  MockFtraceProcfs ftrace;
+  Sequence s;
+
+  EXPECT_CALL(hal, ListCategories())
+      .InSequence(s)
+      .WillOnce(Return(std::vector<std::string>({"gfx"})));
+  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(DiscoverVendorTracepointsWithHal(&hal, &ftrace),
+              ElementsAre(Pair("gfx", ElementsAre(GroupAndName("foo", "bar"),
+                                                  GroupAndName("a", "b")))));
+}
+
+TEST(DiscoverVendorTracepointsTest, DiscoverVendorTracepointsWithFileOk) {
+  base::TmpDirTree tree;
+  std::string contents =
+      "gfx\n"
+      " foo/bar\n"
+      " g/a\n"
+      " g/b\n"
+      "memory\n"
+      " grp/evt\n";
+  tree.AddFile("vendor_atrace.txt", contents);
+
+  std::map<std::string, std::vector<GroupAndName>> result;
+  base::Status status = DiscoverVendorTracepointsWithFile(
+      tree.AbsolutePath("vendor_atrace.txt"), &result);
+
+  ASSERT_TRUE(status.ok()) << status.message();
+  EXPECT_THAT(
+      result,
+      ElementsAre(Pair("gfx", ElementsAre(GroupAndName("foo", "bar"),
+                                          GroupAndName("g", "a"),
+                                          GroupAndName("g", "b"))),
+                  Pair("memory", ElementsAre(GroupAndName("grp", "evt")))));
+}
+
+TEST(DiscoverVendorTracepointsTest,
+     DiscoverVendorTracepointsWithFileEmptyLines) {
+  base::TmpDirTree tree;
+  std::string contents =
+      "\n"
+      "gfx\n"
+      "   \n"
+      " foo/bar\n"
+      "\n";
+  tree.AddFile("vendor_atrace.txt", contents);
+
+  std::map<std::string, std::vector<GroupAndName>> result;
+  base::Status status = DiscoverVendorTracepointsWithFile(
+      tree.AbsolutePath("vendor_atrace.txt"), &result);
+
+  ASSERT_TRUE(status.ok()) << status.message();
+  EXPECT_THAT(result, ElementsAre(Pair(
+                          "gfx", ElementsAre(GroupAndName("foo", "bar")))));
+}
+
+TEST(DiscoverVendorTracepointsTest,
+     DiscoverVendorTracepointsWithFileWhitespaces) {
+  base::TmpDirTree tree;
+  std::string contents =
+      "gfx\n"
+      " path/1\n"
+      "\tpath/2\n"
+      "  path/3\n"
+      "\t\tpath/4\n";
+  tree.AddFile("vendor_atrace.txt", contents);
+
+  std::map<std::string, std::vector<GroupAndName>> result;
+  base::Status status = DiscoverVendorTracepointsWithFile(
+      tree.AbsolutePath("vendor_atrace.txt"), &result);
+
+  ASSERT_TRUE(status.ok()) << status.message();
+  EXPECT_THAT(result,
+              ElementsAre(Pair("gfx", ElementsAre(GroupAndName("path", "1"),
+                                                  GroupAndName("path", "2"),
+                                                  GroupAndName("path", "3"),
+                                                  GroupAndName("path", "4")))));
+}
+
+TEST(DiscoverVendorTracepointsTest,
+     DiscoverVendorTracepointsWithFileNoCategory) {
+  base::TmpDirTree tree;
+  std::string contents =
+      " foo/bar\n"
+      " g/a\n"
+      " g/b\n";
+  tree.AddFile("vendor_atrace.txt", contents);
+
+  std::map<std::string, std::vector<GroupAndName>> result;
+  base::Status status = DiscoverVendorTracepointsWithFile(
+      tree.AbsolutePath("vendor_atrace.txt"), &result);
+
+  EXPECT_THAT(status.message(), HasSubstr("Ftrace event path before category"));
+}
+
+TEST(DiscoverVendorTracepointsTest, DiscoverVendorTracepointsWithFileNoSlash) {
+  base::TmpDirTree tree;
+  std::string contents =
+      "gfx\n"
+      " event\n";
+  tree.AddFile("vendor_atrace.txt", contents);
+
+  std::map<std::string, std::vector<GroupAndName>> result;
+  base::Status status = DiscoverVendorTracepointsWithFile(
+      tree.AbsolutePath("vendor_atrace.txt"), &result);
+
+  EXPECT_THAT(status.message(),
+              HasSubstr("Ftrace event path not in group/event format"));
+}
+
+TEST(DiscoverVendorTracepointsTest,
+     DiscoverVendorTracepointsWithFileEmptyGroup) {
+  base::TmpDirTree tree;
+  std::string contents =
+      "gfx\n"
+      " /event\n";
+  tree.AddFile("vendor_atrace.txt", contents);
+
+  std::map<std::string, std::vector<GroupAndName>> result;
+  base::Status status = DiscoverVendorTracepointsWithFile(
+      tree.AbsolutePath("vendor_atrace.txt"), &result);
+
+  EXPECT_THAT(status.message(), HasSubstr("group is empty"));
+}
+
+TEST(DiscoverVendorTracepointsTest,
+     DiscoverVendorTracepointsWithFileTooManySlash) {
+  base::TmpDirTree tree;
+  std::string contents =
+      "gfx\n"
+      " group/dir/event\n";
+  tree.AddFile("vendor_atrace.txt", contents);
+
+  std::map<std::string, std::vector<GroupAndName>> result;
+  base::Status status = DiscoverVendorTracepointsWithFile(
+      tree.AbsolutePath("vendor_atrace.txt"), &result);
+
+  EXPECT_THAT(status.message(), HasSubstr("extra /"));
+}
+
+TEST(DiscoverVendorTracepointsTest,
+     DiscoverVendorTracepointsWithFileNameEmpty) {
+  base::TmpDirTree tree;
+  std::string contents =
+      "gfx\n"
+      " group/\n";
+  tree.AddFile("vendor_atrace.txt", contents);
+
+  std::map<std::string, std::vector<GroupAndName>> result;
+  base::Status status = DiscoverVendorTracepointsWithFile(
+      tree.AbsolutePath("vendor_atrace.txt"), &result);
+
+  EXPECT_THAT(status.message(), HasSubstr("name empty"));
+}
+
+TEST(DiscoverVendorTracepointsTest,
+     DiscoverAccessibleVendorTracepointsWithFile) {
+  base::TmpDirTree tree;
+  std::string contents =
+      "gfx\n"
+      " g/a\n"
+      " g/b\n"
+      "memory\n"
+      " g/c\n";
+  tree.AddFile("vendor_atrace.txt", contents);
+  MockFtraceProcfs ftrace;
+
+  EXPECT_CALL(ftrace, IsFileWriteable("/root/events/g/a/enable"))
+      .WillOnce(Return(false));
+  EXPECT_CALL(ftrace, IsFileWriteable("/root/events/g/b/enable"))
+      .WillOnce(Return(true));
+  EXPECT_CALL(ftrace, IsFileWriteable("/root/events/g/c/enable"))
+      .WillOnce(Return(false));
+
+  std::map<std::string, std::vector<GroupAndName>> result;
+  base::Status status = DiscoverAccessibleVendorTracepointsWithFile(
+      tree.AbsolutePath("vendor_atrace.txt"), &result, &ftrace);
+
+  ASSERT_TRUE(status.ok()) << status.message();
+  EXPECT_THAT(result,
+              ElementsAre(Pair("gfx", ElementsAre(GroupAndName("g", "b"))),
+                          Pair("memory", ElementsAre())));
+}
+
+}  // namespace
+}  // namespace vendor_tracepoints
+}  // namespace perfetto
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
index 85ea606..d08cfd4 100644
--- 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
@@ -106,6 +106,12 @@
     }
   }
   packet->Finalize();
+  // For most data sources we would not want to flush every time we have
+  // something to write. However this source tends to emit very slowly and it is
+  // very possible that it would only flush at the end of the trace - at which
+  // point it might not be able to write anything (e.g. DISCARD buffer might be
+  // full). Taking the hit of 4kB each time we write seems reasonable to make
+  // this behave more predictably.
   writer_->Flush();
 }
 
diff --git a/src/traced/probes/kmem_activity_trigger.cc b/src/traced/probes/kmem_activity_trigger.cc
index b30108c..7e29a96 100644
--- a/src/traced/probes/kmem_activity_trigger.cc
+++ b/src/traced/probes/kmem_activity_trigger.cc
@@ -52,7 +52,7 @@
 KmemActivityTrigger::WorkerData::~WorkerData() {
   PERFETTO_DCHECK_THREAD(thread_checker_);
   if (ftrace_procfs_) {
-    ftrace_procfs_->DisableTracing();
+    ftrace_procfs_->SetTracingOn(false);
     ftrace_procfs_->ClearTrace();
   }
   DisarmFtraceFDWatches();
@@ -79,7 +79,7 @@
   ftrace_procfs_->DisableAllEvents();
   ftrace_procfs_->EnableEvent("vmscan", "mm_vmscan_direct_reclaim_begin");
   ftrace_procfs_->EnableEvent("compaction", "mm_compaction_begin");
-  ftrace_procfs_->EnableTracing();
+  ftrace_procfs_->SetTracingOn(true);
 
   num_cpus_ = ftrace_procfs_->NumberOfCpus();
   for (size_t cpu = 0; cpu < num_cpus_; cpu++) {
diff --git a/src/traced/probes/power/linux_power_sysfs_data_source.cc b/src/traced/probes/power/linux_power_sysfs_data_source.cc
index 2e7d66e..b4bdec6 100644
--- a/src/traced/probes/power/linux_power_sysfs_data_source.cc
+++ b/src/traced/probes/power/linux_power_sysfs_data_source.cc
@@ -47,8 +47,9 @@
 }  // namespace
 
 LinuxPowerSysfsDataSource::BatteryInfo::BatteryInfo(
-    const char* power_supply_dir_path) {
-  base::ScopedDir power_supply_dir(opendir(power_supply_dir_path));
+    const char* power_supply_dir_path)
+    : power_supply_dir_path_(power_supply_dir_path) {
+  base::ScopedDir power_supply_dir(opendir(power_supply_dir_path_.c_str()));
   if (!power_supply_dir)
     return;
 
@@ -66,39 +67,49 @@
     if (!base::ReadFile(dir_name + "/present", &buf) ||
         base::StripSuffix(buf, "\n") != "1")
       continue;
-    sysfs_battery_dirs_.push_back(dir_name);
+    sysfs_battery_subdirs_.push_back(ent->d_name);
   }
 }
 LinuxPowerSysfsDataSource::BatteryInfo::~BatteryInfo() = default;
 
 size_t LinuxPowerSysfsDataSource::BatteryInfo::num_batteries() const {
-  return sysfs_battery_dirs_.size();
+  return sysfs_battery_subdirs_.size();
 }
 
 base::Optional<int64_t>
 LinuxPowerSysfsDataSource::BatteryInfo::GetChargeCounterUah(
     size_t battery_idx) {
-  PERFETTO_CHECK(battery_idx < sysfs_battery_dirs_.size());
-  return ReadFileAsInt64(sysfs_battery_dirs_[battery_idx] + "/charge_now");
+  PERFETTO_CHECK(battery_idx < sysfs_battery_subdirs_.size());
+  return ReadFileAsInt64(power_supply_dir_path_ + "/" +
+                         sysfs_battery_subdirs_[battery_idx] + "/charge_now");
 }
 
 base::Optional<int64_t>
 LinuxPowerSysfsDataSource::BatteryInfo::GetCapacityPercent(size_t battery_idx) {
-  PERFETTO_CHECK(battery_idx < sysfs_battery_dirs_.size());
-  return ReadFileAsInt64(sysfs_battery_dirs_[battery_idx] + "/capacity");
+  PERFETTO_CHECK(battery_idx < sysfs_battery_subdirs_.size());
+  return ReadFileAsInt64(power_supply_dir_path_ + "/" +
+                         sysfs_battery_subdirs_[battery_idx] + "/capacity");
 }
 
 base::Optional<int64_t> LinuxPowerSysfsDataSource::BatteryInfo::GetCurrentNowUa(
     size_t battery_idx) {
-  PERFETTO_CHECK(battery_idx < sysfs_battery_dirs_.size());
-  return ReadFileAsInt64(sysfs_battery_dirs_[battery_idx] + "/current_now");
+  PERFETTO_CHECK(battery_idx < sysfs_battery_subdirs_.size());
+  return ReadFileAsInt64(power_supply_dir_path_ + "/" +
+                         sysfs_battery_subdirs_[battery_idx] + "/current_now");
 }
 
 base::Optional<int64_t>
 LinuxPowerSysfsDataSource::BatteryInfo::GetAverageCurrentUa(
     size_t battery_idx) {
-  PERFETTO_CHECK(battery_idx < sysfs_battery_dirs_.size());
-  return ReadFileAsInt64(sysfs_battery_dirs_[battery_idx] + "/current_avg");
+  PERFETTO_CHECK(battery_idx < sysfs_battery_subdirs_.size());
+  return ReadFileAsInt64(power_supply_dir_path_ + "/" +
+                         sysfs_battery_subdirs_[battery_idx] + "/current_avg");
+}
+
+std::string LinuxPowerSysfsDataSource::BatteryInfo::GetBatteryName(
+    size_t battery_idx) {
+  PERFETTO_CHECK(battery_idx < sysfs_battery_subdirs_.size());
+  return sysfs_battery_subdirs_[battery_idx];
 }
 
 // static
@@ -143,13 +154,13 @@
 }
 
 void LinuxPowerSysfsDataSource::WriteBatteryCounters() {
-  auto packet = writer_->NewTracePacket();
-  packet->set_timestamp(static_cast<uint64_t>(base::GetBootTimeNs().count()));
-
   // Query battery counters from sysfs. Report the battery counters for each
   // battery.
   for (size_t battery_idx = 0; battery_idx < battery_info_->num_batteries();
        battery_idx++) {
+    auto packet = writer_->NewTracePacket();
+    packet->set_timestamp(static_cast<uint64_t>(base::GetBootTimeNs().count()));
+
     auto* counters_proto = packet->set_battery();
     auto value = battery_info_->GetChargeCounterUah(battery_idx);
     if (value)
@@ -163,6 +174,9 @@
     value = battery_info_->GetAverageCurrentUa(battery_idx);
     if (value)
       counters_proto->set_current_ua(*value);
+    // On systems with multiple batteries, disambiguate with battery names.
+    if (battery_info_->num_batteries() > 1)
+      counters_proto->set_name(battery_info_->GetBatteryName(battery_idx));
   }
 }
 
diff --git a/src/traced/probes/power/linux_power_sysfs_data_source.h b/src/traced/probes/power/linux_power_sysfs_data_source.h
index 9838a15..d757b38 100644
--- a/src/traced/probes/power/linux_power_sysfs_data_source.h
+++ b/src/traced/probes/power/linux_power_sysfs_data_source.h
@@ -50,12 +50,16 @@
     // The smoothed current reading of the battery in µA.
     base::Optional<int64_t> GetAverageCurrentUa(size_t battery_idx);
 
+    // Name of the battery.
+    std::string GetBatteryName(size_t battery_idx);
+
     size_t num_batteries() const;
 
    private:
-    // The subdirectories that contain info of a battery power supply, not
-    // USBPD, AC or other types of power supplies.
-    std::vector<std::string> sysfs_battery_dirs_;
+    std::string power_supply_dir_path_;
+    // The subdirectories that contain info of a battery power supply, e.g.
+    // BAT0.
+    std::vector<std::string> sysfs_battery_subdirs_;
   };
   static const ProbesDataSource::Descriptor descriptor;
 
diff --git a/src/traced/probes/power/linux_power_sysfs_data_source_unittest.cc b/src/traced/probes/power/linux_power_sysfs_data_source_unittest.cc
index 590c926..ff98604 100644
--- a/src/traced/probes/power/linux_power_sysfs_data_source_unittest.cc
+++ b/src/traced/probes/power/linux_power_sysfs_data_source_unittest.cc
@@ -64,5 +64,45 @@
   EXPECT_EQ(battery_info_->GetChargeCounterUah(0), base::nullopt);
 }
 
+TEST(LinuxPowerSysfsDataSourceTest, MultipleBatteries) {
+  base::TmpDirTree tmpdir;
+  std::unique_ptr<LinuxPowerSysfsDataSource::BatteryInfo> battery_info_;
+
+  // Some HID devices (e.g. stylus) can also report battery info.
+  tmpdir.AddDir("hid-0001-battery");
+  tmpdir.AddFile("hid-0001-battery/type", "Battery\n");
+  tmpdir.AddFile("hid-0001-battery/present", "1\n");
+  tmpdir.AddFile("hid-0001-battery/capacity", "88\n");  // 88 percent.
+  // The HID device only reports the battery capacity in percent.
+
+  // Add the main battery.
+  tmpdir.AddDir("BAT0");
+  tmpdir.AddFile("BAT0/type", "Battery\n");
+  tmpdir.AddFile("BAT0/present", "1\n");
+  tmpdir.AddFile("BAT0/capacity", "95\n");         // 95 percent.
+  tmpdir.AddFile("BAT0/charge_now", "3074000\n");  // 3074000 µAh.
+  tmpdir.AddFile("BAT0/current_now", "245000\n");  // 245000 µA.
+  tmpdir.AddFile("BAT0/current_avg", "240000\n");  // 240000 µA.
+
+  battery_info_.reset(
+      new LinuxPowerSysfsDataSource::BatteryInfo(tmpdir.path().c_str()));
+
+  EXPECT_EQ(battery_info_->num_batteries(), 2u);
+  size_t main_battery_idx = battery_info_->GetBatteryName(0) == "BAT0" ? 0 : 1;
+  size_t second_battery_idx = main_battery_idx == 0 ? 1 : 0;
+
+  EXPECT_EQ(*battery_info_->GetCapacityPercent(second_battery_idx), 88);
+  EXPECT_EQ(battery_info_->GetCurrentNowUa(second_battery_idx), base::nullopt);
+  EXPECT_EQ(battery_info_->GetAverageCurrentUa(second_battery_idx),
+            base::nullopt);
+  EXPECT_EQ(battery_info_->GetChargeCounterUah(second_battery_idx),
+            base::nullopt);
+
+  EXPECT_EQ(*battery_info_->GetCapacityPercent(main_battery_idx), 95);
+  EXPECT_EQ(*battery_info_->GetCurrentNowUa(main_battery_idx), 245000);
+  EXPECT_EQ(*battery_info_->GetAverageCurrentUa(main_battery_idx), 240000);
+  EXPECT_EQ(*battery_info_->GetChargeCounterUah(main_battery_idx), 3074000);
+}
+
 }  // namespace
 }  // namespace perfetto
diff --git a/src/traced/probes/probes_producer.cc b/src/traced/probes/probes_producer.cc
index 76469d3..0d2573a 100644
--- a/src/traced/probes/probes_producer.cc
+++ b/src/traced/probes/probes_producer.cc
@@ -125,24 +125,22 @@
   if (ftrace_creation_failed_)
     return nullptr;
 
+  FtraceConfig ftrace_config;
+  ftrace_config.ParseFromString(config.ftrace_config_raw());
   // Lazily create on the first instance.
   if (!ftrace_) {
-    ftrace_ = FtraceController::Create(task_runner_, this);
+    ftrace_ = FtraceController::Create(task_runner_, this,
+                                       ftrace_config.preserve_ftrace_buffer());
 
     if (!ftrace_) {
       PERFETTO_ELOG("Failed to create FtraceController");
       ftrace_creation_failed_ = true;
       return nullptr;
     }
-
-    ftrace_->DisableAllEvents();
-    ftrace_->ClearTrace();
   }
 
   PERFETTO_LOG("Ftrace setup (target_buf=%" PRIu32 ")", config.target_buffer());
   const BufferID buffer_id = static_cast<BufferID>(config.target_buffer());
-  FtraceConfig ftrace_config;
-  ftrace_config.ParseFromString(config.ftrace_config_raw());
   std::unique_ptr<FtraceDataSource> data_source(new FtraceDataSource(
       ftrace_->GetWeakPtr(), session_id, std::move(ftrace_config),
       endpoint_->CreateTraceWriter(buffer_id)));
@@ -457,13 +455,11 @@
   }
   ProbesDataSource* data_source = it->second.get();
 
-  // MetatraceDataSource special case: re-flush and ack the stop (to record the
-  // flushes of other data sources).
+  // MetatraceDataSource special case: re-flush to record the final flushes of
+  // other data sources.
   if (data_source->descriptor == &MetatraceDataSource::descriptor)
     data_source->Flush(FlushRequestID{0}, [] {});
 
-  endpoint_->NotifyDataSourceStopped(id);
-
   TracingSessionID session_id = data_source->tracing_session_id;
 
   auto session_it = session_data_sources_.find(session_id);
@@ -481,6 +477,14 @@
   }
   data_sources_.erase(it);
   watchdogs_.erase(id);
+
+  // We could (and used to) acknowledge the stop before tearing the local state
+  // down, allowing the tracing service and the consumer to carry on quicker.
+  // However in the case of tracebox, the traced_probes subprocess gets killed
+  // as soon as the trace is considered finished (i.e. all data source stops
+  // were acked), and therefore the kill would race against the tracefs
+  // cleanup.
+  endpoint_->NotifyDataSourceStopped(id);
 }
 
 void ProbesProducer::OnTracingSetup() {
@@ -602,6 +606,8 @@
           ps_ds->OnRenamePids(metadata->rename_pids);
         if (!metadata->pids.empty())
           ps_ds->OnPids(metadata->pids);
+        if (!metadata->fds.empty())
+          ps_ds->OnFds(metadata->fds);
       }
       for (auto in_it = ino_range.first; in_it != ino_range.second; in_it++) {
         auto* inode_ds = static_cast<InodeFileDataSource*>(in_it->second);
diff --git a/src/traced/probes/ps/process_stats_data_source.cc b/src/traced/probes/ps/process_stats_data_source.cc
index 52f0b74..6e10c3e 100644
--- a/src/traced/probes/ps/process_stats_data_source.cc
+++ b/src/traced/probes/ps/process_stats_data_source.cc
@@ -17,8 +17,10 @@
 #include "src/traced/probes/ps/process_stats_data_source.h"
 
 #include <stdlib.h>
+#include <unistd.h>
 
 #include <algorithm>
+#include <array>
 #include <utility>
 
 #include "perfetto/base/task_runner.h"
@@ -93,6 +95,7 @@
   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();
+  resolve_process_fds_ = cfg.resolve_process_fds();
 
   enable_on_demand_dumps_ = true;
   for (auto quirk = cfg.quirks(); quirk; ++quirk) {
@@ -118,8 +121,9 @@
 ProcessStatsDataSource::~ProcessStatsDataSource() = default;
 
 void ProcessStatsDataSource::Start() {
-  if (dump_all_procs_on_start_)
+  if (dump_all_procs_on_start_) {
     WriteAllProcesses();
+  }
 
   if (poll_period_ms_) {
     auto weak_this = GetWeakPtr();
@@ -141,6 +145,7 @@
   base::ScopedDir proc_dir = OpenProcDir();
   if (!proc_dir)
     return;
+  base::FlatSet<int32_t> pids;
   while (int32_t pid = ReadNextNumericDir(*proc_dir)) {
     WriteProcessOrThread(pid);
     base::StackString<128> task_path("/proc/%d/task", pid);
@@ -162,6 +167,15 @@
         WriteThread(tid, pid, /*optional_name=*/nullptr, proc_status);
       }
     }
+
+    pids.insert(pid);
+  }
+  FinalizeCurPacket();
+
+  // Also collect any fds open when starting up
+  for (const auto pid : pids) {
+    cur_ps_stats_process_ = nullptr;
+    WriteFds(pid);
   }
   FinalizeCurPacket();
 }
@@ -196,6 +210,33 @@
     seen_pids_.erase(pid);
 }
 
+void ProcessStatsDataSource::OnFds(
+    const base::FlatSet<std::pair<pid_t, uint64_t>>& fds) {
+  if (!resolve_process_fds_)
+    return;
+
+  pid_t last_pid = 0;
+  for (const auto& tid_fd : fds) {
+    const auto tid = tid_fd.first;
+    const auto fd = tid_fd.second;
+
+    auto it = seen_pids_.find(tid);
+    if (it == seen_pids_.end()) {
+      // TID is not known yet, skip resolving the fd and let the
+      // periodic stats scanner resolve the fd together with its TID later
+      continue;
+    }
+    const auto pid = it->tgid;
+
+    if (last_pid != pid) {
+      cur_ps_stats_process_ = nullptr;
+      last_pid = pid;
+    }
+    WriteSingleFd(pid, fd);
+  }
+  FinalizeCurPacket();
+}
+
 void ProcessStatsDataSource::Flush(FlushRequestID,
                                    std::function<void()> callback) {
   // We shouldn't get this in the middle of WriteAllProcesses() or OnPids().
@@ -295,7 +336,7 @@
     // Nothing in cmdline so use the thread name instead (which is == "comm").
     proc->add_cmdline(ReadProcStatusEntry(proc_status, "Name:").c_str());
   }
-  seen_pids_.insert(pid);
+  seen_pids_.insert({pid, pid});
 }
 
 void ProcessStatsDataSource::WriteThread(int32_t tid,
@@ -316,13 +357,18 @@
       break;
     thread->add_nstid(nstid);
   }
-  seen_pids_.insert(tid);
+  seen_pids_.insert({tid, tgid});
+}
+
+const char* ProcessStatsDataSource::GetProcMountpoint() {
+  static constexpr char kDefaultProcMountpoint[] = "/proc";
+  return kDefaultProcMountpoint;
 }
 
 base::ScopedDir ProcessStatsDataSource::OpenProcDir() {
-  base::ScopedDir proc_dir(opendir("/proc"));
+  base::ScopedDir proc_dir(opendir(GetProcMountpoint()));
   if (!proc_dir)
-    PERFETTO_PLOG("Failed to opendir(/proc)");
+    PERFETTO_PLOG("Failed to opendir(%s)", GetProcMountpoint());
   return proc_dir;
 }
 
@@ -469,6 +515,9 @@
       }
     }
 
+    // Ensure we write data on any fds not seen before
+    WriteFds(pid);
+
     pids.insert(pid);
   }
   FinalizeCurPacket();
@@ -589,6 +638,46 @@
   return proc_status_has_mem_counters;
 }
 
+void ProcessStatsDataSource::WriteFds(int32_t pid) {
+  if (!resolve_process_fds_) {
+    return;
+  }
+
+  base::StackString<64> path("%s/%" PRId32 "/fd", GetProcMountpoint(), pid);
+  base::ScopedDir proc_dir(opendir(path.c_str()));
+  if (!proc_dir) {
+    PERFETTO_DPLOG("Failed to opendir(%s)", path.c_str());
+    return;
+  }
+  while (struct dirent* dir_ent = readdir(*proc_dir)) {
+    if (dir_ent->d_type != DT_LNK)
+      continue;
+    auto fd = base::CStringToUInt64(dir_ent->d_name);
+    if (fd)
+      WriteSingleFd(pid, *fd);
+  }
+}
+
+void ProcessStatsDataSource::WriteSingleFd(int32_t pid, uint64_t fd) {
+  CachedProcessStats& cached = process_stats_cache_[pid];
+  if (cached.seen_fds.count(fd)) {
+    return;
+  }
+
+  base::StackString<128> proc_fd("%s/%" PRId32 "/fd/%" PRIu64,
+                                 GetProcMountpoint(), pid, fd);
+  std::array<char, 256> path;
+  ssize_t actual = readlink(proc_fd.c_str(), path.data(), path.size());
+  if (actual >= 0) {
+    auto* fd_info = GetOrCreateStatsProcess(pid)->add_fds();
+    fd_info->set_fd(fd);
+    fd_info->set_path(path.data(), static_cast<size_t>(actual));
+    cached.seen_fds.insert(fd);
+  } else if (ENOENT != errno) {
+    PERFETTO_DPLOG("Failed to readlink '%s'", proc_fd.c_str());
+  }
+}
+
 uint64_t ProcessStatsDataSource::CacheProcFsScanStartTimestamp() {
   if (!cur_procfs_scan_start_timestamp_)
     cur_procfs_scan_start_timestamp_ =
diff --git a/src/traced/probes/ps/process_stats_data_source.h b/src/traced/probes/ps/process_stats_data_source.h
index 04cab5e..706ee2a 100644
--- a/src/traced/probes/ps/process_stats_data_source.h
+++ b/src/traced/probes/ps/process_stats_data_source.h
@@ -62,6 +62,7 @@
   void WriteAllProcesses();
   void OnPids(const base::FlatSet<int32_t>& pids);
   void OnRenamePids(const base::FlatSet<int32_t>& pids);
+  void OnFds(const base::FlatSet<std::pair<pid_t, uint64_t>>& fds);
 
   // ProbesDataSource implementation.
   void Start() override;
@@ -71,6 +72,7 @@
   bool on_demand_dumps_enabled() const { return enable_on_demand_dumps_; }
 
   // Virtual for testing.
+  virtual const char* GetProcMountpoint();
   virtual base::ScopedDir OpenProcDir();
   virtual std::string ReadProcPidFile(int32_t pid, const std::string& file);
 
@@ -88,6 +90,9 @@
 
     // ctime + stime from /proc/pid/stat
     uint64_t cpu_time = std::numeric_limits<uint64_t>::max();
+
+    // file descriptors
+    base::FlatSet<uint64_t> seen_fds;
   };
 
   // Common functions.
@@ -121,6 +126,8 @@
   static void Tick(base::WeakPtr<ProcessStatsDataSource>);
   void WriteAllProcessStats();
   bool WriteMemCounters(int32_t pid, const std::string& proc_status);
+  void WriteFds(int32_t pid);
+  void WriteSingleFd(int32_t pid, uint64_t fd);
   bool ShouldWriteThreadStats(int32_t pid);
   void WriteThreadStats(int32_t pid, int32_t tid);
 
@@ -152,11 +159,25 @@
   bool record_thread_names_ = false;
   bool enable_on_demand_dumps_ = true;
   bool dump_all_procs_on_start_ = false;
+  bool resolve_process_fds_ = 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
   // seen, not just the main thread id (aka thread group ID).
-  base::FlatSet<int32_t> seen_pids_;
+  struct SeenPid {
+    int32_t pid;
+    int32_t tgid;
+
+    inline SeenPid(int32_t _pid, int32_t _tgid = 0) : pid(_pid), tgid(_tgid) {}
+    // TODO(rsavitski): add comparator support to FlatSet
+    inline bool operator==(const SeenPid& other) const {
+      return pid == other.pid;
+    }
+    inline bool operator<(const SeenPid& other) const {
+      return pid < other.pid;
+    }
+  };
+  base::FlatSet<SeenPid> seen_pids_;
 
   // Fields for keeping track of the periodic stats/counters.
   uint32_t poll_period_ms_ = 0;
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 bb12ea7..c39b71e 100644
--- a/src/traced/probes/ps/process_stats_data_source_unittest.cc
+++ b/src/traced/probes/ps/process_stats_data_source_unittest.cc
@@ -19,6 +19,7 @@
 #include <dirent.h>
 
 #include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/temp_file.h"
 #include "perfetto/protozero/scattered_heap_buffer.h"
 #include "perfetto/tracing/core/data_source_config.h"
@@ -33,6 +34,7 @@
 
 using ::perfetto::protos::gen::ProcessStatsConfig;
 using ::testing::_;
+using ::testing::Contains;
 using ::testing::ElementsAre;
 using ::testing::ElementsAreArray;
 using ::testing::Invoke;
@@ -56,6 +58,7 @@
                                config,
                                std::move(cpu_freq_info)) {}
 
+  MOCK_METHOD0(GetProcMountpoint, const char*());
   MOCK_METHOD0(OpenProcDir, base::ScopedDir());
   MOCK_METHOD2(ReadProcPidFile, std::string(int32_t pid, const std::string&));
 };
@@ -321,6 +324,7 @@
   DataSourceConfig ds_config;
   ProcessStatsConfig cfg;
   cfg.set_proc_stats_poll_ms(1);
+  cfg.set_resolve_process_fds(true);
   cfg.add_quirks(ProcessStatsConfig::DISABLE_ON_DEMAND);
   ds_config.set_process_stats_config_raw(cfg.SerializeAsString());
   auto data_source = GetProcessStatsDataSource(ds_config);
@@ -328,31 +332,51 @@
   // Populate a fake /proc/ directory.
   auto fake_proc = base::TempDir::Create();
   const int kPids[] = {1, 2};
+  const uint64_t kFds[] = {5u, 7u};
+  const char kDevice[] = "/dev/dummy";
   std::vector<std::string> dirs_to_delete;
+  std::vector<std::string> links_to_delete;
   for (int pid : kPids) {
-    char path[256];
-    sprintf(path, "%s/%d", fake_proc.path().c_str(), pid);
-    dirs_to_delete.push_back(path);
-    mkdir(path, 0755);
+    base::StackString<256> path("%s/%d", fake_proc.path().c_str(), pid);
+    dirs_to_delete.push_back(path.ToStdString());
+    EXPECT_EQ(mkdir(path.c_str(), 0755), 0)
+        << "mkdir('" << path.c_str() << "') failed";
+
+    base::StackString<256> path_fd("%s/fd", path.c_str());
+    dirs_to_delete.push_back(path_fd.ToStdString());
+    EXPECT_EQ(mkdir(path_fd.c_str(), 0755), 0)
+        << "mkdir('" << path_fd.c_str() << "') failed";
+
+    for (auto fd : kFds) {
+      base::StackString<256> link("%s/%" PRIu64, path_fd.c_str(), fd);
+      links_to_delete.push_back(link.ToStdString());
+      EXPECT_EQ(symlink(kDevice, link.c_str()), 0)
+          << "symlink('" << kDevice << "','" << link.c_str() << "') failed";
+    }
   }
 
   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()));
-  }));
+  const auto fake_proc_path = fake_proc.path();
+  EXPECT_CALL(*data_source, OpenProcDir())
+      .WillRepeatedly(Invoke([&fake_proc_path] {
+        return base::ScopedDir(opendir(fake_proc_path.c_str()));
+      }));
+  EXPECT_CALL(*data_source, GetProcMountpoint())
+      .WillRepeatedly(
+          Invoke([&fake_proc_path] { return fake_proc_path.c_str(); }));
 
   const int kNumIters = 4;
   int iter = 0;
   for (int pid : kPids) {
     EXPECT_CALL(*data_source, ReadProcPidFile(pid, "status"))
-        .WillRepeatedly(Invoke([checkpoint, &iter](int32_t p,
-                                                   const std::string&) {
-          char ret[1024];
-          sprintf(ret, "Name:	pid_10\nVmSize:	 %d kB\nVmRSS:\t%d  kB\n",
+        .WillRepeatedly(
+            Invoke([checkpoint, &iter](int32_t p, const std::string&) {
+              base::StackString<1024> ret(
+                  "Name:	pid_10\nVmSize:	 %d kB\nVmRSS:\t%d  kB\n",
                   p * 100 + iter * 10 + 1, p * 100 + iter * 10 + 2);
-          return std::string(ret);
-        }));
+              return ret.ToStdString();
+            }));
 
     EXPECT_CALL(*data_source, ReadProcPidFile(pid, "oom_score_adj"))
         .WillRepeatedly(Invoke(
@@ -387,13 +411,22 @@
               pid * 100 + iter * 10 + 2);
     ASSERT_EQ(static_cast<int>(proc_counters.oom_score_adj()),
               pid * 100 + iter * 10 + 3);
+    ASSERT_EQ(proc_counters.fds().size(), base::ArraySize(kFds));
+    for (const auto& fd_path : proc_counters.fds()) {
+      ASSERT_THAT(kFds, Contains(fd_path.fd()));
+      ASSERT_EQ(fd_path.path(), kDevice);
+    }
     if (pid == kPids[base::ArraySize(kPids) - 1])
       iter++;
   }
 
   // Cleanup |fake_proc|. TempDir checks that the directory is empty.
-  for (std::string& path : dirs_to_delete)
-    base::Rmdir(path);
+  for (auto path = links_to_delete.rbegin(); path != links_to_delete.rend();
+       path++)
+    unlink(path->c_str());
+  for (auto path = dirs_to_delete.rbegin(); path != dirs_to_delete.rend();
+       path++)
+    base::Rmdir(*path);
 }
 
 TEST_F(ProcessStatsDataSourceTest, CacheProcessStats) {
@@ -409,9 +442,8 @@
   auto fake_proc = base::TempDir::Create();
   const int kPid = 1;
 
-  char path[256];
-  sprintf(path, "%s/%d", fake_proc.path().c_str(), kPid);
-  mkdir(path, 0755);
+  base::StackString<256> path("%s/%d", fake_proc.path().c_str(), kPid);
+  mkdir(path.c_str(), 0755);
 
   auto checkpoint = task_runner_.CreateCheckpoint("all_done");
 
@@ -423,10 +455,10 @@
   int iter = 0;
   EXPECT_CALL(*data_source, ReadProcPidFile(kPid, "status"))
       .WillRepeatedly(Invoke([checkpoint](int32_t p, const std::string&) {
-        char ret[1024];
-        sprintf(ret, "Name:	pid_10\nVmSize:	 %d kB\nVmRSS:\t%d  kB\n",
-                p * 100 + 1, p * 100 + 2);
-        return std::string(ret);
+        base::StackString<1024> ret(
+            "Name:	pid_10\nVmSize:	 %d kB\nVmRSS:\t%d  kB\n", p * 100 + 1,
+            p * 100 + 2);
+        return ret.ToStdString();
       }));
 
   EXPECT_CALL(*data_source, ReadProcPidFile(kPid, "oom_score_adj"))
@@ -461,7 +493,7 @@
   }
 
   // Cleanup |fake_proc|. TempDir checks that the directory is empty.
-  base::Rmdir(path);
+  base::Rmdir(path.ToStdString());
 }
 
 TEST_F(ProcessStatsDataSourceTest, NamespacedProcess) {
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 73e7651..9543d7c 100644
--- a/src/traced/probes/sys_stats/sys_stats_data_source.cc
+++ b/src/traced/probes/sys_stats/sys_stats_data_source.cc
@@ -92,6 +92,7 @@
   vmstat_fd_ = open_fn("/proc/vmstat");
   stat_fd_ = open_fn("/proc/stat");
   buddy_fd_ = open_fn("/proc/buddyinfo");
+  diskstat_fd_ = open_fn("/proc/diskstats");
 
   read_buf_ = base::PagedMemory::Allocate(kReadBufSize);
 
@@ -144,8 +145,8 @@
     stat_enabled_fields_ |= 1ul << static_cast<uint32_t>(*counter);
   }
 
-  std::array<uint32_t, 6> periods_ms{};
-  std::array<uint32_t, 6> ticks{};
+  std::array<uint32_t, 7> periods_ms{};
+  std::array<uint32_t, 7> ticks{};
   static_assert(periods_ms.size() == ticks.size(), "must have same size");
 
   periods_ms[0] = ClampTo10Ms(cfg.meminfo_period_ms(), "meminfo_period_ms");
@@ -154,6 +155,7 @@
   periods_ms[3] = ClampTo10Ms(cfg.devfreq_period_ms(), "devfreq_period_ms");
   periods_ms[4] = ClampTo10Ms(cfg.cpufreq_period_ms(), "cpufreq_period_ms");
   periods_ms[5] = ClampTo10Ms(cfg.buddyinfo_period_ms(), "buddyinfo_period_ms");
+  periods_ms[6] = ClampTo10Ms(cfg.diskstat_period_ms(), "diskstat_period_ms");
 
   tick_period_ms_ = 0;
   for (uint32_t ms : periods_ms) {
@@ -178,6 +180,7 @@
   devfreq_ticks_ = ticks[3];
   cpufreq_ticks_ = ticks[4];
   buddyinfo_ticks_ = ticks[5];
+  diskstat_ticks_ = ticks[6];
 }
 
 void SysStatsDataSource::Start() {
@@ -227,12 +230,68 @@
   if (buddyinfo_ticks_ && tick_ % buddyinfo_ticks_ == 0)
     ReadBuddyInfo(sys_stats);
 
+  if (diskstat_ticks_ && tick_ % diskstat_ticks_ == 0)
+    ReadDiskStat(sys_stats);
+
   sys_stats->set_collection_end_timestamp(
       static_cast<uint64_t>(base::GetBootTimeNs().count()));
 
   tick_++;
 }
 
+void SysStatsDataSource::ReadDiskStat(protos::pbzero::SysStats* sys_stats) {
+  size_t rsize = ReadFile(&diskstat_fd_, "/proc/diskstats");
+  if (!rsize) {
+    return;
+  }
+
+  char* buf = static_cast<char*>(read_buf_.Get());
+  for (base::StringSplitter lines(buf, rsize, '\n'); lines.Next();) {
+    uint32_t index = 0;
+    auto* disk_stat = sys_stats->add_disk_stat();
+    for (base::StringSplitter words(&lines, ' '); words.Next(); index++) {
+      if (index == 2) {  // index for device name (string)
+        disk_stat->set_device_name(words.cur_token());
+      } else if (index >= 5) {  // integer values from index 5
+        base::Optional<uint64_t> value_address =
+            base::CStringToUInt64(words.cur_token());
+        uint64_t value = value_address ? *value_address : 0;
+
+        switch (index) {
+          case 5:
+            disk_stat->set_read_sectors(value);
+            break;
+          case 6:
+            disk_stat->set_read_time_ms(value);
+            break;
+          case 9:
+            disk_stat->set_write_sectors(value);
+            break;
+          case 10:
+            disk_stat->set_write_time_ms(value);
+            break;
+          case 16:
+            disk_stat->set_discard_sectors(value);
+            break;
+          case 17:
+            disk_stat->set_discard_time_ms(value);
+            break;
+          case 18:
+            disk_stat->set_flush_count(value);
+            break;
+          case 19:
+            disk_stat->set_flush_time_ms(value);
+            break;
+        }
+
+        if (index == 19) {
+          break;
+        }
+      }
+    }
+  }
+}
+
 void SysStatsDataSource::ReadBuddyInfo(protos::pbzero::SysStats* sys_stats) {
   size_t rsize = ReadFile(&buddy_fd_, "/proc/buddyinfo");
   if (!rsize) {
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 8b71234..cd4ad16 100644
--- a/src/traced/probes/sys_stats/sys_stats_data_source.h
+++ b/src/traced/probes/sys_stats/sys_stats_data_source.h
@@ -88,6 +88,7 @@
   void ReadDevfreq(protos::pbzero::SysStats* sys_stats);
   void ReadCpufreq(protos::pbzero::SysStats* sys_stats);
   void ReadBuddyInfo(protos::pbzero::SysStats* sys_stats);
+  void ReadDiskStat(protos::pbzero::SysStats* sys_stats);
   size_t ReadFile(base::ScopedFile*, const char* path);
 
   base::TaskRunner* const task_runner_;
@@ -96,6 +97,7 @@
   base::ScopedFile vmstat_fd_;
   base::ScopedFile stat_fd_;
   base::ScopedFile buddy_fd_;
+  base::ScopedFile diskstat_fd_;
   base::PagedMemory read_buf_;
   TraceWriter::TracePacketHandle cur_packet_;
   std::map<const char*, int, CStrCmp> meminfo_counters_;
@@ -110,6 +112,7 @@
   uint32_t devfreq_ticks_ = 0;
   uint32_t cpufreq_ticks_ = 0;
   uint32_t buddyinfo_ticks_ = 0;
+  uint32_t diskstat_ticks_ = 0;
   bool devfreq_error_logged_ = false;
 
   std::unique_ptr<CpuFreqInfo> cpu_freq_info_;
diff --git a/src/traced/probes/sys_stats/sys_stats_data_source_unittest.cc b/src/traced/probes/sys_stats/sys_stats_data_source_unittest.cc
index 98f2d4a..bd9eaa23 100644
--- a/src/traced/probes/sys_stats/sys_stats_data_source_unittest.cc
+++ b/src/traced/probes/sys_stats/sys_stats_data_source_unittest.cc
@@ -17,6 +17,7 @@
 #include <unistd.h>
 
 #include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/temp_file.h"
 #include "src/base/test/test_task_runner.h"
 #include "src/traced/probes/common/cpu_freq_info_for_testing.h"
@@ -197,6 +198,11 @@
 const char kDevfreq1[] = "1000000";
 const char kDevfreq2[] = "20000000";
 
+const char kMockDiskStat[] = R"(
+ 253       0 zram0 13886 0 111088 128 57298 0 458384 48 0 15248 176 0 0 0 0 0 0
+   8       0 sda 54133 5368 8221736 75929 30333 1157434 9599744 143190 0 63672 249858 9595 0 2160072 19411 6649 11327
+   8       1 sda1 18 6 632 7 39 49 704 92 0 156 100 0 0 0 0 0 0)";
+
 class TestSysStatsDataSource : public SysStatsDataSource {
  public:
   TestSysStatsDataSource(base::TaskRunner* task_runner,
@@ -226,6 +232,8 @@
     EXPECT_GT(pwrite(tmp_.fd(), kMockStat, strlen(kMockStat), 0), 0);
   } else if (!strcmp(path, "/proc/buddyinfo")) {
     EXPECT_GT(pwrite(tmp_.fd(), kMockBuddy, strlen(kMockBuddy), 0), 0);
+  } else if (!strcmp(path, "/proc/diskstats")) {
+    EXPECT_GT(pwrite(tmp_.fd(), kMockDiskStat, strlen(kMockDiskStat), 0), 0);
   } else {
     PERFETTO_FATAL("Unexpected file opened %s", path);
   }
@@ -430,14 +438,12 @@
   auto make_devfreq_paths = [&symlinks_to_delete, &dirs_to_delete](
                                 base::TempDir& temp_dir, base::TempDir& sym_dir,
                                 const char* name) {
-    char path[256];
-    sprintf(path, "%s/%s", temp_dir.path().c_str(), name);
-    dirs_to_delete.push_back(path);
-    mkdir(path, 0755);
-    char sym_path[256];
-    sprintf(sym_path, "%s/%s", sym_dir.path().c_str(), name);
-    symlinks_to_delete.push_back(sym_path);
-    symlink(path, sym_path);
+    base::StackString<256> path("%s/%s", temp_dir.path().c_str(), name);
+    dirs_to_delete.push_back(path.ToStdString());
+    mkdir(path.c_str(), 0755);
+    base::StackString<256> sym_path("%s/%s", sym_dir.path().c_str(), name);
+    symlinks_to_delete.push_back(sym_path.ToStdString());
+    symlink(path.c_str(), sym_path.c_str());
   };
   auto fake_devfreq = base::TempDir::Create();
   auto fake_devfreq_symdir = base::TempDir::Create();
@@ -569,5 +575,48 @@
   }
 }
 
+TEST_F(SysStatsDataSourceTest, DiskStat) {
+  protos::gen::SysStatsConfig cfg;
+  cfg.set_diskstat_period_ms(10);
+  DataSourceConfig config_obj;
+  config_obj.set_sys_stats_config_raw(cfg.SerializeAsString());
+  auto data_source = GetSysStatsDataSource(config_obj);
+
+  WaitTick(data_source.get());
+
+  protos::gen::TracePacket packet = writer_raw_->GetOnlyTracePacket();
+  ASSERT_TRUE(packet.has_sys_stats());
+  const auto& sys_stats = packet.sys_stats();
+  EXPECT_EQ(sys_stats.disk_stat_size(), 3);
+
+  EXPECT_EQ(sys_stats.disk_stat()[0].device_name(), "zram0");
+  EXPECT_EQ(sys_stats.disk_stat()[0].read_sectors(), 111088u);
+  EXPECT_EQ(sys_stats.disk_stat()[0].write_sectors(), 458384u);
+  EXPECT_EQ(sys_stats.disk_stat()[0].discard_sectors(), 0u);
+  EXPECT_EQ(sys_stats.disk_stat()[0].flush_count(), 0u);
+  EXPECT_EQ(sys_stats.disk_stat()[0].read_time_ms(), 128u);
+  EXPECT_EQ(sys_stats.disk_stat()[0].write_time_ms(), 48u);
+  EXPECT_EQ(sys_stats.disk_stat()[0].discard_time_ms(), 0u);
+  EXPECT_EQ(sys_stats.disk_stat()[0].flush_time_ms(), 0u);
+  EXPECT_EQ(sys_stats.disk_stat()[1].device_name(), "sda");
+  EXPECT_EQ(sys_stats.disk_stat()[1].read_sectors(), 8221736u);
+  EXPECT_EQ(sys_stats.disk_stat()[1].write_sectors(), 9599744u);
+  EXPECT_EQ(sys_stats.disk_stat()[1].discard_sectors(), 2160072u);
+  EXPECT_EQ(sys_stats.disk_stat()[1].flush_count(), 6649u);
+  EXPECT_EQ(sys_stats.disk_stat()[1].read_time_ms(), 75929u);
+  EXPECT_EQ(sys_stats.disk_stat()[1].write_time_ms(), 143190u);
+  EXPECT_EQ(sys_stats.disk_stat()[1].discard_time_ms(), 19411u);
+  EXPECT_EQ(sys_stats.disk_stat()[1].flush_time_ms(), 11327u);
+  EXPECT_EQ(sys_stats.disk_stat()[2].device_name(), "sda1");
+  EXPECT_EQ(sys_stats.disk_stat()[2].read_sectors(), 632u);
+  EXPECT_EQ(sys_stats.disk_stat()[2].write_sectors(), 704u);
+  EXPECT_EQ(sys_stats.disk_stat()[2].discard_sectors(), 0u);
+  EXPECT_EQ(sys_stats.disk_stat()[2].flush_count(), 0u);
+  EXPECT_EQ(sys_stats.disk_stat()[2].read_time_ms(), 7u);
+  EXPECT_EQ(sys_stats.disk_stat()[2].write_time_ms(), 92u);
+  EXPECT_EQ(sys_stats.disk_stat()[2].discard_time_ms(), 0u);
+  EXPECT_EQ(sys_stats.disk_stat()[2].flush_time_ms(), 0u);
+}
+
 }  // namespace
 }  // namespace perfetto
diff --git a/src/tracing/BUILD.gn b/src/tracing/BUILD.gn
index e149dd1..b4a28a0 100644
--- a/src/tracing/BUILD.gn
+++ b/src/tracing/BUILD.gn
@@ -135,6 +135,21 @@
   }
 }
 
+# Separate target because the embedder might not want this.
+source_set("integrationtests") {
+  testonly = true
+  deps = [
+    "../../gn:default_deps",
+    "../../gn:gtest_and_gmock",
+    "../../include/perfetto/ext/tracing/ipc",
+    "../../include/perfetto/tracing",
+    "../../protos/perfetto/trace:cpp",
+    "../base",
+    "../base:test_support",
+  ]
+  sources = [ "internal/tracing_muxer_impl_integrationtest.cc" ]
+}
+
 perfetto_unittest_source_set("unittests") {
   testonly = true
   deps = [
@@ -181,10 +196,12 @@
       "../../include/perfetto/tracing/core",
       "../base",
       "ipc:common",
-      "ipc/consumer",
       "ipc/producer",
       "ipc/service",
     ]
+    if (enable_perfetto_system_consumer) {
+      deps += [ "ipc/consumer" ]
+    }
     sources = [ "internal/system_tracing_backend.cc" ]
   }
 } else {
diff --git a/src/tracing/README.md b/src/tracing/README.md
index 10de62c..bba1c1d 100644
--- a/src/tracing/README.md
+++ b/src/tracing/README.md
@@ -47,7 +47,7 @@
 **Both have the following sub-structure**:
 
 `{include,src}/core/`
-"Core" is the pure c++11 tracing machinery that deals with bookkeeping,
+"Core" is the pure c++17 tracing machinery that deals with bookkeeping,
 ring-buffering, partitioning and multiplexing but knows nothing about
 platform-specific things like implementation of shared memory and RPC mechanism.
 
diff --git a/src/tracing/core/metatrace_writer.cc b/src/tracing/core/metatrace_writer.cc
index f6acfa0..c270632 100644
--- a/src/tracing/core/metatrace_writer.cc
+++ b/src/tracing/core/metatrace_writer.cc
@@ -26,8 +26,10 @@
 
 namespace perfetto {
 
+#if !PERFETTO_IS_AT_LEAST_CPP17()
 // static
 constexpr char MetatraceWriter::kDataSourceName[];
+#endif
 
 MetatraceWriter::MetatraceWriter() : weak_ptr_factory_(this) {}
 
diff --git a/src/tracing/core/shared_memory_abi.cc b/src/tracing/core/shared_memory_abi.cc
index 9461848..0c4694b 100644
--- a/src/tracing/core/shared_memory_abi.cc
+++ b/src/tracing/core/shared_memory_abi.cc
@@ -69,6 +69,7 @@
 
 }  // namespace
 
+#if !PERFETTO_IS_AT_LEAST_CPP17()
 // static
 constexpr uint32_t SharedMemoryABI::kNumChunksForLayout[];
 constexpr const char* SharedMemoryABI::kChunkStateStr[];
@@ -76,6 +77,7 @@
 constexpr const size_t SharedMemoryABI::kMinPageSize;
 constexpr const size_t SharedMemoryABI::kMaxPageSize;
 constexpr const size_t SharedMemoryABI::kPacketSizeDropPacket;
+#endif
 
 SharedMemoryABI::SharedMemoryABI() = default;
 
diff --git a/src/tracing/core/shared_memory_arbiter_impl.cc b/src/tracing/core/shared_memory_arbiter_impl.cc
index 7928676..f1b830a 100644
--- a/src/tracing/core/shared_memory_arbiter_impl.cc
+++ b/src/tracing/core/shared_memory_arbiter_impl.cc
@@ -52,8 +52,10 @@
 SharedMemoryABI::PageLayout SharedMemoryArbiterImpl::default_page_layout =
     SharedMemoryABI::PageLayout::kPageDiv1;
 
+#if !PERFETTO_IS_AT_LEAST_CPP17()
 // static
 constexpr BufferID SharedMemoryArbiterImpl::kInvalidBufferId;
+#endif
 
 // static
 std::unique_ptr<SharedMemoryArbiter> SharedMemoryArbiter::CreateInstance(
diff --git a/src/tracing/core/trace_buffer.cc b/src/tracing/core/trace_buffer.cc
index 41633aa..3fddc58 100644
--- a/src/tracing/core/trace_buffer.cc
+++ b/src/tracing/core/trace_buffer.cc
@@ -42,8 +42,10 @@
     SharedMemoryABI::ChunkHeader::kChunkNeedsPatching;
 }  // namespace.
 
+#if !PERFETTO_IS_AT_LEAST_CPP17()
 constexpr size_t TraceBuffer::ChunkRecord::kMaxSize;
-constexpr size_t TraceBuffer::InlineChunkHeaderSize = sizeof(ChunkRecord);
+#endif
+const size_t TraceBuffer::InlineChunkHeaderSize = sizeof(ChunkRecord);
 
 // static
 std::unique_ptr<TraceBuffer> TraceBuffer::Create(size_t size_in_bytes,
@@ -67,6 +69,8 @@
   static_assert(
       SharedMemoryABI::kMinPageSize % sizeof(ChunkRecord) == 0,
       "sizeof(ChunkRecord) must be an integer divider of a page size");
+  auto max_size = std::numeric_limits<decltype(ChunkMeta::record_off)>::max();
+  PERFETTO_CHECK(size <= static_cast<size_t>(max_size));
   data_ = base::PagedMemory::Allocate(
       size, base::PagedMemory::kMayFail | base::PagedMemory::kDontCommit);
   if (!data_.IsValid()) {
@@ -96,6 +100,9 @@
                                      bool chunk_complete,
                                      const uint8_t* src,
                                      size_t size) {
+  TRACE_BUFFER_DLOG("CopyChunk @ %lu, size=%zu", wptr_ - begin(), record_size);
+  PERFETTO_CHECK(!read_only_);
+
   // |record_size| = |size| + sizeof(ChunkRecord), rounded up to avoid to end
   // up in a fragmented state where size_to_end() < sizeof(ChunkRecord).
   const size_t record_size =
@@ -106,8 +113,6 @@
     return;
   }
 
-  TRACE_BUFFER_DLOG("CopyChunk @ %lu, size=%zu", wptr_ - begin(), record_size);
-
 #if PERFETTO_DCHECK_IS_ON()
   changed_since_last_read_ = true;
 #endif
@@ -142,7 +147,7 @@
   const auto it = index_.find(key);
   if (PERFETTO_UNLIKELY(it != index_.end())) {
     ChunkMeta* record_meta = &it->second;
-    ChunkRecord* prev = record_meta->chunk_record;
+    ChunkRecord* prev = GetChunkRecordAt(begin() + record_meta->record_off);
 
     // Verify that the old chunk's metadata corresponds to the new one.
     // Overridden chunks should never change size, since the page layout is
@@ -257,9 +262,11 @@
   // Now first insert the new chunk. At the end, if necessary, add the padding.
   stats_.set_chunks_written(stats_.chunks_written() + 1);
   stats_.set_bytes_written(stats_.bytes_written() + record_size);
+
+  uint32_t chunk_off = GetOffset(GetChunkRecordAt(wptr_));
   auto it_and_inserted = index_.emplace(
-      key, ChunkMeta(GetChunkRecordAt(wptr_), num_fragments, chunk_complete,
-                     chunk_flags, producer_uid_trusted, producer_pid_trusted));
+      key, ChunkMeta(chunk_off, num_fragments, chunk_complete, chunk_flags,
+                     producer_uid_trusted, producer_pid_trusted));
   PERFETTO_DCHECK(it_and_inserted.second);
   TRACE_BUFFER_DLOG("  copying @ [%lu - %lu] %zu", wptr_ - begin(),
                     uintptr_t(wptr_ - begin()) + record_size, record_size);
@@ -398,6 +405,7 @@
                                         const Patch* patches,
                                         size_t patches_size,
                                         bool other_patches_pending) {
+  PERFETTO_CHECK(!read_only_);
   ChunkMeta::Key key(producer_id, writer_id, chunk_id);
   auto it = index_.find(key);
   if (it == index_.end()) {
@@ -408,10 +416,12 @@
 
   // Check that the index is consistent with the actual ProducerID/WriterID
   // stored in the ChunkRecord.
-  PERFETTO_DCHECK(ChunkMeta::Key(*chunk_meta.chunk_record) == key);
-  uint8_t* chunk_begin = reinterpret_cast<uint8_t*>(chunk_meta.chunk_record);
+
+  ChunkRecord* chunk_record = GetChunkRecordAt(begin() + chunk_meta.record_off);
+  PERFETTO_DCHECK(ChunkMeta::Key(*chunk_record) == key);
+  uint8_t* chunk_begin = reinterpret_cast<uint8_t*>(chunk_record);
   PERFETTO_DCHECK(chunk_begin >= begin());
-  uint8_t* chunk_end = chunk_begin + chunk_meta.chunk_record->size;
+  uint8_t* chunk_end = chunk_begin + chunk_record->size;
   PERFETTO_DCHECK(chunk_end <= end());
 
   static_assert(Patch::kSize == SharedMemoryABI::kPacketHeaderSize,
@@ -437,14 +447,13 @@
 
     memcpy(ptr, &patches[i].data[0], Patch::kSize);
   }
-  TRACE_BUFFER_DLOG(
-      "Chunk raw (after patch): %s",
-      base::HexDump(chunk_begin, chunk_meta.chunk_record->size).c_str());
+  TRACE_BUFFER_DLOG("Chunk raw (after patch): %s",
+                    base::HexDump(chunk_begin, chunk_record->size).c_str());
 
   stats_.set_patches_succeeded(stats_.patches_succeeded() + patches_size);
   if (!other_patches_pending) {
     chunk_meta.flags &= ~kChunkNeedsPatching;
-    chunk_meta.chunk_record->flags = chunk_meta.flags;
+    chunk_record->flags = chunk_meta.flags;
   }
   return true;
 }
@@ -789,14 +798,15 @@
 }
 
 TraceBuffer::ReadPacketResult TraceBuffer::ReadNextPacketInChunk(
-    ChunkMeta* chunk_meta,
+    ChunkMeta* const chunk_meta,
     TracePacket* packet) {
   PERFETTO_DCHECK(chunk_meta->num_fragments_read < chunk_meta->num_fragments);
   PERFETTO_DCHECK(!(chunk_meta->flags & kChunkNeedsPatching));
 
-  const uint8_t* record_begin =
-      reinterpret_cast<const uint8_t*>(chunk_meta->chunk_record);
-  const uint8_t* record_end = record_begin + chunk_meta->chunk_record->size;
+  const uint8_t* record_begin = begin() + chunk_meta->record_off;
+  DcheckIsAlignedAndWithinBounds(record_begin);
+  auto* chunk_record = reinterpret_cast<const ChunkRecord*>(record_begin);
+  const uint8_t* record_end = record_begin + chunk_record->size;
   const uint8_t* packets_begin = record_begin + sizeof(ChunkRecord);
   const uint8_t* packet_begin = packets_begin + chunk_meta->cur_fragment_offset;
 
@@ -810,8 +820,7 @@
     chunk_meta->num_fragments_read = chunk_meta->num_fragments;
     if (PERFETTO_LIKELY(chunk_meta->is_complete())) {
       stats_.set_chunks_read(stats_.chunks_read() + 1);
-      stats_.set_bytes_read(stats_.bytes_read() +
-                            chunk_meta->chunk_record->size);
+      stats_.set_bytes_read(stats_.bytes_read() + chunk_record->size);
     }
     return ReadPacketResult::kFailedInvalidPacket;
   }
@@ -844,8 +853,7 @@
     chunk_meta->num_fragments_read = chunk_meta->num_fragments;
     if (PERFETTO_LIKELY(chunk_meta->is_complete())) {
       stats_.set_chunks_read(stats_.chunks_read() + 1);
-      stats_.set_bytes_read(stats_.bytes_read() +
-                            chunk_meta->chunk_record->size);
+      stats_.set_bytes_read(stats_.bytes_read() + chunk_record->size);
     }
     return ReadPacketResult::kFailedInvalidPacket;
   }
@@ -858,11 +866,11 @@
                             chunk_meta->num_fragments &&
                         chunk_meta->is_complete())) {
     stats_.set_chunks_read(stats_.chunks_read() + 1);
-    stats_.set_bytes_read(stats_.bytes_read() + chunk_meta->chunk_record->size);
+    stats_.set_bytes_read(stats_.bytes_read() + 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) {
+        chunk_record->size) {
       PERFETTO_DCHECK(suppress_client_dchecks_for_testing_);
     }
   }
@@ -885,4 +893,41 @@
   TRACE_BUFFER_DLOG("  discarding write");
 }
 
+std::unique_ptr<TraceBuffer> TraceBuffer::CloneReadOnly() const {
+  std::unique_ptr<TraceBuffer> buf(new TraceBuffer(CloneCtor(), *this));
+  if (!buf->data_.IsValid())
+    return nullptr;  // PagedMemory::Allocate() failed. We are out of memory.
+  return buf;
+}
+
+TraceBuffer::TraceBuffer(CloneCtor, const TraceBuffer& src)
+    : overwrite_policy_(src.overwrite_policy_),
+      read_only_(true),
+      discard_writes_(src.discard_writes_) {
+  if (!Initialize(src.data_.size()))
+    return;  // TraceBuffer::Clone() will check |data_| and return nullptr.
+
+  // The assignments below must be done after Initialize().
+
+  data_.EnsureCommitted(data_.size());
+  memcpy(data_.Get(), src.data_.Get(), src.data_.size());
+  last_chunk_id_written_ = src.last_chunk_id_written_;
+
+  stats_ = src.stats_;
+  stats_.set_bytes_read(0);
+  stats_.set_chunks_read(0);
+  stats_.set_readaheads_failed(0);
+  stats_.set_readaheads_succeeded(0);
+
+  // Copy the index of chunk metadata and reset the read states.
+  index_ = ChunkMap(src.index_);
+  for (auto& kv : index_) {
+    ChunkMeta& chunk_meta = kv.second;
+    chunk_meta.num_fragments_read = 0;
+    chunk_meta.cur_fragment_offset = 0;
+    chunk_meta.set_last_read_packet_skipped(false);
+  }
+  read_iter_ = SequenceIterator();
+}
+
 }  // namespace perfetto
diff --git a/src/tracing/core/trace_buffer.h b/src/tracing/core/trace_buffer.h
index 415c769..b3ea608 100644
--- a/src/tracing/core/trace_buffer.h
+++ b/src/tracing/core/trace_buffer.h
@@ -261,6 +261,12 @@
                            PacketSequenceProperties* sequence_properties,
                            bool* previous_packet_on_sequence_dropped);
 
+  // Creates a read-only clone of the trace buffer. The read iterators of the
+  // new buffer will be reset, as if no Read() had been called. Calls to
+  // CopyChunkUntrusted() and TryPatchChunkContents() on the returned cloned
+  // TraceBuffer will CHECK().
+  std::unique_ptr<TraceBuffer> CloneReadOnly() const;
+
   const TraceStats::BufferStats& stats() const { return stats_; }
   size_t size() const { return size_; }
 
@@ -340,6 +346,9 @@
       Key(ProducerID p, WriterID w, ChunkID c)
           : producer_id{p}, writer_id{w}, chunk_id{c} {}
 
+      Key(const Key&) noexcept = default;
+      Key& operator=(const Key&) = default;
+
       explicit Key(const ChunkRecord& cr)
           : Key(cr.producer_id, cr.writer_id, cr.chunk_id) {}
 
@@ -379,21 +388,23 @@
       kLastReadPacketSkipped = 1 << 1
     };
 
-    ChunkMeta(ChunkRecord* r,
-              uint16_t p,
+    ChunkMeta(uint32_t _record_off,
+              uint16_t _num_fragments,
               bool complete,
-              uint8_t f,
-              uid_t u,
-              pid_t pid)
-        : chunk_record{r},
-          trusted_uid{u},
-          trusted_pid(pid),
-          flags{f},
-          num_fragments{p} {
+              uint8_t _flags,
+              uid_t _trusted_uid,
+              pid_t _trusted_pid)
+        : record_off{_record_off},
+          trusted_uid{_trusted_uid},
+          trusted_pid(_trusted_pid),
+          flags{_flags},
+          num_fragments{_num_fragments} {
       if (complete)
         index_flags = kComplete;
     }
 
+    ChunkMeta(const ChunkMeta&) noexcept = default;
+
     bool is_complete() const { return index_flags & kComplete; }
 
     void set_complete(bool complete) {
@@ -416,9 +427,9 @@
       }
     }
 
-    ChunkRecord* const chunk_record;  // Addr of ChunkRecord within |data_|.
-    const uid_t trusted_uid;          // uid of the producer.
-    const pid_t trusted_pid;          // pid of the producer.
+    const uint32_t record_off;  // Offset of ChunkRecord within |data_|.
+    const uid_t trusted_uid;    // uid of the producer.
+    const pid_t trusted_pid;    // pid of the producer.
 
     // Flags set by TraceBuffer to track the state of the chunk in the index.
     uint8_t index_flags = 0;
@@ -513,6 +524,11 @@
   TraceBuffer(const TraceBuffer&) = delete;
   TraceBuffer& operator=(const TraceBuffer&) = delete;
 
+  // Not using the implicit copy ctor to avoid unintended copies.
+  // This tagged ctor should be used only for Clone().
+  struct CloneCtor {};
+  TraceBuffer(CloneCtor, const TraceBuffer&);
+
   bool Initialize(size_t size);
 
   // Returns an object that allows to iterate over chunks in the |index_| that
@@ -626,6 +642,13 @@
     memset(wptr + sizeof(record) + size, 0, rounding_size);
   }
 
+  uint32_t GetOffset(const void* _addr) {
+    const uintptr_t addr = reinterpret_cast<uintptr_t>(_addr);
+    const uintptr_t buf_start = reinterpret_cast<uintptr_t>(begin());
+    PERFETTO_DCHECK(addr >= buf_start && addr < buf_start + size_);
+    return static_cast<uint32_t>(addr - buf_start);
+  }
+
   uint8_t* begin() const { return reinterpret_cast<uint8_t*>(data_.Get()); }
   uint8_t* end() const { return begin() + size_; }
   size_t size_to_end() const { return static_cast<size_t>(end() - wptr_); }
@@ -646,6 +669,10 @@
   // See comments at the top of the file.
   OverwritePolicy overwrite_policy_ = kOverwrite;
 
+  // This buffer is a read-only snapshot obtained via Clone(). If this is true
+  // calls to CopyChunkUntrusted() and TryPatchChunkContents() will CHECK().
+  bool read_only_ = false;
+
   // Only used when |overwrite_policy_ == kDiscard|. This is set the first time
   // a write fails because it would overwrite unread chunks.
   bool discard_writes_ = false;
diff --git a/src/tracing/core/trace_buffer_unittest.cc b/src/tracing/core/trace_buffer_unittest.cc
index dbba1e9..38d343d 100644
--- a/src/tracing/core/trace_buffer_unittest.cc
+++ b/src/tracing/core/trace_buffer_unittest.cc
@@ -69,14 +69,15 @@
         p, w, c, patches.data(), patches.size(), other_patches_pending);
   }
 
-  std::vector<FakePacketFragment> ReadPacket(
+  static std::vector<FakePacketFragment> ReadPacket(
+      const std::unique_ptr<TraceBuffer>& buf,
       TraceBuffer::PacketSequenceProperties* sequence_properties = nullptr,
       bool* previous_packet_dropped = nullptr) {
     std::vector<FakePacketFragment> fragments;
     TracePacket packet;
     TraceBuffer::PacketSequenceProperties ignored_sequence_properties{};
     bool ignored_previous_packet_dropped;
-    if (!trace_buffer_->ReadNextTracePacket(
+    if (!buf->ReadNextTracePacket(
             &packet,
             sequence_properties ? sequence_properties
                                 : &ignored_sequence_properties,
@@ -89,6 +90,13 @@
     return fragments;
   }
 
+  std::vector<FakePacketFragment> ReadPacket(
+      TraceBuffer::PacketSequenceProperties* sequence_properties = nullptr,
+      bool* previous_packet_dropped = nullptr) {
+    return ReadPacket(trace_buffer_, sequence_properties,
+                      previous_packet_dropped);
+  }
+
   void AppendChunks(
       std::initializer_list<std::tuple<ProducerID, WriterID, ChunkID>> chunks) {
     for (const auto& c : chunks) {
@@ -1836,8 +1844,89 @@
   ASSERT_TRUE(previous_packet_dropped);
 }
 
-// TODO(primiano): test stats().
-// TODO(primiano): test multiple streams interleaved.
-// TODO(primiano): more testing on packet merging.
+TEST_F(TraceBufferTest, Clone_NoFragments) {
+  const char kNumWriters = 3;
+  for (char num_pre_reads = 0; num_pre_reads < kNumWriters; num_pre_reads++) {
+    ResetBuffer(4096);
+    for (char i = 'A'; i < 'A' + kNumWriters; i++) {
+      ASSERT_EQ(32u, CreateChunk(ProducerID(i), WriterID(i), ChunkID(i))
+                         .AddPacket(32 - 16, i)
+                         .CopyIntoTraceBuffer());
+    }
+
+    // Make some reads *before* cloning. This is to check that the behaviour of
+    // CloneReadOnly() is not affected by reads made before cloning.
+    // On every round (|num_pre_reads|), read a different number of packets.
+    for (char i = 0; i < num_pre_reads; i++) {
+      if (i == 0)
+        trace_buffer()->BeginRead();
+      ASSERT_THAT(ReadPacket(),
+                  ElementsAre(FakePacketFragment(32 - 16, 'A' + i)));
+    }
+
+    // Now create a snapshot and make sure we always read all the packets.
+    std::unique_ptr<TraceBuffer> snap = trace_buffer()->CloneReadOnly();
+    snap->BeginRead();
+    for (char i = 'A'; i < 'A' + kNumWriters; i++) {
+      auto frags = ReadPacket(snap);
+      ASSERT_THAT(frags, ElementsAre(FakePacketFragment(32 - 16, i)));
+    }
+    ASSERT_THAT(ReadPacket(snap), IsEmpty());
+  }
+}
+
+TEST_F(TraceBufferTest, Clone_FragmentsOutOfOrder) {
+  ResetBuffer(4096);
+  CreateChunk(ProducerID(1), WriterID(1), ChunkID(0))
+      .AddPacket(10, 'a')
+      .CopyIntoTraceBuffer();
+  CreateChunk(ProducerID(1), WriterID(1), ChunkID(2))
+      .AddPacket(30, 'c')
+      .CopyIntoTraceBuffer();
+
+  {
+    // Create a snapshot before the middle 'b' chunk is copied. Only 'a' should
+    // be readable at this point.
+    std::unique_ptr<TraceBuffer> snap = trace_buffer()->CloneReadOnly();
+    snap->BeginRead();
+    ASSERT_THAT(ReadPacket(snap), ElementsAre(FakePacketFragment(10, 'a')));
+    ASSERT_THAT(ReadPacket(snap), IsEmpty());
+  }
+
+  CreateChunk(ProducerID(1), WriterID(1), ChunkID(1))
+      .AddPacket(20, 'b')
+      .CopyIntoTraceBuffer();
+
+  // Now all three packes should be readable.
+  std::unique_ptr<TraceBuffer> snap = trace_buffer()->CloneReadOnly();
+  snap->BeginRead();
+  ASSERT_THAT(ReadPacket(snap), ElementsAre(FakePacketFragment(10, 'a')));
+  ASSERT_THAT(ReadPacket(snap), ElementsAre(FakePacketFragment(20, 'b')));
+  ASSERT_THAT(ReadPacket(snap), ElementsAre(FakePacketFragment(30, 'c')));
+  ASSERT_THAT(ReadPacket(snap), IsEmpty());
+}
+
+TEST_F(TraceBufferTest, Clone_WithPatches) {
+  ResetBuffer(4096);
+  CreateChunk(ProducerID(1), WriterID(1), ChunkID(0))
+      .AddPacket(100, 'a')
+      .CopyIntoTraceBuffer();
+  CreateChunk(ProducerID(2), WriterID(1), ChunkID(0))
+      .AddPacket(9, 'b')
+      .ClearBytes(5, 4)  // 5 := 4th payload byte. Byte 0 is the varint header.
+      .CopyIntoTraceBuffer();
+  CreateChunk(ProducerID(3), WriterID(1), ChunkID(0))
+      .AddPacket(100, 'c')
+      .CopyIntoTraceBuffer();
+  ASSERT_TRUE(TryPatchChunkContents(ProducerID(2), WriterID(1), ChunkID(0),
+                                    {{5, {{'Y', 'M', 'C', 'A'}}}}));
+
+  std::unique_ptr<TraceBuffer> snap = trace_buffer()->CloneReadOnly();
+  snap->BeginRead();
+  ASSERT_THAT(ReadPacket(snap), ElementsAre(FakePacketFragment(100, 'a')));
+  ASSERT_THAT(ReadPacket(snap), ElementsAre(FakePacketFragment("b00-YMCA", 8)));
+  ASSERT_THAT(ReadPacket(snap), ElementsAre(FakePacketFragment(100, 'c')));
+  ASSERT_THAT(ReadPacket(snap), IsEmpty());
+}
 
 }  // namespace perfetto
diff --git a/src/tracing/core/trace_writer_impl.cc b/src/tracing/core/trace_writer_impl.cc
index 834e1b4..3a31e23 100644
--- a/src/tracing/core/trace_writer_impl.cc
+++ b/src/tracing/core/trace_writer_impl.cc
@@ -158,6 +158,11 @@
     }
   }
 
+  if (PERFETTO_UNLIKELY(first_packet_on_sequence_)) {
+    cur_packet_->set_first_packet_on_sequence(true);
+    first_packet_on_sequence_ = false;
+  }
+
   return handle;
 }
 
diff --git a/src/tracing/core/trace_writer_impl.h b/src/tracing/core/trace_writer_impl.h
index 13f1367..1880332 100644
--- a/src/tracing/core/trace_writer_impl.h
+++ b/src/tracing/core/trace_writer_impl.h
@@ -158,6 +158,10 @@
   // 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_;
+
+  // True for the first packet on sequence. See the comment for
+  // TracePacket.first_packet_on_sequence for more details.
+  bool first_packet_on_sequence_ = true;
 };
 
 }  // namespace perfetto
diff --git a/src/tracing/core/trace_writer_impl_unittest.cc b/src/tracing/core/trace_writer_impl_unittest.cc
index 7e25a21..05a13c6 100644
--- a/src/tracing/core/trace_writer_impl_unittest.cc
+++ b/src/tracing/core/trace_writer_impl_unittest.cc
@@ -213,6 +213,11 @@
     protos::gen::TracePacket packet;
     EXPECT_TRUE(packet.ParseFromString(packets[i]));
     EXPECT_EQ(packet.for_testing().str(), "foobar " + std::to_string(i));
+    if (i == 0) {
+      EXPECT_TRUE(packet.first_packet_on_sequence());
+    } else {
+      EXPECT_FALSE(packet.first_packet_on_sequence());
+    }
   }
 }
 
@@ -251,6 +256,11 @@
   }
 }
 
+// A prefix corresponding to first_packet_on_sequence = true in a serialized
+// TracePacket proto.
+constexpr char kFirstPacketOnSequenceFlagPrefix[] = {static_cast<char>(0xB8),
+                                                     0x5, 0x1, 0x0};
+
 TEST_P(TraceWriterImplTest, NewTracePacketTakeWriter) {
   const BufferID kBufId = 42;
   std::unique_ptr<TraceWriter> writer = arbiter_->CreateTraceWriter(kBufId);
@@ -271,7 +281,11 @@
   std::vector<std::string> packets = GetPacketsFromShmemAndPatches();
   ASSERT_THAT(packets, SizeIs(kNumPackets));
   for (size_t i = 0; i < kNumPackets; i++) {
-    EXPECT_EQ(packets[i], std::string("RAW_PROTO_BYTES_" + std::to_string(i)));
+    std::string expected = "RAW_PROTO_BYTES_" + std::to_string(i);
+    if (i == 0) {
+      expected = kFirstPacketOnSequenceFlagPrefix + expected;
+    }
+    EXPECT_EQ(packets[i], expected);
   }
 }
 
@@ -366,13 +380,13 @@
   writer.reset();
 
   std::vector<std::string> packets = GetPacketsFromShmemAndPatches();
-  EXPECT_THAT(packets,
-              ElementsAre(std::string("RAW_PROTO_BYTES") +
-                          std::string("\x11\x11\x11\x11") + std::string("X") +
-                          std::string("\x22\x22\x22\x22") +
-                          std::string(chunk_size, 'x') +
-                          std::string("\x33\x33\x33\x33") +
-                          std::string(chunk_size, 'x')));
+  EXPECT_THAT(
+      packets,
+      ElementsAre(
+          kFirstPacketOnSequenceFlagPrefix + std::string("RAW_PROTO_BYTES") +
+          std::string("\x11\x11\x11\x11") + std::string("X") +
+          std::string("\x22\x22\x22\x22") + std::string(chunk_size, 'x') +
+          std::string("\x33\x33\x33\x33") + std::string(chunk_size, 'x')));
 }
 
 TEST_P(TraceWriterImplTest, MixManualTakeAndMessage) {
@@ -453,7 +467,8 @@
   std::vector<std::string> packets = GetPacketsFromShmemAndPatches();
   EXPECT_THAT(
       packets,
-      ElementsAre(std::string("PACKET_1_") + std::string("\xFF\xFF\xFF\xFF") +
+      ElementsAre(kFirstPacketOnSequenceFlagPrefix + std::string("PACKET_1_") +
+                      std::string("\xFF\xFF\xFF\xFF") +
                       std::string(chunk_size, 'x'),
                   std::string("PACKET_2_") + std::string("\x0A") +
                       encoded_size + std::string(chunk_size, 'x'),
@@ -609,7 +624,8 @@
   auto packet = writer->NewTracePacket();
   EXPECT_FALSE(reinterpret_cast<TraceWriterImpl*>(writer.get())
                    ->drop_packets_for_testing());
-  EXPECT_EQ(packet->Finalize(), 0u);
+  // 3 bytes for the first_packet_on_sequence flag.
+  EXPECT_EQ(packet->Finalize(), 3u);
 
   // Grab all the remaining chunks in the SMB in new writers.
   std::array<std::unique_ptr<TraceWriter>, kNumPages * 4 - 1> other_writers;
@@ -706,7 +722,8 @@
   auto packet = writer->NewTracePacket();
   EXPECT_FALSE(reinterpret_cast<TraceWriterImpl*>(writer.get())
                    ->drop_packets_for_testing());
-  EXPECT_EQ(packet->Finalize(), 0u);
+  // 3 bytes for the first_packet_on_sequence flag.
+  EXPECT_EQ(packet->Finalize(), 3u);
 
   // Flush the first chunk away.
   writer->Flush();
@@ -800,7 +817,8 @@
   auto packet = writer->NewTracePacket();
   EXPECT_FALSE(reinterpret_cast<TraceWriterImpl*>(writer.get())
                    ->drop_packets_for_testing());
-  EXPECT_EQ(packet->Finalize(), 0u);
+  // 3 bytes for the first_packet_on_sequence flag.
+  EXPECT_EQ(packet->Finalize(), 3u);
 
   // Grab all but one of the remaining chunks in the SMB in new writers.
   std::array<std::unique_ptr<TraceWriter>, kNumPages * 4 - 2> other_writers;
diff --git a/src/tracing/core/tracing_service_impl.cc b/src/tracing/core/tracing_service_impl.cc
index c7822f3..d304ada 100644
--- a/src/tracing/core/tracing_service_impl.cc
+++ b/src/tracing/core/tracing_service_impl.cc
@@ -88,6 +88,7 @@
 #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/trace_uuid.pbzero.h"
 #include "protos/perfetto/trace/trigger.pbzero.h"
 
 // General note: this class must assume that Producers are malicious and will
@@ -312,10 +313,12 @@
 
 }  // namespace
 
+#if !PERFETTO_IS_AT_LEAST_CPP17()
 // These constants instead are defined in the header because are used by tests.
 constexpr size_t TracingServiceImpl::kMaxShmSize;
 constexpr uint32_t TracingServiceImpl::kDataSourceStopTimeoutMs;
 constexpr uint8_t TracingServiceImpl::kSyncMarker[];
+#endif
 
 std::string GetBugreportPath() {
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
@@ -564,9 +567,17 @@
                                                const TraceConfig& cfg,
                                                base::ScopedFile fd) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
-  PERFETTO_DLOG("Enabling tracing for consumer %p",
-                reinterpret_cast<void*>(consumer));
-  MaybeLogUploadEvent(cfg, PerfettoStatsdAtom::kTracedEnableTracing);
+
+  // If the producer is specifying a UUID, respect that (at least for the first
+  // snapshot). Otherwise generate a new UUID.
+  base::Uuid uuid(cfg.trace_uuid_lsb(), cfg.trace_uuid_msb());
+  if (!uuid)
+    uuid = base::Uuidv4();
+
+  PERFETTO_DLOG("Enabling tracing for consumer %p, UUID: %s",
+                reinterpret_cast<void*>(consumer),
+                uuid.ToPrettyString().c_str());
+  MaybeLogUploadEvent(cfg, uuid, PerfettoStatsdAtom::kTracedEnableTracing);
   if (cfg.lockdown_mode() == TraceConfig::LOCKDOWN_SET)
     lockdown_mode_ = true;
   if (cfg.lockdown_mode() == TraceConfig::LOCKDOWN_CLEAR)
@@ -579,7 +590,8 @@
         GetTracingSession(consumer->tracing_session_id_);
     if (tracing_session) {
       MaybeLogUploadEvent(
-          cfg, PerfettoStatsdAtom::kTracedEnableTracingExistingTraceSession);
+          cfg, uuid,
+          PerfettoStatsdAtom::kTracedEnableTracingExistingTraceSession);
       return PERFETTO_SVC_ERR(
           "A Consumer is trying to EnableTracing() but another tracing "
           "session is already active (forgot a call to FreeBuffers() ?)");
@@ -590,7 +602,7 @@
                                        ? kGuardrailsMaxTracingDurationMillis
                                        : kMaxTracingDurationMillis;
   if (cfg.duration_ms() > max_duration_ms) {
-    MaybeLogUploadEvent(cfg,
+    MaybeLogUploadEvent(cfg, uuid,
                         PerfettoStatsdAtom::kTracedEnableTracingTooLongTrace);
     return PERFETTO_SVC_ERR("Requested too long trace (%" PRIu32
                             "ms  > %" PRIu32 " ms)",
@@ -603,7 +615,8 @@
                              cfg.trigger_config().trigger_timeout_ms() >
                                  kGuardrailsMaxTracingDurationMillis)) {
     MaybeLogUploadEvent(
-        cfg, PerfettoStatsdAtom::kTracedEnableTracingInvalidTriggerTimeout);
+        cfg, uuid,
+        PerfettoStatsdAtom::kTracedEnableTracingInvalidTriggerTimeout);
     return PERFETTO_SVC_ERR(
         "Traces with START_TRACING triggers must provide a positive "
         "trigger_timeout_ms < 7 days (received %" PRIu32 "ms)",
@@ -612,7 +625,7 @@
 
   if (has_trigger_config && cfg.duration_ms() != 0) {
     MaybeLogUploadEvent(
-        cfg, PerfettoStatsdAtom::kTracedEnableTracingDurationWithTrigger);
+        cfg, uuid, PerfettoStatsdAtom::kTracedEnableTracingDurationWithTrigger);
     return PERFETTO_SVC_ERR(
         "duration_ms was set, this must not be set for traces with triggers.");
   }
@@ -627,7 +640,8 @@
     // emitting them wildy out of order breaking windowed sorting in trace
     // processor).
     MaybeLogUploadEvent(
-        cfg, PerfettoStatsdAtom::kTracedEnableTracingStopTracingWriteIntoFile);
+        cfg, uuid,
+        PerfettoStatsdAtom::kTracedEnableTracingStopTracingWriteIntoFile);
     return PERFETTO_SVC_ERR(
         "Specifying trigger mode STOP_TRACING and write_into_file together is "
         "unsupported");
@@ -637,7 +651,8 @@
   for (const auto& trigger : cfg.trigger_config().triggers()) {
     if (!triggers.insert(trigger.name()).second) {
       MaybeLogUploadEvent(
-          cfg, PerfettoStatsdAtom::kTracedEnableTracingDuplicateTriggerName);
+          cfg, uuid,
+          PerfettoStatsdAtom::kTracedEnableTracingDuplicateTriggerName);
       return PERFETTO_SVC_ERR("Duplicate trigger name: %s",
                               trigger.name().c_str());
     }
@@ -646,7 +661,8 @@
   if (cfg.enable_extra_guardrails()) {
     if (cfg.deferred_start()) {
       MaybeLogUploadEvent(
-          cfg, PerfettoStatsdAtom::kTracedEnableTracingInvalidDeferredStart);
+          cfg, uuid,
+          PerfettoStatsdAtom::kTracedEnableTracingInvalidDeferredStart);
       return PERFETTO_SVC_ERR(
           "deferred_start=true is not supported in unsupervised traces");
     }
@@ -654,24 +670,30 @@
     for (const auto& buf : cfg.buffers()) {
       if (buf.size_kb() % 4 != 0) {
         MaybeLogUploadEvent(
-            cfg, PerfettoStatsdAtom::kTracedEnableTracingInvalidBufferSize);
+            cfg, uuid,
+            PerfettoStatsdAtom::kTracedEnableTracingInvalidBufferSize);
         return PERFETTO_SVC_ERR(
             "buffers.size_kb must be a multiple of 4, got %" PRIu32,
             buf.size_kb());
       }
       buf_size_sum += buf.size_kb();
     }
-    if (buf_size_sum > kGuardrailsMaxTracingBufferSizeKb) {
+
+    uint32_t max_tracing_buffer_size_kb =
+        std::max(kGuardrailsMaxTracingBufferSizeKb,
+                 cfg.guardrail_overrides().max_tracing_buffer_size_kb());
+    if (buf_size_sum > max_tracing_buffer_size_kb) {
       MaybeLogUploadEvent(
-          cfg, PerfettoStatsdAtom::kTracedEnableTracingBufferSizeTooLarge);
+          cfg, uuid,
+          PerfettoStatsdAtom::kTracedEnableTracingBufferSizeTooLarge);
       return PERFETTO_SVC_ERR("Requested too large trace buffer (%" PRIu64
                               "kB  > %" PRIu32 " kB)",
-                              buf_size_sum, kGuardrailsMaxTracingBufferSizeKb);
+                              buf_size_sum, max_tracing_buffer_size_kb);
     }
   }
 
   if (cfg.buffers_size() > kMaxBuffersPerConsumer) {
-    MaybeLogUploadEvent(cfg,
+    MaybeLogUploadEvent(cfg, uuid,
                         PerfettoStatsdAtom::kTracedEnableTracingTooManyBuffers);
     return PERFETTO_SVC_ERR("Too many buffers configured (%d)",
                             cfg.buffers_size());
@@ -684,7 +706,7 @@
     size_t target_buffer = cfg_data_source.config().target_buffer();
     if (target_buffer >= num_buffers) {
       MaybeLogUploadEvent(
-          cfg, PerfettoStatsdAtom::kTracedEnableTracingOobTargetBuffer);
+          cfg, uuid, PerfettoStatsdAtom::kTracedEnableTracingOobTargetBuffer);
       return PERFETTO_SVC_ERR(
           "Data source \"%s\" specified an out of bounds target_buffer (%zu >= "
           "%zu)",
@@ -695,9 +717,12 @@
   if (!cfg.unique_session_name().empty()) {
     const std::string& name = cfg.unique_session_name();
     for (auto& kv : tracing_sessions_) {
+      if (kv.second.state == TracingSession::CLONED_READ_ONLY)
+        continue;  // Don't consider cloned sessions in uniqueness checks.
       if (kv.second.config.unique_session_name() == name) {
         MaybeLogUploadEvent(
-            cfg, PerfettoStatsdAtom::kTracedEnableTracingDuplicateSessionName);
+            cfg, uuid,
+            PerfettoStatsdAtom::kTracedEnableTracingDuplicateSessionName);
         static const char fmt[] =
             "A trace with this unique session name (%s) already exists";
         // This happens frequently, don't make it an "E"LOG.
@@ -728,7 +753,8 @@
       previous_s = now_s;
     } else {
       MaybeLogUploadEvent(
-          cfg, PerfettoStatsdAtom::kTracedEnableTracingSessionNameTooRecent);
+          cfg, uuid,
+          PerfettoStatsdAtom::kTracedEnableTracingSessionNameTooRecent);
       return PERFETTO_SVC_ERR(
           "A trace with unique session name \"%s\" began less than %" PRId64
           "s ago (%" PRId64 "s)",
@@ -748,7 +774,8 @@
   }
   if (sessions_for_uid >= per_uid_limit) {
     MaybeLogUploadEvent(
-        cfg, PerfettoStatsdAtom::kTracedEnableTracingTooManySessionsForUid);
+        cfg, uuid,
+        PerfettoStatsdAtom::kTracedEnableTracingTooManySessionsForUid);
     return PERFETTO_SVC_ERR(
         "Too many concurrent tracing sesions (%d) for uid %d limit is %d",
         sessions_for_uid, static_cast<int>(consumer->uid_), per_uid_limit);
@@ -760,7 +787,8 @@
   // trace_probes and the way it handles stalls in the shmem buffer.
   if (tracing_sessions_.size() >= kMaxConcurrentTracingSessions) {
     MaybeLogUploadEvent(
-        cfg, PerfettoStatsdAtom::kTracedEnableTracingTooManyConcurrentSessions);
+        cfg, uuid,
+        PerfettoStatsdAtom::kTracedEnableTracingTooManyConcurrentSessions);
     return PERFETTO_SVC_ERR("Too many concurrent tracing sesions (%zu)",
                             tracing_sessions_.size());
   }
@@ -775,7 +803,7 @@
     trace_filter.reset(new protozero::MessageFilter());
     if (!trace_filter->LoadFilterBytecode(bytecode.data(), bytecode.size())) {
       MaybeLogUploadEvent(
-          cfg, PerfettoStatsdAtom::kTracedEnableTracingInvalidFilter);
+          cfg, uuid, PerfettoStatsdAtom::kTracedEnableTracingInvalidFilter);
       return PERFETTO_SVC_ERR("Trace filter bytecode invalid, aborting");
     }
     // The filter is created using perfetto.protos.Trace as root message
@@ -789,7 +817,7 @@
     uint32_t packet_field_id = TracePacket::kPacketFieldNumber;
     if (!trace_filter->SetFilterRoot(&packet_field_id, 1)) {
       MaybeLogUploadEvent(
-          cfg, PerfettoStatsdAtom::kTracedEnableTracingInvalidFilter);
+          cfg, uuid, PerfettoStatsdAtom::kTracedEnableTracingInvalidFilter);
       return PERFETTO_SVC_ERR("Failed to set filter root.");
     }
   }
@@ -801,6 +829,8 @@
                     std::forward_as_tuple(tsid, consumer, cfg, task_runner_))
            .first->second;
 
+  tracing_session->trace_uuid = uuid;
+
   if (trace_filter)
     tracing_session->trace_filter = std::move(trace_filter);
 
@@ -808,7 +838,7 @@
     if (!fd ^ !cfg.output_path().empty()) {
       tracing_sessions_.erase(tsid);
       MaybeLogUploadEvent(
-          tracing_session->config,
+          tracing_session->config, uuid,
           PerfettoStatsdAtom::kTracedEnableTracingInvalidFdOutputFile);
       return PERFETTO_SVC_ERR(
           "When write_into_file==true either a FD needs to be passed or "
@@ -818,7 +848,7 @@
       fd = CreateTraceFile(cfg.output_path(), /*overwrite=*/false);
       if (!fd) {
         MaybeLogUploadEvent(
-            tracing_session->config,
+            tracing_session->config, uuid,
             PerfettoStatsdAtom::kTracedEnableTracingFailedToCreateFile);
         tracing_sessions_.erase(tsid);
         return PERFETTO_SVC_ERR("Failed to create the trace file %s",
@@ -883,7 +913,7 @@
       buffers_.erase(global_id);
     }
     tracing_sessions_.erase(tsid);
-    MaybeLogUploadEvent(tracing_session->config,
+    MaybeLogUploadEvent(tracing_session->config, uuid,
                         PerfettoStatsdAtom::kTracedEnableTracingOom);
     return PERFETTO_SVC_ERR(
         "Failed to allocate tracing buffers: OOM or too many buffers");
@@ -1087,12 +1117,12 @@
         "StartTracing() failed, invalid session ID %" PRIu64, tsid);
   }
 
-  MaybeLogUploadEvent(tracing_session->config,
+  MaybeLogUploadEvent(tracing_session->config, tracing_session->trace_uuid,
                       PerfettoStatsdAtom::kTracedStartTracing);
 
   if (tracing_session->state != TracingSession::CONFIGURED) {
     MaybeLogUploadEvent(
-        tracing_session->config,
+        tracing_session->config, tracing_session->trace_uuid,
         PerfettoStatsdAtom::kTracedStartTracingInvalidSessionState);
     return PERFETTO_SVC_ERR("StartTracing() failed, invalid session state: %d",
                             tracing_session->state);
@@ -1238,7 +1268,7 @@
     return;
   }
 
-  MaybeLogUploadEvent(tracing_session->config,
+  MaybeLogUploadEvent(tracing_session->config, tracing_session->trace_uuid,
                       PerfettoStatsdAtom::kTracedDisableTracing);
 
   switch (tracing_session->state) {
@@ -1247,6 +1277,10 @@
       PERFETTO_DCHECK(tracing_session->AllDataSourceInstancesStopped());
       return;
 
+    case TracingSession::CLONED_READ_ONLY:
+      PERFETTO_DLOG("DisableTracing() cannot be called on a cloned session");
+      return;
+
     // This is either:
     // A) The case of a graceful DisableTracing() call followed by a call to
     //    FreeBuffers(), iff |disable_immediately| == true. In this case we want
@@ -1424,7 +1458,7 @@
   for (const auto& trigger_name : triggers) {
     PERFETTO_DLOG("Received ActivateTriggers request for \"%s\"",
                   trigger_name.c_str());
-    base::Hash hash;
+    base::Hasher hash;
     hash.Update(trigger_name.c_str(), trigger_name.size());
     std::string triggered_session_name;
     base::Uuid triggered_session_uuid;
@@ -1446,9 +1480,10 @@
           [&trigger_name](const TraceConfig::TriggerConfig::Trigger& trigger) {
             return trigger.name() == trigger_name;
           });
-      if (iter == tracing_session.config.trigger_config().triggers().end()) {
+      if (iter == tracing_session.config.trigger_config().triggers().end())
         continue;
-      }
+      if (tracing_session.state == TracingSession::CLONED_READ_ONLY)
+        continue;
 
       // If this trigger requires a certain producer to have sent it
       // (non-empty producer_name()) ensure the producer who sent this trigger
@@ -1485,9 +1520,8 @@
       trigger_matched = true;
       triggered_session_id = tracing_session.id;
       triggered_session_name = tracing_session.config.unique_session_name();
-      triggered_session_uuid.set_lsb_msb(
-          tracing_session.config.trace_uuid_lsb(),
-          tracing_session.config.trace_uuid_msb());
+      triggered_session_uuid.set_lsb_msb(tracing_session.trace_uuid.lsb(),
+                                         tracing_session.trace_uuid.msb());
       trigger_mode = static_cast<int>(
           tracing_session.config.trigger_config().trigger_mode());
 
@@ -1507,9 +1541,9 @@
             break;
 
           trigger_activated = true;
-          MaybeLogUploadEvent(tracing_session.config,
-                              PerfettoStatsdAtom::kTracedTriggerStartTracing,
-                              iter->name());
+          MaybeLogUploadEvent(
+              tracing_session.config, tracing_session.trace_uuid,
+              PerfettoStatsdAtom::kTracedTriggerStartTracing, iter->name());
 
           // We override the trace duration to be the trigger's requested
           // value, this ensures that the trace will end after this amount
@@ -1526,9 +1560,9 @@
             break;
 
           trigger_activated = true;
-          MaybeLogUploadEvent(tracing_session.config,
-                              PerfettoStatsdAtom::kTracedTriggerStopTracing,
-                              iter->name());
+          MaybeLogUploadEvent(
+              tracing_session.config, tracing_session.trace_uuid,
+              PerfettoStatsdAtom::kTracedTriggerStopTracing, iter->name());
 
           // Now that we've seen a trigger we need to stop, flush, and disable
           // this session after the configured |stop_delay_ms|.
@@ -1619,7 +1653,7 @@
     tracing_session->on_disable_callback_for_bugreport = nullptr;
   }
 
-  MaybeLogUploadEvent(tracing_session->config,
+  MaybeLogUploadEvent(tracing_session->config, tracing_session->trace_uuid,
                       PerfettoStatsdAtom::kTracedNotifyTracingDisabled);
 
   if (tracing_session->consumer_maybe_null)
@@ -2105,6 +2139,13 @@
 }
 
 bool TracingServiceImpl::IsWaitingForTrigger(TracingSession* tracing_session) {
+  // Ignore the logic below for cloned tracing sessions. In this case we
+  // actually want to read the (cloned) trace buffers even if no trigger was
+  // hit.
+  if (tracing_session->state == TracingSession::CLONED_READ_ONLY) {
+    return false;
+  }
+
   // When a tracing session is waiting for a trigger, it is considered empty. If
   // a tracing session finishes and moves into DISABLED without ever receiving a
   // trigger, the trace should never return any data. This includes the
@@ -2148,7 +2189,7 @@
   }
 
   if (!tracing_session->config.builtin_data_sources().disable_trace_config()) {
-    MaybeEmitTraceConfig(tracing_session, &packets);
+    MaybeEmitUuidAndTraceConfig(tracing_session, &packets);
     MaybeEmitReceivedTriggers(tracing_session, &packets);
   }
   if (!tracing_session->config.builtin_data_sources().disable_system_info())
@@ -2410,7 +2451,9 @@
     PERFETTO_DCHECK(buffers_.count(buffer_id) == 1);
     buffers_.erase(buffer_id);
   }
-  bool notify_traceur = tracing_session->config.notify_traceur();
+  bool notify_traceur =
+      tracing_session->config.notify_traceur() &&
+      tracing_session->state != TracingSession::CLONED_READ_ONLY;
   bool is_long_trace =
       (tracing_session->config.write_into_file() &&
        tracing_session->config.file_write_period_ms() < kMillisPerDay);
@@ -3211,17 +3254,30 @@
   return trace_stats;
 }
 
-void TracingServiceImpl::MaybeEmitTraceConfig(
+void TracingServiceImpl::MaybeEmitUuidAndTraceConfig(
     TracingSession* tracing_session,
     std::vector<TracePacket>* packets) {
   if (tracing_session->did_emit_config)
     return;
   tracing_session->did_emit_config = true;
-  protozero::HeapBuffered<protos::pbzero::TracePacket> packet;
-  packet->set_trusted_uid(static_cast<int32_t>(uid_));
-  packet->set_trusted_packet_sequence_id(kServicePacketSequenceID);
-  tracing_session->config.Serialize(packet->set_trace_config());
-  SerializeAndAppendPacket(packets, packet.SerializeAsArray());
+
+  {
+    protozero::HeapBuffered<protos::pbzero::TracePacket> packet;
+    packet->set_trusted_uid(static_cast<int32_t>(uid_));
+    packet->set_trusted_packet_sequence_id(kServicePacketSequenceID);
+    auto* uuid = packet->set_trace_uuid();
+    uuid->set_lsb(tracing_session->trace_uuid.lsb());
+    uuid->set_msb(tracing_session->trace_uuid.msb());
+    SerializeAndAppendPacket(packets, packet.SerializeAsArray());
+  }
+
+  {
+    protozero::HeapBuffered<protos::pbzero::TracePacket> packet;
+    packet->set_trusted_uid(static_cast<int32_t>(uid_));
+    packet->set_trusted_packet_sequence_id(kServicePacketSequenceID);
+    tracing_session->config.Serialize(packet->set_trace_config());
+    SerializeAndAppendPacket(packets, packet.SerializeAsArray());
+  }
 }
 
 void TracingServiceImpl::MaybeEmitSystemInfo(
@@ -3421,17 +3477,15 @@
 }
 
 void TracingServiceImpl::MaybeLogUploadEvent(const TraceConfig& cfg,
+                                             const base::Uuid& uuid,
                                              PerfettoStatsdAtom atom,
                                              const std::string& trigger_name) {
   if (!ShouldLogEvent(cfg))
     return;
 
-  // If the UUID is not set for some reason, don't log anything.
-  if (cfg.trace_uuid_lsb() == 0 && cfg.trace_uuid_msb() == 0)
-    return;
-
-  android_stats::MaybeLogUploadEvent(atom, cfg.trace_uuid_lsb(),
-                                     cfg.trace_uuid_msb(), trigger_name);
+  PERFETTO_DCHECK(uuid);  // The UUID must be set at this point.
+  android_stats::MaybeLogUploadEvent(atom, uuid.lsb(), uuid.msb(),
+                                     trigger_name);
 }
 
 void TracingServiceImpl::MaybeLogTriggerEvent(const TraceConfig& cfg,
@@ -3460,6 +3514,111 @@
   return trigger_count;
 }
 
+void TracingServiceImpl::FlushAndCloneSession(ConsumerEndpointImpl* consumer,
+                                              TracingSessionID tsid) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  auto weak_this = weak_ptr_factory_.GetWeakPtr();
+  auto weak_consumer = consumer->GetWeakPtr();
+  Flush(tsid, 0, [weak_this, tsid, weak_consumer](bool final_flush_outcome) {
+    PERFETTO_LOG("FlushAndCloneSession(%" PRIu64 ") started, success=%d", tsid,
+                 final_flush_outcome);
+    if (!weak_this || !weak_consumer)
+      return;
+    base::Status result =
+        weak_this->DoCloneSession(&*weak_consumer, tsid, final_flush_outcome);
+    weak_consumer->consumer_->OnSessionCloned(result.ok(), result.message());
+  });
+}
+
+base::Status TracingServiceImpl::DoCloneSession(ConsumerEndpointImpl* consumer,
+                                                TracingSessionID src_tsid,
+                                                bool final_flush_outcome) {
+  PERFETTO_DLOG("CloneSession(%" PRIu64 ") started, consumer uid: %d", src_tsid,
+                static_cast<int>(consumer->uid_));
+  TracingSession* src = GetTracingSession(src_tsid);
+
+  // The session might be gone by the time we try to clone it.
+  if (!src)
+    return PERFETTO_SVC_ERR("session not found");
+
+  if (consumer->tracing_session_id_) {
+    return PERFETTO_SVC_ERR(
+        "The consumer is already attached to another tracing session");
+  }
+
+  if (src->consumer_uid != consumer->uid_ && consumer->uid_ != 0)
+    return PERFETTO_SVC_ERR("Not allowed to clone a session from another UID");
+
+  // First clone all TraceBuffer(s). This can fail because of ENOMEM. If it
+  // happens bail out early before creating any session.
+  std::vector<std::pair<BufferID, std::unique_ptr<TraceBuffer>>> buf_snaps;
+  buf_snaps.reserve(src->num_buffers());
+  bool buf_clone_failed = false;
+  for (BufferID src_buf_id : src->buffers_index) {
+    TraceBuffer* src_buf = GetBufferByID(src_buf_id);
+    std::unique_ptr<TraceBuffer> buf_snap = src_buf->CloneReadOnly();
+    BufferID buf_global_id = buffer_ids_.Allocate();
+    buf_clone_failed |= !buf_snap.get() || !buf_global_id;
+    buf_snaps.emplace_back(buf_global_id, std::move(buf_snap));
+  }
+
+  // Free up allocated IDs in case of failure. No need to free the TraceBuffers,
+  // as they are still owned by the temporary |buf_snaps|.
+  if (buf_clone_failed) {
+    for (auto& kv : buf_snaps) {
+      if (kv.first)
+        buffer_ids_.Free(kv.first);
+    }
+    return PERFETTO_SVC_ERR("Buffer allocation failed");
+  }
+
+  const TracingSessionID tsid = ++last_tracing_session_id_;
+  TracingSession* cloned_session =
+      &tracing_sessions_
+           .emplace(
+               std::piecewise_construct, std::forward_as_tuple(tsid),
+               std::forward_as_tuple(tsid, consumer, src->config, task_runner_))
+           .first->second;
+
+  cloned_session->state = TracingSession::CLONED_READ_ONLY;
+  cloned_session->trace_uuid = base::Uuidv4();  // Generate a new UUID.
+
+  for (auto& kv : buf_snaps) {
+    BufferID buf_global_id = kv.first;
+    std::unique_ptr<TraceBuffer>& buf = kv.second;
+    buffers_.emplace(buf_global_id, std::move(buf));
+    cloned_session->buffers_index.emplace_back(buf_global_id);
+  }
+  UpdateMemoryGuardrail();
+
+  // Copy over relevant state that we want to persist in the cloned session.
+  // Mostly stats and metadata that is emitted in the trace file by the service.
+  cloned_session->received_triggers = src->received_triggers;
+  cloned_session->lifecycle_events =
+      std::vector<TracingSession::LifecycleEvent>(src->lifecycle_events);
+  cloned_session->initial_clock_snapshot = src->initial_clock_snapshot;
+  cloned_session->clock_snapshot_ring_buffer = src->clock_snapshot_ring_buffer;
+  cloned_session->invalid_packets = src->invalid_packets;
+  cloned_session->flushes_requested = src->flushes_requested;
+  cloned_session->flushes_succeeded = src->flushes_succeeded;
+  cloned_session->flushes_failed = src->flushes_failed;
+  if (src->trace_filter) {
+    // Copy the trace filter.
+    cloned_session->trace_filter.reset(
+        new protozero::MessageFilter(*src->trace_filter));
+  }
+
+  PERFETTO_DLOG("Consumer (uid:%d) cloned tracing session %" PRIu64
+                " -> %" PRIu64,
+                static_cast<int>(consumer->uid_), src_tsid, tsid);
+
+  consumer->tracing_session_id_ = tsid;
+  cloned_session->final_flush_outcome = final_flush_outcome
+                                            ? TraceStats::FINAL_FLUSH_SUCCEEDED
+                                            : TraceStats::FINAL_FLUSH_FAILED;
+  return base::OkStatus();
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // TracingServiceImpl::ConsumerEndpointImpl implementation
 ////////////////////////////////////////////////////////////////////////////////
@@ -3745,6 +3904,9 @@
       case TracingSession::State::DISABLING_WAITING_STOP_ACKS:
         session->set_state("STOP_WAIT");
         break;
+      case TracingSession::State::CLONED_READ_ONLY:
+        session->set_state("CLONED_READ_ONLY");
+        break;
     }
   }
   callback(/*success=*/true, svc_state);
@@ -3783,6 +3945,13 @@
   }
 }
 
+void TracingServiceImpl::ConsumerEndpointImpl::CloneSession(
+    TracingSessionID tsid) {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  // FlushAndCloneSession will call OnSessionCloned after the async flush.
+  service_->FlushAndCloneSession(this, tsid);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // TracingServiceImpl::ProducerEndpointImpl implementation
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/src/tracing/core/tracing_service_impl.h b/src/tracing/core/tracing_service_impl.h
index b3a97f2..77bdb6b 100644
--- a/src/tracing/core/tracing_service_impl.h
+++ b/src/tracing/core/tracing_service_impl.h
@@ -33,6 +33,7 @@
 #include "perfetto/ext/base/circular_queue.h"
 #include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/periodic_task.h"
+#include "perfetto/ext/base/uuid.h"
 #include "perfetto/ext/base/weak_ptr.h"
 #include "perfetto/ext/tracing/core/basic_types.h"
 #include "perfetto/ext/tracing/core/commit_data_request.h"
@@ -224,12 +225,17 @@
     void QueryServiceState(QueryServiceStateCallback) override;
     void QueryCapabilities(QueryCapabilitiesCallback) override;
     void SaveTraceForBugreport(SaveTraceForBugreportCallback) override;
+    void CloneSession(TracingSessionID) override;
 
     // Will queue a task to notify the consumer about the state change.
     void OnDataSourceInstanceStateChange(const ProducerEndpointImpl&,
                                          const DataSourceInstance&);
     void OnAllDataSourcesStarted();
 
+    base::WeakPtr<ConsumerEndpointImpl> GetWeakPtr() {
+      return weak_ptr_factory_.GetWeakPtr();
+    }
+
    private:
     friend class TracingServiceImpl;
     ConsumerEndpointImpl(const ConsumerEndpointImpl&) = delete;
@@ -299,6 +305,7 @@
              uint32_t timeout_ms,
              ConsumerEndpoint::FlushCallback);
   void FlushAndDisableTracing(TracingSessionID);
+  void FlushAndCloneSession(ConsumerEndpointImpl*, TracingSessionID);
 
   // Starts reading the internal tracing buffers from the tracing session `tsid`
   // and sends them to `*consumer` (which must be != nullptr).
@@ -421,7 +428,8 @@
       DISABLED = 0,
       CONFIGURED,
       STARTED,
-      DISABLING_WAITING_STOP_ACKS
+      DISABLING_WAITING_STOP_ACKS,
+      CLONED_READ_ONLY,
     };
 
     TracingSession(TracingSessionID,
@@ -639,6 +647,17 @@
     uint64_t filter_input_bytes = 0;
     uint64_t filter_output_bytes = 0;
     uint64_t filter_errors = 0;
+
+    // A randomly generated trace identifier. Note that this does NOT always
+    // match the requested TraceConfig.trace_uuid_msb/lsb. Spcifically, it does
+    // until a gap-less snapshot is requested. Each snapshot re-generates the
+    // uuid to avoid emitting two different traces with the same uuid.
+    base::Uuid trace_uuid;
+
+    // NOTE: when adding new fields here consider whether that state should be
+    // copied over in DoCloneSession() or not. Ask yourself: is this a
+    // "runtime state" (e.g. active data sources) or a "trace (meta)data state"?
+    // If the latter, it should be handled by DoCloneSession()).
   };
 
   TracingServiceImpl(const TracingServiceImpl&) = delete;
@@ -685,7 +704,7 @@
   TraceStats GetTraceStats(TracingSession*);
   void EmitLifecycleEvents(TracingSession*, std::vector<TracePacket>*);
   void EmitSeizedForBugreportLifecycleEvent(std::vector<TracePacket>*);
-  void MaybeEmitTraceConfig(TracingSession*, std::vector<TracePacket>*);
+  void MaybeEmitUuidAndTraceConfig(TracingSession*, std::vector<TracePacket>*);
   void MaybeEmitSystemInfo(TracingSession*, std::vector<TracePacket>*);
   void MaybeEmitReceivedTriggers(TracingSession*, std::vector<TracePacket>*);
   void MaybeNotifyAllDataSourcesStarted(TracingSession*);
@@ -700,6 +719,9 @@
   void ScrapeSharedMemoryBuffers(TracingSession*, ProducerEndpointImpl*);
   void PeriodicClearIncrementalStateTask(TracingSessionID, bool post_next_only);
   TraceBuffer* GetBufferByID(BufferID);
+  base::Status DoCloneSession(ConsumerEndpointImpl*,
+                              TracingSessionID,
+                              bool final_flush_outcome);
 
   // Returns true if `*tracing_session` is waiting for a trigger that hasn't
   // happened.
@@ -729,6 +751,7 @@
                      std::vector<TracePacket> packets);
   void OnStartTriggersTimeout(TracingSessionID tsid);
   void MaybeLogUploadEvent(const TraceConfig&,
+                           const base::Uuid&,
                            PerfettoStatsdAtom atom,
                            const std::string& trigger_name = "");
   void MaybeLogTriggerEvent(const TraceConfig&,
diff --git a/src/tracing/core/tracing_service_impl_unittest.cc b/src/tracing/core/tracing_service_impl_unittest.cc
index 6ef6088..3a12a75 100644
--- a/src/tracing/core/tracing_service_impl_unittest.cc
+++ b/src/tracing/core/tracing_service_impl_unittest.cc
@@ -46,6 +46,7 @@
 #include "protos/perfetto/trace/trace.gen.h"
 #include "protos/perfetto/trace/trace_packet.gen.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "protos/perfetto/trace/trace_uuid.gen.h"
 #include "protos/perfetto/trace/trigger.gen.h"
 
 using ::testing::_;
@@ -53,6 +54,7 @@
 using ::testing::AssertionResult;
 using ::testing::AssertionSuccess;
 using ::testing::Contains;
+using ::testing::Each;
 using ::testing::ElementsAreArray;
 using ::testing::Eq;
 using ::testing::ExplainMatchResult;
@@ -61,6 +63,7 @@
 using ::testing::InvokeWithoutArgs;
 using ::testing::IsEmpty;
 using ::testing::Mock;
+using ::testing::Ne;
 using ::testing::Not;
 using ::testing::Property;
 using ::testing::StrictMock;
@@ -1632,12 +1635,13 @@
   // Trace start clock snapshot
   // Trace most recent clock snapshot
   // Trace synchronisation
+  // TraceUuid
   // Config
   // SystemInfo
   // Tracing started (TracingServiceEvent)
   // All data source started (TracingServiceEvent)
   // Tracing disabled (TracingServiceEvent)
-  static const int kNumPreamblePackets = 8;
+  static const int kNumPreamblePackets = 9;
   static const int kNumTestPackets = 9;
   static const char kPayload[] = "1234567890abcdef-";
 
@@ -3866,4 +3870,160 @@
   EXPECT_EQ(producer->endpoint()->shared_memory(), nullptr);
 }
 
+// If the consumer specifies a UUID in the TraceConfig, the TraceUuid packet
+// must match that.
+TEST_F(TracingServiceImplTest, UuidPacketMatchesConfigUuid) {
+  std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
+  consumer->Connect(svc.get());
+  TraceConfig trace_config;
+  trace_config.set_trace_uuid_lsb(1);
+  trace_config.set_trace_uuid_msb(2);
+  trace_config.add_buffers()->set_size_kb(8);
+  auto* ds_config = trace_config.add_data_sources()->mutable_config();
+  ds_config->set_name("data_source");
+
+  consumer->EnableTracing(trace_config);
+  consumer->DisableTracing();
+  consumer->WaitForTracingDisabled();
+
+  auto packets = consumer->ReadBuffers();
+
+  EXPECT_THAT(
+      packets,
+      Contains(Property(&protos::gen::TracePacket::trace_uuid,
+                        AllOf(Property(&protos::gen::TraceUuid::lsb, Eq(1)),
+                              Property(&protos::gen::TraceUuid::msb, Eq(2))))));
+}
+
+// If the consumer does not specify any UUID in the TraceConfig, a random
+// UUID must be generated and reported in the TraceUuid packet.
+TEST_F(TracingServiceImplTest, RandomUuidIfNoConfigUuid) {
+  std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
+  consumer->Connect(svc.get());
+  TraceConfig trace_config;
+  trace_config.add_buffers()->set_size_kb(8);
+  auto* ds_config = trace_config.add_data_sources()->mutable_config();
+  ds_config->set_name("data_source");
+
+  consumer->EnableTracing(trace_config);
+  consumer->DisableTracing();
+  consumer->WaitForTracingDisabled();
+
+  auto packets = consumer->ReadBuffers();
+
+  EXPECT_THAT(packets,
+              Contains(Property(
+                  &protos::gen::TracePacket::trace_uuid,
+                  Not(AnyOf(Property(&protos::gen::TraceUuid::lsb, Eq(0)),
+                            Property(&protos::gen::TraceUuid::msb, Eq(0)))))));
+}
+
+TEST_F(TracingServiceImplTest, CloneSession) {
+  // The consumer the creates the initial tracing session.
+  std::unique_ptr<MockConsumer> consumer = CreateMockConsumer();
+  consumer->Connect(svc.get());
+
+  // The consumer that clones it and reads back the data.
+  std::unique_ptr<MockConsumer> consumer2 = CreateMockConsumer();
+  consumer2->Connect(svc.get());
+
+  std::unique_ptr<MockProducer> producer = CreateMockProducer();
+  producer->Connect(svc.get(), "mock_producer");
+
+  // Create two data sources, as we'll write on two distinct buffers.
+  producer->RegisterDataSource("ds_1");
+  producer->RegisterDataSource("ds_2");
+
+  TraceConfig trace_config;
+  trace_config.add_buffers()->set_size_kb(32);  // Buf 0.
+  trace_config.add_buffers()->set_size_kb(32);  // Buf 1.
+  auto* ds_cfg = trace_config.add_data_sources()->mutable_config();
+  ds_cfg->set_name("ds_1");
+  ds_cfg->set_target_buffer(0);
+  ds_cfg = trace_config.add_data_sources()->mutable_config();
+  ds_cfg->set_name("ds_2");
+  ds_cfg->set_target_buffer(1);
+
+  // Add a filter and check that the filter is propagated to the cloned session.
+  // The filter allows the `for_testing` field but not the root `timestamp`.
+  protozero::FilterBytecodeGenerator filt;
+  // Message 0: root Trace proto.
+  filt.AddNestedField(1 /* root trace.packet*/, 1);
+  filt.EndMessage();
+  // Message 1: TracePacket proto. Allow only the `for_testing` sub-field.
+  filt.AddSimpleField(protos::pbzero::TracePacket::kForTestingFieldNumber);
+  filt.EndMessage();
+  trace_config.mutable_trace_filter()->set_bytecode(filt.Serialize());
+
+  consumer->EnableTracing(trace_config);
+  producer->WaitForTracingSetup();
+
+  producer->WaitForDataSourceSetup("ds_1");
+  producer->WaitForDataSourceSetup("ds_2");
+
+  producer->WaitForDataSourceStart("ds_1");
+  producer->WaitForDataSourceStart("ds_2");
+
+  std::unique_ptr<TraceWriter> writers[] = {
+      producer->CreateTraceWriter("ds_1"),
+      producer->CreateTraceWriter("ds_2"),
+  };
+
+  // Add some data to both buffers.
+  static constexpr size_t kNumTestPackets = 20;
+  for (size_t i = 0; i < kNumTestPackets; i++) {
+    auto tp = writers[i % 1]->NewTracePacket();
+    std::string payload("payload" + std::to_string(i));
+    tp->set_for_testing()->set_str(payload.c_str(), payload.size());
+    tp->set_timestamp(static_cast<uint64_t>(i));
+  }
+
+  auto clone_done = task_runner.CreateCheckpoint("clone_done");
+  EXPECT_CALL(*consumer2, OnSessionCloned(true, ""))
+      .WillOnce(InvokeWithoutArgs(clone_done));
+  consumer2->CloneSession(1);
+  // CloneSession() will implicitly issue a flush. Linearize with that.
+  producer->WaitForFlush({writers[0].get(), writers[1].get()});
+  task_runner.RunUntilCheckpoint("clone_done");
+
+  // Overwrite the ring buffer of the original session to check that clone
+  // actually returns a copy.
+  for (size_t i = 0; i < 1000; i++) {
+    auto tp = writers[i % 2]->NewTracePacket();
+    std::string payload(1000u, 'x');
+    tp->set_for_testing()->set_str(payload.c_str(), payload.size());
+  }
+
+  auto flush_request = consumer->Flush();
+  producer->WaitForFlush({writers[0].get(), writers[1].get()});
+  ASSERT_TRUE(flush_request.WaitForReply());
+
+  // Delete the initial tracing session.
+  consumer->DisableTracing();
+  consumer->FreeBuffers();
+  producer->WaitForDataSourceStop("ds_1");
+  producer->WaitForDataSourceStop("ds_2");
+  consumer->WaitForTracingDisabled();
+
+  // Read back the cloned trace and check the contents.
+  auto packets = consumer2->ReadBuffers();
+  for (size_t i = 0; i < kNumTestPackets; i++) {
+    std::string payload = "payload" + std::to_string(i);
+    EXPECT_THAT(packets,
+                Contains(Property(
+                    &protos::gen::TracePacket::for_testing,
+                    Property(&protos::gen::TestEvent::str, Eq(payload)))));
+  }
+
+  // Check that the "x" payload written after cloning the session is not there.
+  EXPECT_THAT(packets,
+              Not(Contains(Property(&protos::gen::TracePacket::for_testing,
+                                    Property(&protos::gen::TestEvent::str,
+                                             testing::StartsWith("x"))))));
+
+  // Check that the `timestamp` field is filtered out.
+  EXPECT_THAT(packets,
+              Each(Property(&protos::gen::TracePacket::has_timestamp, false)));
+}
+
 }  // namespace perfetto
diff --git a/src/tracing/core/virtual_destructors.cc b/src/tracing/core/virtual_destructors.cc
index 3bcedce..5b7534f 100644
--- a/src/tracing/core/virtual_destructors.cc
+++ b/src/tracing/core/virtual_destructors.cc
@@ -36,7 +36,13 @@
 SharedMemory::Factory::~Factory() = default;
 SharedMemoryArbiter::~SharedMemoryArbiter() = default;
 
+// TODO(primiano): make pure virtual after various 3way patches.
+void ConsumerEndpoint::CloneSession(TracingSessionID) {}
+void Consumer::OnSessionCloned(bool, const std::string&) {}
+
+#if !PERFETTO_IS_AT_LEAST_CPP17()
 constexpr size_t TracingService::kDefaultShmSize;
 constexpr size_t TracingService::kDefaultShmPageSize;
+#endif
 
 }  // namespace perfetto
diff --git a/src/tracing/event_context.cc b/src/tracing/event_context.cc
index b73aee8..35981de 100644
--- a/src/tracing/event_context.cc
+++ b/src/tracing/event_context.cc
@@ -61,4 +61,11 @@
   return annotation;
 }
 
+protos::pbzero::DebugAnnotation* EventContext::AddDebugAnnotation(
+    ::perfetto::DynamicString name) {
+  auto annotation = event()->add_debug_annotations();
+  annotation->set_name(name.value);
+  return annotation;
+}
+
 }  // namespace perfetto
diff --git a/src/tracing/internal/system_tracing_backend.cc b/src/tracing/internal/system_tracing_backend.cc
index b9da7fe..824311d 100644
--- a/src/tracing/internal/system_tracing_backend.cc
+++ b/src/tracing/internal/system_tracing_backend.cc
@@ -19,24 +19,25 @@
 #include "perfetto/base/logging.h"
 #include "perfetto/base/task_runner.h"
 #include "perfetto/ext/tracing/core/tracing_service.h"
-#include "perfetto/ext/tracing/ipc/consumer_ipc_client.h"
 #include "perfetto/ext/tracing/ipc/default_socket.h"
 #include "perfetto/ext/tracing/ipc/producer_ipc_client.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_SYSTEM_CONSUMER)
+#include "perfetto/ext/tracing/ipc/consumer_ipc_client.h"
+#endif
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include "src/tracing/ipc/shared_memory_windows.h"
+#else
 #include "src/tracing/ipc/posix_shared_memory.h"
+#endif
 
 namespace perfetto {
 namespace internal {
+namespace {
 
-// static
-TracingBackend* SystemTracingBackend::GetInstance() {
-  static auto* instance = new SystemTracingBackend();
-  return instance;
-}
-
-SystemTracingBackend::SystemTracingBackend() {}
-
-std::unique_ptr<ProducerEndpoint> SystemTracingBackend::ConnectProducer(
-    const ConnectProducerArgs& args) {
+std::unique_ptr<ProducerEndpoint> CreateProducerEndpoint(
+    const TracingBackend::ConnectProducerArgs& args) {
   PERFETTO_DCHECK(args.task_runner->RunsTasksOnCurrentThread());
 
   std::unique_ptr<SharedMemory> shm;
@@ -48,7 +49,11 @@
       shmem_size_hint = TracingService::kDefaultShmSize;
     if (shmem_page_size_hint == 0)
       shmem_page_size_hint = TracingService::kDefaultShmPageSize;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+    shm = SharedMemoryWindows::Create(shmem_size_hint);
+#else
     shm = PosixSharedMemory::Create(shmem_size_hint);
+#endif
     arbiter = SharedMemoryArbiter::CreateUnboundInstance(shm.get(),
                                                          shmem_page_size_hint);
   }
@@ -62,12 +67,57 @@
   return endpoint;
 }
 
+}  // namespace
+
+// static
+TracingBackend* SystemTracingBackend::GetInstance() {
+  static auto* instance = new SystemTracingBackend();
+  return instance;
+}
+
+SystemTracingBackend::SystemTracingBackend() {}
+
+std::unique_ptr<ProducerEndpoint> SystemTracingBackend::ConnectProducer(
+    const ConnectProducerArgs& args) {
+  return CreateProducerEndpoint(args);
+}
+
 std::unique_ptr<ConsumerEndpoint> SystemTracingBackend::ConnectConsumer(
     const ConnectConsumerArgs& args) {
+#if PERFETTO_BUILDFLAG(PERFETTO_SYSTEM_CONSUMER)
   auto endpoint = ConsumerIPCClient::Connect(GetConsumerSocket(), args.consumer,
                                              args.task_runner);
   PERFETTO_CHECK(endpoint);
   return endpoint;
+#else
+  base::ignore_result(args);
+  PERFETTO_FATAL("System backend consumer support disabled");
+  return nullptr;
+#endif
+}
+
+// static
+TracingBackend* SystemTracingProducerOnlyBackend::GetInstance() {
+  static auto* instance = new SystemTracingProducerOnlyBackend();
+  return instance;
+}
+
+SystemTracingProducerOnlyBackend::SystemTracingProducerOnlyBackend() {}
+
+std::unique_ptr<ProducerEndpoint>
+SystemTracingProducerOnlyBackend::ConnectProducer(
+    const ConnectProducerArgs& args) {
+  return CreateProducerEndpoint(args);
+}
+
+std::unique_ptr<ConsumerEndpoint>
+SystemTracingProducerOnlyBackend::ConnectConsumer(
+    const ConnectConsumerArgs& args) {
+  base::ignore_result(args);
+  PERFETTO_FATAL(
+      "System backend consumer support disabled. "
+      "TracingInitArgs::enable_system_consumer was false");
+  return nullptr;
 }
 
 }  // namespace internal
diff --git a/src/tracing/internal/system_tracing_backend_fake.cc b/src/tracing/internal/system_tracing_backend_fake.cc
index 9ca90d0..ea992a1 100644
--- a/src/tracing/internal/system_tracing_backend_fake.cc
+++ b/src/tracing/internal/system_tracing_backend_fake.cc
@@ -27,5 +27,11 @@
   return nullptr;
 }
 
+// static
+TracingBackend* SystemTracingProducerOnlyBackend::GetInstance() {
+  PERFETTO_FATAL("System tracing not implemented");
+  return nullptr;
+}
+
 }  // namespace internal
 }  // namespace perfetto
diff --git a/src/tracing/internal/tracing_backend_fake.cc b/src/tracing/internal/tracing_backend_fake.cc
index 6b2fc5f..4b0d31e 100644
--- a/src/tracing/internal/tracing_backend_fake.cc
+++ b/src/tracing/internal/tracing_backend_fake.cc
@@ -131,6 +131,7 @@
   void QueryCapabilities(QueryCapabilitiesCallback) override {}
 
   void SaveTraceForBugreport(SaveTraceForBugreportCallback) override {}
+  void CloneSession(TracingSessionID) override {}
 
  private:
   Consumer* const consumer_;
diff --git a/src/tracing/internal/tracing_muxer_fake.cc b/src/tracing/internal/tracing_muxer_fake.cc
index a6700f3..59c3d5b 100644
--- a/src/tracing/internal/tracing_muxer_fake.cc
+++ b/src/tracing/internal/tracing_muxer_fake.cc
@@ -55,6 +55,7 @@
 
 bool TracingMuxerFake::RegisterDataSource(const DataSourceDescriptor&,
                                           DataSourceFactory,
+                                          DataSourceParams,
                                           DataSourceStaticState*) {
   FailUninitialized();
 }
@@ -85,5 +86,10 @@
   FailUninitialized();
 }
 
+void TracingMuxerFake::ActivateTriggers(const std::vector<std::string>&,
+                                        uint32_t) {
+  FailUninitialized();
+}
+
 }  // namespace internal
 }  // namespace perfetto
diff --git a/src/tracing/internal/tracing_muxer_fake.h b/src/tracing/internal/tracing_muxer_fake.h
index 78fc769..f541d82 100644
--- a/src/tracing/internal/tracing_muxer_fake.h
+++ b/src/tracing/internal/tracing_muxer_fake.h
@@ -54,6 +54,7 @@
   // TracingMuxer implementation.
   bool RegisterDataSource(const DataSourceDescriptor&,
                           DataSourceFactory,
+                          DataSourceParams,
                           DataSourceStaticState*) override;
   void UpdateDataSourceDescriptor(const DataSourceDescriptor&,
                                   const DataSourceStaticState*) override;
@@ -67,6 +68,7 @@
                            InterceptorFactory,
                            InterceptorBase::TLSFactory,
                            InterceptorBase::TracePacketCallback) override;
+  void ActivateTriggers(const std::vector<std::string>&, uint32_t) override;
 
  private:
   static TracingMuxerFake instance;
diff --git a/src/tracing/internal/tracing_muxer_impl.cc b/src/tracing/internal/tracing_muxer_impl.cc
index 5c9f684..463d0d4 100644
--- a/src/tracing/internal/tracing_muxer_impl.cc
+++ b/src/tracing/internal/tracing_muxer_impl.cc
@@ -46,6 +46,7 @@
 #include "src/tracing/core/null_trace_writer.h"
 #include "src/tracing/internal/tracing_muxer_fake.h"
 
+#include "protos/perfetto/config/chrome/chrome_config.gen.h"
 #include "protos/perfetto/config/interceptor_config.gen.h"
 
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
@@ -125,7 +126,7 @@
 };
 
 uint64_t ComputeConfigHash(const DataSourceConfig& config) {
-  base::Hash hasher;
+  base::Hasher hasher;
   std::string config_bytes = config.SerializeAsString();
   hasher.Update(config_bytes.data(), config_bytes.size());
   return hasher.digest();
@@ -144,7 +145,20 @@
   config.set_trace_duration_ms(0);
   config.set_stop_timeout_ms(0);
   config.set_enable_extra_guardrails(false);
-  base::Hash hasher;
+  // Clear some fields inside Chrome config:
+  // * client_priority, because Chrome always sets the priority to
+  // USER_INITIATED when setting up startup tracing.
+  // * convert_to_legacy_json, because Telemetry initiates tracing with proto
+  // format, but in some cases adopts the tracing session later via devtools
+  // which expect json format.
+  // TODO(khokhlov): Don't clear client_priority when Chrome correctly sets it
+  // for startup tracing (and propagates it to all child processes).
+  if (config.has_chrome_config()) {
+    config.mutable_chrome_config()->set_client_priority(
+        perfetto::protos::gen::ChromeConfig::UNKNOWN);
+    config.mutable_chrome_config()->set_convert_to_legacy_json(false);
+  }
+  base::Hasher hasher;
   std::string config_bytes = config.SerializeAsString();
   hasher.Update(config_bytes.data(), config_bytes.size());
   return hasher.digest();
@@ -211,6 +225,7 @@
   }
   connected_ = true;
   muxer_->UpdateDataSourcesOnAllBackends();
+  SendOnConnectTriggers();
 }
 
 void TracingMuxerImpl::ProducerImpl::OnDisconnect() {
@@ -317,6 +332,22 @@
   return dead_services_.empty();
 }
 
+void TracingMuxerImpl::ProducerImpl::SendOnConnectTriggers() {
+  PERFETTO_DCHECK_THREAD(thread_checker_);
+  base::TimeMillis now = base::GetWallTimeMs();
+  std::vector<std::string> triggers;
+  while (!on_connect_triggers_.empty()) {
+    // Skip if we passed TTL.
+    if (on_connect_triggers_.front().second > now) {
+      triggers.push_back(std::move(on_connect_triggers_.front().first));
+    }
+    on_connect_triggers_.pop_front();
+  }
+  if (!triggers.empty()) {
+    service_->ActivateTriggers(triggers);
+  }
+}
+
 // ----- End of TracingMuxerImpl::ProducerImpl methods.
 
 // ----- Begin of TracingMuxerImpl::ConsumerImpl
@@ -536,6 +567,13 @@
   }
 }
 
+void TracingMuxerImpl::ConsumerImpl::OnSessionCloned(
+    bool /*success*/,
+    const std::string& /*error*/) {
+  // CloneSession is not exposed in the SDK. This should never happen.
+  PERFETTO_DCHECK(false);
+}
+
 void TracingMuxerImpl::ConsumerImpl::OnTraceStats(
     bool success,
     const TraceStats& trace_stats) {
@@ -821,6 +859,8 @@
   PERFETTO_DCHECK_THREAD(thread_checker_);  // Rebind the thread checker.
 
   policy_ = args.tracing_policy;
+  supports_multiple_data_source_instances_ =
+      args.supports_multiple_data_source_instances;
 
   auto add_backend = [this, &args](TracingBackend* backend, BackendType type) {
     if (!backend) {
@@ -835,6 +875,7 @@
     rb.backend = backend;
     rb.id = backend_id;
     rb.type = type;
+    rb.consumer_enabled = type != kSystemBackend || args.enable_system_consumer;
     rb.producer.reset(new ProducerImpl(this, backend_id,
                                        args.shmem_batch_commits_duration_ms));
     rb.producer_conn_args.producer = rb.producer.get();
@@ -877,6 +918,7 @@
 bool TracingMuxerImpl::RegisterDataSource(
     const DataSourceDescriptor& descriptor,
     DataSourceFactory factory,
+    DataSourceParams params,
     DataSourceStaticState* static_state) {
   // Ignore repeated registrations.
   if (static_state->index != kMaxDataSources)
@@ -898,16 +940,20 @@
   static_state->index = new_index;
 
   // Generate a semi-unique id for this data source.
-  base::Hash hash;
+  base::Hasher hash;
   hash.Update(reinterpret_cast<intptr_t>(static_state));
   hash.Update(base::GetWallTimeNs().count());
   static_state->id = hash.digest() ? hash.digest() : 1;
 
-  task_runner_->PostTask([this, descriptor, factory, static_state] {
+  task_runner_->PostTask([this, descriptor, factory, static_state, params] {
     data_sources_.emplace_back();
     RegisteredDataSource& rds = data_sources_.back();
     rds.descriptor = descriptor;
     rds.factory = factory;
+    rds.supports_multiple_instances =
+        supports_multiple_data_source_instances_ &&
+        params.supports_multiple_instances;
+    rds.requires_callbacks_under_lock = params.requires_callbacks_under_lock;
     rds.static_state = static_state;
 
     UpdateDataSourceOnAllBackends(rds, /*is_changed=*/false);
@@ -967,6 +1013,25 @@
       });
 }
 
+void TracingMuxerImpl::ActivateTriggers(
+    const std::vector<std::string>& triggers,
+    uint32_t ttl_ms) {
+  base::TimeMillis expire_time =
+      base::GetWallTimeMs() + base::TimeMillis(ttl_ms);
+  task_runner_->PostTask([this, triggers, expire_time] {
+    for (RegisteredBackend& backend : backends_) {
+      if (backend.producer->connected_) {
+        backend.producer->service_->ActivateTriggers(triggers);
+      } else {
+        for (const std::string& trigger : triggers) {
+          backend.producer->on_connect_triggers_.emplace_back(trigger,
+                                                              expire_time);
+        }
+      }
+    }
+  });
+}
+
 // Checks if there is any matching startup tracing data source instance for a
 // new SetupDataSource call. If so, moves the data source to this tracing
 // session (and its target buffer) and returns true, otherwise returns false.
@@ -982,6 +1047,7 @@
     DataSourceStaticState* static_state = rds.static_state;
     for (uint32_t i = 0; i < kMaxDataSourceInstances; i++) {
       auto* internal_state = static_state->TryGet(i);
+
       // TODO(eseckler): Instead of comparing config_hashes here, should we ask
       // the data source instance for a compat check of the config?
       if (internal_state &&
@@ -995,11 +1061,13 @@
                       " %s by adopting it from a startup tracing session",
                       instance_id, cfg.name().c_str());
 
+        std::lock_guard<std::recursive_mutex> lock(internal_state->lock);
         // Set the associations. The actual takeover happens in
         // StartDataSource().
         internal_state->data_source_instance_id = instance_id;
         internal_state->buffer_id =
             static_cast<internal::BufferId>(cfg.target_buffer());
+        internal_state->config_hash = ComputeConfigHash(cfg);
 
         // TODO(eseckler): Should the data souce config provided by the service
         // be allowed to specify additional interceptors / additional data
@@ -1078,6 +1146,16 @@
   PERFETTO_DCHECK_THREAD(thread_checker_);
   DataSourceStaticState& static_state = *rds.static_state;
 
+  // If any bit is set in `static_state.valid_instances` then at least one
+  // other instance of data source is running.
+  if (!rds.supports_multiple_instances &&
+      static_state.valid_instances.load(std::memory_order_acquire) != 0) {
+    PERFETTO_ELOG(
+        "Failed to setup data source because some another instance of this "
+        "data source is already active");
+    return FindDataSourceRes();
+  }
+
   for (uint32_t i = 0; i < kMaxDataSourceInstances; i++) {
     // Find a free slot.
     if (static_state.TryGet(i))
@@ -1085,7 +1163,7 @@
 
     auto* internal_state =
         reinterpret_cast<DataSourceState*>(&static_state.instances[i]);
-    std::lock_guard<std::recursive_mutex> guard(internal_state->lock);
+    std::unique_lock<std::recursive_mutex> lock(internal_state->lock);
     static_assert(
         std::is_same<decltype(internal_state->data_source_instance_id),
                      DataSourceInstanceID>::value,
@@ -1147,8 +1225,13 @@
     DataSourceBase::SetupArgs setup_args;
     setup_args.config = &cfg;
     setup_args.internal_instance_index = i;
+
+    if (!rds.requires_callbacks_under_lock)
+      lock.unlock();
     internal_state->data_source->OnSetup(setup_args);
-    return FindDataSourceRes(&static_state, internal_state, i);
+
+    return FindDataSourceRes(&static_state, internal_state, i,
+                             rds.requires_callbacks_under_lock);
   }
   PERFETTO_ELOG(
       "Maximum number of data source instances exhausted. "
@@ -1228,10 +1311,14 @@
   DataSourceBase::StartArgs start_args{};
   start_args.internal_instance_index = ds.instance_idx;
 
-  std::lock_guard<std::recursive_mutex> guard(ds.internal_state->lock);
+  std::unique_lock<std::recursive_mutex> lock(ds.internal_state->lock);
   if (ds.internal_state->interceptor)
     ds.internal_state->interceptor->OnStart({});
   ds.internal_state->trace_lambda_enabled = true;
+  PERFETTO_DCHECK(ds.internal_state->data_source != nullptr);
+
+  if (!ds.requires_callbacks_under_lock)
+    lock.unlock();
   ds.internal_state->data_source->OnStart(start_args);
 }
 
@@ -1274,9 +1361,12 @@
   };
 
   {
-    std::lock_guard<std::recursive_mutex> guard(ds.internal_state->lock);
+    std::unique_lock<std::recursive_mutex> lock(ds.internal_state->lock);
     if (ds.internal_state->interceptor)
       ds.internal_state->interceptor->OnStop({});
+
+    if (!ds.requires_callbacks_under_lock)
+      lock.unlock();
     ds.internal_state->data_source->OnStop(stop_args);
   }
 
@@ -1393,9 +1483,10 @@
 
   DataSourceBase::ClearIncrementalStateArgs clear_incremental_state_args;
   clear_incremental_state_args.internal_instance_index = ds.instance_idx;
-
   {
-    std::lock_guard<std::recursive_mutex> guard(ds.internal_state->lock);
+    std::unique_lock<std::recursive_mutex> lock;
+    if (ds.requires_callbacks_under_lock)
+      lock = std::unique_lock<std::recursive_mutex>(ds.internal_state->lock);
     ds.internal_state->data_source->WillClearIncrementalState(
         clear_incremental_state_args);
   }
@@ -1878,7 +1969,8 @@
               backend.producer->connection_id_.load(
                   std::memory_order_relaxed) &&
           internal_state->data_source_instance_id == instance_id) {
-        return FindDataSourceRes(static_state, internal_state, i);
+        return FindDataSourceRes(static_state, internal_state, i,
+                                 rds.requires_callbacks_under_lock);
       }
     }
   }
@@ -1960,6 +2052,10 @@
         continue;
       }
 
+      if (!backend.consumer_enabled) {
+        continue;
+      }
+
       TracingBackendId backend_id = backend.id;
 
       // Create the consumer now, even if we have to ask the embedder below, so
@@ -2201,7 +2297,8 @@
           // StartDataSource().
           session_it->num_aborting_data_sources++;
           StopDataSource_AsyncBeginImpl(
-              FindDataSourceRes(static_state, internal_state, i));
+              FindDataSourceRes(static_state, internal_state, i,
+                                rds.requires_callbacks_under_lock));
         }
       }
     }
@@ -2289,9 +2386,9 @@
     muxer->interceptors_.clear();
 
     for (auto& ds : muxer->data_sources_) {
-      ds.static_state->~DataSourceStaticState();
-      new (ds.static_state) DataSourceStaticState{};
+      ds.static_state->ResetForTesting();
     }
+
     muxer->data_sources_.clear();
     muxer->next_data_source_index_ = 0;
 
@@ -2346,8 +2443,10 @@
     // The task runner must be deleted outside the muxer thread. This is done by
     // `owned_task_runner` above.
     muxer->task_runner_.release();
+    auto* platform = muxer->platform_;
     delete muxer;
     instance_ = TracingMuxerFake::Get();
+    platform->Shutdown();
     shutdown_done.Notify();
   });
   shutdown_done.Wait();
diff --git a/src/tracing/internal/tracing_muxer_impl.h b/src/tracing/internal/tracing_muxer_impl.h
index bf04fd3..4d83ab9 100644
--- a/src/tracing/internal/tracing_muxer_impl.h
+++ b/src/tracing/internal/tracing_muxer_impl.h
@@ -28,6 +28,7 @@
 #include <memory>
 #include <vector>
 
+#include "perfetto/base/time.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/thread_checker.h"
 #include "perfetto/ext/tracing/core/basic_types.h"
@@ -103,6 +104,8 @@
   struct RegisteredDataSource {
     DataSourceDescriptor descriptor;
     DataSourceFactory factory{};
+    bool supports_multiple_instances = false;
+    bool requires_callbacks_under_lock = false;
     DataSourceStaticState* static_state = nullptr;
   };
 
@@ -113,6 +116,7 @@
   // TracingMuxer implementation.
   bool RegisterDataSource(const DataSourceDescriptor&,
                           DataSourceFactory,
+                          DataSourceParams,
                           DataSourceStaticState*) override;
   void UpdateDataSourceDescriptor(const DataSourceDescriptor&,
                                   const DataSourceStaticState*) override;
@@ -127,6 +131,8 @@
                            InterceptorBase::TLSFactory,
                            InterceptorBase::TracePacketCallback) override;
 
+  void ActivateTriggers(const std::vector<std::string>&, uint32_t) override;
+
   std::unique_ptr<TracingSession> CreateTracingSession(BackendType);
   std::unique_ptr<StartupTracingSession> CreateStartupTracingSession(
       const TraceConfig& config,
@@ -215,6 +221,7 @@
     void ClearIncrementalState(const DataSourceInstanceID*, size_t) override;
 
     bool SweepDeadServices();
+    void SendOnConnectTriggers();
 
     PERFETTO_THREAD_CHECKER(thread_checker_)
     TracingMuxerImpl* muxer_;
@@ -240,6 +247,10 @@
     // that no longer have any writers (see SweepDeadServices).
     std::list<std::shared_ptr<ProducerEndpoint>> dead_services_;
 
+    // Triggers that should be sent when the service connects (trigger_name,
+    // expiration).
+    std::list<std::pair<std::string, base::TimeMillis>> on_connect_triggers_;
+
     // The currently active service endpoint is maintained as an atomic shared
     // pointer so it won't get deleted from underneath threads that are creating
     // trace writers. At any given time one endpoint can be shared (and thus
@@ -278,6 +289,7 @@
     void OnAttach(bool success, const TraceConfig&) override;
     void OnTraceStats(bool success, const TraceStats&) override;
     void OnObservableEvents(const ObservableEvents&) override;
+    void OnSessionCloned(bool, const std::string&) override;
 
     void NotifyStartComplete();
     void NotifyError(const TracingError&);
@@ -427,6 +439,8 @@
     std::vector<std::unique_ptr<ConsumerImpl>> consumers;
 
     std::vector<RegisteredStartupSession> startup_sessions;
+
+    bool consumer_enabled = true;
   };
 
   void UpdateDataSourceOnAllBackends(RegisteredDataSource& rds,
@@ -442,13 +456,20 @@
 
   struct FindDataSourceRes {
     FindDataSourceRes() = default;
-    FindDataSourceRes(DataSourceStaticState* a, DataSourceState* b, uint32_t c)
-        : static_state(a), internal_state(b), instance_idx(c) {}
+    FindDataSourceRes(DataSourceStaticState* a,
+                      DataSourceState* b,
+                      uint32_t c,
+                      bool d)
+        : static_state(a),
+          internal_state(b),
+          instance_idx(c),
+          requires_callbacks_under_lock(d) {}
     explicit operator bool() const { return !!internal_state; }
 
     DataSourceStaticState* static_state = nullptr;
     DataSourceState* internal_state = nullptr;
     uint32_t instance_idx = 0;
+    bool requires_callbacks_under_lock = false;
   };
   FindDataSourceRes FindDataSource(TracingBackendId, DataSourceInstanceID);
 
@@ -476,6 +497,9 @@
   std::vector<RegisteredInterceptor> interceptors_;
   TracingPolicy* policy_ = nullptr;
 
+  // Learn more at TracingInitArgs::supports_multiple_data_source_instances
+  bool supports_multiple_data_source_instances_ = true;
+
   std::atomic<TracingSessionGlobalID> next_tracing_session_id_{};
   std::atomic<uint32_t> next_data_source_index_{};
   uint32_t muxer_id_for_testing_{};
diff --git a/src/tracing/internal/tracing_muxer_impl_integrationtest.cc b/src/tracing/internal/tracing_muxer_impl_integrationtest.cc
new file mode 100644
index 0000000..e1cfbfd
--- /dev/null
+++ b/src/tracing/internal/tracing_muxer_impl_integrationtest.cc
@@ -0,0 +1,172 @@
+#include "perfetto/tracing/tracing.h"
+
+#include <stdio.h>
+
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/thread_task_runner.h"
+#include "perfetto/ext/base/waitable_event.h"
+#include "perfetto/ext/tracing/ipc/service_ipc_host.h"
+#include "perfetto/tracing/backend_type.h"
+#include "protos/perfetto/config/trace_config.gen.h"
+#include "protos/perfetto/trace/trace.gen.h"
+#include "protos/perfetto/trace/trace_packet.gen.h"
+#include "protos/perfetto/trace/trigger.gen.h"
+#include "src/base/test/test_task_runner.h"
+#include "src/base/test/tmp_dir_tree.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace internal {
+namespace {
+
+using ::testing::NiceMock;
+using ::testing::NotNull;
+using ::testing::Property;
+
+class TracingMuxerImplIntegrationTest : public testing::Test {
+ protected:
+  // Sets the environment variable `name` to `value`. Restores it to the
+  // previous value when the test finishes.
+  void SetEnvVar(const char* name, const char* value) {
+    prev_state_.emplace();
+    EnvVar& var = prev_state_.top();
+    var.name = name;
+    const char* prev_value = getenv(name);
+    if (prev_value) {
+      var.value.emplace(prev_value);
+    }
+    base::SetEnv(name, value);
+  }
+
+  ~TracingMuxerImplIntegrationTest() {
+    perfetto::Tracing::ResetForTesting();
+    while (!prev_state_.empty()) {
+      const EnvVar& var = prev_state_.top();
+      if (var.value) {
+        base::SetEnv(var.name, *var.value);
+      } else {
+        base::UnsetEnv(var.name);
+      }
+      prev_state_.pop();
+    }
+  }
+
+  struct EnvVar {
+    const char* name;
+    base::Optional<std::string> value;
+  };
+  // Stores previous values of environment variables overridden by tests. We
+  // need to to this because some android integration tests need to talk to the
+  // real system tracing service and need the PERFETTO_PRODUCER_SOCK_NAME and
+  // PERFETTO_CONSUMER_SOCK_NAME to be set to their original value.
+  std::stack<EnvVar> prev_state_;
+};
+
+class TracingServiceThread {
+ public:
+  TracingServiceThread(const std::string& producer_socket,
+                       const std::string& consumer_socket)
+      : runner_(base::ThreadTaskRunner::CreateAndStart("perfetto.svc")),
+        producer_socket_(producer_socket),
+        consumer_socket_(consumer_socket) {
+    runner_.PostTaskAndWaitForTesting([this]() {
+      svc_ = ServiceIPCHost::CreateInstance(&runner_);
+      bool res =
+          svc_->Start(producer_socket_.c_str(), consumer_socket_.c_str());
+      if (!res) {
+        PERFETTO_FATAL("Failed to start service listening on %s and %s",
+                       producer_socket_.c_str(), consumer_socket_.c_str());
+      }
+    });
+  }
+
+  ~TracingServiceThread() {
+    runner_.PostTaskAndWaitForTesting([this]() { svc_.reset(); });
+  }
+
+  const std::string& producer_socket() const { return producer_socket_; }
+  const std::string& consumer_socket() const { return consumer_socket_; }
+
+ private:
+  base::ThreadTaskRunner runner_;
+
+  std::string producer_socket_;
+  std::string consumer_socket_;
+  std::unique_ptr<ServiceIPCHost> svc_;
+};
+
+TEST_F(TracingMuxerImplIntegrationTest, ActivateTriggers) {
+  base::TmpDirTree tmpdir_;
+
+  base::TestTaskRunner task_runner;
+
+  ASSERT_FALSE(perfetto::Tracing::IsInitialized());
+
+  tmpdir_.TrackFile("producer2.sock");
+  tmpdir_.TrackFile("consumer.sock");
+  TracingServiceThread tracing_service(tmpdir_.AbsolutePath("producer2.sock"),
+                                       tmpdir_.AbsolutePath("consumer.sock"));
+  // Instead of being a unix socket, producer.sock is a regular empty file.
+  tmpdir_.AddFile("producer.sock", "");
+
+  // Wrong producer socket: the producer won't connect yet, but the consumer
+  // will.
+  SetEnvVar("PERFETTO_PRODUCER_SOCK_NAME",
+            tmpdir_.AbsolutePath("producer.sock").c_str());
+  SetEnvVar("PERFETTO_CONSUMER_SOCK_NAME",
+            tmpdir_.AbsolutePath("consumer.sock").c_str());
+
+  TracingInitArgs args;
+  args.backends = perfetto::kSystemBackend;
+  perfetto::Tracing::Initialize(args);
+
+  // TracingMuxerImpl::ActivateTriggers will be called without the producer side
+  // of the service being connected. It should store the trigger for 10000ms.
+  perfetto::Tracing::ActivateTriggers({"trigger2", "trigger1"}, 10000);
+
+  perfetto::TraceConfig cfg;
+  cfg.add_buffers()->set_size_kb(1024);
+  perfetto::TraceConfig::TriggerConfig* tr_cfg = cfg.mutable_trigger_config();
+  tr_cfg->set_trigger_mode(perfetto::TraceConfig::TriggerConfig::STOP_TRACING);
+  tr_cfg->set_trigger_timeout_ms(10000);
+  perfetto::TraceConfig::TriggerConfig::Trigger* trigger =
+      tr_cfg->add_triggers();
+  trigger->set_name("trigger1");
+
+  std::unique_ptr<TracingSession> session =
+      perfetto::Tracing::NewTrace(perfetto::kSystemBackend);
+  base::WaitableEvent on_stop;
+  session->SetOnStopCallback([&on_stop] { on_stop.Notify(); });
+  session->Setup(cfg);
+
+  session->StartBlocking();
+
+  // Swap producer.sock and producer2.sock. Now the client should connect to the
+  // tracing service as a producer.
+  ASSERT_EQ(rename(tmpdir_.AbsolutePath("producer2.sock").c_str(),
+                   tmpdir_.AbsolutePath("producer3.sock").c_str()),
+            0);
+  ASSERT_EQ(rename(tmpdir_.AbsolutePath("producer.sock").c_str(),
+                   tmpdir_.AbsolutePath("producer2.sock").c_str()),
+            0);
+  ASSERT_EQ(rename(tmpdir_.AbsolutePath("producer3.sock").c_str(),
+                   tmpdir_.AbsolutePath("producer.sock").c_str()),
+            0);
+
+  on_stop.Wait();
+
+  std::vector<char> bytes = session->ReadTraceBlocking();
+  perfetto::protos::gen::Trace parsed_trace;
+  ASSERT_TRUE(parsed_trace.ParseFromArray(bytes.data(), bytes.size()));
+  EXPECT_THAT(
+      parsed_trace,
+      Property(&perfetto::protos::gen::Trace::packet,
+               Contains(Property(
+                   &perfetto::protos::gen::TracePacket::trigger,
+                   Property(&perfetto::protos::gen::Trigger::trigger_name,
+                            "trigger1")))));
+}
+
+}  // namespace
+}  // namespace internal
+}  // namespace perfetto
diff --git a/src/tracing/internal/track_event_internal.cc b/src/tracing/internal/track_event_internal.cc
index d49b737..736e2dd 100644
--- a/src/tracing/internal/track_event_internal.cc
+++ b/src/tracing/internal/track_event_internal.cc
@@ -57,18 +57,54 @@
 
 constexpr auto kClockIdAbsolute = TrackEventIncrementalState::kClockIdAbsolute;
 
-void ForEachObserver(
-    std::function<bool(TrackEventSessionObserver*&)> callback) {
-  // Session observers, shared by all track event data source instances.
-  static constexpr int kMaxObservers = 8;
-  static std::recursive_mutex* mutex = new std::recursive_mutex{};  // Leaked.
-  static std::array<TrackEventSessionObserver*, kMaxObservers> observers{};
-  std::unique_lock<std::recursive_mutex> lock(*mutex);
-  for (auto& o : observers) {
-    if (!callback(o))
-      break;
+class TrackEventSessionObserverRegistry {
+ public:
+  static TrackEventSessionObserverRegistry* GetInstance() {
+    static TrackEventSessionObserverRegistry* instance =
+        new TrackEventSessionObserverRegistry();  // leaked
+    return instance;
   }
-}
+
+  void AddObserverForRegistry(const TrackEventCategoryRegistry& registry,
+                              TrackEventSessionObserver* observer) {
+    std::unique_lock<std::recursive_mutex> lock(mutex_);
+    observers_.emplace_back(&registry, observer);
+  }
+
+  void RemoveObserverForRegistry(const TrackEventCategoryRegistry& registry,
+                                 TrackEventSessionObserver* observer) {
+    std::unique_lock<std::recursive_mutex> lock(mutex_);
+    observers_.erase(std::remove(observers_.begin(), observers_.end(),
+                                 RegisteredObserver(&registry, observer)),
+                     observers_.end());
+  }
+
+  void ForEachObserverForRegistry(
+      const TrackEventCategoryRegistry& registry,
+      std::function<void(TrackEventSessionObserver*)> callback) {
+    std::unique_lock<std::recursive_mutex> lock(mutex_);
+    for (auto& registered_observer : observers_) {
+      if (&registry == registered_observer.registry) {
+        callback(registered_observer.observer);
+      }
+    }
+  }
+
+ private:
+  struct RegisteredObserver {
+    RegisteredObserver(const TrackEventCategoryRegistry* r,
+                       TrackEventSessionObserver* o)
+        : registry(r), observer(o) {}
+    bool operator==(const RegisteredObserver& other) {
+      return registry == other.registry && observer == other.observer;
+    }
+    const TrackEventCategoryRegistry* registry;
+    TrackEventSessionObserver* observer;
+  };
+
+  std::recursive_mutex mutex_;
+  std::vector<RegisteredObserver> observers_;
+};
 
 enum class MatchType { kExact, kPattern };
 
@@ -137,31 +173,33 @@
 
 // static
 bool TrackEventInternal::AddSessionObserver(
+    const TrackEventCategoryRegistry& registry,
     TrackEventSessionObserver* observer) {
-  bool result = false;
-  ForEachObserver([&](TrackEventSessionObserver*& o) {
-    if (!o) {
-      o = observer;
-      result = true;
-      return false;
-    }
-    return true;
-  });
-  return result;
+  TrackEventSessionObserverRegistry::GetInstance()->AddObserverForRegistry(
+      registry, observer);
+  return true;
 }
 
 // static
 void TrackEventInternal::RemoveSessionObserver(
+    const TrackEventCategoryRegistry& registry,
     TrackEventSessionObserver* observer) {
-  ForEachObserver([&](TrackEventSessionObserver*& o) {
-    if (o == observer) {
-      o = nullptr;
-      return false;
-    }
-    return true;
-  });
+  TrackEventSessionObserverRegistry::GetInstance()->RemoveObserverForRegistry(
+      registry, observer);
 }
 
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) && \
+    !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+static constexpr protos::pbzero::BuiltinClock kDefaultTraceClock =
+    protos::pbzero::BUILTIN_CLOCK_BOOTTIME;
+#else
+static constexpr protos::pbzero::BuiltinClock kDefaultTraceClock =
+    protos::pbzero::BUILTIN_CLOCK_MONOTONIC;
+#endif
+
+// static
+protos::pbzero::BuiltinClock TrackEventInternal::clock_ = kDefaultTraceClock;
+
 // static
 void TrackEventInternal::EnableTracing(
     const TrackEventCategoryRegistry& registry,
@@ -171,44 +209,41 @@
     if (IsCategoryEnabled(registry, config, *registry.GetCategory(i)))
       registry.EnableCategoryForInstance(i, args.internal_instance_index);
   }
-  ForEachObserver([&](TrackEventSessionObserver*& o) {
-    if (o)
-      o->OnSetup(args);
-    return true;
-  });
+  TrackEventSessionObserverRegistry::GetInstance()->ForEachObserverForRegistry(
+      registry, [&](TrackEventSessionObserver* o) { o->OnSetup(args); });
 }
 
 // static
-void TrackEventInternal::OnStart(const DataSourceBase::StartArgs& args) {
+void TrackEventInternal::OnStart(const TrackEventCategoryRegistry& registry,
+                                 const DataSourceBase::StartArgs& args) {
   session_count_.fetch_add(1);
-  ForEachObserver([&](TrackEventSessionObserver*& o) {
-    if (o)
-      o->OnStart(args);
-    return true;
-  });
+  TrackEventSessionObserverRegistry::GetInstance()->ForEachObserverForRegistry(
+      registry, [&](TrackEventSessionObserver* o) { o->OnStart(args); });
+}
+
+// static
+void TrackEventInternal::OnStop(const TrackEventCategoryRegistry& registry,
+                                const DataSourceBase::StopArgs& args) {
+  TrackEventSessionObserverRegistry::GetInstance()->ForEachObserverForRegistry(
+      registry, [&](TrackEventSessionObserver* o) { o->OnStop(args); });
 }
 
 // static
 void TrackEventInternal::DisableTracing(
     const TrackEventCategoryRegistry& registry,
-    const DataSourceBase::StopArgs& args) {
-  ForEachObserver([&](TrackEventSessionObserver*& o) {
-    if (o)
-      o->OnStop(args);
-    return true;
-  });
+    uint32_t internal_instance_index) {
   for (size_t i = 0; i < registry.category_count(); i++)
-    registry.DisableCategoryForInstance(i, args.internal_instance_index);
+    registry.DisableCategoryForInstance(i, internal_instance_index);
 }
 
 // static
 void TrackEventInternal::WillClearIncrementalState(
+    const TrackEventCategoryRegistry& registry,
     const DataSourceBase::ClearIncrementalStateArgs& args) {
-  ForEachObserver([&](TrackEventSessionObserver*& o) {
-    if (o)
-      o->WillClearIncrementalState(args);
-    return true;
-  });
+  TrackEventSessionObserverRegistry::GetInstance()->ForEachObserverForRegistry(
+      registry, [&](TrackEventSessionObserver* o) {
+        o->WillClearIncrementalState(args);
+      });
 }
 
 // static
@@ -284,6 +319,21 @@
       return true;
     }
 
+    // 2.5. A special case for Chrome's legacy disabled-by-default categories.
+    // We treat them as having a "slow" tag with one exception: they can be
+    // enabled by a pattern if the pattern starts with "disabled-by-default-"
+    // itself.
+    if (match_type == MatchType::kExact &&
+        !strncmp(category.name, kLegacySlowPrefix, strlen(kLegacySlowPrefix))) {
+      for (const auto& pattern : config.enabled_categories()) {
+        if (!strncmp(pattern.c_str(), kLegacySlowPrefix,
+                     strlen(kLegacySlowPrefix)) &&
+            NameMatchesPattern(pattern, category.name, MatchType::kPattern)) {
+          return true;
+        }
+      }
+    }
+
     // 3. Disabled categories.
     if (NameMatchesPatternList(config.disabled_categories(), category.name,
                                match_type)) {
@@ -313,8 +363,10 @@
 uint64_t TrackEventInternal::GetTimeNs() {
   if (GetClockId() == protos::pbzero::BUILTIN_CLOCK_BOOTTIME)
     return static_cast<uint64_t>(perfetto::base::GetBootTimeNs().count());
-  PERFETTO_DCHECK(GetClockId() == protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
-  return static_cast<uint64_t>(perfetto::base::GetWallTimeNs().count());
+  else if (GetClockId() == protos::pbzero::BUILTIN_CLOCK_MONOTONIC)
+    return static_cast<uint64_t>(perfetto::base::GetWallTimeNs().count());
+  PERFETTO_DCHECK(GetClockId() == protos::pbzero::BUILTIN_CLOCK_MONOTONIC_RAW);
+  return static_cast<uint64_t>(perfetto::base::GetWallTimeRawNs().count());
 }
 
 // static
@@ -334,7 +386,8 @@
     const TrackEventTlsState& tls_state,
     const TraceTimestamp& timestamp) {
   auto sequence_timestamp = timestamp;
-  if (timestamp.clock_id != TrackEventInternal::GetClockId() &&
+  if (timestamp.clock_id !=
+          static_cast<uint32_t>(TrackEventInternal::GetClockId()) &&
       timestamp.clock_id != kClockIdIncremental) {
     sequence_timestamp = TrackEventInternal::GetTraceTime();
   }
@@ -363,11 +416,11 @@
           thread_time_counter_track.uuid);
     }
 
-    if (tls_state.default_clock != GetClockId()) {
+    if (tls_state.default_clock != static_cast<uint32_t>(GetClockId())) {
       ClockSnapshot* clocks = packet->set_clock_snapshot();
       // Trace clock.
       ClockSnapshot::Clock* trace_clock = clocks->add_clocks();
-      trace_clock->set_clock_id(GetClockId());
+      trace_clock->set_clock_id(static_cast<uint32_t>(GetClockId()));
       trace_clock->set_timestamp(sequence_timestamp.value);
 
       if (PERFETTO_LIKELY(tls_state.default_clock == kClockIdIncremental)) {
@@ -426,8 +479,9 @@
       // No need to set the clock id here, since kClockIdIncremental is the
       // clock id assumed by default.
       auto time_diff_ns = timestamp.value - incr_state->last_timestamp_ns;
-      packet->set_timestamp(time_diff_ns / ts_unit_multiplier);
-      incr_state->last_timestamp_ns = timestamp.value;
+      auto time_diff_units = time_diff_ns / ts_unit_multiplier;
+      packet->set_timestamp(time_diff_units);
+      incr_state->last_timestamp_ns += time_diff_units * ts_unit_multiplier;
     } else {
       packet->set_timestamp(timestamp.value / ts_unit_multiplier);
       packet->set_timestamp_clock_id(ts_unit_multiplier == 1
@@ -515,5 +569,14 @@
   return annotation;
 }
 
+// static
+protos::pbzero::DebugAnnotation* TrackEventInternal::AddDebugAnnotation(
+    perfetto::EventContext* event_ctx,
+    perfetto::DynamicString name) {
+  auto annotation = event_ctx->event()->add_debug_annotations();
+  annotation->set_name(name.value);
+  return annotation;
+}
+
 }  // namespace internal
 }  // namespace perfetto
diff --git a/src/tracing/ipc/consumer/consumer_ipc_client_impl.cc b/src/tracing/ipc/consumer/consumer_ipc_client_impl.cc
index e8309e9..7eecd09 100644
--- a/src/tracing/ipc/consumer/consumer_ipc_client_impl.cc
+++ b/src/tracing/ipc/consumer/consumer_ipc_client_impl.cc
@@ -459,4 +459,32 @@
   consumer_port_.SaveTraceForBugreport(req, std::move(async_response));
 }
 
+void ConsumerIPCClientImpl::CloneSession(TracingSessionID tsid) {
+  if (!connected_) {
+    PERFETTO_DLOG("Cannot CloneSession(), not connected to tracing service");
+    return;
+  }
+
+  protos::gen::CloneSessionRequest req;
+  req.set_session_id(tsid);
+  ipc::Deferred<protos::gen::CloneSessionResponse> async_response;
+  auto weak_this = weak_ptr_factory_.GetWeakPtr();
+
+  async_response.Bind(
+      [weak_this](
+          ipc::AsyncResult<protos::gen::CloneSessionResponse> response) {
+        if (!weak_this)
+          return;
+        if (!response) {
+          // If the IPC fails, we are talking to an older version of the service
+          // that didn't support CloneSession at all.
+          weak_this->consumer_->OnSessionCloned(
+              false, "CloneSession IPC not supported");
+        } else {
+          weak_this->consumer_->OnSessionCloned(response->success(),
+                                                response->error());
+        }
+      });
+  consumer_port_.CloneSession(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 7d7d6fa..71f4968 100644
--- a/src/tracing/ipc/consumer/consumer_ipc_client_impl.h
+++ b/src/tracing/ipc/consumer/consumer_ipc_client_impl.h
@@ -74,6 +74,7 @@
   void QueryServiceState(QueryServiceStateCallback) override;
   void QueryCapabilities(QueryCapabilitiesCallback) override;
   void SaveTraceForBugreport(SaveTraceForBugreportCallback) override;
+  void CloneSession(TracingSessionID) override;
 
   // ipc::ServiceProxy::EventListener implementation.
   // These methods are invoked by the IPC layer, which knows nothing about
diff --git a/src/tracing/ipc/posix_shared_memory.cc b/src/tracing/ipc/posix_shared_memory.cc
index a21a290..40fa962 100644
--- a/src/tracing/ipc/posix_shared_memory.cc
+++ b/src/tracing/ipc/posix_shared_memory.cc
@@ -19,7 +19,8 @@
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
     PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
     PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) ||   \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
+    PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)
 
 #include <fcntl.h>
 #include <stdint.h>
diff --git a/src/tracing/ipc/posix_shared_memory.h b/src/tracing/ipc/posix_shared_memory.h
index 0ba1a31..d620991 100644
--- a/src/tracing/ipc/posix_shared_memory.h
+++ b/src/tracing/ipc/posix_shared_memory.h
@@ -21,8 +21,9 @@
 
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
     PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
+    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)
 
 #include <stddef.h>
 
diff --git a/src/tracing/ipc/producer/producer_ipc_client_impl.cc b/src/tracing/ipc/producer/producer_ipc_client_impl.cc
index 1980f35..95a304a 100644
--- a/src/tracing/ipc/producer/producer_ipc_client_impl.cc
+++ b/src/tracing/ipc/producer/producer_ipc_client_impl.cc
@@ -97,6 +97,8 @@
     std::unique_ptr<SharedMemoryArbiter> shm_arbiter)
     : producer_(producer),
       task_runner_(task_runner),
+      receive_shmem_fd_cb_fuchsia_(
+          std::move(conn_args.receive_shmem_fd_cb_fuchsia)),
       ipc_channel_(
           ipc::Client::CreateInstance(std::move(conn_args), task_runner)),
       producer_port_(
@@ -216,6 +218,24 @@
   producer_->OnDisconnect();  // Note: may delete |this|.
 }
 
+void ProducerIPCClientImpl::ScheduleDisconnect() {
+  // |ipc_channel| doesn't allow disconnection in the middle of handling
+  // an IPC call, so the connection drop must take place over two phases.
+
+  // First, synchronously drop the |producer_port_| so that no more IPC
+  // messages are handled.
+  producer_port_.reset();
+
+  // Then schedule an async task for performing the remainder of the
+  // disconnection operations outside the context of the IPC method handler.
+  auto weak_this = weak_factory_.GetWeakPtr();
+  task_runner_->PostTask([weak_this]() {
+    if (weak_this) {
+      weak_this->Disconnect();
+    }
+  });
+}
+
 void ProducerIPCClientImpl::OnConnectionInitialized(
     bool connection_succeeded,
     bool using_shmem_provided_by_producer,
@@ -278,6 +298,23 @@
     const std::string& shm_key = cmd.setup_tracing().shm_key_windows();
     if (!shm_key.empty())
       ipc_shared_memory = SharedMemoryWindows::Attach(shm_key);
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
+    // On Fuchsia, the embedder is responsible for routing the shared memory
+    // FD, which is provided to this code via a blocking callback.
+    PERFETTO_CHECK(receive_shmem_fd_cb_fuchsia_);
+
+    base::ScopedFile shmem_fd(receive_shmem_fd_cb_fuchsia_());
+    if (!shmem_fd) {
+      // Failure to get a shared memory buffer is a protocol violation and
+      // therefore we should drop the Protocol connection.
+      PERFETTO_ELOG("Could not get shared memory FD from embedder.");
+      ScheduleDisconnect();
+      return;
+    }
+
+    ipc_shared_memory =
+        PosixSharedMemory::AttachToFd(std::move(shmem_fd),
+                                      /*require_seals_if_supported=*/false);
 #else
     base::ScopedFile shmem_fd = ipc_channel_->TakeReceivedFD();
     if (shmem_fd) {
diff --git a/src/tracing/ipc/producer/producer_ipc_client_impl.h b/src/tracing/ipc/producer/producer_ipc_client_impl.h
index 0ead5c5..06e73ae 100644
--- a/src/tracing/ipc/producer/producer_ipc_client_impl.h
+++ b/src/tracing/ipc/producer/producer_ipc_client_impl.h
@@ -23,6 +23,7 @@
 #include <vector>
 
 #include "perfetto/ext/base/thread_checker.h"
+#include "perfetto/ext/base/weak_ptr.h"
 #include "perfetto/ext/ipc/client.h"
 #include "perfetto/ext/ipc/service_proxy.h"
 #include "perfetto/ext/tracing/core/basic_types.h"
@@ -92,6 +93,10 @@
   ipc::Client* GetClientForTesting() { return ipc_channel_.get(); }
 
  private:
+  // Drops the provider connection if a protocol error was detected while
+  // processing an IPC command.
+  void ScheduleDisconnect();
+
   // Invoked soon after having established the connection with the service.
   void OnConnectionInitialized(bool connection_succeeded,
                                bool using_shmem_provided_by_producer,
@@ -105,6 +110,9 @@
   Producer* const producer_;
   base::TaskRunner* const task_runner_;
 
+  // A callback used to receive the shmem region out of band of the socket.
+  std::function<int(void)> receive_shmem_fd_cb_fuchsia_;
+
   // The object that owns the client socket and takes care of IPC traffic.
   std::unique_ptr<ipc::Client> ipc_channel_;
 
@@ -124,6 +132,7 @@
   bool is_shmem_provided_by_producer_ = false;
   bool direct_smb_patching_supported_ = false;
   std::vector<std::function<void()>> pending_sync_reqs_;
+  base::WeakPtrFactory<ProducerIPCClientImpl> weak_factory_{this};
   PERFETTO_THREAD_CHECKER(thread_checker_)
 };
 
diff --git a/src/tracing/ipc/service/consumer_ipc_service.cc b/src/tracing/ipc/service/consumer_ipc_service.cc
index 09b33a8..2f45713 100644
--- a/src/tracing/ipc/service/consumer_ipc_service.cc
+++ b/src/tracing/ipc/service/consumer_ipc_service.cc
@@ -322,6 +322,14 @@
   remote_consumer->service_endpoint->SaveTraceForBugreport(callback);
 }
 
+void ConsumerIPCService::CloneSession(
+    const protos::gen::CloneSessionRequest& req,
+    DeferredCloneSessionResponse resp) {
+  RemoteConsumer* remote_consumer = GetConsumerForCurrentRequest();
+  remote_consumer->clone_session_response = std::move(resp);
+  remote_consumer->service_endpoint->CloneSession(req.session_id());
+}
+
 // Called by the service in response to
 // service_endpoint->SaveTraceForBugreport().
 void ConsumerIPCService::OnSaveTraceForBugreportCallback(
@@ -471,4 +479,16 @@
   observe_events_response.Resolve(std::move(result));
 }
 
+void ConsumerIPCService::RemoteConsumer::OnSessionCloned(
+    bool success,
+    const std::string& error) {
+  if (!clone_session_response.IsBound())
+    return;
+
+  auto resp = ipc::AsyncResult<protos::gen::CloneSessionResponse>::Create();
+  resp->set_success(success);
+  resp->set_error(error);
+  std::move(clone_session_response).Resolve(std::move(resp));
+}
+
 }  // namespace perfetto
diff --git a/src/tracing/ipc/service/consumer_ipc_service.h b/src/tracing/ipc/service/consumer_ipc_service.h
index e31f1c7..d570c51 100644
--- a/src/tracing/ipc/service/consumer_ipc_service.h
+++ b/src/tracing/ipc/service/consumer_ipc_service.h
@@ -71,6 +71,8 @@
                          DeferredQueryCapabilitiesResponse) override;
   void SaveTraceForBugreport(const protos::gen::SaveTraceForBugreportRequest&,
                              DeferredSaveTraceForBugreportResponse) override;
+  void CloneSession(const protos::gen::CloneSessionRequest&,
+                    DeferredCloneSessionResponse) override;
   void OnClientDisconnected() override;
 
  private:
@@ -92,6 +94,7 @@
     void OnAttach(bool, const TraceConfig&) override;
     void OnTraceStats(bool, const TraceStats&) override;
     void OnObservableEvents(const ObservableEvents&) override;
+    void OnSessionCloned(bool, const std::string&) override;
 
     void CloseObserveEventsResponseStream();
 
@@ -118,6 +121,9 @@
     // As above, but for GetTraceStats().
     DeferredGetTraceStatsResponse get_trace_stats_response;
 
+    // As above, but for CloneSession().
+    DeferredCloneSessionResponse clone_session_response;
+
     // After ObserveEvents() is invoked, this binds the async callback that
     // allows to stream ObservableEvents back to the client.
     DeferredObserveEventsResponse observe_events_response;
diff --git a/src/tracing/platform.cc b/src/tracing/platform.cc
index 6b2cd2f..05f79f4 100644
--- a/src/tracing/platform.cc
+++ b/src/tracing/platform.cc
@@ -23,6 +23,8 @@
 PlatformThreadLocalObject::~PlatformThreadLocalObject() = default;
 Platform::~Platform() = default;
 
+void Platform::Shutdown() {}
+
 // static
 std::unique_ptr<PlatformThreadLocalObject>
 PlatformThreadLocalObject::CreateInstance() {
diff --git a/src/tracing/platform_posix.cc b/src/tracing/platform_posix.cc
index cf2bcff..e2e8e95 100644
--- a/src/tracing/platform_posix.cc
+++ b/src/tracing/platform_posix.cc
@@ -18,6 +18,7 @@
 
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
     PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA) || \
     PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
 
 #include "perfetto/ext/base/file_utils.h"
@@ -43,6 +44,7 @@
   std::unique_ptr<base::TaskRunner> CreateTaskRunner(
       const CreateTaskRunnerArgs&) override;
   std::string GetCurrentProcessName() override;
+  void Shutdown() override;
 
  private:
   pthread_key_t tls_key_{};
@@ -70,10 +72,23 @@
 }
 
 PlatformPosix::~PlatformPosix() {
+  // pthread_key_delete doesn't call destructors, so do it manually for the
+  // calling thread.
+  void* tls_ptr = pthread_getspecific(tls_key_);
+  delete static_cast<ThreadLocalObject*>(tls_ptr);
+
   pthread_key_delete(tls_key_);
   g_instance = nullptr;
 }
 
+void PlatformPosix::Shutdown() {
+  PERFETTO_CHECK(g_instance == this);
+  delete this;
+  PERFETTO_CHECK(!g_instance);
+  // We're not clearing out the instance in GetDefaultPlatform() since it's not
+  // possible to re-initialize Perfetto after calling this function anyway.
+}
+
 ThreadLocalObject* PlatformPosix::GetOrCreateThreadLocalObject() {
   // In chromium this should be implemented using base::ThreadLocalStorage.
   void* tls_ptr = pthread_getspecific(tls_key_);
@@ -116,4 +131,4 @@
 }
 
 }  // namespace perfetto
-#endif  // OS_LINUX || OS_ANDROID || OS_APPLE
+#endif  // OS_LINUX || OS_ANDROID || OS_APPLE || OS_FUCHSIA
diff --git a/src/tracing/test/BUILD.gn b/src/tracing/test/BUILD.gn
index 5de4a83..51f2348 100644
--- a/src/tracing/test/BUILD.gn
+++ b/src/tracing/test/BUILD.gn
@@ -59,7 +59,7 @@
   }
 }
 
-if (enable_perfetto_ipc) {
+if (enable_perfetto_ipc && enable_perfetto_system_consumer) {
   perfetto_unittest_source_set("tracing_integration_test") {
     testonly = true
     deps = [
@@ -118,9 +118,11 @@
     ]
     sources = [
       "api_integrationtest.cc",
+      "api_integrationtest_main.cc",
       "tracing_module.cc",
       "tracing_module.h",
       "tracing_module2.cc",
+      "tracing_module3.cc",
       "tracing_module_categories.h",
     ]
   }
diff --git a/src/tracing/test/api_integrationtest.cc b/src/tracing/test/api_integrationtest.cc
index 15901b8..f0f2f5d 100644
--- a/src/tracing/test/api_integrationtest.cc
+++ b/src/tracing/test/api_integrationtest.cc
@@ -45,6 +45,7 @@
 #include "src/tracing/test/api_test_support.h"
 #include "src/tracing/test/tracing_module.h"
 
+#include "perfetto/base/time.h"
 #include "perfetto/protozero/scattered_heap_buffer.h"
 #include "perfetto/tracing/core/data_source_descriptor.h"
 #include "perfetto/tracing/core/trace_config.h"
@@ -91,6 +92,7 @@
 #include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"
 #include "protos/perfetto/trace/track_event/track_descriptor.gen.h"
 #include "protos/perfetto/trace/track_event/track_event.gen.h"
+#include "protos/perfetto/trace/trigger.gen.h"
 
 // Events in categories starting with "dynamic" will use dynamic category
 // lookup.
@@ -113,6 +115,12 @@
     perfetto::Category(TRACE_DISABLED_BY_DEFAULT("cat")));
 PERFETTO_TRACK_EVENT_STATIC_STORAGE();
 
+// Test declaring an extra set of categories in a namespace in addition to the
+// default one.
+PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE(other_ns,
+                                        perfetto::Category("other_ns"));
+PERFETTO_TRACK_EVENT_STATIC_STORAGE_IN_NAMESPACE(other_ns);
+
 // For testing interning of complex objects.
 using SourceLocation = std::tuple<const char* /* file_name */,
                                   const char* /* function_name */,
@@ -139,8 +147,8 @@
 
 // Unused in merged code, but very handy for debugging when trace generated in
 // a test needs to be exported, to understand it further with other tools.
-__attribute__((unused)) static void WriteFile(const std::string& file_name,
-                                              const std::vector<char>& data) {
+PERFETTO_UNUSED static void WriteFile(const std::string& file_name,
+                                      const std::vector<char>& data) {
   return WriteFile(file_name, data.data(), data.size());
 }
 
@@ -181,7 +189,7 @@
 struct TraceTimestampTraits<MyTimestamp> {
   static TraceTimestamp ConvertTimestampToTraceTimeNs(
       const MyTimestamp& timestamp) {
-    return {TrackEvent::GetTraceClockId(), timestamp.ts};
+    return {static_cast<uint32_t>(TrackEvent::GetTraceClockId()), timestamp.ts};
   }
 };
 
@@ -194,6 +202,7 @@
 using perfetto::internal::TrackEventInternal;
 using ::testing::_;
 using ::testing::ContainerEq;
+using ::testing::Contains;
 using ::testing::ElementsAre;
 using ::testing::HasSubstr;
 using ::testing::Invoke;
@@ -222,12 +231,8 @@
   }
 
   void Notify() {
-    {
-      std::lock_guard<std::mutex> lock(mutex_);
-      notified_ = true;
-    }
-    // Do not notify while holding the lock, because then we wake up the other
-    // end, only for it to fail to acquire the lock.
+    std::lock_guard<std::mutex> lock(mutex_);
+    notified_ = true;
     cv_.notify_one();
   }
 
@@ -291,6 +296,7 @@
   bool RegisterDataSource(
       const perfetto::DataSourceDescriptor& dsd,
       DataSourceFactory,
+      perfetto::internal::DataSourceParams,
       perfetto::internal::DataSourceStaticState* static_state) override {
     data_sources.emplace_back(DataSource{dsd, static_state});
     return true;
@@ -322,6 +328,8 @@
       perfetto::InterceptorBase::TLSFactory,
       perfetto::InterceptorBase::TracePacketCallback) override {}
 
+  void ActivateTriggers(const std::vector<std::string>&, uint32_t) override {}
+
   std::vector<DataSource> data_sources;
 
  private:
@@ -449,6 +457,218 @@
   std::set<uint64_t> seen_tracks_;
 };
 
+std::vector<std::string> ReadSlicesFromTrace(
+    const perfetto::protos::gen::Trace& parsed_trace,
+    bool expect_incremental_state_cleared = true) {
+  // Read back the trace, maintaining interning tables as we go.
+  std::vector<std::string> slices;
+  if (parsed_trace.packet().size() == 0)
+    return slices;
+  ParsedIncrementalState incremental_state;
+
+  uint32_t sequence_id = 0;
+  for (const auto& packet : parsed_trace.packet()) {
+    incremental_state.ClearIfNeeded(packet);
+
+    if (packet.has_track_descriptor()) {
+      // Make sure we haven't seen any events on this track before the
+      // descriptor was written.
+      EXPECT_FALSE(
+          incremental_state.HasSeenTrack(packet.track_descriptor().uuid()));
+    }
+
+    if (!packet.has_track_event())
+      continue;
+
+    // Make sure we only see track events on one sequence.
+    if (packet.trusted_packet_sequence_id()) {
+      if (!sequence_id)
+        sequence_id = packet.trusted_packet_sequence_id();
+      EXPECT_EQ(sequence_id, packet.trusted_packet_sequence_id());
+    }
+
+    incremental_state.Parse(packet);
+
+    const auto& track_event = packet.track_event();
+    std::string slice;
+
+    if (track_event.has_track_uuid()) {
+      incremental_state.InsertTrack(track_event.track_uuid());
+      std::stringstream track;
+      track << "[track=" << track_event.track_uuid() << "]";
+      slice += track.str();
+    }
+
+    switch (track_event.type()) {
+      case perfetto::protos::gen::TrackEvent::TYPE_SLICE_BEGIN:
+        slice += "B";
+        break;
+      case perfetto::protos::gen::TrackEvent::TYPE_SLICE_END:
+        slice += "E";
+        break;
+      case perfetto::protos::gen::TrackEvent::TYPE_INSTANT:
+        slice += "I";
+        break;
+      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.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++ ? "," : ":") + incremental_state.GetCategory(it);
+    for (const auto& it : track_event.categories())
+      slice += (category_count++ ? ",$" : ":$") + it;
+    if (track_event.has_name() || track_event.has_name_iid())
+      slice += "." + incremental_state.GetEventName(track_event);
+
+    if (track_event.debug_annotations_size()) {
+      slice += "(";
+      bool first_annotation = true;
+      for (const auto& it : track_event.debug_annotations()) {
+        if (!first_annotation) {
+          slice += ",";
+        }
+        if (it.has_name_iid()) {
+          slice += incremental_state.GetDebugAnnotationName(it.name_iid());
+        } else {
+          slice += it.name();
+        }
+        slice += "=";
+        std::stringstream value;
+        if (it.has_bool_value()) {
+          value << "(bool)" << it.bool_value();
+        } else if (it.has_uint_value()) {
+          value << "(uint)" << it.uint_value();
+        } else if (it.has_int_value()) {
+          value << "(int)" << it.int_value();
+        } else if (it.has_double_value()) {
+          value << "(double)" << it.double_value();
+        } else if (it.has_string_value()) {
+          value << "(string)" << it.string_value();
+        } else if (it.has_pointer_value()) {
+          value << "(pointer)" << std::hex << it.pointer_value();
+        } else if (it.has_legacy_json_value()) {
+          value << "(json)" << it.legacy_json_value();
+        } else if (it.has_nested_value()) {
+          value << "(nested)" << it.nested_value().string_value();
+        }
+        slice += value.str();
+        first_annotation = false;
+      }
+      slice += ")";
+    }
+
+    if (track_event.flow_ids_old_size()) {
+      slice += "(flow_ids_old=";
+      std::stringstream value;
+      bool first_annotation = true;
+      for (uint64_t id : track_event.flow_ids_old()) {
+        if (!first_annotation) {
+          value << ",";
+        }
+        first_annotation = false;
+        value << id;
+      }
+      slice += value.str() + ")";
+    }
+
+    if (track_event.flow_ids_size()) {
+      slice += "(flow_ids=";
+      std::stringstream value;
+      bool first_annotation = true;
+      for (uint64_t id : track_event.flow_ids()) {
+        if (!first_annotation) {
+          value << ",";
+        }
+        first_annotation = false;
+        value << id;
+      }
+      slice += value.str() + ")";
+    }
+
+    if (track_event.terminating_flow_ids_old_size()) {
+      slice += "(terminating_flow_ids_old=";
+      std::stringstream value;
+      bool first_annotation = true;
+      for (uint64_t id : track_event.terminating_flow_ids_old()) {
+        if (!first_annotation) {
+          value << ",";
+        }
+        value << id;
+        first_annotation = false;
+      }
+      slice += value.str() + ")";
+    }
+
+    if (track_event.terminating_flow_ids_size()) {
+      slice += "(terminating_flow_ids=";
+      std::stringstream value;
+      bool first_annotation = true;
+      for (uint64_t id : track_event.terminating_flow_ids()) {
+        if (!first_annotation) {
+          value << ",";
+        }
+        value << id;
+        first_annotation = false;
+      }
+      slice += value.str() + ")";
+    }
+
+    slices.push_back(slice);
+  }
+  if (expect_incremental_state_cleared) {
+    EXPECT_TRUE(incremental_state.WasCleared());
+  }
+  return slices;
+}
+
+std::vector<std::string> ReadSlicesFromTrace(
+    const std::vector<char>& raw_trace,
+    bool expect_incremental_state_cleared = true) {
+  EXPECT_GE(raw_trace.size(), 0u);
+
+  perfetto::protos::gen::Trace parsed_trace;
+  EXPECT_TRUE(parsed_trace.ParseFromArray(raw_trace.data(), raw_trace.size()));
+  return ReadSlicesFromTrace(parsed_trace, expect_incremental_state_cleared);
+}
+
 // -------------------------
 // Declaration of test class
 // -------------------------
@@ -461,11 +681,13 @@
     g_test_tracing_policy->should_allow_consumer_connection = true;
 
     // Start a fresh system service for this test, tearing down any previous
-    // service that was running. If the system backend isn't supported, skip all
-    // system backend tests.
-    if (GetParam() == perfetto::kSystemBackend &&
-        !perfetto::test::StartSystemService()) {
-      GTEST_SKIP();
+    // service that was running.
+    if (GetParam() == perfetto::kSystemBackend) {
+      system_service_ = perfetto::test::SystemService::Start();
+      // If the system backend isn't supported, skip all system backend tests.
+      if (!system_service_.valid()) {
+        GTEST_SKIP();
+      }
     }
 
     EXPECT_FALSE(perfetto::Tracing::IsInitialized());
@@ -499,20 +721,6 @@
     perfetto::Tracing::ResetForTesting();
   }
 
-  static void TearDownTestSuite() {
-    // Test shutting down Perfetto only when all other tests have been run and
-    // no more tracing code will be executed.
-    PERFETTO_CHECK(!perfetto::Tracing::IsInitialized());
-    TracingInitArgs args;
-    args.backends = perfetto::kInProcessBackend;
-    perfetto::Tracing::Initialize(args);
-    perfetto::Tracing::Shutdown();
-    PERFETTO_CHECK(!perfetto::Tracing::IsInitialized());
-    // Shutting down again is a no-op.
-    perfetto::Tracing::Shutdown();
-    PERFETTO_CHECK(!perfetto::Tracing::IsInitialized());
-  }
-
   template <typename DataSourceType>
   TestDataSourceHandle* RegisterDataSource(std::string name) {
     perfetto::DataSourceDescriptor dsd;
@@ -624,7 +832,7 @@
     return log_messages;
   }
 
-  std::vector<std::string> ReadSlicesFromTrace(
+  std::vector<std::string> ReadSlicesFromTraceSession(
       perfetto::TracingSession* tracing_session) {
     return ReadSlicesFromTrace(tracing_session->ReadTraceBlocking());
   }
@@ -634,209 +842,6 @@
     return ReadSlicesFromTrace(StopSessionAndReturnBytes(tracing_session));
   }
 
-  std::vector<std::string> ReadSlicesFromTrace(
-      const std::vector<char>& raw_trace) {
-    EXPECT_GE(raw_trace.size(), 0u);
-
-    perfetto::protos::gen::Trace parsed_trace;
-    EXPECT_TRUE(
-        parsed_trace.ParseFromArray(raw_trace.data(), raw_trace.size()));
-    return ReadSlicesFromTrace(parsed_trace);
-  }
-
-  std::vector<std::string> ReadSlicesFromTrace(
-      const perfetto::protos::gen::Trace& parsed_trace) {
-    // Read back the trace, maintaining interning tables as we go.
-    std::vector<std::string> slices;
-    ParsedIncrementalState incremental_state;
-
-    uint32_t sequence_id = 0;
-    for (const auto& packet : parsed_trace.packet()) {
-      incremental_state.ClearIfNeeded(packet);
-
-      if (packet.has_track_descriptor()) {
-        // Make sure we haven't seen any events on this track before the
-        // descriptor was written.
-        EXPECT_FALSE(
-            incremental_state.HasSeenTrack(packet.track_descriptor().uuid()));
-      }
-
-      if (!packet.has_track_event())
-        continue;
-
-      // Make sure we only see track events on one sequence.
-      if (packet.trusted_packet_sequence_id()) {
-        if (!sequence_id)
-          sequence_id = packet.trusted_packet_sequence_id();
-        EXPECT_EQ(sequence_id, packet.trusted_packet_sequence_id());
-      }
-
-      incremental_state.Parse(packet);
-
-      const auto& track_event = packet.track_event();
-      std::string slice;
-
-      if (track_event.has_track_uuid()) {
-        incremental_state.InsertTrack(track_event.track_uuid());
-        std::stringstream track;
-        track << "[track=" << track_event.track_uuid() << "]";
-        slice += track.str();
-      }
-
-      switch (track_event.type()) {
-        case perfetto::protos::gen::TrackEvent::TYPE_SLICE_BEGIN:
-          slice += "B";
-          break;
-        case perfetto::protos::gen::TrackEvent::TYPE_SLICE_END:
-          slice += "E";
-          break;
-        case perfetto::protos::gen::TrackEvent::TYPE_INSTANT:
-          slice += "I";
-          break;
-        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.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++ ? "," : ":") + incremental_state.GetCategory(it);
-      for (const auto& it : track_event.categories())
-        slice += (category_count++ ? ",$" : ":$") + it;
-      if (track_event.has_name() || track_event.has_name_iid())
-        slice += "." + incremental_state.GetEventName(track_event);
-
-      if (track_event.debug_annotations_size()) {
-        slice += "(";
-        bool first_annotation = true;
-        for (const auto& it : track_event.debug_annotations()) {
-          if (!first_annotation) {
-            slice += ",";
-          }
-          slice +=
-              incremental_state.GetDebugAnnotationName(it.name_iid()) + "=";
-          std::stringstream value;
-          if (it.has_bool_value()) {
-            value << "(bool)" << it.bool_value();
-          } else if (it.has_uint_value()) {
-            value << "(uint)" << it.uint_value();
-          } else if (it.has_int_value()) {
-            value << "(int)" << it.int_value();
-          } else if (it.has_double_value()) {
-            value << "(double)" << it.double_value();
-          } else if (it.has_string_value()) {
-            value << "(string)" << it.string_value();
-          } else if (it.has_pointer_value()) {
-            value << "(pointer)" << std::hex << it.pointer_value();
-          } else if (it.has_legacy_json_value()) {
-            value << "(json)" << it.legacy_json_value();
-          } else if (it.has_nested_value()) {
-            value << "(nested)" << it.nested_value().string_value();
-          }
-          slice += value.str();
-          first_annotation = false;
-        }
-        slice += ")";
-      }
-
-      if (track_event.flow_ids_old_size()) {
-        slice += "(flow_ids_old=";
-        std::stringstream value;
-        bool first_annotation = true;
-        for (uint64_t id : track_event.flow_ids_old()) {
-          if (!first_annotation) {
-            value << ",";
-          }
-          first_annotation = false;
-          value << id;
-        }
-        slice += value.str() + ")";
-      }
-
-      if (track_event.flow_ids_size()) {
-        slice += "(flow_ids=";
-        std::stringstream value;
-        bool first_annotation = true;
-        for (uint64_t id : track_event.flow_ids()) {
-          if (!first_annotation) {
-            value << ",";
-          }
-          first_annotation = false;
-          value << id;
-        }
-        slice += value.str() + ")";
-      }
-
-      if (track_event.terminating_flow_ids_old_size()) {
-        slice += "(terminating_flow_ids_old=";
-        std::stringstream value;
-        bool first_annotation = true;
-        for (uint64_t id : track_event.terminating_flow_ids_old()) {
-          if (!first_annotation) {
-            value << ",";
-          }
-          value << id;
-          first_annotation = false;
-        }
-        slice += value.str() + ")";
-      }
-
-      if (track_event.terminating_flow_ids_size()) {
-        slice += "(terminating_flow_ids=";
-        std::stringstream value;
-        bool first_annotation = true;
-        for (uint64_t id : track_event.terminating_flow_ids()) {
-          if (!first_annotation) {
-            value << ",";
-          }
-          value << id;
-          first_annotation = false;
-        }
-        slice += value.str() + ")";
-      }
-
-      slices.push_back(slice);
-    }
-    EXPECT_TRUE(incremental_state.WasCleared());
-    return slices;
-  }
-
   uint32_t GetMainThreadPacketSequenceId(
       const perfetto::protos::gen::Trace& trace) {
     for (const auto& packet : trace.packet()) {
@@ -869,6 +874,7 @@
     }
   }
 
+  perfetto::test::SystemService system_service_;
   std::map<std::string, TestDataSourceHandle> data_sources_;
   std::list<TestTracingSessionHandle> sessions_;  // Needs stable pointers.
 };
@@ -923,6 +929,7 @@
 // Disabled by default because it leaks tracing sessions into subsequent tests,
 // which can result in the per-uid tracing session limit (5) to be hit in later
 // tests.
+// TODO(b/261493947): fix or remove.
 TEST_P(PerfettoApiTest, DISABLED_TrackEventStartStopAndDestroy) {
   // This test used to cause a use after free as the tracing session got
   // destroyed. It needed to be run approximately 2000 times to catch it so test
@@ -1153,6 +1160,36 @@
   }
 }
 
+// Tests that we don't accumulate error when using incremental timestamps with
+// timestamp unit multiplier.
+TEST_P(PerfettoApiTest, TrackEventTimestampIncrementalAccumulatedError) {
+  constexpr uint64_t kUnitMultiplier = 100000;
+  constexpr uint64_t kNumberOfEvents = 1000;
+  constexpr uint64_t kTimeBetweenEventsNs = 50000;
+
+  perfetto::protos::gen::TrackEventConfig te_cfg;
+  te_cfg.set_timestamp_unit_multiplier(kUnitMultiplier);
+  auto* tracing_session = NewTraceWithCategories({"foo"}, te_cfg);
+  tracing_session->get()->StartBlocking();
+  auto start = perfetto::TrackEvent::GetTraceTimeNs();
+  TRACE_EVENT_BEGIN("foo", "Start");
+  for (uint64_t i = 0; i < kNumberOfEvents; ++i) {
+    SpinForThreadTimeNanos(kTimeBetweenEventsNs);
+    TRACE_EVENT_BEGIN("foo", "Event");
+  }
+  auto end = perfetto::TrackEvent::GetTraceTimeNs();
+  auto trace = StopSessionAndReturnParsedTrace(tracing_session);
+  uint64_t accumulated_timestamp = 0;
+  for (const auto& packet : trace.packet()) {
+    if (packet.has_track_event()) {
+      accumulated_timestamp += packet.timestamp() * kUnitMultiplier;
+    }
+  }
+
+  EXPECT_GE(accumulated_timestamp, kNumberOfEvents * kTimeBetweenEventsNs);
+  EXPECT_LE(accumulated_timestamp, end - start);
+}
+
 TEST_P(PerfettoApiTest, TrackEvent) {
   // Create a new trace session.
   auto* tracing_session = NewTraceWithCategories({"test"});
@@ -1443,14 +1480,17 @@
   EXPECT_EQ(1u, muxer.data_sources.size());
 
   tracing_module::InitializeCategories();
-  EXPECT_EQ(2u, muxer.data_sources.size());
+  EXPECT_EQ(3u, muxer.data_sources.size());
 
   // Both data sources have the same name but distinct static data (i.e.,
   // individual instance states).
   EXPECT_EQ("track_event", muxer.data_sources[0].dsd.name());
   EXPECT_EQ("track_event", muxer.data_sources[1].dsd.name());
+  EXPECT_EQ("track_event", muxer.data_sources[2].dsd.name());
   EXPECT_NE(muxer.data_sources[0].static_state,
             muxer.data_sources[1].static_state);
+  EXPECT_NE(muxer.data_sources[0].static_state,
+            muxer.data_sources[2].static_state);
 }
 
 TEST_P(PerfettoApiTest, TrackEventDescriptor) {
@@ -1555,6 +1595,43 @@
   }
 }
 
+TEST_P(PerfettoApiTest, TrackEventNamespaces) {
+  perfetto::TrackEvent::Register();
+  other_ns::TrackEvent::Register();
+  tracing_module::InitializeCategories();
+
+  auto* tracing_session =
+      NewTraceWithCategories({"test", "cat1", "extra", "other_ns"});
+  tracing_session->get()->StartBlocking();
+
+  // Default namespace.
+  TRACE_EVENT_INSTANT("test", "MainNamespaceEvent");
+  EXPECT_TRUE(TRACE_EVENT_CATEGORY_ENABLED("test"));
+
+  // Other namespace in a block scope.
+  {
+    PERFETTO_USE_CATEGORIES_FROM_NAMESPACE_SCOPED(other_ns);
+    TRACE_EVENT_INSTANT("other_ns", "OtherNamespaceEvent");
+    EXPECT_TRUE(TRACE_EVENT_CATEGORY_ENABLED("other_ns"));
+  }
+
+  // Back to the default namespace.
+  TRACE_EVENT_INSTANT("test", "MainNamespaceEvent2");
+
+  // More namespaces defined in another module.
+  tracing_module::EmitTrackEventsFromAllNamespaces();
+
+  auto slices = StopSessionAndReadSlicesFromTrace(tracing_session);
+  EXPECT_THAT(
+      slices,
+      ElementsAre("I:test.MainNamespaceEvent", "I:other_ns.OtherNamespaceEvent",
+                  "I:test.MainNamespaceEvent2",
+                  "B:cat1.DefaultNamespaceFromModule",
+                  "B:extra.ExtraNamespaceFromModule",
+                  "B:extra.OverrideNamespaceFromModule",
+                  "B:extra.DefaultNamespace", "B:cat1.DefaultNamespace"));
+}
+
 TEST_P(PerfettoApiTest, TrackEventDynamicCategories) {
   // Setup the trace config.
   perfetto::TraceConfig cfg;
@@ -1882,22 +1959,24 @@
   auto* tracing_session = NewTraceWithCategories({"foo"});
   tracing_session->get()->StartBlocking();
 
-  const perfetto::protos::pbzero::BuiltinClock kMyClockId =
+  static constexpr perfetto::protos::pbzero::BuiltinClock kMyClockId =
       static_cast<perfetto::protos::pbzero::BuiltinClock>(700);
-  const uint64_t kTimestamp = 12345678;
+  static constexpr uint64_t kTimestamp = 12345678;
 
   // First emit a clock snapshot that maps our custom clock to regular trace
   // time. Note that the clock snapshot should come before any events
   // referencing that clock.
   perfetto::TrackEvent::Trace([](perfetto::TrackEvent::TraceContext ctx) {
     auto packet = ctx.NewTracePacket();
-    packet->set_timestamp_clock_id(perfetto::TrackEvent::GetTraceClockId());
+    packet->set_timestamp_clock_id(
+        static_cast<uint32_t>(perfetto::TrackEvent::GetTraceClockId()));
     packet->set_timestamp(perfetto::TrackEvent::GetTraceTimeNs());
     auto* clock_snapshot = packet->set_clock_snapshot();
     // First set the reference clock, i.e., the default trace clock in this
     // case.
     auto* clock = clock_snapshot->add_clocks();
-    clock->set_clock_id(perfetto::TrackEvent::GetTraceClockId());
+    clock->set_clock_id(
+        static_cast<uint32_t>(perfetto::TrackEvent::GetTraceClockId()));
     clock->set_timestamp(perfetto::TrackEvent::GetTraceTimeNs());
     // Then set the value of our reference clock at the same point in time. We
     // pretend our clock is one second behind trace time.
@@ -1931,7 +2010,10 @@
   EXPECT_TRUE(found_event);
 }
 
-TEST_P(PerfettoApiTest, LegacyEventWithThreadOverride) {
+// Only synchronous phases are supported for other threads. Hence disabled this
+// test.
+// TODO(b/261493947): fix or remove.
+TEST_P(PerfettoApiTest, DISABLED_LegacyEventWithThreadOverride) {
   // Create a new trace session.
   auto* tracing_session = NewTraceWithCategories({"cat"});
   tracing_session->get()->StartBlocking();
@@ -1959,7 +2041,7 @@
       continue;
     auto track_event = packet.track_event();
     if (track_event.legacy_event().phase() == TRACE_EVENT_PHASE_ASYNC_BEGIN) {
-      EXPECT_EQ(track.uuid, track_event.track_uuid());
+      EXPECT_EQ(0u, track_event.track_uuid());
       found_event = true;
     }
   }
@@ -1968,7 +2050,9 @@
   perfetto::TrackEvent::EraseTrackDescriptor(track);
 }
 
-TEST_P(PerfettoApiTest, LegacyEventWithProcessOverride) {
+// Only synchronous phases are supported for other threads. Hence disabled this
+// test.
+TEST_P(PerfettoApiTest, DISABLED_LegacyEventWithProcessOverride) {
   // Create a new trace session.
   auto* tracing_session = NewTraceWithCategories({"cat"});
   tracing_session->get()->StartBlocking();
@@ -2558,6 +2642,40 @@
       });
 }
 
+TEST_P(PerfettoApiTest, TrackEventArgs_Lambda_Multisession) {
+  // Create two simultaneous tracing sessions.
+  auto* tracing_session = NewTraceWithCategories({"foo"});
+  auto* tracing_session2 = NewTraceWithCategories({"foo"});
+  tracing_session->get()->StartBlocking();
+  tracing_session2->get()->StartBlocking();
+
+  // Emit an event in both sessions using an argument function.
+  auto make_arg = []() -> std::function<void(perfetto::EventContext&)> {
+    return [](perfetto::EventContext& ctx) {
+      ctx.event()->set_type(perfetto::protos::pbzero::TrackEvent::TYPE_INSTANT);
+      ctx.event()->add_flow_ids(42);
+    };
+  };
+  TRACE_EVENT_INSTANT("foo", "E1", make_arg());
+
+  std::vector<char> raw_trace = StopSessionAndReturnBytes(tracing_session);
+  std::vector<char> raw_trace2 = StopSessionAndReturnBytes(tracing_session2);
+
+  // Check that the event was output twice.
+  CheckTypedArguments(
+      raw_trace, "E1", perfetto::protos::gen::TrackEvent::TYPE_INSTANT,
+      [](const perfetto::protos::gen::TrackEvent& track_event) {
+        EXPECT_TRUE(track_event.flow_ids_old().empty());
+        EXPECT_THAT(track_event.flow_ids(), testing::ElementsAre(42u));
+      });
+  CheckTypedArguments(
+      raw_trace2, "E1", perfetto::protos::gen::TrackEvent::TYPE_INSTANT,
+      [](const perfetto::protos::gen::TrackEvent& track_event) {
+        EXPECT_TRUE(track_event.flow_ids_old().empty());
+        EXPECT_THAT(track_event.flow_ids(), testing::ElementsAre(42u));
+      });
+}
+
 TEST_P(PerfettoApiTest, TrackEventArgs_MultipleFlows) {
   // Create a new trace session.
   auto* tracing_session = NewTraceWithCategories({"foo"});
@@ -3335,9 +3453,11 @@
                     perfetto::DynamicString(std::string("arg1_value6")));
   const char* value7 = "arg1_value7";
   TRACE_EVENT_BEGIN("foo", "Event7", "arg1", perfetto::StaticString(value7));
+  const char* arg_name = "new_arg1";
+  TRACE_EVENT_BEGIN("foo", "Event8", perfetto::DynamicString{arg_name}, 5);
 
   auto slices = StopSessionAndReadSlicesFromTrace(tracing_session);
-  ASSERT_EQ(7u, slices.size());
+  ASSERT_EQ(8u, slices.size());
   EXPECT_EQ("B:foo.Event1(arg1=(string)arg1_value1)", slices[0]);
   EXPECT_EQ("B:foo.Event2(arg1=(string)arg1_value2)", slices[1]);
   EXPECT_EQ("B:foo.Event3(arg1=(string)arg1_value3)", slices[2]);
@@ -3345,6 +3465,7 @@
   EXPECT_EQ("B:foo.Event5(arg1=(string)arg1_value5)", slices[4]);
   EXPECT_EQ("B:foo.Event6(arg1=(string)arg1_value6)", slices[5]);
   EXPECT_EQ("B:foo.Event7(arg1=(string)arg1_value7)", slices[6]);
+  EXPECT_EQ("B:foo.Event8(new_arg1=(int)5)", slices[7]);
 }
 
 TEST_P(PerfettoApiTest, FilterDynamicEventName) {
@@ -3515,6 +3636,16 @@
                             "B:disabled-by-default-cat.SlowDisabledEvent"));
   }
 
+  // Enable all legacy disabled-by-default categories by a pattern
+  {
+    perfetto::protos::gen::TrackEventConfig te_cfg;
+    te_cfg.add_disabled_categories("*");
+    te_cfg.add_enabled_categories("disabled-by-default-*");
+    auto slices = check_config(te_cfg);
+    EXPECT_THAT(slices,
+                ElementsAre("B:disabled-by-default-cat.SlowDisabledEvent"));
+  }
+
   // Enable everything including slow/debug categories.
   {
     perfetto::protos::gen::TrackEventConfig te_cfg;
@@ -4178,6 +4309,37 @@
   tracing_session->get()->StopBlocking();
 }
 
+TEST_P(PerfettoApiTest, CustomDataSource) {
+  perfetto::TraceConfig cfg;
+  cfg.add_buffers()->set_size_kb(1024);
+  auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+  ds_cfg->set_name("CustomDataSource");
+  auto* tracing_session = NewTrace(cfg);
+  tracing_session->get()->StartBlocking();
+  CustomDataSource::Trace([](CustomDataSource::TraceContext ctx) {
+    auto packet = ctx.NewTracePacket();
+    packet->set_timestamp(4200000);
+    packet->set_for_testing()->set_str("Test String");
+  });
+  CustomDataSource::Trace(
+      [](CustomDataSource::TraceContext ctx) { ctx.Flush(); });
+
+  tracing_session->get()->StopBlocking();
+  auto bytes = tracing_session->get()->ReadTraceBlocking();
+  perfetto::protos::gen::Trace parsed_trace;
+  EXPECT_TRUE(parsed_trace.ParseFromArray(bytes.data(), bytes.size()));
+  bool found_for_testing = false;
+  for (auto& packet : parsed_trace.packet()) {
+    if (packet.has_for_testing()) {
+      EXPECT_FALSE(found_for_testing);
+      found_for_testing = true;
+      EXPECT_EQ(4200000u, packet.timestamp());
+      EXPECT_EQ("Test String", packet.for_testing().str());
+    }
+  }
+  EXPECT_TRUE(found_for_testing);
+}
+
 TEST_P(PerfettoApiTest, QueryServiceState) {
   class QueryTestDataSource : public perfetto::DataSource<QueryTestDataSource> {
   };
@@ -4267,14 +4429,16 @@
 }
 
 TEST_P(PerfettoApiTest, LegacyTraceEventsCopyDynamicString) {
-  char ptr1[] = "ABC";
-  char ptr2[] = "XYZ";
+  char ptr1[] = "A1";
+  char ptr2[] = "B1";
+  char arg_name1[] = "C1";
+  char arg_name2[] = "D1";
   auto* tracing_session = NewTraceWithCategories({"cat"});
   tracing_session->get()->StartBlocking();
   {
     TRACE_EVENT_MARK_WITH_TIMESTAMP0("cat", ptr1, MyTimestamp{0});
-    ptr1[0] = 'D';
-    // Old value of event name ("ABC") is recorded here in trace.
+    ptr1[1] = '3';
+    // Old value of event name ("A1") is recorded here in trace.
     // The reason being, in legacy macros, event name was expected to be static
     // by default unless `_COPY` version of these macro is used.
     // Perfetto is caching pointer values and if a event-name-pointer matches an
@@ -4285,12 +4449,34 @@
   }
   {
     TRACE_EVENT_COPY_MARK_WITH_TIMESTAMP("cat", ptr2, MyTimestamp{0});
-    ptr2[0] = 'W';
+    ptr2[1] = '4';
     TRACE_EVENT_COPY_MARK_WITH_TIMESTAMP("cat", ptr2, MyTimestamp{0});
   }
+  {
+    TRACE_EVENT_INSTANT1("cat", "event_name", TRACE_EVENT_FLAG_NONE, arg_name1,
+                         /*arg_value=*/5);
+    arg_name1[1] = '5';
+    // Since we don't use the _COPY version here, this event will record the old
+    // value of arg_name1 (see earlier comment for full explanation).
+    TRACE_EVENT_INSTANT1("cat", "event_name", TRACE_EVENT_FLAG_NONE, arg_name1,
+                         /*arg_value=*/5);
+  }
+  {
+    TRACE_EVENT_COPY_INSTANT1("cat", "event_name", TRACE_EVENT_FLAG_NONE,
+                              arg_name2, /*arg_value=*/5);
+    arg_name2[1] = '6';
+    TRACE_EVENT_COPY_INSTANT1("cat", "event_name", TRACE_EVENT_FLAG_NONE,
+                              arg_name2, /*arg_value=*/5);
+  }
   auto slices = StopSessionAndReadSlicesFromTrace(tracing_session);
-  EXPECT_THAT(slices, ElementsAre("Legacy_R:cat.ABC", "Legacy_R:cat.ABC",
-                                  "Legacy_R:cat.XYZ", "Legacy_R:cat.WYZ"));
+  EXPECT_THAT(
+      slices,
+      ElementsAre("[track=0]Legacy_R:cat.A1", "[track=0]Legacy_R:cat.A1",
+                  "[track=0]Legacy_R:cat.B1", "[track=0]Legacy_R:cat.B4",
+                  "[track=0]I:cat.event_name(C1=(int)5)",
+                  "[track=0]I:cat.event_name(C1=(int)5)",
+                  "[track=0]I:cat.event_name(D1=(int)5)",
+                  "[track=0]I:cat.event_name(D6=(int)5)"));
 }
 
 TEST_P(PerfettoApiTest, LegacyTraceEvents) {
@@ -4325,11 +4511,6 @@
                                       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,
-      MyThreadId(123), MyTimestamp{3});
-
   // Event with id.
   TRACE_COUNTER1("cat", "LegacyCounter", 1234);
   TRACE_COUNTER_ID1("cat", "LegacyCounterWithId", 1234, 9000);
@@ -4361,18 +4542,16 @@
           "B(bind_id=3671771902)(flow_direction=1):disabled-by-default-cat."
           "LegacyFlowEvent",
           "[track=0]I:cat.LegacyInstantEvent",
-          std::string("[track=") +
-              std::to_string(perfetto::ThreadTrack::ForThread(123).uuid) +
-              "]Legacy_S(unscoped_id=1):cat.LegacyWithIdTidAndTimestamp",
-          "Legacy_C:cat.LegacyCounter(value=(int)1234)",
-          "Legacy_C(unscoped_id=1234):cat.LegacyCounterWithId(value=(int)9000)",
-          "Legacy_M:cat.LegacyMetadata",
-          "Legacy_b(unscoped_id=5678):cat.LegacyAsync",
-          "Legacy_e(unscoped_id=5678):cat.LegacyAsync",
-          "Legacy_b(unscoped_id=9000):cat.LegacyAsync2",
-          "Legacy_e(unscoped_id=9000):cat.LegacyAsync2",
-          "Legacy_b(unscoped_id=9001):cat.LegacyAsync3",
-          "Legacy_e(unscoped_id=9001):cat.LegacyAsync3"));
+          "[track=0]Legacy_C:cat.LegacyCounter(value=(int)1234)",
+          "[track=0]Legacy_C(unscoped_id=1234):cat.LegacyCounterWithId(value=("
+          "int)9000)",
+          "[track=0]Legacy_M:cat.LegacyMetadata",
+          "[track=0]Legacy_b(unscoped_id=5678):cat.LegacyAsync",
+          "[track=0]Legacy_e(unscoped_id=5678):cat.LegacyAsync",
+          "[track=0]Legacy_b(unscoped_id=9000):cat.LegacyAsync2",
+          "[track=0]Legacy_e(unscoped_id=9000):cat.LegacyAsync2",
+          "[track=0]Legacy_b(unscoped_id=9001):cat.LegacyAsync3",
+          "[track=0]Legacy_e(unscoped_id=9001):cat.LegacyAsync3"));
 }
 
 TEST_P(PerfettoApiTest, LegacyTraceEventsWithCustomAnnotation) {
@@ -4409,8 +4588,7 @@
   EXPECT_THAT(slices,
               ElementsAre("B:cat.LegacyEvent(arg=(json){\"key\": 123})"));
 
-  tracing_session2->get()->StopBlocking();
-  slices = ReadSlicesFromTrace(tracing_session2->get());
+  slices = StopSessionAndReadSlicesFromTrace(tracing_session2);
   EXPECT_THAT(slices,
               ElementsAre("B:cat.LegacyEvent(arg=(json){\"key\": 123})"));
 }
@@ -4427,11 +4605,12 @@
       TRACE_ID_WITH_SCOPE("scope string", TRACE_ID_GLOBAL(0x4000)));
 
   auto slices = StopSessionAndReadSlicesFromTrace(tracing_session);
-  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"));
+  EXPECT_THAT(slices,
+              ElementsAre("[track=0]Legacy_S(unscoped_id=4096):cat.UnscopedId",
+                          "[track=0]Legacy_S(local_id=8192):cat.LocalId",
+                          "[track=0]Legacy_S(global_id=12288):cat.GlobalId",
+                          "[track=0]Legacy_S(global_id=16384)(id_scope=\"scope "
+                          "string\"):cat.WithScope"));
 }
 
 TEST_P(PerfettoApiTest, NestableAsyncTraceEvent) {
@@ -4894,7 +5073,7 @@
     tracing_session->get()->StopBlocking();
     EXPECT_TRUE(observer.stop_called);
     perfetto::TrackEvent::RemoveSessionObserver(&observer);
-    auto slices = ReadSlicesFromTrace(tracing_session->get());
+    auto slices = ReadSlicesFromTraceSession(tracing_session->get());
     EXPECT_THAT(slices, ElementsAre("I:foo.OnStart", "I:foo.OnStop"));
   }
 
@@ -4936,7 +5115,7 @@
     tracing_session->get()->StopBlocking();
     perfetto::TrackEvent::RemoveSessionObserver(&observer1);
     perfetto::TrackEvent::RemoveSessionObserver(&observer2);
-    auto slices = ReadSlicesFromTrace(tracing_session->get());
+    auto slices = ReadSlicesFromTraceSession(tracing_session->get());
     EXPECT_THAT(slices, ElementsAre("I:foo.OnStart", "I:foo.OnStart",
                                     "I:foo.OnStop", "I:foo.OnStop"));
   }
@@ -4952,7 +5131,7 @@
     perfetto::TrackEvent::RemoveSessionObserver(&observer1);
     tracing_session->get()->StopBlocking();
     perfetto::TrackEvent::RemoveSessionObserver(&observer2);
-    auto slices = ReadSlicesFromTrace(tracing_session->get());
+    auto slices = ReadSlicesFromTraceSession(tracing_session->get());
     EXPECT_THAT(slices,
                 ElementsAre("I:foo.OnStart", "I:foo.OnStart", "I:foo.OnStop"));
   }
@@ -4998,13 +5177,125 @@
 
     EXPECT_TRUE(observer.clear_incremental_state_called);
     perfetto::TrackEvent::RemoveSessionObserver(&observer);
-    auto slices = ReadSlicesFromTrace(tracing_session->get());
+    auto slices = ReadSlicesFromTraceSession(tracing_session->get());
     EXPECT_THAT(slices, ElementsAre("I:foo.OnStart",
                                     "I:foo.WillClearIncrementalState"));
   }
   EXPECT_FALSE(perfetto::TrackEvent::IsEnabled());
 }
 
+TEST_P(PerfettoApiTest, TrackEventObserver_TwoDataSources) {
+  class Observer : public perfetto::TrackEventSessionObserver {
+   public:
+    ~Observer() override = default;
+
+    void OnStart(const perfetto::DataSourceBase::StartArgs&) {
+      EXPECT_FALSE(start_called);
+      start_called = true;
+    }
+
+    bool start_called{};
+  };
+
+  EXPECT_FALSE(perfetto::TrackEvent::IsEnabled());
+  EXPECT_FALSE(tracing_module::IsEnabled());
+
+  {
+    Observer observer1, observer2;
+    perfetto::TrackEvent::AddSessionObserver(&observer1);
+    tracing_module::AddSessionObserver(&observer2);
+
+    perfetto::TraceConfig cfg;
+    auto* tracing_session = NewTraceWithCategories({"foo"}, {}, cfg);
+
+    tracing_session->get()->StartBlocking();
+    tracing_session->on_stop.Wait();
+
+    // The tracing_module hasn't registered its data source yet, so observer2
+    // should not be notified.
+    EXPECT_TRUE(observer1.start_called);
+    EXPECT_FALSE(observer2.start_called);
+    perfetto::TrackEvent::RemoveSessionObserver(&observer1);
+    tracing_module::RemoveSessionObserver(&observer2);
+  }
+
+  tracing_module::InitializeCategories();
+
+  {
+    Observer observer1, observer2;
+    perfetto::TrackEvent::AddSessionObserver(&observer1);
+    tracing_module::AddSessionObserver(&observer2);
+
+    perfetto::TraceConfig cfg;
+    auto* tracing_session = NewTraceWithCategories({"foo"}, {}, cfg);
+
+    tracing_session->get()->StartBlocking();
+    tracing_session->on_stop.Wait();
+
+    // Each observer should be notified exactly once.
+    EXPECT_TRUE(observer1.start_called);
+    EXPECT_TRUE(observer2.start_called);
+    perfetto::TrackEvent::RemoveSessionObserver(&observer1);
+    tracing_module::RemoveSessionObserver(&observer2);
+  }
+
+  EXPECT_FALSE(perfetto::TrackEvent::IsEnabled());
+  EXPECT_FALSE(tracing_module::IsEnabled());
+}
+
+TEST_P(PerfettoApiTest, TrackEventObserver_AsyncStop) {
+  class Observer : public perfetto::TrackEventSessionObserver {
+   public:
+    ~Observer() override = default;
+
+    void OnStop(const perfetto::DataSourceBase::StopArgs& args) {
+      async_stop_closure_ = args.HandleStopAsynchronously();
+    }
+
+    void EmitFinalEvents() {
+      EXPECT_TRUE(perfetto::TrackEvent::IsEnabled());
+      EXPECT_TRUE(TRACE_EVENT_CATEGORY_ENABLED("foo"));
+      TRACE_EVENT_INSTANT("foo", "FinalEvent");
+      perfetto::TrackEvent::Flush();
+      async_stop_closure_();
+    }
+
+   private:
+    std::function<void()> async_stop_closure_;
+  };
+
+  EXPECT_FALSE(perfetto::TrackEvent::IsEnabled());
+  {
+    Observer observer;
+    perfetto::TrackEvent::AddSessionObserver(&observer);
+
+    auto* tracing_session = NewTraceWithCategories({"foo"});
+    WaitableTestEvent consumer_stop_signal;
+    tracing_session->get()->SetOnStopCallback(
+        [&consumer_stop_signal] { consumer_stop_signal.Notify(); });
+    tracing_session->get()->StartBlocking();
+
+    // Stop and wait for the producer to have seen the stop event.
+    tracing_session->get()->Stop();
+
+    // At this point tracing should be still allowed because of the
+    // HandleStopAsynchronously() call. This usleep is here just to prevent that
+    // we accidentally pass the test just by virtue of hitting some race. We
+    // should be able to trace up until 5 seconds after seeing the stop when
+    // using the deferred stop mechanism.
+    std::this_thread::sleep_for(std::chrono::milliseconds(250));
+    observer.EmitFinalEvents();
+
+    // Wait that the stop is propagated to the consumer.
+    consumer_stop_signal.Wait();
+
+    perfetto::TrackEvent::RemoveSessionObserver(&observer);
+    auto slices = ReadSlicesFromTraceSession(tracing_session->get());
+    EXPECT_THAT(slices, ElementsAre("I:foo.FinalEvent"));
+  }
+  EXPECT_FALSE(perfetto::TrackEvent::IsEnabled());
+}
+
 #if PERFETTO_BUILDFLAG(PERFETTO_COMPILER_CLANG)
 struct __attribute__((capability("mutex"))) MockMutex {
   void Lock() __attribute__((acquire_capability())) {}
@@ -5177,32 +5468,28 @@
 }
 
 TEST_P(PerfettoApiTest, EmptyEvent) {
-  auto* tracing_session = NewTraceWithCategories({"cat"});
+  auto* tracing_session = NewTraceWithCategories({"test"});
   tracing_session->get()->StartBlocking();
 
-  // Emit an empty event.
+  TRACE_EVENT_BEGIN("test", "MainEvent");
+
+  // An empty event will allow the previous track packet to be scraped.
   PERFETTO_INTERNAL_ADD_EMPTY_EVENT();
-  auto trace = StopSessionAndReturnParsedTrace(tracing_session);
-  auto it = std::find_if(trace.packet().begin(), trace.packet().end(),
-                         [](const perfetto::protos::gen::TracePacket& packet) {
-                           return packet.has_trace_stats();
-                         });
-  EXPECT_NE(it, trace.packet().end());
-  // The empty event required a trace chunk.
-  EXPECT_EQ(it->trace_stats().buffer_stats()[0].chunks_read(), 1u);
-  // But it isn't in the trace, because empty packets are skipped when reading
-  // from TraceBuffer.
-  it = std::find_if(trace.packet().begin(), trace.packet().end(),
-                    [](const perfetto::protos::gen::TracePacket& packet) {
-                      return packet.has_track_event();
-                    });
-  EXPECT_EQ(it, trace.packet().end());
+
+  // Stop tracing but don't flush. Rely on scraping to get the chunk contents.
+  tracing_session->get()->StopBlocking();
+
+  auto slices = ReadSlicesFromTraceSession(tracing_session->get());
+
+  EXPECT_THAT(slices, ElementsAre("B:test.MainEvent"));
 }
 
 TEST_P(PerfettoApiTest, ConsecutiveEmptyEventsSkipped) {
-  auto* tracing_session = NewTraceWithCategories({"cat"});
+  auto* tracing_session = NewTraceWithCategories({"test"});
   tracing_session->get()->StartBlocking();
 
+  TRACE_EVENT_BEGIN("test", "MainEvent");
+
   // Emit many empty events that wouldn't fit into one chunk.
   constexpr int kNumEvents = 10000;
   for (int i = 0; i < kNumEvents; ++i) {
@@ -5218,6 +5505,51 @@
   EXPECT_EQ(it->trace_stats().buffer_stats()[0].chunks_read(), 1u);
 }
 
+// Make sure that we set correct track_uuid for legacy events
+// of type TrackEvent::TYPE_UNSPECIFIED.
+// For such events we set fields of `track_event.legacy_event` and
+// we set `track_event.track_uuid` to zero to dissociate it with
+// default track.
+TEST_P(PerfettoApiTest, CorrectTrackUUIDForLegacyEvents) {
+  auto* tracing_session = NewTraceWithCategories({"cat"});
+  tracing_session->get()->StartBlocking();
+  TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("cat", "foo",
+                                    TRACE_ID_WITH_SCOPE("foo", 1));
+
+  auto slices = StopSessionAndReadSlicesFromTrace(tracing_session);
+  EXPECT_THAT(slices,
+              ElementsAre("[track=0]Legacy_b(unscoped_id=11250026935264495724)("
+                          "id_scope=\"foo\"):cat.foo"));
+}
+
+TEST_P(PerfettoApiTest, ActivateTriggers) {
+  perfetto::TraceConfig cfg;
+  cfg.add_buffers()->set_size_kb(1024);
+  perfetto::TraceConfig::TriggerConfig* tr_cfg = cfg.mutable_trigger_config();
+  tr_cfg->set_trigger_mode(perfetto::TraceConfig::TriggerConfig::STOP_TRACING);
+  tr_cfg->set_trigger_timeout_ms(5000);
+  perfetto::TraceConfig::TriggerConfig::Trigger* trigger =
+      tr_cfg->add_triggers();
+  trigger->set_name("trigger1");
+  auto* tracing_session = NewTrace(cfg);
+  tracing_session->get()->StartBlocking();
+
+  perfetto::Tracing::ActivateTriggers({"trigger2", "trigger1"}, 10000);
+
+  tracing_session->on_stop.Wait();
+
+  std::vector<char> bytes = tracing_session->get()->ReadTraceBlocking();
+  perfetto::protos::gen::Trace parsed_trace;
+  ASSERT_TRUE(parsed_trace.ParseFromArray(bytes.data(), bytes.size()));
+  EXPECT_THAT(
+      parsed_trace,
+      Property(&perfetto::protos::gen::Trace::packet,
+               Contains(Property(
+                   &perfetto::protos::gen::TracePacket::trigger,
+                   Property(&perfetto::protos::gen::Trigger::trigger_name,
+                            "trigger1")))));
+}
+
 class PerfettoStartupTracingApiTest : public PerfettoApiTest {
  public:
   using SetupStartupTracingOpts = perfetto::Tracing::SetupStartupTracingOpts;
@@ -5262,13 +5594,6 @@
     this->PerfettoApiTest::TearDown();
   }
 
-  static void TearDownTestSuite() {
-    // Keep it empty to avoid running TearDownTestSuite() of parent class.
-    // because `PerfettoApiTest::TearDownTestSuite` is not really a cleanup
-    // step but it is just another test case which should run after all other
-    // tests. hence we don't need to run it again here.
-  }
-
  protected:
   std::unique_ptr<perfetto::StartupTracingSession> session_;
 };
@@ -5303,11 +5628,8 @@
 
   // Emit another event after starting.
   TRACE_EVENT_END("test");
-  perfetto::TrackEvent::Flush();
-
-  tracing_session->get()->StopBlocking();
   // Both events should be retained.
-  auto slices = ReadSlicesFromTrace(tracing_session->get());
+  auto slices = StopSessionAndReadSlicesFromTrace(tracing_session);
   EXPECT_THAT(slices, ElementsAre("B:test.Event", "E"));
 }
 
@@ -5328,11 +5650,9 @@
 
   // Emit another event after starting.
   TRACE_EVENT_END("test");
-  perfetto::TrackEvent::Flush();
 
-  tracing_session->get()->StopBlocking();
   // Both events should be retained.
-  auto slices = ReadSlicesFromTrace(tracing_session->get());
+  auto slices = StopSessionAndReadSlicesFromTrace(tracing_session);
   EXPECT_THAT(slices, ElementsAre("B:test.Event", "E"));
 }
 
@@ -5350,11 +5670,9 @@
 
   // Emit another event after starting.
   TRACE_EVENT_END("test");
-  perfetto::TrackEvent::Flush();
 
-  tracing_session->get()->StopBlocking();
   // Both events should be retained.
-  auto slices = ReadSlicesFromTrace(tracing_session->get());
+  auto slices = StopSessionAndReadSlicesFromTrace(tracing_session);
   EXPECT_THAT(slices, ElementsAre("B:test.Event", "E"));
 }
 
@@ -5368,11 +5686,8 @@
   tracing_session->get()->StartBlocking();
 
   TRACE_EVENT_END("test");
-  perfetto::TrackEvent::Flush();
 
-  tracing_session->get()->StopBlocking();
-
-  auto slices = ReadSlicesFromTrace(tracing_session->get());
+  auto slices = StopSessionAndReadSlicesFromTrace(tracing_session);
 
   EXPECT_THAT(slices, ElementsAre("B:test.Event", "E"));
 }
@@ -5438,7 +5753,6 @@
     ctx.Flush();
   });
 
-  PERFETTO_LOG("P");
   auto trace = StopSessionAndReturnParsedTrace(tracing_session);
   auto slices = ReadSlicesFromTrace(trace);
   EXPECT_THAT(slices, ElementsAre("B:test.TrackEvent.Startup",
@@ -5455,7 +5769,7 @@
 
 // Startup tracing requires BufferExhaustedPolicy::kDrop, i.e. once the SMB is
 // filled with startup events, any further events should be dropped.
-// TODO(mohitms): It seems flaky. Debug and enable again - go/aosp_ci_failure23
+// TODO(b/261493947): fix or remove. go/aosp_ci_failure23
 TEST_P(PerfettoStartupTracingApiTest, DISABLED_DropPolicy) {
   SetupStartupTracing();
   constexpr int kNumEvents = 100000;
@@ -5466,11 +5780,7 @@
   auto* tracing_session = NewTraceWithCategories({"test"});
   tracing_session->get()->StartBlocking();
 
-  perfetto::TrackEvent::Flush();
-
-  tracing_session->get()->StopBlocking();
-
-  auto slices = ReadSlicesFromTrace(tracing_session->get());
+  auto slices = StopSessionAndReadSlicesFromTrace(tracing_session);
   std::unordered_map<std::string, int> freq_map;
   for (auto& slice : slices) {
     freq_map[slice]++;
@@ -5479,7 +5789,7 @@
   EXPECT_LT(freq_map["B:test.StartupEvent"], kNumEvents);
 }
 
-// TODO(mohitms): It seems flaky. Debug and enable again.
+// TODO(b/261493947): fix or remove.
 TEST_P(PerfettoStartupTracingApiTest, DISABLED_Abort) {
   SetupStartupTracing();
   TRACE_EVENT_BEGIN("test", "StartupEvent");
@@ -5489,11 +5799,8 @@
   tracing_session->get()->StartBlocking();
 
   TRACE_EVENT_BEGIN("test", "MainEvent");
-  perfetto::TrackEvent::Flush();
 
-  tracing_session->get()->StopBlocking();
-
-  auto slices = ReadSlicesFromTrace(tracing_session->get());
+  auto slices = StopSessionAndReadSlicesFromTrace(tracing_session);
 
   EXPECT_THAT(slices, ElementsAre("B:test.MainEvent"));
 }
@@ -5513,7 +5820,7 @@
 
   tracing_session->get()->StopBlocking();
 
-  auto slices = ReadSlicesFromTrace(tracing_session->get());
+  auto slices = ReadSlicesFromTraceSession(tracing_session->get());
 
   EXPECT_THAT(slices, ElementsAre("B:test.StartupEvent2", "B:test.MainEvent"));
 }
@@ -5535,11 +5842,12 @@
 
   tracing_session->get()->StopBlocking();
 
-  auto slices = ReadSlicesFromTrace(tracing_session->get());
+  auto slices = ReadSlicesFromTraceSession(tracing_session->get());
   EXPECT_THAT(slices, ElementsAre("B:test.MainEvent"));
 }
 
-TEST_P(PerfettoStartupTracingApiTest, Callbacks) {
+// TODO(b/261493947): fix or remove.
+TEST_P(PerfettoStartupTracingApiTest, DISABLED_Callbacks) {
   for (bool abort : {true, false}) {
     SetupStartupTracingOpts args;
     std::vector<std::string> callback_events;
@@ -5564,7 +5872,7 @@
 
     tracing_session->get()->StopBlocking();
 
-    auto slices = ReadSlicesFromTrace(tracing_session->get());
+    auto slices = ReadSlicesFromTraceSession(tracing_session->get());
 
     ASSERT_EQ(2u, callback_events.size());
     EXPECT_EQ("OnSetup(num_data_sources_started=1)", callback_events.at(0));
@@ -5580,6 +5888,7 @@
 }
 
 // Test that it's ok if main tracing is never started.
+// TODO(b/261493947): fix or remove.
 TEST_P(PerfettoStartupTracingApiTest, DISABLED_MainTracingNeverStarted) {
   SetupStartupTracing();
   TRACE_EVENT_BEGIN("test", "StartupEvent");
@@ -5596,10 +5905,147 @@
   TRACE_EVENT_BEGIN("test", "MainEvent");
   perfetto::TrackEvent::Flush();
   tracing_session->get()->StopBlocking();
-  auto slices = ReadSlicesFromTrace(tracing_session->get());
+  auto slices = ReadSlicesFromTraceSession(tracing_session->get());
   EXPECT_THAT(slices, ElementsAre("B:test.MainEvent"));
 }
 
+class ConcurrentSessionTest : public ::testing::Test {
+ public:
+  void SetUp() override {
+    system_service_ = perfetto::test::SystemService::Start();
+    if (!system_service_.valid()) {
+      GTEST_SKIP();
+    }
+    ASSERT_FALSE(perfetto::Tracing::IsInitialized());
+  }
+
+  void InitPerfetto(bool supports_multiple_data_source_instances = true) {
+    TracingInitArgs args;
+    args.backends = perfetto::kInProcessBackend | perfetto::kSystemBackend;
+    args.supports_multiple_data_source_instances =
+        supports_multiple_data_source_instances;
+    g_test_tracing_policy->should_allow_consumer_connection = true;
+    args.tracing_policy = g_test_tracing_policy;
+    perfetto::Tracing::Initialize(args);
+    perfetto::TrackEvent::Register();
+    perfetto::test::SyncProducers();
+    perfetto::test::DisableReconnectLimit();
+  }
+
+  void TearDown() override { perfetto::Tracing::ResetForTesting(); }
+
+  static std::unique_ptr<perfetto::TracingSession> StartTracing(
+      perfetto::BackendType backend_type) {
+    perfetto::TraceConfig cfg;
+    cfg.add_buffers()->set_size_kb(1024);
+    auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+    ds_cfg->set_name("track_event");
+    auto tracing_session = perfetto::Tracing::NewTrace(backend_type);
+    tracing_session->Setup(cfg);
+    tracing_session->StartBlocking();
+    return tracing_session;
+  }
+  std::vector<std::string> StopTracing(
+      std::unique_ptr<perfetto::TracingSession> tracing_session,
+      bool expect_incremental_state_cleared = true) {
+    perfetto::TrackEvent::Flush();
+    tracing_session->StopBlocking();
+    std::vector<char> trace_data(tracing_session->ReadTraceBlocking());
+    return ReadSlicesFromTrace(trace_data, expect_incremental_state_cleared);
+  }
+
+  perfetto::test::SystemService system_service_;
+};
+
+// Verify that concurrent sessions works well by default.
+// (i.e. when `disallow_concurrent_sessions` param is not set)
+TEST_F(ConcurrentSessionTest, ConcurrentBackends) {
+  InitPerfetto();
+  auto tracing_session1 = StartTracing(perfetto::kSystemBackend);
+  TRACE_EVENT_BEGIN("test", "DrawGame1");
+
+  auto tracing_session2 = StartTracing(perfetto::kInProcessBackend);
+  // Should be recorded by both sessions.
+  TRACE_EVENT_BEGIN("test", "DrawGame2");
+
+  auto slices1 = StopTracing(std::move(tracing_session1));
+  EXPECT_THAT(slices1, ElementsAre("B:test.DrawGame1", "B:test.DrawGame2"));
+
+  auto slices2 = StopTracing(std::move(tracing_session2));
+  EXPECT_THAT(slices2, ElementsAre("B:test.DrawGame2"));
+
+  auto tracing_session3 = StartTracing(perfetto::kInProcessBackend);
+  TRACE_EVENT_BEGIN("test", "DrawGame3");
+
+  auto slices3 = StopTracing(std::move(tracing_session3));
+  EXPECT_THAT(slices3, ElementsAre("B:test.DrawGame3"));
+}
+
+// When `supports_multiple_data_source_instances = false`, second session
+// should not be started.
+TEST_F(ConcurrentSessionTest, DisallowMultipleSessionBasic) {
+  InitPerfetto(/* supports_multiple_data_source_instances = */ false);
+  auto tracing_session1 = StartTracing(perfetto::kInProcessBackend);
+  TRACE_EVENT_BEGIN("test", "DrawGame1");
+
+  auto tracing_session2 = StartTracing(perfetto::kInProcessBackend);
+  TRACE_EVENT_BEGIN("test", "DrawGame2");
+
+  auto slices1 = StopTracing(std::move(tracing_session1));
+  EXPECT_THAT(slices1, ElementsAre("B:test.DrawGame1", "B:test.DrawGame2"));
+
+  auto slices2 = StopTracing(std::move(tracing_session2),
+                             false /* expect_incremental_state_cleared */);
+  // Because `tracing_session2` was not really started.
+  EXPECT_THAT(slices2, ElementsAre());
+
+  auto tracing_session3 = StartTracing(perfetto::kInProcessBackend);
+  TRACE_EVENT_BEGIN("test", "DrawGame3");
+
+  auto slices3 = StopTracing(std::move(tracing_session3));
+  EXPECT_THAT(slices3, ElementsAre("B:test.DrawGame3"));
+}
+
+TEST(PerfettoApiInitTest, DisableSystemConsumer) {
+  g_test_tracing_policy->should_allow_consumer_connection = true;
+
+  auto system_service = perfetto::test::SystemService::Start();
+  // If the system backend isn't supported, skip
+  if (!system_service.valid()) {
+    GTEST_SKIP();
+  }
+
+  EXPECT_FALSE(perfetto::Tracing::IsInitialized());
+  TracingInitArgs args;
+  args.backends = perfetto::kSystemBackend;
+  args.tracing_policy = g_test_tracing_policy;
+  args.enable_system_consumer = false;
+  perfetto::Tracing::Initialize(args);
+
+  // If this wasn't the first test to run in this process, any producers
+  // connected to the old system service will have been disconnected by the
+  // service restarting above. Wait for all producers to connect again before
+  // proceeding with the test.
+  perfetto::test::SyncProducers();
+
+  perfetto::test::DisableReconnectLimit();
+
+  std::unique_ptr<perfetto::TracingSession> ts =
+      perfetto::Tracing::NewTrace(perfetto::kSystemBackend);
+
+  // Creating the consumer should cause an asynchronous disconnect error.
+  WaitableTestEvent got_error;
+  ts->SetOnErrorCallback([&](perfetto::TracingError error) {
+    EXPECT_EQ(perfetto::TracingError::kDisconnected, error.code);
+    EXPECT_FALSE(error.message.empty());
+    got_error.Notify();
+  });
+  got_error.Wait();
+  ts.reset();
+
+  perfetto::Tracing::ResetForTesting();
+}
+
 struct BackendTypeAsString {
   std::string operator()(
       const ::testing::TestParamInfo<perfetto::BackendType>& info) const {
diff --git a/src/tracing/test/api_integrationtest_main.cc b/src/tracing/test/api_integrationtest_main.cc
new file mode 100644
index 0000000..5073244
--- /dev/null
+++ b/src/tracing/test/api_integrationtest_main.cc
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 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 "test/gtest_and_gmock.h"
+
+#include "perfetto/tracing.h"
+
+namespace {
+
+class PerfettoApiEnvironment : public ::testing::Environment {
+ public:
+  void TearDown() override {
+    // Test shutting down Perfetto only when all other tests have been run and
+    // no more tracing code will be executed.
+    PERFETTO_CHECK(!perfetto::Tracing::IsInitialized());
+    perfetto::TracingInitArgs args;
+    args.backends = perfetto::kInProcessBackend;
+    perfetto::Tracing::Initialize(args);
+    perfetto::Tracing::Shutdown();
+    PERFETTO_CHECK(!perfetto::Tracing::IsInitialized());
+    // Shutting down again is a no-op.
+    perfetto::Tracing::Shutdown();
+    PERFETTO_CHECK(!perfetto::Tracing::IsInitialized());
+  }
+};
+
+}  // namespace
+
+int main(int argc, char** argv) {
+  ::testing::AddGlobalTestEnvironment(new PerfettoApiEnvironment);
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/src/tracing/test/api_test_support.cc b/src/tracing/test/api_test_support.cc
index c386ccb..064f259 100644
--- a/src/tracing/test/api_test_support.cc
+++ b/src/tracing/test/api_test_support.cc
@@ -16,8 +16,10 @@
 
 #include "src/tracing/test/api_test_support.h"
 
+#include "perfetto/base/compiler.h"
 #include "perfetto/base/proc_utils.h"
 #include "perfetto/base/time.h"
+#include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/temp_file.h"
 #include "src/tracing/internal/tracing_muxer_impl.h"
 
@@ -47,38 +49,69 @@
     test_helper_.StartServiceIfRequired();
   }
 
+  void CleanEnv() { test_helper_.CleanEnv(); }
+
  private:
   perfetto::base::TestTaskRunner task_runner_;
   perfetto::TestHelper test_helper_;
 };
 
+InProcessSystemService* g_system_service = nullptr;
+
 }  // namespace
 
-bool StartSystemService() {
-  static InProcessSystemService* system_service;
-
+// static
+SystemService SystemService::Start() {
   // If there already was a system service running, make sure the new one is
   // running before tearing down the old one. This avoids a 1 second
   // reconnection delay between each test since the connection to the new
   // service succeeds immediately.
-  std::unique_ptr<InProcessSystemService> old_service(system_service);
-  system_service = new InProcessSystemService();
+  std::unique_ptr<InProcessSystemService> old_service(g_system_service);
+  if (old_service) {
+    old_service->CleanEnv();
+  }
+  g_system_service = new InProcessSystemService();
 
   // Tear down the service at process exit to make sure temporary files get
   // deleted.
   static bool cleanup_registered;
   if (!cleanup_registered) {
-    atexit([] { delete system_service; });
+    atexit([] { delete g_system_service; });
     cleanup_registered = true;
   }
-  return true;
+  SystemService ret;
+  ret.valid_ = true;
+  return ret;
+}
+
+void SystemService::Clean() {
+  if (valid_) {
+    if (g_system_service) {
+      // Does not really stop. We want to reuse the service in future tests. It
+      // is important to clean the environment variables, though.
+      g_system_service->CleanEnv();
+    }
+  }
+  valid_ = false;
 }
 #else   // !PERFETTO_BUILDFLAG(PERFETTO_IPC)
-bool StartSystemService() {
-  return false;
+// static
+SystemService SystemService::Start() {
+  return SystemService();
+}
+void SystemService::Clean() {
+  valid_ = false;
 }
 #endif  // !PERFETTO_BUILDFLAG(PERFETTO_IPC)
 
+SystemService& SystemService::operator=(SystemService&& other) noexcept {
+  PERFETTO_CHECK(!valid_ || !other.valid_);
+  Clean();
+  valid_ = other.valid_;
+  other.valid_ = false;
+  return *this;
+}
+
 int32_t GetCurrentProcessId() {
   return static_cast<int32_t>(base::GetProcessId());
 }
@@ -113,9 +146,8 @@
 TestTempFile CreateTempFile() {
   TestTempFile res{};
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  char temp_file[255]{};
-  sprintf(temp_file, "%s\\perfetto-XXXXXX", getenv("TMP"));
-  PERFETTO_CHECK(_mktemp_s(temp_file, strlen(temp_file) + 1) == 0);
+  base::StackString<255> temp_file("%s\\perfetto-XXXXXX", getenv("TMP"));
+  PERFETTO_CHECK(_mktemp_s(temp_file.c_str(), temp_file.len() + 1) == 0);
   HANDLE handle =
       ::CreateFileA(temp_file, GENERIC_READ | GENERIC_WRITE,
                     FILE_SHARE_DELETE | FILE_SHARE_READ, nullptr, CREATE_ALWAYS,
diff --git a/src/tracing/test/api_test_support.h b/src/tracing/test/api_test_support.h
index aa7915d..3fbc039 100644
--- a/src/tracing/test/api_test_support.h
+++ b/src/tracing/test/api_test_support.h
@@ -27,13 +27,37 @@
 //  IMPORTANT: This header must not pull any non-public perfetto header.
 
 #include <stdint.h>
+
 #include "perfetto/tracing.h"
 
 namespace perfetto {
 namespace test {
 
 int32_t GetCurrentProcessId();
-bool StartSystemService();
+
+// RAII wrapper to start and stop an in process system service. Only one at a
+// time can be started.
+class SystemService {
+ public:
+  static SystemService Start();
+  SystemService() = default;
+  SystemService(SystemService&& other) noexcept { *this = std::move(other); }
+  SystemService& operator=(SystemService&&) noexcept;
+
+  ~SystemService() { Clean(); }
+
+  // Returns true if this SystemService has been started successfully and can be
+  // used.
+  bool valid() const { return valid_; }
+
+  void Clean();
+
+ private:
+  SystemService(const SystemService&) = delete;
+  SystemService& operator=(const SystemService&) = delete;
+  bool valid_ = false;
+};
+
 void SyncProducers();
 
 void SetBatchCommitsDuration(uint32_t batch_commits_duration_ms,
diff --git a/src/tracing/test/mock_consumer.cc b/src/tracing/test/mock_consumer.cc
index c26c13d..dc44b31 100644
--- a/src/tracing/test/mock_consumer.cc
+++ b/src/tracing/test/mock_consumer.cc
@@ -69,6 +69,10 @@
   service_endpoint_->FreeBuffers();
 }
 
+void MockConsumer::CloneSession(TracingSessionID tsid) {
+  service_endpoint_->CloneSession(tsid);
+}
+
 void MockConsumer::WaitForTracingDisabled(uint32_t timeout_ms) {
   static int i = 0;
   auto checkpoint_name = "on_tracing_disabled_consumer_" + std::to_string(i++);
diff --git a/src/tracing/test/mock_consumer.h b/src/tracing/test/mock_consumer.h
index f6329bf..a840349 100644
--- a/src/tracing/test/mock_consumer.h
+++ b/src/tracing/test/mock_consumer.h
@@ -61,6 +61,7 @@
   TracingServiceState QueryServiceState();
   void ObserveEvents(uint32_t enabled_event_types);
   ObservableEvents WaitForObservableEvents();
+  void CloneSession(TracingSessionID);
 
   TracingService::ConsumerEndpoint* endpoint() {
     return service_endpoint_.get();
@@ -76,6 +77,7 @@
   MOCK_METHOD2(OnAttach, void(bool, const TraceConfig&));
   MOCK_METHOD2(OnTraceStats, void(bool, const TraceStats&));
   MOCK_METHOD1(OnObservableEvents, void(const ObservableEvents&));
+  MOCK_METHOD2(OnSessionCloned, void(bool, const std::string&));
 
   // gtest doesn't support move-only types. This wrapper is here jut to pass
   // a pointer to the vector (rather than the vector itself) to the mock method.
diff --git a/src/tracing/test/tracing_integration_test.cc b/src/tracing/test/tracing_integration_test.cc
index 51e225b..b14f121 100644
--- a/src/tracing/test/tracing_integration_test.cc
+++ b/src/tracing/test/tracing_integration_test.cc
@@ -86,6 +86,7 @@
   MOCK_METHOD2(OnAttach, void(bool, const TraceConfig&));
   MOCK_METHOD2(OnTraceStats, void(bool, const TraceStats&));
   MOCK_METHOD1(OnObservableEvents, void(const ObservableEvents&));
+  MOCK_METHOD2(OnSessionCloned, void(bool, const std::string&));
 
   // Workaround, gmock doesn't support yet move-only types, passing a pointer.
   void OnTraceData(std::vector<TracePacket> packets, bool has_more) {
diff --git a/src/tracing/test/tracing_module.cc b/src/tracing/test/tracing_module.cc
index 743ff68..d2ade28 100644
--- a/src/tracing/test/tracing_module.cc
+++ b/src/tracing/test/tracing_module.cc
@@ -24,12 +24,61 @@
 // This file is for checking that multiple sets of trace event categories can be
 // combined into the same program.
 
-PERFETTO_TRACK_EVENT_STATIC_STORAGE();
+PERFETTO_TRACK_EVENT_STATIC_STORAGE_IN_NAMESPACE(tracing_module);
+PERFETTO_TRACK_EVENT_STATIC_STORAGE_IN_NAMESPACE_WITH_ATTRS(
+    tracing_extra,
+    PERFETTO_SDK_EXPORT);
+
+namespace tracing_extra {
+namespace {
+
+void EmitEventFromExtraNamespace() {
+  TRACE_EVENT_BEGIN("extra", "ExtraNamespaceFromModule");
+  TRACE_EVENT_BEGIN("extra2", "ExtraNamespaceFromModuleNotEnabled");
+}
+
+}  // namespace
+}  // namespace tracing_extra
 
 namespace tracing_module {
 
+// The following two functions test selecting the category set on a
+// per-namespace level.
+namespace test_ns1 {
+PERFETTO_USE_CATEGORIES_FROM_NAMESPACE(tracing_extra);
+
+void EmitEvent();
+void EmitEvent() {
+  TRACE_EVENT_BEGIN("extra", "DefaultNamespace");
+}
+
+}  // namespace test_ns1
+
+namespace test_ns2 {
+PERFETTO_USE_CATEGORIES_FROM_NAMESPACE(tracing_module);
+
+void EmitEvent();
+void EmitEvent() {
+  TRACE_EVENT_BEGIN("cat1", "DefaultNamespace");
+}
+
+}  // namespace test_ns2
+
 void InitializeCategories() {
   TrackEvent::Register();
+  tracing_extra::TrackEvent::Register();
+}
+
+void AddSessionObserver(perfetto::TrackEventSessionObserver* observer) {
+  TrackEvent::AddSessionObserver(observer);
+}
+
+void RemoveSessionObserver(perfetto::TrackEventSessionObserver* observer) {
+  TrackEvent::RemoveSessionObserver(observer);
+}
+
+bool IsEnabled() {
+  return TrackEvent::IsEnabled();
 }
 
 void EmitTrackEvents() {
@@ -43,6 +92,22 @@
   TRACE_EVENT_END("foo");
 }
 
+void EmitTrackEventsFromAllNamespaces() {
+  // Since we're in the `tracing_module` namespace, that registry is used by
+  // default.
+  TRACE_EVENT_BEGIN("cat1", "DefaultNamespaceFromModule");
+
+  // Emit an event from the other namespace.
+  tracing_extra::EmitEventFromExtraNamespace();
+
+  // Make the other namespace the default.
+  PERFETTO_USE_CATEGORIES_FROM_NAMESPACE_SCOPED(tracing_extra);
+  TRACE_EVENT_BEGIN("extra", "OverrideNamespaceFromModule");
+
+  test_ns1::EmitEvent();
+  test_ns2::EmitEvent();
+}
+
 perfetto::internal::TrackEventIncrementalState* GetIncrementalState() {
   perfetto::internal::TrackEventIncrementalState* state = nullptr;
   TrackEvent::Trace([&state](TrackEvent::TraceContext ctx) {
diff --git a/src/tracing/test/tracing_module.h b/src/tracing/test/tracing_module.h
index 76c105c..cda9e43 100644
--- a/src/tracing/test/tracing_module.h
+++ b/src/tracing/test/tracing_module.h
@@ -20,6 +20,7 @@
 // Note: No non-client API header includes are allowed here.
 
 namespace perfetto {
+class TrackEventSessionObserver;
 namespace internal {
 struct TrackEventIncrementalState;
 }  // namespace internal
@@ -28,8 +29,12 @@
 namespace tracing_module {
 
 void InitializeCategories();
+void AddSessionObserver(perfetto::TrackEventSessionObserver* observer);
+void RemoveSessionObserver(perfetto::TrackEventSessionObserver* observer);
+bool IsEnabled();
 void EmitTrackEvents();
 void EmitTrackEvents2();
+void EmitTrackEventsFromAllNamespaces();
 perfetto::internal::TrackEventIncrementalState* GetIncrementalState();
 
 // These functions are used to check the instruction size overhead track events.
@@ -46,4 +51,6 @@
 
 }  // namespace tracing_module
 
+void TestDeprecatedNamespacing();
+
 #endif  // SRC_TRACING_TEST_TRACING_MODULE_H_
diff --git a/src/tracing/test/tracing_module3.cc b/src/tracing/test/tracing_module3.cc
new file mode 100644
index 0000000..4d417a8
--- /dev/null
+++ b/src/tracing/test/tracing_module3.cc
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 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/test/tracing_module.h"
+
+// This file checks that the deprecated track event namespacing mechanism
+// (PERFETTO_TRACK_EVENT_NAMESPACE) keeps working.
+
+#define PERFETTO_TRACK_EVENT_NAMESPACE deprecated_ns
+#include "perfetto/tracing.h"
+
+PERFETTO_DEFINE_CATEGORIES(PERFETTO_CATEGORY(cat1));
+PERFETTO_TRACK_EVENT_STATIC_STORAGE();
+
+void TestDeprecatedNamespacing() {
+  deprecated_ns::TrackEvent::Register();
+  TRACE_EVENT_BEGIN("cat1", "DeprecatedNamespaceEvent");
+}
diff --git a/src/tracing/test/tracing_module_categories.h b/src/tracing/test/tracing_module_categories.h
index 03c0425..9b1dd7b 100644
--- a/src/tracing/test/tracing_module_categories.h
+++ b/src/tracing/test/tracing_module_categories.h
@@ -22,21 +22,29 @@
 // ones defined in api_integrationtest.cc, but events for both sets of
 // 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),
-                           PERFETTO_CATEGORY(cat4),
-                           PERFETTO_CATEGORY(cat5),
-                           PERFETTO_CATEGORY(cat6),
-                           PERFETTO_CATEGORY(cat7),
-                           PERFETTO_CATEGORY(cat8),
-                           PERFETTO_CATEGORY(cat9),
-                           PERFETTO_CATEGORY(foo));
+PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE(tracing_module,
+                                        PERFETTO_CATEGORY(cat1),
+                                        PERFETTO_CATEGORY(cat2),
+                                        PERFETTO_CATEGORY(cat3),
+                                        PERFETTO_CATEGORY(cat4),
+                                        PERFETTO_CATEGORY(cat5),
+                                        PERFETTO_CATEGORY(cat6),
+                                        PERFETTO_CATEGORY(cat7),
+                                        PERFETTO_CATEGORY(cat8),
+                                        PERFETTO_CATEGORY(cat9),
+                                        PERFETTO_CATEGORY(foo));
+
+// Define a second set of categories in a different namespace with custom
+// linkage attributes.
+PERFETTO_DEFINE_CATEGORIES_IN_NAMESPACE_WITH_ATTRS(
+    tracing_extra,
+    PERFETTO_SDK_EXPORT,
+    perfetto::Category("extra"),
+    perfetto::Category("extra2"));
 
 #endif  // SRC_TRACING_TEST_TRACING_MODULE_CATEGORIES_H_
diff --git a/src/tracing/tracing.cc b/src/tracing/tracing.cc
index c78de24..030f49c 100644
--- a/src/tracing/tracing.cc
+++ b/src/tracing/tracing.cc
@@ -55,6 +55,12 @@
   if (args.log_message_callback) {
     base::SetLogMessageCallback(args.log_message_callback);
   }
+
+  if (args.use_monotonic_raw_clock) {
+    internal::TrackEventInternal::SetClockId(
+        protos::pbzero::BUILTIN_CLOCK_MONOTONIC_RAW);
+  }
+
   internal::TracingMuxerImpl::InitializeInstance(args);
   internal::TrackRegistry::InitializeInstance();
   g_was_initialized = true;
@@ -109,6 +115,12 @@
       ->CreateStartupTracingSessionBlocking(config, std::move(opts));
 }
 
+//  static
+void Tracing::ActivateTriggers(const std::vector<std::string>& triggers,
+                               uint32_t ttl_ms) {
+  internal::TracingMuxer::Get()->ActivateTriggers(triggers, ttl_ms);
+}
+
 TracingSession::~TracingSession() = default;
 
 // Can be called from any thread.
diff --git a/src/tracing/track.cc b/src/tracing/track.cc
index f24138b..6c35754 100644
--- a/src/tracing/track.cc
+++ b/src/tracing/track.cc
@@ -173,7 +173,7 @@
   // framework), events emitted by each will be consistently interleaved on
   // common thread and process tracks.
   if (uint64_t start_time = GetProcessStartTime()) {
-    base::Hash hash;
+    base::Hasher hash;
     hash.Update(start_time);
     hash.Update(Platform::GetCurrentProcessId());
     Track::process_uuid = hash.digest();
@@ -184,8 +184,7 @@
 }
 
 void TrackRegistry::ResetForTesting() {
-  delete instance_;
-  instance_ = nullptr;
+  instance_->tracks_.clear();
 }
 
 void TrackRegistry::UpdateTrack(Track track,
diff --git a/src/tracing/track_event_legacy.cc b/src/tracing/track_event_legacy.cc
index 25fd0d5..4eab33e 100644
--- a/src/tracing/track_event_legacy.cc
+++ b/src/tracing/track_event_legacy.cc
@@ -54,7 +54,7 @@
                                       legacy::kTraceEventFlagHasGlobalId);
   uint64_t id = raw_id_;
   if (scope_ && scope_flags != legacy::kTraceEventFlagHasGlobalId) {
-    id = base::Hash::Combine(id, scope_);
+    id = base::Hasher::Combine(id, scope_);
   }
 
   switch (scope_flags) {
diff --git a/src/tracing/track_event_state_tracker.cc b/src/tracing/track_event_state_tracker.cc
index 28262ad..1574292 100644
--- a/src/tracing/track_event_state_tracker.cc
+++ b/src/tracing/track_event_state_tracker.cc
@@ -92,7 +92,7 @@
   }
 
   if (name.data) {
-    base::Hash hash;
+    base::Hasher hash;
     hash.Update(name.data, name.size);
     name_hash = hash.digest();
   }
diff --git a/test/ci/ui_tests.sh b/test/ci/ui_tests.sh
index 33a7a85..75418f8 100755
--- a/test/ci/ui_tests.sh
+++ b/test/ci/ui_tests.sh
@@ -16,6 +16,8 @@
 INSTALL_BUILD_DEPS_ARGS="--ui"
 source $(dirname ${BASH_SOURCE[0]})/common.sh
 
+infra/perfetto.dev/build
+
 ui/build --out ${OUT_PATH}
 
 cp -a ${OUT_PATH}/ui/dist/ /ci/artifacts/ui
diff --git a/test/cts/heapprofd_java_test_cts.cc b/test/cts/heapprofd_java_test_cts.cc
index 7d81128..c2f3f77 100644
--- a/test/cts/heapprofd_java_test_cts.cc
+++ b/test/cts/heapprofd_java_test_cts.cc
@@ -89,6 +89,56 @@
   return helper.trace();
 }
 
+std::vector<protos::gen::TracePacket> TriggerOomHeapDump(std::string app_name,
+                                                         std::string heap_dump_target) {
+  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", 10000 /*ms*/);
+  }
+
+  // set up tracing
+  TestHelper helper(&task_runner);
+  helper.ConnectConsumer();
+  helper.WaitForConsumerConnect();
+
+  TraceConfig trace_config;
+  trace_config.add_buffers()->set_size_kb(40 * 1024);
+  trace_config.set_unique_session_name(RandomSessionName().c_str());
+  trace_config.set_data_source_stop_timeout_ms(60000);
+
+  auto* trigger_config = trace_config.mutable_trigger_config();
+  trigger_config->set_trigger_mode(perfetto::protos::gen::TraceConfig::TriggerConfig::START_TRACING);
+  trigger_config->set_trigger_timeout_ms(60000);
+  auto* oom_trigger = trigger_config->add_triggers();
+  oom_trigger->set_name("com.android.telemetry.art-outofmemory");
+  oom_trigger->set_stop_delay_ms(10000);
+
+  auto* ds_config = trace_config.add_data_sources()->mutable_config();
+  ds_config->set_name("android.java_hprof.oom");
+  ds_config->set_target_buffer(0);
+
+  protos::gen::JavaHprofConfig java_hprof_config;
+  java_hprof_config.add_process_cmdline(heap_dump_target.c_str());
+  ds_config->set_java_hprof_config_raw(java_hprof_config.SerializeAsString());
+
+  // start tracing
+  helper.StartTracing(trace_config);
+  StartAppActivity(app_name, "JavaOomActivity", "target.app.running", &task_runner,
+                   /*delay_ms=*/100);
+
+  helper.WaitForTracingDisabled();
+  helper.ReadData();
+  helper.WaitForReadData();
+
+  PERFETTO_CHECK(IsAppRunning(app_name));
+  StopApp(app_name, "new.app.stopped", &task_runner);
+  task_runner.RunUntilCheckpoint("new.app.stopped", 10000 /*ms*/);
+  return helper.trace();
+}
+
 void AssertGraphPresent(std::vector<protos::gen::TracePacket> packets) {
   ASSERT_GT(packets.size(), 0u);
 
@@ -184,5 +234,21 @@
   AssertGraphPresent(packets);
 }
 
+TEST(HeapprofdJavaCtsTest, DebuggableAppOom) {
+  if (IsUserBuild()) return;
+
+  std::string app_name = "android.perfetto.cts.app.debuggable";
+  const auto& packets = TriggerOomHeapDump(app_name, "*");
+  AssertGraphPresent(packets);
+}
+
+TEST(HeapprofdJavaCtsTest, DebuggableAppOomNotSelected) {
+  if (IsUserBuild()) return;
+
+  std::string app_name = "android.perfetto.cts.app.debuggable";
+  const auto& packets = TriggerOomHeapDump(app_name, "not.this.app");
+  AssertNoProfileContents(packets);
+}
+
 }  // namespace
 }  // namespace perfetto
diff --git a/test/cts/test_apps/AndroidManifest_debuggable.xml b/test/cts/test_apps/AndroidManifest_debuggable.xml
index cb746c1..291469e 100755
--- a/test/cts/test_apps/AndroidManifest_debuggable.xml
+++ b/test/cts/test_apps/AndroidManifest_debuggable.xml
@@ -58,5 +58,18 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity-alias>
+        <activity
+          android:name="android.perfetto.cts.app.JavaOomActivity"
+          android:exported="true">
+        </activity>
+        <activity-alias
+          android:name="android.perfetto.cts.app.debuggable.JavaOomActivity"
+          android:targetActivity="android.perfetto.cts.app.JavaOomActivity"
+          android:exported="true">
+            <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/test_apps/src/android/perfetto/cts/app/JavaOomActivity.java b/test/cts/test_apps/src/android/perfetto/cts/app/JavaOomActivity.java
new file mode 100644
index 0000000..4165a92
--- /dev/null
+++ b/test/cts/test_apps/src/android/perfetto/cts/app/JavaOomActivity.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+package android.perfetto.cts.app;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import java.util.ArrayList;
+
+public class JavaOomActivity extends Activity {
+    @Override
+    public void onCreate(Bundle state) {
+        super.onCreate(state);
+
+        new Thread(new Runnable() {
+            public void run() {
+                try {
+                    runAllocationLoop();
+                } catch (Exception ex) {
+                    ex.printStackTrace();
+                }
+            }
+        }).start();
+    }
+
+    private static void runAllocationLoop() {
+        ArrayList<byte[]> leaky = new ArrayList<>();
+        try {
+            for (;;) {
+                leaky.add(new byte[1024 * 1024]);
+                try {
+                    Thread.sleep(10);
+                } catch (InterruptedException ignored) {
+                }
+            }
+        } catch (OutOfMemoryError e) {
+          leaky.clear();
+        }
+    }
+}
diff --git a/test/data/chrome_custom_navigation_trace.gz b/test/data/chrome_custom_navigation_trace.gz
new file mode 100644
index 0000000..2b0be23
--- /dev/null
+++ b/test/data/chrome_custom_navigation_trace.gz
Binary files differ
diff --git a/test/data/chrome_custom_navigation_trace.gz.sha256 b/test/data/chrome_custom_navigation_trace.gz.sha256
new file mode 100644
index 0000000..23ff61b
--- /dev/null
+++ b/test/data/chrome_custom_navigation_trace.gz.sha256
@@ -0,0 +1 @@
+ff68279e3cec94076b69259d756eed181a63eaf834d8b956a7f4ba665fabf939
\ No newline at end of file
diff --git a/test/data/chrome_missing_track_names.pb.gz.sha256 b/test/data/chrome_missing_track_names.pb.gz.sha256
new file mode 100644
index 0000000..0901531
--- /dev/null
+++ b/test/data/chrome_missing_track_names.pb.gz.sha256
@@ -0,0 +1 @@
+9e4bfdf42a7e180599ab9fe7872333e4ae37f5a58bd228896724fdf3c7df7d67
\ No newline at end of file
diff --git a/test/data/cpu_powerups_1.pb.sha256 b/test/data/cpu_powerups_1.pb.sha256
new file mode 100644
index 0000000..04e0249
--- /dev/null
+++ b/test/data/cpu_powerups_1.pb.sha256
@@ -0,0 +1 @@
+70f5511ba0cd6ce1359e3cadb4d1d9301fb6e26be85158e3384b06f41418d386
diff --git a/test/data/event_latency_with_args.perfetto-trace.sha256 b/test/data/event_latency_with_args.perfetto-trace.sha256
new file mode 100644
index 0000000..6b5afdc
--- /dev/null
+++ b/test/data/event_latency_with_args.perfetto-trace.sha256
@@ -0,0 +1 @@
+c3d28fa97b1fcb515cacccdcddcda8981f5f2c6d20df682c59bcfdd8762954c2
\ No newline at end of file
diff --git a/test/data/long_task_tracking_trace.sha256 b/test/data/long_task_tracking_trace.sha256
new file mode 100644
index 0000000..6651eee
--- /dev/null
+++ b/test/data/long_task_tracking_trace.sha256
@@ -0,0 +1 @@
+dbcb9f6baa0e89ea519b93aaee373e37a4b4e453a9d0f4ad8fef0279f25c33f2
\ No newline at end of file
diff --git a/test/data/perf_sample_annotations.pftrace.sha256 b/test/data/perf_sample_annotations.pftrace.sha256
new file mode 100644
index 0000000..4eb6deb
--- /dev/null
+++ b/test/data/perf_sample_annotations.pftrace.sha256
@@ -0,0 +1 @@
+067957102d4f2c4dbb05e6d1948c76ab426b06d0ba07d0e7237e1442218b700e
\ No newline at end of file
diff --git a/test/data/top_level_java_choreographer_slices.sha256 b/test/data/top_level_java_choreographer_slices.sha256
new file mode 100644
index 0000000..1b40d54
--- /dev/null
+++ b/test/data/top_level_java_choreographer_slices.sha256
@@ -0,0 +1 @@
+8001e73b2458e94f65a606bb558a645ba5bca553b57fe416001f6c2175675a8a
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-android_trace_30s_expand_camera.png.sha256 b/test/data/ui-screenshots/ui-android_trace_30s_expand_camera.png.sha256
index f0f270b..f48f5b8 100644
--- a/test/data/ui-screenshots/ui-android_trace_30s_expand_camera.png.sha256
+++ b/test/data/ui-screenshots/ui-android_trace_30s_expand_camera.png.sha256
@@ -1 +1 @@
-9afd1d1f82d762dc2b2e294bdbd68f834f10ca7c7eab4a9787eaf3e75e8f1bbb
\ No newline at end of file
+2642993d0ccbdcc083d863a15d3a60e4e4458c278190bd057491a68727601ac5
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-android_trace_30s_load.png.sha256 b/test/data/ui-screenshots/ui-android_trace_30s_load.png.sha256
index de62f0b..8049eda 100644
--- a/test/data/ui-screenshots/ui-android_trace_30s_load.png.sha256
+++ b/test/data/ui-screenshots/ui-android_trace_30s_load.png.sha256
@@ -1 +1 @@
-a7da2d0e1f2f20dffad44e554dc5a6998bf70d0128ca900907157b4a2303c63a
\ No newline at end of file
+604b0837fc54444c0f347a26821892ce502796859daea4634bbd371b0248b4fd
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-chrome_missing_track_names_load.png.sha256 b/test/data/ui-screenshots/ui-chrome_missing_track_names_load.png.sha256
new file mode 100644
index 0000000..f5893a1
--- /dev/null
+++ b/test/data/ui-screenshots/ui-chrome_missing_track_names_load.png.sha256
@@ -0,0 +1 @@
+9ce961410f075c01643dbee74ad0f57155f799c2af1c0164e9cbebc9681c65a1
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-chrome_rendering_desktop_area_selection.png.sha256 b/test/data/ui-screenshots/ui-chrome_rendering_desktop_area_selection.png.sha256
new file mode 100644
index 0000000..6b5d5d7
--- /dev/null
+++ b/test/data/ui-screenshots/ui-chrome_rendering_desktop_area_selection.png.sha256
@@ -0,0 +1 @@
+8713b7474180ec39428930c6f11c156aaff4540da6725b37fc2466e8bbf91493
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-chrome_rendering_desktop_expand_browser_proc.png.sha256 b/test/data/ui-screenshots/ui-chrome_rendering_desktop_expand_browser_proc.png.sha256
index e223d72..9950bf8 100644
--- a/test/data/ui-screenshots/ui-chrome_rendering_desktop_expand_browser_proc.png.sha256
+++ b/test/data/ui-screenshots/ui-chrome_rendering_desktop_expand_browser_proc.png.sha256
@@ -1 +1 @@
-0f3c280c0b44ed4e9d4a0849512ca3f21914cd86581cde1bb5e6f1dce4ea7843
\ No newline at end of file
+b94937e792f3103cbb504357fb3cf4c7e0e6e0bad788c8eef05fb7954f52abf9
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-chrome_rendering_desktop_load.png.sha256 b/test/data/ui-screenshots/ui-chrome_rendering_desktop_load.png.sha256
index b4eb4f7..bd9d924 100644
--- a/test/data/ui-screenshots/ui-chrome_rendering_desktop_load.png.sha256
+++ b/test/data/ui-screenshots/ui-chrome_rendering_desktop_load.png.sha256
@@ -1 +1 @@
-77ad6a3f90b414957bd1fd78d39ceb380b28f384b4d4a0a3ed1e11e394f635be
\ No newline at end of file
+fd6a8a39085b3b2d4fd9f719f08c3f529eac99c06f30dc457278ebc4231dc6fe
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-chrome_rendering_desktop_select_slice_with_flows.png.sha256 b/test/data/ui-screenshots/ui-chrome_rendering_desktop_select_slice_with_flows.png.sha256
index 43b10e1..70a462d 100644
--- a/test/data/ui-screenshots/ui-chrome_rendering_desktop_select_slice_with_flows.png.sha256
+++ b/test/data/ui-screenshots/ui-chrome_rendering_desktop_select_slice_with_flows.png.sha256
@@ -1 +1 @@
-062485c571af2770af36e7356dc8d238e4a49b6340c6d6df7d9205a8002eab73
\ No newline at end of file
+72b31cf41144b44831443984cf80abe87fb213a8d73df767268d7c9ba0a63398
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-modal_dialog_show_dialog_1.png.sha256 b/test/data/ui-screenshots/ui-modal_dialog_show_dialog_1.png.sha256
index 1bc4017..5863d55 100644
--- a/test/data/ui-screenshots/ui-modal_dialog_show_dialog_1.png.sha256
+++ b/test/data/ui-screenshots/ui-modal_dialog_show_dialog_1.png.sha256
@@ -1 +1 @@
-9da477a6b9736f3e06a2940543ef54fb86ed21a9ec975d61851d99cb16004e59
\ No newline at end of file
+f3c0069c0ef9643552081835fd06c972b87448b7df5ac538a4d3e213a437bcb1
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-modal_dialog_show_dialog_2.png.sha256 b/test/data/ui-screenshots/ui-modal_dialog_show_dialog_2.png.sha256
index d5dcb60..e70add7 100644
--- a/test/data/ui-screenshots/ui-modal_dialog_show_dialog_2.png.sha256
+++ b/test/data/ui-screenshots/ui-modal_dialog_show_dialog_2.png.sha256
@@ -1 +1 @@
-f8bcea9fc715ac226cd7a50201eac183603c22b881070265d2fb9fb015357434
\ No newline at end of file
+04b6ed7959eef3625b2d25d9daa68e7caea3f8ad4846234a45a6c264aebc8e47
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_navigate_navigate_back_and_forward.png.sha256 b/test/data/ui-screenshots/ui-routing_navigate_navigate_back_and_forward.png.sha256
index d43f596..035f335 100644
--- a/test/data/ui-screenshots/ui-routing_navigate_navigate_back_and_forward.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_navigate_navigate_back_and_forward.png.sha256
@@ -1 +1 @@
-1b3e711b1928f603d789340459257eaf75a92f96f5b6d6afdabf20bdca7c0341
\ No newline at end of file
+e8ffe0046ea98d27b4216c8d1ff64c665392c90ab3d90e2db99069ae54d12707
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_navigate_open_trace_from_url.png.sha256 b/test/data/ui-screenshots/ui-routing_navigate_open_trace_from_url.png.sha256
index 1f5c528..d9fed2d 100644
--- a/test/data/ui-screenshots/ui-routing_navigate_open_trace_from_url.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_navigate_open_trace_from_url.png.sha256
@@ -1 +1 @@
-59bcbc50cdacd66ea38894a1831b49459ea24affb8347d4566bdff2035596414
\ No newline at end of file
+a9bf192426e9a29380ae8dd47d3974e8607bc7fd01ab0498e9b2cb1cdca75acb
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_open_trace_and_go_back_to_landing_page.png.sha256 b/test/data/ui-screenshots/ui-routing_open_trace_and_go_back_to_landing_page.png.sha256
index 4f40c30..727703f 100644
--- a/test/data/ui-screenshots/ui-routing_open_trace_and_go_back_to_landing_page.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_open_trace_and_go_back_to_landing_page.png.sha256
@@ -1 +1 @@
-6150b9e984e4e8ace644002edd9f643f696a3cf4c220e2ab426a27f606212d1d
\ No newline at end of file
+2c07ddb7c8b3cdede9cb9aec41ab8f206ee50e6fa789e8913044e209141a9365
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_access_subpage_then_go_back.png.sha256 b/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_access_subpage_then_go_back.png.sha256
index cfb384c..4ee2372 100644
--- a/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_access_subpage_then_go_back.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_access_subpage_then_go_back.png.sha256
@@ -1 +1 @@
-a07fb42e61182f2409a46266a77c592d869c977eb4fab642bbc8c81ab9ec6cc1
\ No newline at end of file
+c4773556fad9c5b83fc6d8cd2f271faef9e088da862b32a0b1ef14f9d56fa162
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_open_first_trace_from_url.png.sha256 b/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_open_first_trace_from_url.png.sha256
index 1f5c528..d9fed2d 100644
--- a/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_open_first_trace_from_url.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_open_first_trace_from_url.png.sha256
@@ -1 +1 @@
-59bcbc50cdacd66ea38894a1831b49459ea24affb8347d4566bdff2035596414
\ No newline at end of file
+a9bf192426e9a29380ae8dd47d3974e8607bc7fd01ab0498e9b2cb1cdca75acb
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_open_second_trace_from_url.png.sha256 b/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_open_second_trace_from_url.png.sha256
index 32cf9b1..4ee2372 100644
--- a/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_open_second_trace_from_url.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_open_two_traces_then_go_back_open_second_trace_from_url.png.sha256
@@ -1 +1 @@
-6bb4ec59ccea148ed9ddd2878dad220dcdeb0264a3a61334c9ee66d57cd8094d
\ No newline at end of file
+c4773556fad9c5b83fc6d8cd2f271faef9e088da862b32a0b1ef14f9d56fa162
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_start_from_no_trace_go_back_to_first_trace.png.sha256 b/test/data/ui-screenshots/ui-routing_start_from_no_trace_go_back_to_first_trace.png.sha256
index 37bf83c..a00bc34 100644
--- a/test/data/ui-screenshots/ui-routing_start_from_no_trace_go_back_to_first_trace.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_start_from_no_trace_go_back_to_first_trace.png.sha256
@@ -1 +1 @@
-3c7d8d5425911d796df29d75c60413e72bf72d14a17d949ebbc839f6b2ec844b
\ No newline at end of file
+c3461778784561480bd02f6202651ffa3736ac19abfefc8ddd3bbed1e5a2fe08
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_second_trace.png.sha256 b/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_second_trace.png.sha256
index cda53a0..d9fed2d 100644
--- a/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_second_trace.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_second_trace.png.sha256
@@ -1 +1 @@
-f3436f970e4fd8103a97478ca4ccd97d74080a69403780fe5b3cef3582a7dfe1
\ No newline at end of file
+a9bf192426e9a29380ae8dd47d3974e8607bc7fd01ab0498e9b2cb1cdca75acb
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_trace_.png.sha256 b/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_trace_.png.sha256
index cfb384c..4ee2372 100644
--- a/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_trace_.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_start_from_no_trace_open_trace_.png.sha256
@@ -1 +1 @@
-a07fb42e61182f2409a46266a77c592d869c977eb4fab642bbc8c81ab9ec6cc1
\ No newline at end of file
+c4773556fad9c5b83fc6d8cd2f271faef9e088da862b32a0b1ef14f9d56fa162
\ No newline at end of file
diff --git a/test/data/ui-screenshots/ui-routing_start_from_no_trace_refresh.png.sha256 b/test/data/ui-screenshots/ui-routing_start_from_no_trace_refresh.png.sha256
index cfb384c..4ee2372 100644
--- a/test/data/ui-screenshots/ui-routing_start_from_no_trace_refresh.png.sha256
+++ b/test/data/ui-screenshots/ui-routing_start_from_no_trace_refresh.png.sha256
@@ -1 +1 @@
-a07fb42e61182f2409a46266a77c592d869c977eb4fab642bbc8c81ab9ec6cc1
\ No newline at end of file
+c4773556fad9c5b83fc6d8cd2f271faef9e088da862b32a0b1ef14f9d56fa162
\ No newline at end of file
diff --git a/test/ftrace_integrationtest.cc b/test/ftrace_integrationtest.cc
index 42d251f..068ebf7 100644
--- a/test/ftrace_integrationtest.cc
+++ b/test/ftrace_integrationtest.cc
@@ -173,7 +173,7 @@
   helper.StartTracing(trace_config);
 
   // Wait for traced_probes to start.
-  helper.WaitFor([&] { return ftrace_procfs_->IsTracingEnabled(); }, "ftrace");
+  helper.WaitFor([&] { return ftrace_procfs_->GetTracingOn(); }, "ftrace");
 
   // Do a first flush just to synchronize with the producer. The problem here
   // is that, on a Linux workstation, the producer can take several seconds just
diff --git a/test/gtest_and_gmock.h b/test/gtest_and_gmock.h
index 82918d8..710d07d 100644
--- a/test/gtest_and_gmock.h
+++ b/test/gtest_and_gmock.h
@@ -27,14 +27,14 @@
 
 #include "perfetto/base/build_config.h"
 
-#if defined(__GNUC__)  // GCC & clang
+#if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wundef"
 #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__
+#endif  // defined(__GNUC__) || defined(__clang__)
 
 #if defined(__clang__)
 #pragma GCC diagnostic ignored "-Wshift-sign-overflow"
@@ -44,12 +44,12 @@
 #pragma GCC diagnostic ignored "-Wcomma"
 #endif  // PERFETTO_OS_NACL
 
-#endif
+#endif  // defined(__clang__)
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-#if defined(__GNUC__)
+#if defined(__GNUC__) || defined(__clang__)
 #pragma GCC diagnostic pop
 #endif
 
diff --git a/test/sanitizers/sanitizers_unittest.cc b/test/sanitizers/sanitizers_unittest.cc
index 8655dbc..3cbf583 100644
--- a/test/sanitizers/sanitizers_unittest.cc
+++ b/test/sanitizers/sanitizers_unittest.cc
@@ -75,10 +75,10 @@
   EXPECT_DEATH(
       {
         std::unique_ptr<int> alloc(new int(1));
-        *reinterpret_cast<volatile char*>(alloc.get()) = 1;
+        *reinterpret_cast<volatile int*>(alloc.get()) = 1;
         alloc.release();
         alloc.reset(new int(2));
-        *reinterpret_cast<volatile char*>(alloc.get()) = 2;
+        *reinterpret_cast<volatile int*>(alloc.get()) = 2;
         exit(0);  // LSan runs on the atexit handler.
       },
       "LeakSanitizer:.*detected memory leaks");
diff --git a/test/stress_test/stress_test.cc b/test/stress_test/stress_test.cc
index 1420d98..213017e 100644
--- a/test/stress_test/stress_test.cc
+++ b/test/stress_test/stress_test.cc
@@ -27,6 +27,7 @@
 
 #include "perfetto/base/build_config.h"
 #include "perfetto/base/compiler.h"
+#include "perfetto/base/time.h"
 #include "perfetto/ext/base/ctrl_c_handler.h"
 #include "perfetto/ext/base/file_utils.h"
 #include "perfetto/ext/base/scoped_file.h"
@@ -452,7 +453,7 @@
   }
 
   g_sig = new SigHandlerCtx();
-  base::InstallCtrCHandler(&CtrlCHandler);
+  base::InstallCtrlCHandler(&CtrlCHandler);
 
   for (size_t i = 0; i < base::ArraySize(kStressTestConfigs) && !g_sig->aborted;
        ++i) {
diff --git a/test/synth_common.py b/test/synth_common.py
index be52735..7dadca0 100644
--- a/test/synth_common.py
+++ b/test/synth_common.py
@@ -102,7 +102,7 @@
         ss.prev_state = 0
       elif prev_state == 'S':
         ss.prev_state = 1
-      elif prev_state == 'U':
+      elif prev_state == 'U' or prev_state == 'D':
         ss.prev_state = 2
       else:
         raise Exception('Invalid prev state {}'.format(prev_state))
diff --git a/test/test_helper.cc b/test/test_helper.cc
index 79c054a..bc8cfdf 100644
--- a/test/test_helper.cc
+++ b/test/test_helper.cc
@@ -93,9 +93,10 @@
     PERFETTO_CHECK(
         packet.ParseFromString(encoded_packet.GetRawBytesForTesting()));
     full_trace_.push_back(packet);
-    if (packet.has_clock_snapshot() || packet.has_trace_config() ||
-        packet.has_trace_stats() || !packet.synchronization_marker().empty() ||
-        packet.has_system_info() || packet.has_service_event()) {
+    if (packet.has_clock_snapshot() || packet.has_trace_uuid() ||
+        packet.has_trace_config() || packet.has_trace_stats() ||
+        !packet.synchronization_marker().empty() || packet.has_system_info() ||
+        packet.has_service_event()) {
       continue;
     }
     PERFETTO_CHECK(packet.has_trusted_uid());
@@ -112,7 +113,7 @@
 
 void TestHelper::StartServiceIfRequired() {
   if (mode_ == Mode::kStartDaemons)
-    service_thread_.Start();
+    env_cleaner_ = service_thread_.Start();
 }
 
 FakeProducer* TestHelper::ConnectFakeProducer() {
@@ -295,6 +296,8 @@
 
 void TestHelper::OnObservableEvents(const ObservableEvents&) {}
 
+void TestHelper::OnSessionCloned(bool, const std::string&) {}
+
 // static
 const char* TestHelper::GetDefaultModeConsumerSocketName() {
   return ConsumerSocketForMode(TestHelper::kDefaultMode);
diff --git a/test/test_helper.h b/test/test_helper.h
index 7331c64..afc0830 100644
--- a/test/test_helper.h
+++ b/test/test_helper.h
@@ -64,6 +64,54 @@
 #endif
 }
 
+// Captures the values of some environment variables when constructed and
+// restores them when destroyed.
+class TestEnvCleaner {
+ public:
+  TestEnvCleaner() {}
+  TestEnvCleaner(std::initializer_list<const char*> env_vars) {
+    prev_state_.reserve(env_vars.size());
+    for (const char* name : env_vars) {
+      prev_state_.emplace_back();
+      Var& var = prev_state_.back();
+      var.name = name;
+      const char* prev_value = getenv(name);
+      if (prev_value) {
+        var.value.emplace(prev_value);
+      }
+    }
+  }
+  ~TestEnvCleaner() { Clean(); }
+
+  TestEnvCleaner(const TestEnvCleaner&) = delete;
+  TestEnvCleaner(TestEnvCleaner&& obj) noexcept { *this = std::move(obj); }
+  TestEnvCleaner& operator=(const TestEnvCleaner&) = delete;
+  TestEnvCleaner& operator=(TestEnvCleaner&& obj) noexcept {
+    PERFETTO_CHECK(prev_state_.empty());
+    this->prev_state_ = std::move(obj.prev_state_);
+    obj.prev_state_.clear();
+    return *this;
+  }
+
+  void Clean() {
+    for (const Var& var : prev_state_) {
+      if (var.value) {
+        base::SetEnv(var.name, *var.value);
+      } else {
+        base::UnsetEnv(var.name);
+      }
+    }
+    prev_state_.clear();
+  }
+
+ private:
+  struct Var {
+    const char* name;
+    base::Optional<std::string> value;
+  };
+  std::vector<Var> prev_state_;
+};
+
 // This is used only in daemon starting integrations tests.
 class ServiceThread {
  public:
@@ -77,7 +125,9 @@
     runner_->PostTaskAndWaitForTesting([this]() { svc_.reset(); });
   }
 
-  void Start() {
+  TestEnvCleaner Start() {
+    TestEnvCleaner env_cleaner(
+        {"PERFETTO_PRODUCER_SOCK_NAME", "PERFETTO_CONSUMER_SOCK_NAME"});
     runner_ = base::ThreadTaskRunner::CreateAndStart("perfetto.svc");
     runner_->PostTaskAndWaitForTesting([this]() {
       svc_ = ServiceIPCHost::CreateInstance(runner_->get());
@@ -98,6 +148,7 @@
                        producer_socket_.c_str(), consumer_socket_.c_str());
       }
     });
+    return env_cleaner;
   }
 
   base::ThreadTaskRunner* runner() { return runner_ ? &*runner_ : nullptr; }
@@ -234,6 +285,7 @@
   void OnAttach(bool, const TraceConfig&) override;
   void OnTraceStats(bool, const TraceStats&) override;
   void OnObservableEvents(const ObservableEvents&) override;
+  void OnSessionCloned(bool, const std::string&) override;
 
   // Starts the tracing service if in kStartDaemons mode.
   void StartServiceIfRequired();
@@ -293,6 +345,13 @@
   }
   const std::vector<protos::gen::TracePacket>& trace() { return trace_; }
 
+  // Some fixtures want to reuse a global TestHelper in different testcases
+  // without destroying and recreating it, but they still need to avoid
+  // polluting environment variables.
+  //
+  // This restores the previous environment variables.
+  void CleanEnv() { env_cleaner_.Clean(); }
+
  private:
   static uint64_t next_instance_num_;
   uint64_t instance_num_;
@@ -315,6 +374,8 @@
   ServiceThread service_thread_;
   FakeProducerThread fake_producer_thread_;
 
+  TestEnvCleaner env_cleaner_;
+
   std::unique_ptr<TracingService::ConsumerEndpoint> endpoint_;  // Keep last.
 };
 
diff --git a/test/trace_processor/PRESUBMIT.py b/test/trace_processor/PRESUBMIT.py
index a882d75..23d5d64 100644
--- a/test/trace_processor/PRESUBMIT.py
+++ b/test/trace_processor/PRESUBMIT.py
@@ -18,6 +18,9 @@
 from os.path import relpath
 
 
+USE_PYTHON3 = True
+
+
 def RunAndReportIfLong(func, *args, **kargs):
   start = time.time()
   results = func(*args, **kargs)
diff --git a/test/trace_processor/android/android_bugreport_logs_test.out b/test/trace_processor/android/android_bugreport_logs_test.out
deleted file mode 100644
index 041f9ba..0000000
--- a/test/trace_processor/android/android_bugreport_logs_test.out
+++ /dev/null
@@ -1,11 +0,0 @@
-"cnt","ts","prio","tag","msg"
-33073,1629848143333617000,4,"crash_dump64","performing dump of process 733 (target tid = 733)"
-33073,1629848143333382000,4,"tombstoned","found intercept fd 512 for pid 733 and type kDebuggerdNativeBacktrace"
-33073,1629848143333341000,4,"tombstoned","received crash request for pid 733"
-33073,1629848143332905000,4,"crash_dump64","obtaining output fd from tombstoned, type: kDebuggerdNativeBacktrace"
-33073,1629848143317213000,4,"","c1      1 init: Untracked pid 8530 exited with status 0"
-33073,1629848143316238000,4,"","c1      1 init: Untracked pid 8528 exited with status 0"
-33073,1629848143314679000,4,"libc","Requested dump for tid 733 (Binder:733_2)"
-33073,1629848143314401000,4,"tombstoned","registered intercept for pid 733 and type kDebuggerdNativeBacktrace"
-33073,1629848143313939000,4,"dumpstate","libdebuggerd_client: started dumping process 733"
-33073,1629848143312890000,5,"tombstoned","missing output fd"
diff --git a/test/trace_processor/android/android_bugreport_logs_test.sql b/test/trace_processor/android/android_bugreport_logs_test.sql
deleted file mode 100644
index bebeab5..0000000
--- a/test/trace_processor/android/android_bugreport_logs_test.sql
+++ /dev/null
@@ -1,9 +0,0 @@
-select
-  (select count(*) from android_logs) as cnt,
-  ts,
-  prio,
-  tag,
-  msg
-from android_logs
-order by ts desc
-limit 10;
diff --git a/test/trace_processor/android/android_system_property.textproto b/test/trace_processor/android/android_system_property.textproto
deleted file mode 100644
index ffcb98d..0000000
--- a/test/trace_processor/android/android_system_property.textproto
+++ /dev/null
@@ -1,32 +0,0 @@
-packet {
-  timestamp: 1000
-  android_system_property {
-    values {
-      name: "debug.tracing.screen_state"
-      value: "2"
-    }
-    values {
-      name: "debug.tracing.device_state"
-      value: "some_state_from_sysprops"
-    }
-  }
-}
-packet {
-  ftrace_events {
-    cpu: 1
-    event {
-      timestamp: 2000
-      pid: 1
-      print {
-        buf: "C|1000|ScreenState|1\n"
-      }
-    }
-    event {
-      timestamp: 3000
-      pid: 1
-      print {
-        buf: "N|1000|DeviceStateChanged|some_state_from_atrace\n"
-      }
-    }
-  }
-}
diff --git a/test/trace_processor/android/android_system_property_counter.out b/test/trace_processor/android/android_system_property_counter.out
deleted file mode 100644
index 9a52c9f..0000000
--- a/test/trace_processor/android/android_system_property_counter.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"id","type","name","id","ts","type","value"
-0,"counter_track","ScreenState",0,1000,"counter",2.000000
-0,"counter_track","ScreenState",1,2000,"counter",1.000000
diff --git a/test/trace_processor/android/android_system_property_counter_test.sql b/test/trace_processor/android/android_system_property_counter_test.sql
deleted file mode 100644
index 580e4a9..0000000
--- a/test/trace_processor/android/android_system_property_counter_test.sql
+++ /dev/null
@@ -1,3 +0,0 @@
-select t.id, t.type, t.name, c.id, c.ts, c.type, c.value
-from counter_track t join counter c on t.id = c.track_id
-where name = 'ScreenState';
diff --git a/test/trace_processor/android/android_system_property_slice_test.sql b/test/trace_processor/android/android_system_property_slice_test.sql
deleted file mode 100644
index 96e0fcf..0000000
--- a/test/trace_processor/android/android_system_property_slice_test.sql
+++ /dev/null
@@ -1,3 +0,0 @@
-select t.id, t.type, t.name, s.id, s.ts, s.dur, s.type, s.name
-from track t join slice s on s.track_id = t.id
-where t.name = 'DeviceStateChanged';
diff --git a/test/trace_processor/android/game_intervention_list_test.sql b/test/trace_processor/android/game_intervention_list_test.sql
deleted file mode 100644
index 5ecef53..0000000
--- a/test/trace_processor/android/game_intervention_list_test.sql
+++ /dev/null
@@ -1,34 +0,0 @@
---
--- Copyright 2022 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
-  package_name,
-  uid,
-  current_mode,
-  standard_mode_supported,
-  standard_mode_downscale,
-  standard_mode_use_angle,
-  standard_mode_fps,
-  perf_mode_supported,
-  perf_mode_downscale,
-  perf_mode_use_angle,
-  perf_mode_fps,
-  battery_mode_supported,
-  battery_mode_downscale,
-  battery_mode_use_angle,
-  battery_mode_fps
-FROM android_game_intervention_list
-ORDER BY package_name
diff --git a/test/trace_processor/android/game_intervention_list_test.textproto b/test/trace_processor/android/game_intervention_list_test.textproto
deleted file mode 100644
index 62a21e9..0000000
--- a/test/trace_processor/android/game_intervention_list_test.textproto
+++ /dev/null
@@ -1,46 +0,0 @@
-packet {
-    android_game_intervention_list {
-      parse_error: false
-      read_error: false
-      game_packages {
-        name: "com.test.game1"
-        uid: 1001
-        current_mode: 1
-        game_mode_info {
-          mode: 1
-          use_angle: true
-          resolution_downscale: 1.0
-          fps: 0.0
-        }
-        game_mode_info {
-          mode: 2
-          use_angle: false
-          resolution_downscale: 1.0
-          fps: 60.0
-        }
-        game_mode_info {
-          mode: 3
-          use_angle: true
-          resolution_downscale: 0.75
-          fps: 120.0
-        }
-      }
-      game_packages {
-        name: "com.test.game2"
-        uid: 1002
-        current_mode: 3
-        game_mode_info {
-          mode: 1
-          use_angle: false
-          resolution_downscale: 1.0
-          fps: 0.0
-        }
-        game_mode_info {
-          mode: 3
-          use_angle: false
-          resolution_downscale:  0.95
-          fps: 45.0
-        }
-      }
-    }
-}
\ No newline at end of file
diff --git a/test/trace_processor/android/index b/test/trace_processor/android/index
deleted file mode 100644
index 498ba708..0000000
--- a/test/trace_processor/android/index
+++ /dev/null
@@ -1,7 +0,0 @@
-# Ensure Android game intervntion list are parsed correctly
-game_intervention_list_test.textproto game_intervention_list_test.sql game_intervention_list_test.out
-
-android_system_property.textproto android_system_property_counter_test.sql android_system_property_counter.out
-android_system_property.textproto android_system_property_slice_test.sql android_system_property_slice.out
-
-../../data/bugreport-crosshatch-SPB5.zip android_bugreport_logs_test.sql android_bugreport_logs_test.out
diff --git a/test/trace_processor/atrace/android_async_slice.textproto b/test/trace_processor/atrace/android_async_slice.textproto
deleted file mode 100644
index 787d929..0000000
--- a/test/trace_processor/atrace/android_async_slice.textproto
+++ /dev/null
@@ -1,26 +0,0 @@
-packet {
-  ftrace_events {
-    cpu: 3
-    event {
-      timestamp: 74289018336
-      pid: 4064
-      print {
-        ip: 18446743562018522420
-        buf: "S|1204|launching: com.android.chrome|0\n"
-      }
-    }
-  }
-}
-packet {
-  ftrace_events {
-    cpu: 2
-    event {
-      timestamp: 74662603008
-      pid: 1257
-      print {
-        ip: 18446743562018522420
-        buf: "F|1204|launching: com.android.chrome|0\n"
-      }
-    }
-  }
-}
diff --git a/test/trace_processor/atrace/android_b2b_async_begin_list_slices.out b/test/trace_processor/atrace/android_b2b_async_begin_list_slices.out
deleted file mode 100644
index a11b96c..0000000
--- a/test/trace_processor/atrace/android_b2b_async_begin_list_slices.out
+++ /dev/null
@@ -1,4 +0,0 @@
-"ts","dur","name"
-1000,30,"multistart"
-1015,45,"multistart"
-1030,20,"multistart"
diff --git a/test/trace_processor/atrace/async_track_atrace_process_track_slices.out b/test/trace_processor/atrace/async_track_atrace_process_track_slices.out
deleted file mode 100644
index 3023fcf..0000000
--- a/test/trace_processor/atrace/async_track_atrace_process_track_slices.out
+++ /dev/null
@@ -1,4 +0,0 @@
-"ts","dur","pid","slice_name","track_name"
-50,25,1,"ev","track"
-55,15,1,"ev","track"
-60,5,2,"ev","track"
diff --git a/test/trace_processor/atrace/bad_print_systrace_list_slices.out b/test/trace_processor/atrace/bad_print_systrace_list_slices.out
deleted file mode 100644
index 6b081c8..0000000
--- a/test/trace_processor/atrace/bad_print_systrace_list_slices.out
+++ /dev/null
@@ -1,2 +0,0 @@
-"ts","dur","name"
-10852771242000,3000,"some event"
diff --git a/test/trace_processor/atrace/bad_print_textproto_list_slices.out b/test/trace_processor/atrace/bad_print_textproto_list_slices.out
deleted file mode 100644
index 1f44a90..0000000
--- a/test/trace_processor/atrace/bad_print_textproto_list_slices.out
+++ /dev/null
@@ -1,2 +0,0 @@
-"ts","dur","name"
-74662603048,2,"valid_print"
diff --git a/test/trace_processor/atrace/index b/test/trace_processor/atrace/index
deleted file mode 100644
index 02b6798..0000000
--- a/test/trace_processor/atrace/index
+++ /dev/null
@@ -1,13 +0,0 @@
-# Check error handling when parsing print events.
-bad_print.textproto ../common/list_slices_test.sql bad_print_textproto_list_slices.out
-bad_print.systrace ../common/list_slices_test.sql bad_print_systrace_list_slices.out
-instant_atrace.py instant_with_thread_test.sql instant_atrace_instant_with_thread.out
-instant_async_atrace.py instant_async_test.sql instant_async_atrace_instant_async.out
-
-# Match legacy Catapult behaviour when we see multiple S events b2b with the same cookie
-# name and upid.
-android_b2b_async_begin.textproto ../common/list_slices_test.sql android_b2b_async_begin_list_slices.out
-
-# Android userspace async slices
-android_async_slice.textproto ../common/process_track_slices_test.sql process_track_slices_android_async_slice.out
-async_track_atrace.py ../common/process_track_slices_test.sql async_track_atrace_process_track_slices.out
diff --git a/test/trace_processor/atrace/instant_async_atrace_instant_async.out b/test/trace_processor/atrace/instant_async_atrace_instant_async.out
deleted file mode 100644
index d95fe51..0000000
--- a/test/trace_processor/atrace/instant_async_atrace_instant_async.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"process_name","track_name","instant_name","ts"
-"p2","track_p2","ev1",51
-"p1","track_p1","ev2",53
diff --git a/test/trace_processor/atrace/instant_async_test.sql b/test/trace_processor/atrace/instant_async_test.sql
deleted file mode 100644
index a02b2ba..0000000
--- a/test/trace_processor/atrace/instant_async_test.sql
+++ /dev/null
@@ -1,9 +0,0 @@
-SELECT
-  process.name AS process_name,
-  process_track.name as track_name,
-  instant.name as instant_name,
-  ts
-FROM slice instant
-JOIN process_track ON instant.track_id = process_track.id
-JOIN process USING (upid)
-WHERE dur = 0;
diff --git a/test/trace_processor/atrace/instant_atrace_instant_with_thread.out b/test/trace_processor/atrace/instant_atrace_instant_with_thread.out
deleted file mode 100644
index c1b7305..0000000
--- a/test/trace_processor/atrace/instant_atrace_instant_with_thread.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"thread_name","track_name","ts"
-"t2","t2_event",51
-"t1","t1_event",53
diff --git a/test/trace_processor/atrace/instant_with_thread_test.sql b/test/trace_processor/atrace/instant_with_thread_test.sql
deleted file mode 100644
index 2e39608..0000000
--- a/test/trace_processor/atrace/instant_with_thread_test.sql
+++ /dev/null
@@ -1,5 +0,0 @@
-SELECT thread.name as thread_name, instant.name as track_name, instant.ts
-FROM slice instant
-JOIN thread_track ON instant.track_id = thread_track.id
-JOIN thread USING (utid)
-WHERE dur = 0;
diff --git a/test/trace_processor/camera/camera-ion-mem-trace_android_camera.out b/test/trace_processor/camera/camera-ion-mem-trace_android_camera.out
deleted file mode 100644
index 3928113..0000000
--- a/test/trace_processor/camera/camera-ion-mem-trace_android_camera.out
+++ /dev/null
@@ -1,7 +0,0 @@
-android_camera {
-  gc_rss_and_dma {
-    min: 47779840.0
-    max: 2529583104.0
-    avg: 1459479416.3297353
-  }
-}
diff --git a/test/trace_processor/camera/index b/test/trace_processor/camera/index
deleted file mode 100644
index 5927fa4..0000000
--- a/test/trace_processor/camera/index
+++ /dev/null
@@ -1,2 +0,0 @@
-../../data/camera-ion-mem-trace android_camera camera-ion-mem-trace_android_camera.out
-../../data/camera-ion-mem-trace android_camera_unagg camera-ion-mem-trace_android_camera_unagg.out
diff --git a/test/trace_processor/chrome/actual_power_by_combined_rail_mode.out b/test/trace_processor/chrome/actual_power_by_combined_rail_mode.out
deleted file mode 100644
index 6c6e08cf..0000000
--- a/test/trace_processor/chrome/actual_power_by_combined_rail_mode.out
+++ /dev/null
@@ -1,12 +0,0 @@
-
-"id","ts","dur","rail_mode","subsystem","joules","drain_w"
-1,0,10000000,"response","cellular",0.000000,0.000000
-1,0,10000000,"response","cpu_little",0.000140,0.014000
-2,10000000,20000000,"animation","cellular",0.000350,0.017500
-2,10000000,20000000,"animation","cpu_little",0.000140,0.007000
-3,30000000,5000000,"background","cellular",0.000018,0.003500
-3,30000000,5000000,"background","cpu_little",0.000007,0.001400
-4,35000000,10000000,"animation","cellular",0.000021,0.002100
-4,35000000,10000000,"animation","cpu_little",0.000070,0.007000
-5,45000000,10000000,"background","cellular",0.000003,0.000350
-5,45000000,10000000,"background","cpu_little",0.000070,0.007000
diff --git a/test/trace_processor/chrome/actual_power_by_combined_rail_mode_test.sql b/test/trace_processor/chrome/actual_power_by_combined_rail_mode_test.sql
deleted file mode 100644
index 9690f17..0000000
--- a/test/trace_processor/chrome/actual_power_by_combined_rail_mode_test.sql
+++ /dev/null
@@ -1,17 +0,0 @@
--- 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('chrome/chrome_processes.sql') AS suppress_query_output;
--- SELECT * FROM cpu_time_by_rail_mode;
-SELECT RUN_METRIC('chrome/actual_power_by_rail_mode.sql') AS suppress_query_output;
-SELECT * FROM real_power_by_rail_mode;
diff --git a/test/trace_processor/chrome/async-trace-1_count_slices.out b/test/trace_processor/chrome/async-trace-1_count_slices.out
deleted file mode 100644
index 4f8f580..0000000
--- a/test/trace_processor/chrome/async-trace-1_count_slices.out
+++ /dev/null
@@ -1,2 +0,0 @@
-"COUNT(1)"
-16
diff --git a/test/trace_processor/chrome/async-trace-2_count_slices.out b/test/trace_processor/chrome/async-trace-2_count_slices.out
deleted file mode 100644
index d2e85f9..0000000
--- a/test/trace_processor/chrome/async-trace-2_count_slices.out
+++ /dev/null
@@ -1,2 +0,0 @@
-"COUNT(1)"
-35
diff --git a/test/trace_processor/chrome/chrome_dropped_frames_metric.out b/test/trace_processor/chrome/chrome_dropped_frames_metric.out
deleted file mode 100644
index f1fbf5a..0000000
--- a/test/trace_processor/chrome/chrome_dropped_frames_metric.out
+++ /dev/null
@@ -1,12 +0,0 @@
-[perfetto.protos.chrome_dropped_frames]: {
-  dropped_frame: {
-    ts: 166479338462000
-    process_name: "Renderer"
-    pid: 12743
-  }
-  dropped_frame: {
-    ts: 166479355302000
-    process_name: "Renderer"
-    pid: 12743
-  }
-}
\ No newline at end of file
diff --git a/test/trace_processor/chrome/chrome_dropped_frames_metric_test.sql b/test/trace_processor/chrome/chrome_dropped_frames_metric_test.sql
deleted file mode 100644
index 372d8df..0000000
--- a/test/trace_processor/chrome/chrome_dropped_frames_metric_test.sql
+++ /dev/null
@@ -1,18 +0,0 @@
---
--- Copyright 2021 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('experimental/chrome_dropped_frames.sql') AS suppress_query_output;
-
-SELECT * FROM dropped_frames_with_process_info;
\ No newline at end of file
diff --git a/test/trace_processor/chrome/chrome_histogram_hashes.out b/test/trace_processor/chrome/chrome_histogram_hashes.out
deleted file mode 100644
index 4cd61a5..0000000
--- a/test/trace_processor/chrome/chrome_histogram_hashes.out
+++ /dev/null
@@ -1,4 +0,0 @@
-[perfetto.protos.chrome_histogram_hashes]: {
-  hash: 10
-  hash: 20
-}
diff --git a/test/trace_processor/chrome/chrome_histogram_hashes.textproto b/test/trace_processor/chrome/chrome_histogram_hashes.textproto
deleted file mode 100644
index dfc1260..0000000
--- a/test/trace_processor/chrome/chrome_histogram_hashes.textproto
+++ /dev/null
@@ -1,27 +0,0 @@
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 0
-  incremental_state_cleared: true
-  track_event {
-    categories: "cat1"
-    type: 3
-    name_iid: 1
-    chrome_histogram_sample {
-      name_hash: 10
-      sample: 100
-    }
-  }
-}
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 0
-  incremental_state_cleared: true
-  track_event {
-    categories: "cat2"
-    type: 3
-    name_iid: 2
-    chrome_histogram_sample {
-      name_hash: 20
-    }
-  }
-}
diff --git a/test/trace_processor/chrome/chrome_input_to_browser_intervals_test.sql b/test/trace_processor/chrome/chrome_input_to_browser_intervals_test.sql
deleted file mode 100644
index c802094..0000000
--- a/test/trace_processor/chrome/chrome_input_to_browser_intervals_test.sql
+++ /dev/null
@@ -1,22 +0,0 @@
---
--- Copyright 2022 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('chrome/chrome_input_to_browser_intervals.sql') AS suppress_query_output;
-
-SELECT
-  *
-FROM chrome_input_to_browser_intervals
-WHERE window_start_ts >= 60934320005158
-  AND window_start_ts <= 60934338798158;
\ No newline at end of file
diff --git a/test/trace_processor/chrome/chrome_long_latency_metric.out b/test/trace_processor/chrome/chrome_long_latency_metric.out
deleted file mode 100644
index 8c47494..0000000
--- a/test/trace_processor/chrome/chrome_long_latency_metric.out
+++ /dev/null
@@ -1,5 +0,0 @@
-
-"ts","event_type","process_name","process_id"
-200111000,"FirstGestureScrollUpdate,GestureScrollUpdate","Renderer",1001
-200111000,"GestureScrollUpdate","Renderer",1002
-280111001,"GestureScrollUpdate","Renderer",1001
diff --git a/test/trace_processor/chrome/chrome_long_latency_metric_test.sql b/test/trace_processor/chrome/chrome_long_latency_metric_test.sql
deleted file mode 100644
index 13db1ff..0000000
--- a/test/trace_processor/chrome/chrome_long_latency_metric_test.sql
+++ /dev/null
@@ -1,18 +0,0 @@
---
--- Copyright 2022 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('experimental/chrome_long_latency.sql') AS suppress_query_output;
-
-SELECT * FROM long_latency_with_process_info;
\ No newline at end of file
diff --git a/test/trace_processor/chrome/chrome_performance_mark_hashes.out b/test/trace_processor/chrome/chrome_performance_mark_hashes.out
deleted file mode 100644
index 9c581ba..0000000
--- a/test/trace_processor/chrome/chrome_performance_mark_hashes.out
+++ /dev/null
@@ -1,6 +0,0 @@
-[perfetto.protos.chrome_performance_mark_hashes]: {
-  site_hash: 10
-  site_hash: 20
-  mark_hash: 100
-  mark_hash: 200
-}
diff --git a/test/trace_processor/chrome/chrome_performance_mark_hashes.textproto b/test/trace_processor/chrome/chrome_performance_mark_hashes.textproto
deleted file mode 100644
index fb53642..0000000
--- a/test/trace_processor/chrome/chrome_performance_mark_hashes.textproto
+++ /dev/null
@@ -1,28 +0,0 @@
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 0
-  incremental_state_cleared: true
-  track_event {
-    categories: "cat1"
-    type: 3
-    name: "name1"
-    [perfetto.protos.ChromeTrackEvent.chrome_hashed_performance_mark] {
-      site_hash: 10
-      mark_hash: 100
-    }
-  }
-}
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 0
-  incremental_state_cleared: true
-  track_event {
-    categories: "cat2"
-    type: 3
-    name: "name2"
-    [perfetto.protos.ChromeTrackEvent.chrome_hashed_performance_mark] {
-      site_hash: 20
-      mark_hash: 200
-    }
-  }
-}
diff --git a/test/trace_processor/chrome/chrome_processes.out b/test/trace_processor/chrome/chrome_processes.out
deleted file mode 100644
index 31cd59e..0000000
--- a/test/trace_processor/chrome/chrome_processes.out
+++ /dev/null
@@ -1,6 +0,0 @@
-
-"pid","name","process_type"
-18250,"Renderer","Renderer"
-17547,"Browser","Browser"
-18277,"GPU Process","Gpu"
-17578,"Browser","Browser"
diff --git a/test/trace_processor/chrome/chrome_processes_test.sql b/test/trace_processor/chrome/chrome_processes_test.sql
deleted file mode 100644
index 5ca4ded..0000000
--- a/test/trace_processor/chrome/chrome_processes_test.sql
+++ /dev/null
@@ -1,15 +0,0 @@
--- Copyright 2021 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('chrome/chrome_processes.sql') AS suppress_query_output;
-SELECT pid, name, process_type FROM chrome_process;
diff --git a/test/trace_processor/chrome/chrome_processes_type.out b/test/trace_processor/chrome/chrome_processes_type.out
deleted file mode 100644
index b6a42f9..0000000
--- a/test/trace_processor/chrome/chrome_processes_type.out
+++ /dev/null
@@ -1,5 +0,0 @@
-"pid","name","chrome_process_type"
-17547,"Browser","Browser"
-17578,"Browser","Browser"
-18250,"Renderer","Renderer"
-18277,"GPU Process","Gpu"
diff --git a/test/trace_processor/chrome/chrome_processes_type_test.sql b/test/trace_processor/chrome/chrome_processes_type_test.sql
deleted file mode 100644
index b857d83..0000000
--- a/test/trace_processor/chrome/chrome_processes_type_test.sql
+++ /dev/null
@@ -1,25 +0,0 @@
--- Copyright 2022 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 pid, name, string_value AS chrome_process_type
-FROM
-    process
--- EXTRACT_ARG doesn't make sense here - it would get chrome.process_type
--- (or NULL) for each process in the trace. But what we want is - a subset
--- of process that have a "chrome.process_type" argument, whether it's NULL or
--- not.
-JOIN
-    (SELECT * FROM args WHERE key = "chrome.process_type") chrome_process_args
-ON
-    process.arg_set_id = chrome_process_args.arg_set_id
-ORDER BY pid;
diff --git a/test/trace_processor/chrome/chrome_scroll_inputs_per_frame_test.out b/test/trace_processor/chrome/chrome_scroll_inputs_per_frame_test.out
deleted file mode 100644
index ae2c3db..0000000
--- a/test/trace_processor/chrome/chrome_scroll_inputs_per_frame_test.out
+++ /dev/null
@@ -1,3 +0,0 @@
-
-"count_for_frame","ts"
-4,60934316798158
diff --git a/test/trace_processor/chrome/chrome_scroll_inputs_per_frame_test.sql b/test/trace_processor/chrome/chrome_scroll_inputs_per_frame_test.sql
deleted file mode 100644
index 6f8bc5a..0000000
--- a/test/trace_processor/chrome/chrome_scroll_inputs_per_frame_test.sql
+++ /dev/null
@@ -1,20 +0,0 @@
--- Copyright 2022 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('chrome/chrome_scroll_inputs_per_frame.sql') AS suppress_query_output;
-
-SELECT
-count_for_frame,
-ts
-FROM chrome_scroll_inputs_per_frame
-WHERE ts = 60934316798158;
\ No newline at end of file
diff --git a/test/trace_processor/chrome/chrome_scroll_jank_caused_by_scheduling_test.out b/test/trace_processor/chrome/chrome_scroll_jank_caused_by_scheduling_test.out
deleted file mode 100644
index 5b78dca..0000000
--- a/test/trace_processor/chrome/chrome_scroll_jank_caused_by_scheduling_test.out
+++ /dev/null
@@ -1,3 +0,0 @@
-
-"full_name","total_duration_ms","total_thread_duration_ms","count","window_start_ts","window_end_ts"
-"RunTask(posted_from=cc/scheduler/scheduler.cc:PostPendingBeginFrameTask),RunTask(posted_from=cc/scheduler/scheduler.cc:ScheduleBeginImplFrameDeadline),SingleThreadProxy::BeginMainFrame(java_views=ToolbarLayout),blink.mojom.WidgetInputHandler reply (hash=3392143105),cc.mojom.RenderFrameMetadataObserverClient message (hash=330497194),viz.mojom.CompositorFrameSinkClient message (hash=3114070324),viz.mojom.CompositorFrameSinkClient message (hash=50871626),viz.mojom.FrameSinkManagerClient message (hash=532012934)",7.568000,6.745000,11,666960999011,666972176011
diff --git a/test/trace_processor/chrome/chrome_scroll_jank_caused_by_scheduling_test.sql b/test/trace_processor/chrome/chrome_scroll_jank_caused_by_scheduling_test.sql
deleted file mode 100644
index 4caea94..0000000
--- a/test/trace_processor/chrome/chrome_scroll_jank_caused_by_scheduling_test.sql
+++ /dev/null
@@ -1,27 +0,0 @@
---
--- Copyright 2022 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('chrome/chrome_scroll_jank_caused_by_scheduling.sql',
-'dur_causes_jank_ms',
-/* dur_causes_jank_ms = */ '5') AS suppress_query_output;
-
-SELECT
-  full_name,
-  total_duration_ms,
-  total_thread_duration_ms,
-  count,
-  window_start_ts,
-  window_end_ts
-FROM chrome_scroll_jank_caused_by_scheduling;
\ No newline at end of file
diff --git a/test/trace_processor/chrome/chrome_slice_names.out b/test/trace_processor/chrome/chrome_slice_names.out
deleted file mode 100644
index b6890d2..0000000
--- a/test/trace_processor/chrome/chrome_slice_names.out
+++ /dev/null
@@ -1,5 +0,0 @@
-[perfetto.protos.chrome_slice_names]: {
-  chrome_version_code: 123
-  slice_name: "Looper.Dispatch: class1"
-  slice_name: "name2"
-}
diff --git a/test/trace_processor/chrome/chrome_slice_names.textproto b/test/trace_processor/chrome/chrome_slice_names.textproto
deleted file mode 100644
index f864416..0000000
--- a/test/trace_processor/chrome/chrome_slice_names.textproto
+++ /dev/null
@@ -1,23 +0,0 @@
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 1000
-  track_event {
-    categories: "cat"
-    name: "Looper.Dispatch: class1"
-    type: 3
-  }
-}
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 2000
-  track_event {
-    categories: "cat"
-    name: "name2"
-    type: 3
-  }
-}
-packet {
-  chrome_metadata {
-    chrome_version_code: 123
-  }
-}
diff --git a/test/trace_processor/chrome/chrome_stack_samples_for_task_test.sql b/test/trace_processor/chrome/chrome_stack_samples_for_task_test.sql
deleted file mode 100644
index 3b33d47..0000000
--- a/test/trace_processor/chrome/chrome_stack_samples_for_task_test.sql
+++ /dev/null
@@ -1,34 +0,0 @@
---
--- Copyright 2022 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('chrome/chrome_stack_samples_for_task.sql',
-    'target_duration_ms', '0.000001',
-    'thread_name', '"CrBrowserMain"',
-    'task_name', '"sendTouchEvent"') AS suppress_query_output;
-
-SELECT
-    sample.description,
-    sample.ts,
-    sample.depth
-FROM chrome_stack_samples_for_task sample
-JOIN (
-    SELECT
-        ts,
-        dur
-    FROM slice
-    WHERE ts = 696373965001470
-) test_slice
-    ON sample.ts >= test_slice.ts
-    AND sample.ts <= test_slice.ts + test_slice.dur;
\ No newline at end of file
diff --git a/test/trace_processor/chrome/chrome_tasks_delaying_input_processing_test.out b/test/trace_processor/chrome/chrome_tasks_delaying_input_processing_test.out
deleted file mode 100644
index 4707368..0000000
--- a/test/trace_processor/chrome/chrome_tasks_delaying_input_processing_test.out
+++ /dev/null
@@ -1,5 +0,0 @@
-
-"full_name","duration_ms","thread_dur_ms"
-"content.mojom.FrameHost message (hash=2168461044)",16.111000,10.451000
-"Looper.dispatch: android.net.ConnectivityManager$CallbackHandler(null)",10.507000,0.878000
-"blink.mojom.PresentationService message (hash=3202951471)",22.524000,9.992000
diff --git a/test/trace_processor/chrome/chrome_tasks_delaying_input_processing_test.sql b/test/trace_processor/chrome/chrome_tasks_delaying_input_processing_test.sql
deleted file mode 100644
index d0b2a19..0000000
--- a/test/trace_processor/chrome/chrome_tasks_delaying_input_processing_test.sql
+++ /dev/null
@@ -1,24 +0,0 @@
---
--- Copyright 2022 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('chrome/chrome_tasks_delaying_input_processing.sql',
-'duration_causing_jank_ms',
- /* duration_causing_jank_ms = */ '8') AS suppress_query_output;
-
-SELECT
-  full_name,
-  duration_ms,
-  thread_dur_ms
-FROM chrome_tasks_delaying_input_processing;
\ No newline at end of file
diff --git a/test/trace_processor/chrome/chrome_tasks_test.sql b/test/trace_processor/chrome/chrome_tasks_test.sql
deleted file mode 100644
index 4e9d9f6..0000000
--- a/test/trace_processor/chrome/chrome_tasks_test.sql
+++ /dev/null
@@ -1,22 +0,0 @@
---
--- Copyright 2022 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('chrome/chrome_tasks.sql') AS suppress_query_output;
-
-SELECT full_name, task_type, count() as count
-FROM chrome_tasks
-GROUP BY full_name, task_type
-ORDER BY count DESC
-LIMIT 50;
diff --git a/test/trace_processor/chrome/chrome_thread_slice.out b/test/trace_processor/chrome/chrome_thread_slice.out
deleted file mode 100644
index e3ed290..0000000
--- a/test/trace_processor/chrome/chrome_thread_slice.out
+++ /dev/null
@@ -1,7 +0,0 @@
-
-"trace_id","dur","thread_dur"
-2734,25000,25000
-2734,1000,2000
-2734,2000,2000
-2734,258000,171000
-2734,1000,1000
diff --git a/test/trace_processor/chrome/chrome_thread_slice_repeated.out b/test/trace_processor/chrome/chrome_thread_slice_repeated.out
deleted file mode 100644
index 1efb2b1..0000000
--- a/test/trace_processor/chrome/chrome_thread_slice_repeated.out
+++ /dev/null
@@ -1,9 +0,0 @@
-
-"name","ts","dur","thread_dur"
-"event1_on_t1",1000,100,10000
-"event2_on_t1",2000,200,30000
-"event3_on_t1",2000,200,10000
-"event4_on_t1",4000,0,0
-"float_counter_on_t1",4300,0,"[NULL]"
-"float_counter_on_t1",4500,0,"[NULL]"
-"event1_on_t3",4000,100,5000
diff --git a/test/trace_processor/chrome/chrome_thread_slice_repeated_test.sql b/test/trace_processor/chrome/chrome_thread_slice_repeated_test.sql
deleted file mode 100644
index 124d306..0000000
--- a/test/trace_processor/chrome/chrome_thread_slice_repeated_test.sql
+++ /dev/null
@@ -1,25 +0,0 @@
---
--- 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('chrome/chrome_thread_slice.sql')
-    AS suppress_query_output;
-
-SELECT
-  name,
-  ts,
-  dur,
-  thread_dur
-FROM chrome_thread_slice
diff --git a/test/trace_processor/chrome/chrome_thread_slice_test.sql b/test/trace_processor/chrome/chrome_thread_slice_test.sql
deleted file mode 100644
index 654f424..0000000
--- a/test/trace_processor/chrome/chrome_thread_slice_test.sql
+++ /dev/null
@@ -1,27 +0,0 @@
---
--- 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('chrome/chrome_thread_slice.sql')
-    AS suppress_query_output;
-
-SELECT
-  EXTRACT_ARG(arg_set_id, 'chrome_latency_info.trace_id') AS trace_id,
-  dur,
-  thread_dur
-FROM chrome_thread_slice
-WHERE
-  name = 'LatencyInfo.Flow' AND
-  EXTRACT_ARG(arg_set_id, 'chrome_latency_info.trace_id') = 2734;
diff --git a/test/trace_processor/chrome/chrome_threads_test.sql b/test/trace_processor/chrome/chrome_threads_test.sql
deleted file mode 100644
index 8fb2839..0000000
--- a/test/trace_processor/chrome/chrome_threads_test.sql
+++ /dev/null
@@ -1,17 +0,0 @@
--- Copyright 2021 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('chrome/chrome_processes.sql') AS suppress_query_output;
-SELECT tid, name, is_main_thread, canonical_name
-FROM chrome_thread
-ORDER BY tid, name;
diff --git a/test/trace_processor/chrome/chrome_user_event_hashes.out b/test/trace_processor/chrome/chrome_user_event_hashes.out
deleted file mode 100644
index 69ca70f..0000000
--- a/test/trace_processor/chrome/chrome_user_event_hashes.out
+++ /dev/null
@@ -1,5 +0,0 @@
-[perfetto.protos.chrome_user_event_hashes]: {
-  action_hash: 10
-  action_hash: 20
-}
-
diff --git a/test/trace_processor/chrome/chrome_user_event_hashes.textproto b/test/trace_processor/chrome/chrome_user_event_hashes.textproto
deleted file mode 100644
index 41b21b9..0000000
--- a/test/trace_processor/chrome/chrome_user_event_hashes.textproto
+++ /dev/null
@@ -1,27 +0,0 @@
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 0
-  incremental_state_cleared: true
-  track_event {
-    categories: "cat1"
-    type: 3
-    name_iid: 1
-    chrome_user_event {
-      action_hash: 10
-    }
-  }
-}
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 0
-  incremental_state_cleared: true
-  track_event {
-    categories: "cat2"
-    type: 3
-    name_iid: 2
-    chrome_user_event {
-      action_hash: 20
-    }
-  }
-}
-
diff --git a/test/trace_processor/chrome/combined_rail_modes.out b/test/trace_processor/chrome/combined_rail_modes.out
deleted file mode 100644
index b1b6a29..0000000
--- a/test/trace_processor/chrome/combined_rail_modes.out
+++ /dev/null
@@ -1,5 +0,0 @@
-
-"id","ts","dur","rail_mode"
-1,0,10000,"response"
-2,10000,25000,"animation"
-3,35000,10000,"background"
diff --git a/test/trace_processor/chrome/combined_rail_modes_test.sql b/test/trace_processor/chrome/combined_rail_modes_test.sql
deleted file mode 100644
index 5b49749..0000000
--- a/test/trace_processor/chrome/combined_rail_modes_test.sql
+++ /dev/null
@@ -1,15 +0,0 @@
--- 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('chrome/rail_modes.sql') AS suppress_query_output;
-SELECT * FROM combined_overall_rail_slices;
diff --git a/test/trace_processor/chrome/count_slices_test.sql b/test/trace_processor/chrome/count_slices_test.sql
deleted file mode 100644
index 69387a6..0000000
--- a/test/trace_processor/chrome/count_slices_test.sql
+++ /dev/null
@@ -1 +0,0 @@
-SELECT COUNT(1) FROM slice;
diff --git a/test/trace_processor/chrome/cpu_time_by_combined_rail_mode.out b/test/trace_processor/chrome/cpu_time_by_combined_rail_mode.out
deleted file mode 100644
index 3c55cf3..0000000
--- a/test/trace_processor/chrome/cpu_time_by_combined_rail_mode.out
+++ /dev/null
@@ -1,7 +0,0 @@
-
-"id","ts","dur","rail_mode","cpu_dur"
-1,0,10000,"response",26000
-2,10000,20000,"animation",20000
-3,30000,5000,"background",8000
-4,35000,10000,"animation",21000
-5,45000,10000,"background",1000
diff --git a/test/trace_processor/chrome/cpu_time_by_combined_rail_mode_test.sql b/test/trace_processor/chrome/cpu_time_by_combined_rail_mode_test.sql
deleted file mode 100644
index b66726a..0000000
--- a/test/trace_processor/chrome/cpu_time_by_combined_rail_mode_test.sql
+++ /dev/null
@@ -1,17 +0,0 @@
--- 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('chrome/chrome_processes.sql') AS suppress_query_output;
--- SELECT * FROM cpu_time_by_rail_mode;
-SELECT RUN_METRIC('chrome/cpu_time_by_rail_mode.sql') AS suppress_query_output;
-SELECT * FROM cpu_time_by_rail_mode;
diff --git a/test/trace_processor/chrome/estimated_power_by_combined_rail_mode.out b/test/trace_processor/chrome/estimated_power_by_combined_rail_mode.out
deleted file mode 100644
index ebb8e23..0000000
--- a/test/trace_processor/chrome/estimated_power_by_combined_rail_mode.out
+++ /dev/null
@@ -1,7 +0,0 @@
-
-"id","ts","dur","rail_mode","mas","ma"
-1,0,10000000,"response",0.554275,55.427500
-2,10000000,20000000,"animation",0.284850,14.242500
-3,30000000,5000000,"background",0.076233,15.246667
-4,35000000,10000000,"animation",0.536850,53.685000
-5,45000000,10000000,"background",0.071580,7.158000
diff --git a/test/trace_processor/chrome/estimated_power_by_combined_rail_mode_test.sql b/test/trace_processor/chrome/estimated_power_by_combined_rail_mode_test.sql
deleted file mode 100644
index 7aab878..0000000
--- a/test/trace_processor/chrome/estimated_power_by_combined_rail_mode_test.sql
+++ /dev/null
@@ -1,17 +0,0 @@
--- 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('chrome/chrome_processes.sql') AS suppress_query_output;
--- SELECT * FROM cpu_time_by_rail_mode;
-SELECT RUN_METRIC('chrome/estimated_power_by_rail_mode.sql') AS suppress_query_output;
-SELECT * FROM power_by_rail_mode;
diff --git a/test/trace_processor/chrome/frame_times_metric_test.sql b/test/trace_processor/chrome/frame_times_metric_test.sql
deleted file mode 100644
index 28c0c42..0000000
--- a/test/trace_processor/chrome/frame_times_metric_test.sql
+++ /dev/null
@@ -1,18 +0,0 @@
---
--- Copyright 2021 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('experimental/frame_times.sql') AS suppress_query_output;
-
-SELECT * FROM AvgSurfaceFps;
diff --git a/test/trace_processor/chrome/index b/test/trace_processor/chrome/index
deleted file mode 100644
index 5e1ed0d..0000000
--- a/test/trace_processor/chrome/index
+++ /dev/null
@@ -1,86 +0,0 @@
-# Tests related to Chrome's use of Perfetto.
-
-# Chrome metrics (found in the trace_processor/chrome directory).
-
-# Scroll jank metrics
-../../data/chrome_scroll_without_vsync.pftrace scroll_jank_general_validation_test.sql scroll_jank_general_validation.out
-../../data/chrome_scroll_without_vsync.pftrace scroll_jank_test.sql scroll_jank.out
-../../data/chrome_scroll_without_vsync.pftrace scroll_flow_event_test.sql scroll_flow_event.out
-../../data/chrome_scroll_without_vsync.pftrace scroll_flow_event_general_validation_test.sql scroll_flow_event_general_validation.out
-../../data/chrome_scroll_without_vsync.pftrace scroll_jank_cause_test.sql scroll_jank_cause.out
-../../data/chrome_scroll_without_vsync.pftrace scroll_flow_event_queuing_delay_test.sql scroll_flow_event_queuing_delay.out
-../../data/chrome_scroll_without_vsync.pftrace scroll_flow_event_queuing_delay_general_validation_test.sql scroll_flow_event_general_validation.out
-../../data/chrome_scroll_without_vsync.pftrace scroll_jank_cause_queuing_delay_test.sql scroll_jank_cause_queuing_delay.out
-../../data/chrome_scroll_without_vsync.pftrace scroll_jank_cause_queuing_delay_restricted_test.sql scroll_jank_cause_queuing_delay_restricted.out
-../../data/chrome_scroll_without_vsync.pftrace scroll_jank_cause_queuing_delay_general_validation_test.sql scroll_jank_cause_queuing_delay_general_validation.out
-../../data/chrome_scroll_without_vsync.pftrace chrome_thread_slice_test.sql chrome_thread_slice.out
-../../data/scrolling_with_blocked_nonblocked_frames.pftrace chrome_input_to_browser_intervals_test.sql chrome_input_to_browser_intervals.out
-../../data/fling_with_input_delay.pftrace chrome_scroll_jank_caused_by_scheduling_test.sql chrome_scroll_jank_caused_by_scheduling_test.out
-../../data/fling_with_input_delay.pftrace chrome_tasks_delaying_input_processing_test.sql chrome_tasks_delaying_input_processing_test.out
-../../data/scrolling_with_blocked_nonblocked_frames.pftrace chrome_scroll_inputs_per_frame_test.sql chrome_scroll_inputs_per_frame_test.out
-../track_event/track_event_counters.textproto chrome_thread_slice_repeated_test.sql chrome_thread_slice_repeated.out
-../../data/chrome_rendering_desktop.pftrace frame_times frame_times_metric.out
-../../data/chrome_rendering_desktop.pftrace chrome_dropped_frames chrome_dropped_frames_metric.out
-../chrome/long_event_latency.textproto chrome_long_latency_metric_test.sql chrome_long_latency_metric.out
-scroll_jank_mojo_simple_watcher.py scroll_jank_mojo_simple_watcher_test.sql scroll_jank_mojo_simple_watcher.out
-scroll_jank_gpu_check.py scroll_jank_gpu_check_test.sql scroll_jank_gpu_check.out
-
-# Touch gesture metrics
-../../data/chrome_touch_gesture_scroll.pftrace touch_jank_test.sql touch_jank.out
-../../data/chrome_touch_gesture_scroll.pftrace touch_flow_event_test.sql touch_flow_event.out
-../../data/chrome_touch_gesture_scroll.pftrace touch_flow_event_queuing_delay_test.sql touch_flow_event_queuing_delay.out
-touch_jank.py touch_jank_test.sql touch_jank_synth.out
-touch_jank.py touch_flow_event_test.sql touch_flow_event_synth.out
-touch_jank.py touch_flow_event_queuing_delay_full_test.sql touch_flow_event_queuing_delay_synth.out
-
-# Chrome memory snapshots.
-../../data/chrome_memory_snapshot.pftrace memory_snapshot_general_validation_test.sql memory_snapshot_general_validation.out
-../../data/chrome_memory_snapshot.pftrace memory_snapshot_os_dump_events_test.sql memory_snapshot_os_dump_events.out
-../../data/chrome_memory_snapshot.pftrace memory_snapshot_chrome_dump_events_test.sql memory_snapshot_chrome_dump_events.out
-../../data/chrome_memory_snapshot.pftrace memory_snapshot_nodes_test.sql memory_snapshot_nodes.out
-../../data/chrome_memory_snapshot.pftrace memory_snapshot_edges_test.sql memory_snapshot_edges.out
-../../data/chrome_memory_snapshot.pftrace memory_snapshot_node_args_test.sql memory_snapshot_node_args.out
-../../data/chrome_memory_snapshot.pftrace memory_snapshot_smaps_test.sql memory_snapshot_smaps.out
-
-# RAIL modes.
-combined_rail_modes.py combined_rail_modes_test.sql combined_rail_modes.out
-cpu_time_by_combined_rail_mode.py cpu_time_by_combined_rail_mode_test.sql cpu_time_by_combined_rail_mode.out
-actual_power_by_combined_rail_mode.py actual_power_by_combined_rail_mode_test.sql actual_power_by_combined_rail_mode.out
-estimated_power_by_combined_rail_mode.py estimated_power_by_combined_rail_mode_test.sql estimated_power_by_combined_rail_mode.out
-modified_rail_modes.py modified_rail_modes_test.sql modified_rail_modes.out
-modified_rail_modes_no_vsyncs.py modified_rail_modes_test.sql modified_rail_modes_no_vsyncs.out
-modified_rail_modes_with_input.py modified_rail_modes_with_input_test.sql modified_rail_modes_with_input.out
-modified_rail_modes_long.py modified_rail_modes_test.sql modified_rail_modes_long.out
-modified_rail_modes_extra_long.py modified_rail_modes_test.sql modified_rail_modes_extra_long.out
-
-# Chrome processes.
-../../data/chrome_scroll_without_vsync.pftrace chrome_processes_test.sql chrome_processes.out
-../../data/chrome_android_systrace.pftrace chrome_processes_test.sql chrome_processes_android_systrace.out
-../../data/chrome_scroll_without_vsync.pftrace chrome_threads_test.sql chrome_threads.out
-../../data/chrome_android_systrace.pftrace chrome_threads_test.sql chrome_threads_android_systrace.out
-../../data/chrome_scroll_without_vsync.pftrace chrome_processes_type_test.sql chrome_processes_type.out
-../../data/chrome_android_systrace.pftrace chrome_processes_type_test.sql chrome_processes_type_android_systrace.out
-track_with_chrome_process.textproto chrome_processes_type_test.sql track_with_chrome_process.out
-
-# Chrome histogram hashes
-chrome_histogram_hashes.textproto chrome_histogram_hashes chrome_histogram_hashes.out
-
-# Chrome user events
-chrome_user_event_hashes.textproto chrome_user_event_hashes chrome_user_event_hashes.out
-
-# Chrome performance mark
-chrome_performance_mark_hashes.textproto chrome_performance_mark_hashes chrome_performance_mark_hashes.out
-
-# Chrome slices
-chrome_slice_names.textproto chrome_slice_names chrome_slice_names.out
-
-# Chrome tasks.
-../../data/chrome_page_load_all_categories_not_extended.pftrace.gz chrome_tasks_test.sql chrome_tasks.out
-
-# Chrome stack samples.
-../../data/chrome_stack_traces_symbolized_trace.pftrace chrome_stack_samples_for_task_test.sql chrome_stack_samples_for_task_test.out
-
-# Unsymbolized args.
-unsymbolized_args.textproto chrome_unsymbolized_args unsymbolized_args.out
-../../data/async-trace-1.json count_slices_test.sql async-trace-1_count_slices.out
-../../data/async-trace-2.json count_slices_test.sql async-trace-2_count_slices.out
diff --git a/test/trace_processor/chrome/memory_snapshot_chrome_dump_events_test.sql b/test/trace_processor/chrome/memory_snapshot_chrome_dump_events_test.sql
deleted file mode 100644
index 67fbf86..0000000
--- a/test/trace_processor/chrome/memory_snapshot_chrome_dump_events_test.sql
+++ /dev/null
@@ -1,25 +0,0 @@
---
--- 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
-  pms.id AS process_snapshot_id,
-  upid,
-  snapshot_id,
-  timestamp,
-  detail_level
-FROM memory_snapshot ms
-LEFT JOIN process_memory_snapshot pms
-  ON ms.id = pms.snapshot_id
diff --git a/test/trace_processor/chrome/memory_snapshot_edges_test.sql b/test/trace_processor/chrome/memory_snapshot_edges_test.sql
deleted file mode 100644
index 63307ad..0000000
--- a/test/trace_processor/chrome/memory_snapshot_edges_test.sql
+++ /dev/null
@@ -1,23 +0,0 @@
---
--- 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
-  id,
-  source_node_id,
-  target_node_id,
-  importance
-FROM memory_snapshot_edge
-LIMIT 20
diff --git a/test/trace_processor/chrome/memory_snapshot_general_validation_test.sql b/test/trace_processor/chrome/memory_snapshot_general_validation_test.sql
deleted file mode 100644
index 4075041..0000000
--- a/test/trace_processor/chrome/memory_snapshot_general_validation_test.sql
+++ /dev/null
@@ -1,41 +0,0 @@
---
--- 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
-  (
-    SELECT COUNT(*) FROM memory_snapshot
-  ) AS total_snapshots,
-  (
-    SELECT COUNT(*) FROM process
-  ) AS total_processes,
-  (
-    SELECT COUNT(*) FROM process_memory_snapshot
-  ) AS total_process_snapshots,
-  (
-    SELECT COUNT(*) FROM memory_snapshot_node
-  ) AS total_nodes,
-  (
-    SELECT COUNT(*) FROM memory_snapshot_edge
-  ) AS total_edges,
-  (
-    SELECT COUNT(DISTINCT args.id)
-    FROM args
-    INNER JOIN memory_snapshot_node
-    ON args.arg_set_id = memory_snapshot_node.arg_set_id
-  ) AS total_node_args,
-  (
-    SELECT COUNT(*) FROM profiler_smaps
-    INNER JOIN memory_snapshot ON timestamp = ts
-  ) AS total_smaps
diff --git a/test/trace_processor/chrome/memory_snapshot_node_args_test.sql b/test/trace_processor/chrome/memory_snapshot_node_args_test.sql
deleted file mode 100644
index 03df9e9..0000000
--- a/test/trace_processor/chrome/memory_snapshot_node_args_test.sql
+++ /dev/null
@@ -1,25 +0,0 @@
---
--- 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
-  node.id AS node_id,
-  key,
-  value_type,
-  int_value,
-  string_value
-FROM memory_snapshot_node node
-INNER JOIN args ON node.arg_set_id = args.arg_set_id
-LIMIT 20
diff --git a/test/trace_processor/chrome/memory_snapshot_nodes_test.sql b/test/trace_processor/chrome/memory_snapshot_nodes_test.sql
deleted file mode 100644
index 0c014d3..0000000
--- a/test/trace_processor/chrome/memory_snapshot_nodes_test.sql
+++ /dev/null
@@ -1,25 +0,0 @@
---
--- 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
-  id,
-  process_snapshot_id,
-  parent_node_id,
-  path,
-  size,
-  effective_size
-FROM memory_snapshot_node
-LIMIT 20
diff --git a/test/trace_processor/chrome/memory_snapshot_os_dump_events_test.sql b/test/trace_processor/chrome/memory_snapshot_os_dump_events_test.sql
deleted file mode 100644
index b266773..0000000
--- a/test/trace_processor/chrome/memory_snapshot_os_dump_events_test.sql
+++ /dev/null
@@ -1,42 +0,0 @@
---
--- 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
-  p.upid,
-  pid,
-  p.name,
-  timestamp,
-  detail_level,
-  pf.value AS private_footprint_kb,
-  prs.value AS peak_resident_set_kb,
-  EXTRACT_ARG(p.arg_set_id, 'is_peak_rss_resettable') AS is_peak_rss_resettable
-FROM process p
-LEFT JOIN memory_snapshot
-LEFT JOIN (
-  SELECT id, upid
-  FROM process_counter_track
-  WHERE name IS 'chrome.private_footprint_kb'
-  ) AS pct_pf
-  ON p.upid = pct_pf.upid
-LEFT JOIN counter pf ON timestamp = pf.ts AND pct_pf.id = pf.track_id
-LEFT JOIN (
-  SELECT id, upid
-  FROM process_counter_track
-  WHERE name IS 'chrome.peak_resident_set_kb'
-  ) AS pct_prs
-  ON p.upid = pct_prs.upid
-LEFT JOIN counter prs ON timestamp = prs.ts AND pct_prs.id = prs.track_id
-ORDER BY timestamp
diff --git a/test/trace_processor/chrome/memory_snapshot_smaps_test.sql b/test/trace_processor/chrome/memory_snapshot_smaps_test.sql
deleted file mode 100644
index dfd1fce..0000000
--- a/test/trace_processor/chrome/memory_snapshot_smaps_test.sql
+++ /dev/null
@@ -1,39 +0,0 @@
---
--- 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
-  process.upid,
-  process.name,
-  smap.ts,
-  path,
-  size_kb,
-  private_dirty_kb,
-  swap_kb,
-  file_name,
-  start_address,
-  module_timestamp,
-  module_debugid,
-  module_debug_path,
-  protection_flags,
-  private_clean_resident_kb,
-  shared_dirty_resident_kb,
-  shared_clean_resident_kb,
-  locked_kb,
-  proportional_resident_kb
-FROM process
-INNER JOIN profiler_smaps smap ON process.upid = smap.upid
-INNER JOIN memory_snapshot ms ON ms.timestamp = smap.ts
-LIMIT 20
diff --git a/test/trace_processor/chrome/modified_rail_modes.out b/test/trace_processor/chrome/modified_rail_modes.out
deleted file mode 100644
index 688609b..0000000
--- a/test/trace_processor/chrome/modified_rail_modes.out
+++ /dev/null
@@ -1,7 +0,0 @@
-
-"id","ts","dur","mode"
-2,0,1000000000,"response"
-3,1000000000,1950000000,"foreground_idle"
-4,2950000000,333333324,"animation"
-5,3283333324,216666676,"foreground_idle"
-6,3500000000,1000000000,"background"
diff --git a/test/trace_processor/chrome/modified_rail_modes_extra_long.out b/test/trace_processor/chrome/modified_rail_modes_extra_long.out
deleted file mode 100644
index 9223cac..0000000
--- a/test/trace_processor/chrome/modified_rail_modes_extra_long.out
+++ /dev/null
@@ -1,2 +0,0 @@
-
-"id","ts","dur","mode"
diff --git a/test/trace_processor/chrome/modified_rail_modes_long.out b/test/trace_processor/chrome/modified_rail_modes_long.out
deleted file mode 100644
index 5b9f13a..0000000
--- a/test/trace_processor/chrome/modified_rail_modes_long.out
+++ /dev/null
@@ -1,4 +0,0 @@
-
-"id","ts","dur","mode"
-2,0,1000000000,"response"
-3,1000000000,1,"background"
diff --git a/test/trace_processor/chrome/modified_rail_modes_no_vsyncs.out b/test/trace_processor/chrome/modified_rail_modes_no_vsyncs.out
deleted file mode 100644
index 70b920c..0000000
--- a/test/trace_processor/chrome/modified_rail_modes_no_vsyncs.out
+++ /dev/null
@@ -1,5 +0,0 @@
-
-"id","ts","dur","mode"
-2,0,1000000000,"response"
-3,1000000000,2500000000,"foreground_idle"
-4,3500000000,1000000000,"background"
diff --git a/test/trace_processor/chrome/modified_rail_modes_test.sql b/test/trace_processor/chrome/modified_rail_modes_test.sql
deleted file mode 100644
index 053aca8..0000000
--- a/test/trace_processor/chrome/modified_rail_modes_test.sql
+++ /dev/null
@@ -1,15 +0,0 @@
--- 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('chrome/rail_modes.sql') AS suppress_query_output;
-SELECT * FROM modified_rail_slices;
diff --git a/test/trace_processor/chrome/modified_rail_modes_with_input.out b/test/trace_processor/chrome/modified_rail_modes_with_input.out
deleted file mode 100644
index 66af894..0000000
--- a/test/trace_processor/chrome/modified_rail_modes_with_input.out
+++ /dev/null
@@ -1,9 +0,0 @@
-
-"id","ts","dur","mode"
-2,0,1000000000,"response"
-3,1000000000,1950000000,"foreground_idle"
-4,2950000000,50000000,"animation"
-5,3000000000,66666674,"response"
-6,3066666674,216666650,"animation"
-7,3283333324,216666676,"foreground_idle"
-8,3500000000,1000000000,"background"
diff --git a/test/trace_processor/chrome/modified_rail_modes_with_input_test.sql b/test/trace_processor/chrome/modified_rail_modes_with_input_test.sql
deleted file mode 100644
index 053aca8..0000000
--- a/test/trace_processor/chrome/modified_rail_modes_with_input_test.sql
+++ /dev/null
@@ -1,15 +0,0 @@
--- 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('chrome/rail_modes.sql') AS suppress_query_output;
-SELECT * FROM modified_rail_slices;
diff --git a/test/trace_processor/chrome/scroll_flow_event_general_validation_test.sql b/test/trace_processor/chrome/scroll_flow_event_general_validation_test.sql
deleted file mode 100644
index f6ceab6..0000000
--- a/test/trace_processor/chrome/scroll_flow_event_general_validation_test.sql
+++ /dev/null
@@ -1,39 +0,0 @@
---
--- 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('chrome/scroll_flow_event.sql') AS suppress_query_output;
-
-SELECT
-  -- Each trace_id (in our example trace not true in general) has 8 steps. There
-  -- are 139 scrolls. So we expect 1112 rows in total 72 of which are janky.
-  (
-    SELECT
-      COUNT(*)
-    FROM (
-      SELECT
-        trace_id,
-        COUNT(*)
-      FROM scroll_flow_event
-      GROUP BY trace_id
-    )
-  ) AS total_scroll_updates,
-  (
-    SELECT COUNT(*) FROM scroll_flow_event
-  ) AS total_flow_event_steps,
-  (
-    SELECT COUNT(*) FROM scroll_flow_event WHERE jank
-  ) AS total_janky_flow_event_steps,
-  (
-    SELECT COUNT(*) FROM (SELECT step FROM scroll_flow_event GROUP BY step)
-  ) AS number_of_unique_steps;
diff --git a/test/trace_processor/chrome/scroll_flow_event_queuing_delay_general_validation_test.sql b/test/trace_processor/chrome/scroll_flow_event_queuing_delay_general_validation_test.sql
deleted file mode 100644
index a8e78bf..0000000
--- a/test/trace_processor/chrome/scroll_flow_event_queuing_delay_general_validation_test.sql
+++ /dev/null
@@ -1,40 +0,0 @@
---
--- 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('chrome/scroll_flow_event_queuing_delay.sql')
-    AS suppress_query_output;
-
-SELECT
-  -- Each trace_id (in our example trace not true in general) has 8 steps. There
-  -- are 139 scrolls. So we expect 1112 rows in total 72 of which are janky.
-  (
-    SELECT
-      COUNT(*)
-    FROM (
-      SELECT
-        trace_id,
-        COUNT(*)
-      FROM scroll_flow_event_queuing_delay
-      GROUP BY trace_id
-    )
-  ) AS total_scroll_updates,
-  (
-    SELECT COUNT(*) FROM scroll_flow_event_queuing_delay
-  ) AS total_flow_event_steps,
-  (
-    SELECT COUNT(*) FROM scroll_flow_event_queuing_delay WHERE jank
-  ) AS total_janky_flow_event_steps,
-  (
-    SELECT COUNT(*) FROM (SELECT step FROM scroll_flow_event_queuing_delay GROUP BY step)
-  ) AS number_of_unique_steps;
diff --git a/test/trace_processor/chrome/scroll_flow_event_queuing_delay_test.sql b/test/trace_processor/chrome/scroll_flow_event_queuing_delay_test.sql
deleted file mode 100644
index 50e79b8..0000000
--- a/test/trace_processor/chrome/scroll_flow_event_queuing_delay_test.sql
+++ /dev/null
@@ -1,31 +0,0 @@
---
--- 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('chrome/scroll_flow_event_queuing_delay.sql')
-  AS suppress_query_output;
-
--- trace 2956 is janky and 2954 and 2960 surround it (both are not janky). We
--- just manually computed these values to ensure the queuing time is correct.
-SELECT
-  trace_id,
-  jank,
-  step,
-  next_step,
-  ancestor_end,
-  maybe_next_ancestor_ts,
-  queuing_time_ns
-FROM scroll_flow_event_queuing_delay
-WHERE trace_id = 2954 OR trace_id = 2956 OR trace_id = 2960
-ORDER BY trace_id, ts;
diff --git a/test/trace_processor/chrome/scroll_flow_event_test.sql b/test/trace_processor/chrome/scroll_flow_event_test.sql
deleted file mode 100644
index 00dbba6..0000000
--- a/test/trace_processor/chrome/scroll_flow_event_test.sql
+++ /dev/null
@@ -1,29 +0,0 @@
---
--- 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('chrome/scroll_flow_event.sql') AS suppress_query_output;
-
-SELECT
-  trace_id,
-  ts,
-  dur,
-  jank,
-  step,
-  ancestor_end,
-  maybe_next_ancestor_ts,
-  next_ts,
-  next_trace_id,
-  next_step
-FROM scroll_flow_event
-ORDER BY gesture_scroll_id, trace_id, ts;
diff --git a/test/trace_processor/chrome/scroll_jank_cause.out b/test/trace_processor/chrome/scroll_jank_cause.out
deleted file mode 100644
index c8d577e..0000000
--- a/test/trace_processor/chrome/scroll_jank_cause.out
+++ /dev/null
@@ -1,3 +0,0 @@
-
-"total","total_jank","sum_explained_and_unexplained","error_rows"
-139,7,7,0
diff --git a/test/trace_processor/chrome/scroll_jank_cause_queuing_delay_general_validation_test.sql b/test/trace_processor/chrome/scroll_jank_cause_queuing_delay_general_validation_test.sql
deleted file mode 100644
index 9441841..0000000
--- a/test/trace_processor/chrome/scroll_jank_cause_queuing_delay_general_validation_test.sql
+++ /dev/null
@@ -1,41 +0,0 @@
---
--- 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('chrome/scroll_jank_cause_queuing_delay.sql')
-    AS suppress_query_output;
-
-SELECT
-  COUNT(*) as total,
-  (
-    SELECT
-      DISTINCT(avg_no_jank_dur_overlapping_ns)
-    FROM scroll_jank_cause_queuing_delay
-    WHERE
-      location = "LatencyInfo.Flow" AND
-      jank
-  ) AS janky_latency_info_non_jank_avg_dur,
-  (
-    SELECT
-      DISTINCT(avg_no_jank_dur_overlapping_ns)
-    FROM scroll_jank_cause_queuing_delay
-    WHERE
-      location = "LatencyInfo.Flow" AND
-      NOT jank
-  ) AS non_janky_latency_info_non_jank_avg_dur
-FROM (
-  SELECT
-    trace_id
-  FROM scroll_jank_cause_queuing_delay
-  GROUP BY trace_id
-);
diff --git a/test/trace_processor/chrome/scroll_jank_cause_queuing_delay_restricted_test.sql b/test/trace_processor/chrome/scroll_jank_cause_queuing_delay_restricted_test.sql
deleted file mode 100644
index 7c8b59a..0000000
--- a/test/trace_processor/chrome/scroll_jank_cause_queuing_delay_restricted_test.sql
+++ /dev/null
@@ -1,27 +0,0 @@
---
--- 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('chrome/scroll_jank_cause_queuing_delay.sql')
-    AS suppress_query_output;
-
-SELECT
-  process_name,
-  thread_name,
-  trace_id,
-  jank,
-  dur_overlapping_ns,
-  restricted_metric_name
-FROM scroll_jank_cause_queuing_delay
-WHERE trace_id = 2918 OR trace_id = 2926
-ORDER BY trace_id ASC, ts ASC
diff --git a/test/trace_processor/chrome/scroll_jank_cause_queuing_delay_test.sql b/test/trace_processor/chrome/scroll_jank_cause_queuing_delay_test.sql
deleted file mode 100644
index 4941383..0000000
--- a/test/trace_processor/chrome/scroll_jank_cause_queuing_delay_test.sql
+++ /dev/null
@@ -1,27 +0,0 @@
---
--- 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('chrome/scroll_jank_cause_queuing_delay.sql')
-    AS suppress_query_output;
-
-SELECT
-  process_name,
-  thread_name,
-  trace_id,
-  jank,
-  dur_overlapping_ns,
-  metric_name
-FROM scroll_jank_cause_queuing_delay
-WHERE trace_id = 2918 OR trace_id = 2926
-ORDER BY trace_id ASC, ts ASC
diff --git a/test/trace_processor/chrome/scroll_jank_cause_test.sql b/test/trace_processor/chrome/scroll_jank_cause_test.sql
deleted file mode 100644
index 658ec22..0000000
--- a/test/trace_processor/chrome/scroll_jank_cause_test.sql
+++ /dev/null
@@ -1,32 +0,0 @@
---
--- 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('chrome/scroll_jank_cause.sql') AS suppress_query_output;
-
-SELECT
-  COUNT(*) AS total,
-  SUM(jank) as total_jank,
-  SUM(explained_jank + unexplained_jank) AS sum_explained_and_unexplained,
-  SUM(
-    CASE WHEN explained_jank THEN
-      unexplained_jank
-    ELSE
-      CASE WHEN jank AND NOT unexplained_jank THEN
-        1
-      ELSE
-        0
-      END
-    END
-  ) AS error_rows
-FROM scroll_jank_cause;
diff --git a/test/trace_processor/chrome/scroll_jank_general_validation_test.sql b/test/trace_processor/chrome/scroll_jank_general_validation_test.sql
deleted file mode 100644
index 1aa3059b..0000000
--- a/test/trace_processor/chrome/scroll_jank_general_validation_test.sql
+++ /dev/null
@@ -1,82 +0,0 @@
---
--- 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('chrome/scroll_jank.sql') AS suppress_query_output;
-
-SELECT (
-  -- There are only two valid scrolls (one additional scroll is missing a begin
-  -- and the other is missing an end).
-  SELECT COUNT(*) FROM joined_scroll_begin_and_end
-) AS total,
-(
-  -- Of the two valid scrolls
-  -- gesture_scroll_id: 2708
-  --     starts at: 544958000403
-  --     ends at:   545859896829
-  --     adds dur:     901896426 nanoseconds of scrolling.
-  --
-  -- gesture_scroll_id: 2917
-  --     starts at: 546027000403
-  --     ends at:   546753574829
-  --     adds dur:     726574426 nanoseconds of scrolling.
-  -- This means we should have scroll_dur == 1628470852
-  SELECT SUM(scroll_dur) FROM (
-    SELECT
-      gesture_scroll_id, max(maybe_gesture_end) - begin_ts AS scroll_dur
-    FROM scroll_jank
-    GROUP BY gesture_scroll_id
-  )
-) AS scroll_dur,
-(
-  -- This can be verified by the following simple query to ensure the end result
-  -- in scroll_jank table is sane. The result should be 139.
-  -- SELECT
-  --   COUNT(*)
-  -- FROM slice
-  -- WHERE
-  --    name = "InputLatency::GestureScrollUpdate" AND
-  --    NOT EXTRACT_ARG(arg_set_id, 'chrome_latency_info.is_coalesced') AND
-  --    (
-  --    EXTRACT_ARG(arg_set_id, 'chrome_latency_info.gesture_scroll_id') = 2708
-  --    OR
-  --    EXTRACT_ARG(arg_set_id, 'chrome_latency_info.gesture_scroll_id') = 2917
-  --    )
-  SELECT COUNT(*) FROM scroll_jank
-) AS non_coalesced_updates,
-(
-  -- This can be verified by the following simple query as above but replace
-  -- COUNT(*) with SUM(dur). The result should be 3974685214.
-  SELECT SUM(dur) FROM scroll_jank
-) AS non_coalesced_dur,
-(
-  -- This was found by running the previous metric before porting on the
-  -- example trace.
-  SELECT COUNT(*) FROM scroll_jank WHERE jank
-) AS non_coalesced_janky_updates,
-(
-  -- This was found by running the previous metric before porting on the
-  -- example trace, and also manually summing them.
-  SELECT SUM(dur) FROM scroll_jank WHERE jank
-) AS non_coalesced_janky_dur,
-(
-  -- This is floor((non_coalesced_janky_dur/non_coalesced_dur) * 100) in SQLite.
-  SELECT
-    CAST((CAST((SELECT SUM(dur) FROM scroll_jank WHERE jank) AS FLOAT) /
-    CAST((SELECT SUM(dur) FROM scroll_jank) AS FLOAT)) * 100 AS INT)
-) AS janky_percentage,
-(
-  SELECT avg_vsync_interval FROM joined_scroll_begin_and_end
-) as avg_vsync_interval;
-
diff --git a/test/trace_processor/chrome/scroll_jank_gpu_check.out b/test/trace_processor/chrome/scroll_jank_gpu_check.out
deleted file mode 100644
index f5dad3d..0000000
--- a/test/trace_processor/chrome/scroll_jank_gpu_check.out
+++ /dev/null
@@ -1,5 +0,0 @@
-
-"ts","jank"
-15000000,0
-30000000,1
-115000000,0
diff --git a/test/trace_processor/chrome/scroll_jank_gpu_check_test.sql b/test/trace_processor/chrome/scroll_jank_gpu_check_test.sql
deleted file mode 100644
index d3740b5..0000000
--- a/test/trace_processor/chrome/scroll_jank_gpu_check_test.sql
+++ /dev/null
@@ -1,20 +0,0 @@
---
--- Copyright 2021 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('chrome/scroll_jank.sql') AS suppress_query_output;
-
-SELECT ts, jank
-FROM scroll_jank
-ORDER BY ts ASC;
diff --git a/test/trace_processor/chrome/scroll_jank_mojo_simple_watcher_test.sql b/test/trace_processor/chrome/scroll_jank_mojo_simple_watcher_test.sql
deleted file mode 100644
index 3b090e6..0000000
--- a/test/trace_processor/chrome/scroll_jank_mojo_simple_watcher_test.sql
+++ /dev/null
@@ -1,24 +0,0 @@
---
--- Copyright 2021 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('chrome/scroll_jank_cause_queuing_delay.sql')
-    AS suppress_query_output;
-
-SELECT
-  trace_id,
-  jank,
-  dur_overlapping_ns,
-  metric_name
-FROM scroll_jank_cause_queuing_delay
-ORDER BY trace_id ASC, ts ASC;
diff --git a/test/trace_processor/chrome/scroll_jank_test.sql b/test/trace_processor/chrome/scroll_jank_test.sql
deleted file mode 100644
index 9aaa99b..0000000
--- a/test/trace_processor/chrome/scroll_jank_test.sql
+++ /dev/null
@@ -1,25 +0,0 @@
---
--- 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('chrome/scroll_jank.sql') AS suppress_query_output;
-
-SELECT
-  gesture_scroll_id,
-  trace_id,
-  jank,
-  ts,
-  dur,
-  jank_budget
-FROM scroll_jank;
diff --git a/test/trace_processor/chrome/touch_flow_event_queuing_delay_full_test.sql b/test/trace_processor/chrome/touch_flow_event_queuing_delay_full_test.sql
deleted file mode 100644
index f1e9c33..0000000
--- a/test/trace_processor/chrome/touch_flow_event_queuing_delay_full_test.sql
+++ /dev/null
@@ -1,29 +0,0 @@
---
--- Copyright 2021 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('chrome/touch_flow_event_queuing_delay.sql')
-  AS suppress_query_output;
-
--- trace 6911 is janky and 6915 and 6940 succeed it (both are not janky).
-SELECT
-  trace_id,
-  jank,
-  step,
-  next_step,
-  ancestor_end,
-  maybe_next_ancestor_ts,
-  queuing_time_ns
-FROM touch_flow_event_queuing_delay
-ORDER BY trace_id, ts;
diff --git a/test/trace_processor/chrome/touch_flow_event_queuing_delay_test.sql b/test/trace_processor/chrome/touch_flow_event_queuing_delay_test.sql
deleted file mode 100644
index 0230908..0000000
--- a/test/trace_processor/chrome/touch_flow_event_queuing_delay_test.sql
+++ /dev/null
@@ -1,30 +0,0 @@
---
--- Copyright 2021 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('chrome/touch_flow_event_queuing_delay.sql')
-  AS suppress_query_output;
-
--- trace 6911 is janky and 6915 and 6940 succeed it (both are not janky).
-SELECT
-  trace_id,
-  jank,
-  step,
-  next_step,
-  ancestor_end,
-  maybe_next_ancestor_ts,
-  queuing_time_ns
-FROM touch_flow_event_queuing_delay
-WHERE trace_id = 6915 OR trace_id = 6911 OR trace_id = 6940
-ORDER BY trace_id, ts;
diff --git a/test/trace_processor/chrome/touch_flow_event_test.sql b/test/trace_processor/chrome/touch_flow_event_test.sql
deleted file mode 100644
index dbb5c89..0000000
--- a/test/trace_processor/chrome/touch_flow_event_test.sql
+++ /dev/null
@@ -1,29 +0,0 @@
---
--- Copyright 2021 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('chrome/touch_flow_event.sql') AS suppress_query_output;
-
-SELECT
-  trace_id,
-  ts,
-  dur,
-  jank,
-  step,
-  ancestor_end,
-  maybe_next_ancestor_ts,
-  next_ts,
-  next_trace_id,
-  next_step
-FROM touch_flow_event
-ORDER BY touch_id, trace_id, ts;
diff --git a/test/trace_processor/chrome/touch_jank_synth.out b/test/trace_processor/chrome/touch_jank_synth.out
deleted file mode 100644
index 44758af..0000000
--- a/test/trace_processor/chrome/touch_jank_synth.out
+++ /dev/null
@@ -1,5 +0,0 @@
-
-"touch_id","trace_id","jank","ts","dur","jank_budget"
-87654,34577,0,0,10000000,-31333333.350000
-87654,34578,1,16000000,33000000,14666666.650000
-87654,34579,0,55000000,33000000,-8333333.350000
diff --git a/test/trace_processor/chrome/touch_jank_test.sql b/test/trace_processor/chrome/touch_jank_test.sql
deleted file mode 100644
index d601148..0000000
--- a/test/trace_processor/chrome/touch_jank_test.sql
+++ /dev/null
@@ -1,25 +0,0 @@
---
--- 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('chrome/touch_jank.sql') AS suppress_query_output;
-
-SELECT
-  touch_id,
-  trace_id,
-  jank,
-  ts,
-  dur,
-  jank_budget
-FROM touch_jank;
diff --git a/test/trace_processor/chrome/track_with_chrome_process.out b/test/trace_processor/chrome/track_with_chrome_process.out
deleted file mode 100644
index e774683..0000000
--- a/test/trace_processor/chrome/track_with_chrome_process.out
+++ /dev/null
@@ -1,2 +0,0 @@
-"pid","name","chrome_process_type"
-5,"p5","[NULL]"
diff --git a/test/trace_processor/chrome/track_with_chrome_process.textproto b/test/trace_processor/chrome/track_with_chrome_process.textproto
deleted file mode 100644
index 4e1fa26..0000000
--- a/test/trace_processor/chrome/track_with_chrome_process.textproto
+++ /dev/null
@@ -1,17 +0,0 @@
-packet {
-  trusted_packet_sequence_id: 1
-  incremental_state_cleared: true
-  timestamp: 0
-  track_descriptor {
-    uuid: 10
-    process {
-      pid: 5
-      process_name: "p5"
-    }
-    # Empty Chrome process. This is similar to a process descriptor emitted by
-    # Chrome for a process with an unknown Chrome process_type. This process
-    # should still receive a "chrome_process_type" arg in the args table, but
-    # with a NULL value.
-    chrome_process {}
-  }
-}
\ No newline at end of file
diff --git a/test/trace_processor/chrome/unsymbolized_args.out b/test/trace_processor/chrome/unsymbolized_args.out
deleted file mode 100644
index 61d7cfc..0000000
--- a/test/trace_processor/chrome/unsymbolized_args.out
+++ /dev/null
@@ -1,14 +0,0 @@
-[perfetto.protos.chrome_unsymbolized_args]: {
-  args {
-     module: "/liblib.so"
-     build_id: "6275696c642d6964"
-     address: 123
-     google_lookup_id: "6275696c642d6964"
-   }
-   args {
-     module: "/libmonochrome_64.so"
-     build_id: "7f0715c286f8b16c10e4ad349cda3b9b56c7a773"
-     address: 234
-     google_lookup_id: "c215077ff8866cb110e4ad349cda3b9b0"
-   }
-}
\ No newline at end of file
diff --git a/test/trace_processor/common/list_slices_test.sql b/test/trace_processor/common/list_slices_test.sql
deleted file mode 100644
index 55b0d72..0000000
--- a/test/trace_processor/common/list_slices_test.sql
+++ /dev/null
@@ -1,2 +0,0 @@
-SELECT ts, dur, name
-FROM slice
diff --git a/test/trace_processor/common/oom_kill.textproto b/test/trace_processor/common/oom_kill.textproto
deleted file mode 100644
index 5beccd8..0000000
--- a/test/trace_processor/common/oom_kill.textproto
+++ /dev/null
@@ -1,25 +0,0 @@
-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/common/process_track_slices_test.sql b/test/trace_processor/common/process_track_slices_test.sql
deleted file mode 100644
index 1b9151d..0000000
--- a/test/trace_processor/common/process_track_slices_test.sql
+++ /dev/null
@@ -1,24 +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
-  ts,
-  dur,
-  pid,
-  slice.name as slice_name,
-  process_track.name as track_name
-FROM slice
-INNER JOIN process_track ON slice.track_id = process_track.id
-INNER JOIN process USING (upid)
diff --git a/test/trace_processor/common/process_tracking_test.sql b/test/trace_processor/common/process_tracking_test.sql
deleted file mode 100644
index b28859d..0000000
--- a/test/trace_processor/common/process_tracking_test.sql
+++ /dev/null
@@ -1,20 +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 tid, pid, process.name as pname, thread.name as tname
-from thread
-left join process using(upid)
-where tid > 0
-order by tid
diff --git a/test/trace_processor/common/smoke_slices_test.sql b/test/trace_processor/common/smoke_slices_test.sql
deleted file mode 100644
index f876c41..0000000
--- a/test/trace_processor/common/smoke_slices_test.sql
+++ /dev/null
@@ -1,20 +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 track.type as type, depth, count(*) as count
-from slice
-inner join track on slice.track_id = track.id
-group by track.type, depth
-order by track.type, depth;
diff --git a/test/trace_processor/common/smoke_test.sql b/test/trace_processor/common/smoke_test.sql
deleted file mode 100644
index c3af84a..0000000
--- a/test/trace_processor/common/smoke_test.sql
+++ /dev/null
@@ -1,26 +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
-  ts,
-  cpu,
-  dur,
-  end_state,
-  priority,
-  tid
-FROM sched
-JOIN thread USING(utid)
-ORDER BY ts
-LIMIT 10;
diff --git a/test/trace_processor/common/to_systrace_test.sql b/test/trace_processor/common/to_systrace_test.sql
deleted file mode 100644
index b2b6ed0..0000000
--- a/test/trace_processor/common/to_systrace_test.sql
+++ /dev/null
@@ -1,2 +0,0 @@
-select to_ftrace(id) as line
-from raw
\ No newline at end of file
diff --git a/test/trace_processor/cros/cros_ec_sensorhub_data.textproto b/test/trace_processor/cros/cros_ec_sensorhub_data.textproto
deleted file mode 100644
index dedcdc5..0000000
--- a/test/trace_processor/cros/cros_ec_sensorhub_data.textproto
+++ /dev/null
@@ -1,18 +0,0 @@
-packet {
-  ftrace_events {
-    cpu: 0
-    event {
-      timestamp: 223951135789653
-      pid: 181
-      cros_ec_sensorhub_data {
-        current_time: 223951135778716
-        current_timestamp: 223951052378946
-        delta: -83399770
-        ec_fifo_timestamp: 2128620968
-        ec_sensor_num: 0
-        fifo_timestamp: 223951132978872
-      }
-    }
-  }
-}
-
diff --git a/test/trace_processor/cros/cros_ec_sensorhub_data_test.sql b/test/trace_processor/cros/cros_ec_sensorhub_data_test.sql
deleted file mode 100644
index 805f194..0000000
--- a/test/trace_processor/cros/cros_ec_sensorhub_data_test.sql
+++ /dev/null
@@ -1,11 +0,0 @@
-SELECT
-  t.name,
-  c.ts,
-  c.value,
-  EXTRACT_ARG(c.arg_set_id, 'ec_num') AS ec_num,
-  EXTRACT_ARG(c.arg_set_id, 'ec_delta') AS ec_delta,
-  EXTRACT_ARG(c.arg_set_id, 'sample_ts') AS sample_ts
-FROM counter c
-JOIN track t
-  ON c.track_id = t.id
-WHERE t.name == 'cros_ec.cros_ec_sensorhub_data.0';
diff --git a/test/trace_processor/cros/index b/test/trace_processor/cros/index
deleted file mode 100644
index 29e59e2..0000000
--- a/test/trace_processor/cros/index
+++ /dev/null
@@ -1,5 +0,0 @@
-# Tests related to cros's use of Perfetto.
-
-# cros_ec_sensorhub_data
-cros_ec_sensorhub_data.textproto cros_ec_sensorhub_data_test.sql cros_ec_sensorhub_data.out
-
diff --git a/test/trace_processor/diff_tests/android/android_bugreport_dumpstate_test.out b/test/trace_processor/diff_tests/android/android_bugreport_dumpstate_test.out
new file mode 100644
index 0000000..ee5f0ef
--- /dev/null
+++ b/test/trace_processor/diff_tests/android/android_bugreport_dumpstate_test.out
@@ -0,0 +1,1168 @@
+"section","service","linecount"
+"[NULL]","[NULL]",62
+"ANR FILES (ls -lt /data/anr/)","[NULL]",1
+"APP ACTIVITIES (/system/bin/dumpsys -T 60000 activity -v all)","[NULL]",407
+"APP PROVIDERS NON-PLATFORM (/system/bin/dumpsys -T 60000 activity provider all-non-platform)","[NULL]",3566
+"APP PROVIDERS PLATFORM (/system/bin/dumpsys -T 60000 activity provider all-platform)","[NULL]",174
+"APP SERVICES NON-PLATFORM (/system/bin/dumpsys -T 60000 activity service all-non-platform)","[NULL]",5299
+"APP SERVICES PLATFORM (/system/bin/dumpsys -T 60000 activity service all-platform-non-critical)","[NULL]",3061
+"BINDER FAILED TRANSACTION LOG (/sys/kernel/debug/binder/failed_transaction_log)","[NULL]",3
+"BINDER STATE (/sys/kernel/debug/binder/state)","[NULL]",8600
+"BINDER STATS (/sys/kernel/debug/binder/stats)","[NULL]",4837
+"BINDER TRANSACTION LOG (/sys/kernel/debug/binder/transaction_log)","[NULL]",33
+"BINDER TRANSACTIONS (/sys/kernel/debug/binder/transactions)","[NULL]",157
+"BLOCK STAT","[NULL]",237
+"BLOCKED PROCESS WAIT-CHANNELS","[NULL]",2782
+"BUDDYINFO (/proc/buddyinfo)","[NULL]",3
+"CHECKIN BATTERYSTATS (/system/bin/dumpsys -T 30000 batterystats -c)","[NULL]",24268
+"CHECKIN MEMINFO (/system/bin/dumpsys -T 30000 meminfo --checkin)","[NULL]",50
+"CHECKIN NETSTATS (/system/bin/dumpsys -T 30000 netstats --checkin)","[NULL]",2
+"CHECKIN PACKAGE (/system/bin/dumpsys -T 30000 package --checkin)","[NULL]",1782
+"CHECKIN PROCSTATS (/system/bin/dumpsys -T 30000 procstats -c)","[NULL]",5635
+"CHECKIN USAGESTATS (/system/bin/dumpsys -T 30000 usagestats -c)","[NULL]",2929
+"CPU INFO (top -b -n 1 -H -s 6 -o pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name)","[NULL]",2879
+"DETAILED SOCKET STATE (ss -eionptu)","[NULL]",1
+"DEVICE-MAPPER (gsid dump-device-mapper)","[NULL]",20
+"DROPBOX SYSTEM APP CRASHES (/system/bin/dumpsys -T 30000 dropbox -p system_app_crash)","[NULL]",7
+"DROPBOX SYSTEM SERVER CRASHES (/system/bin/dumpsys -T 30000 dropbox -p system_server_crash)","[NULL]",7
+"DUMP BLOCK STAT","[NULL]",1
+"DUMPSYS (/system/bin/dumpsys)","DockObserver",5
+"DUMPSYS (/system/bin/dumpsys)","accessibility",27
+"DUMPSYS (/system/bin/dumpsys)","account",18
+"DUMPSYS (/system/bin/dumpsys)","activity",28662
+"DUMPSYS (/system/bin/dumpsys)","activity_task",1
+"DUMPSYS (/system/bin/dumpsys)","adb",12
+"DUMPSYS (/system/bin/dumpsys)","alarm",654
+"DUMPSYS (/system/bin/dumpsys)","android.frameworks.stats.IStats/default",1
+"DUMPSYS (/system/bin/dumpsys)","android.hardware.identity.IIdentityCredentialStore/default",1
+"DUMPSYS (/system/bin/dumpsys)","android.hardware.power.IPower/default",60
+"DUMPSYS (/system/bin/dumpsys)","android.hardware.rebootescrow.IRebootEscrow/default",1
+"DUMPSYS (/system/bin/dumpsys)","android.os.UpdateEngineService",1
+"DUMPSYS (/system/bin/dumpsys)","android.os.UpdateEngineStableService",1
+"DUMPSYS (/system/bin/dumpsys)","android.security.apc",1
+"DUMPSYS (/system/bin/dumpsys)","android.security.authorization",1
+"DUMPSYS (/system/bin/dumpsys)","android.security.compat",1
+"DUMPSYS (/system/bin/dumpsys)","android.security.identity",1
+"DUMPSYS (/system/bin/dumpsys)","android.security.legacykeystore",1
+"DUMPSYS (/system/bin/dumpsys)","android.security.maintenance",1
+"DUMPSYS (/system/bin/dumpsys)","android.security.metrics",1
+"DUMPSYS (/system/bin/dumpsys)","android.system.keystore2.IKeystoreService/default",1
+"DUMPSYS (/system/bin/dumpsys)","app_binding",19
+"DUMPSYS (/system/bin/dumpsys)","app_hibernation",556
+"DUMPSYS (/system/bin/dumpsys)","app_integrity",1
+"DUMPSYS (/system/bin/dumpsys)","app_prediction",1
+"DUMPSYS (/system/bin/dumpsys)","app_search",1
+"DUMPSYS (/system/bin/dumpsys)","appops",2140
+"DUMPSYS (/system/bin/dumpsys)","appwidget",38
+"DUMPSYS (/system/bin/dumpsys)","audio",394
+"DUMPSYS (/system/bin/dumpsys)","auth",1
+"DUMPSYS (/system/bin/dumpsys)","autofill",90
+"DUMPSYS (/system/bin/dumpsys)","backup",297
+"DUMPSYS (/system/bin/dumpsys)","battery",16
+"DUMPSYS (/system/bin/dumpsys)","batteryproperties",1
+"DUMPSYS (/system/bin/dumpsys)","batterystats",25363
+"DUMPSYS (/system/bin/dumpsys)","binder_calls_stats",17
+"DUMPSYS (/system/bin/dumpsys)","biometric",10
+"DUMPSYS (/system/bin/dumpsys)","blob_store",27
+"DUMPSYS (/system/bin/dumpsys)","bluetooth_manager",310
+"DUMPSYS (/system/bin/dumpsys)","bugreport",1
+"DUMPSYS (/system/bin/dumpsys)","cacheinfo",9911
+"DUMPSYS (/system/bin/dumpsys)","carrier_config",545
+"DUMPSYS (/system/bin/dumpsys)","clipboard",1
+"DUMPSYS (/system/bin/dumpsys)","cneservice",3
+"DUMPSYS (/system/bin/dumpsys)","color_display",38
+"DUMPSYS (/system/bin/dumpsys)","companiondevice",2
+"DUMPSYS (/system/bin/dumpsys)","connectivity",133
+"DUMPSYS (/system/bin/dumpsys)","connmetrics",14
+"DUMPSYS (/system/bin/dumpsys)","consumer_ir",1
+"DUMPSYS (/system/bin/dumpsys)","content",859
+"DUMPSYS (/system/bin/dumpsys)","content_capture",68
+"DUMPSYS (/system/bin/dumpsys)","content_suggestions",1
+"DUMPSYS (/system/bin/dumpsys)","contexthub",42
+"DUMPSYS (/system/bin/dumpsys)","country_detector",1
+"DUMPSYS (/system/bin/dumpsys)","crossprofileapps",1
+"DUMPSYS (/system/bin/dumpsys)","dataloader_manager",1
+"DUMPSYS (/system/bin/dumpsys)","dbinfo",2337
+"DUMPSYS (/system/bin/dumpsys)","device_config",1
+"DUMPSYS (/system/bin/dumpsys)","device_identifiers",1
+"DUMPSYS (/system/bin/dumpsys)","device_policy",192
+"DUMPSYS (/system/bin/dumpsys)","device_state",11
+"DUMPSYS (/system/bin/dumpsys)","deviceidle",172
+"DUMPSYS (/system/bin/dumpsys)","devicestoragemonitor",9
+"DUMPSYS (/system/bin/dumpsys)","diskstats",20
+"DUMPSYS (/system/bin/dumpsys)","display",350
+"DUMPSYS (/system/bin/dumpsys)","dnsresolver",16
+"DUMPSYS (/system/bin/dumpsys)","domain_verification",1
+"DUMPSYS (/system/bin/dumpsys)","dreams",26
+"DUMPSYS (/system/bin/dumpsys)","drm.drmManager",1
+"DUMPSYS (/system/bin/dumpsys)","dropbox",135
+"DUMPSYS (/system/bin/dumpsys)","dynamic_system",1
+"DUMPSYS (/system/bin/dumpsys)","econtroller",65
+"DUMPSYS (/system/bin/dumpsys)","emergency_affordance",8
+"DUMPSYS (/system/bin/dumpsys)","ethernet",19
+"DUMPSYS (/system/bin/dumpsys)","euicc_card_controller",3
+"DUMPSYS (/system/bin/dumpsys)","external_vibrator_service",1
+"DUMPSYS (/system/bin/dumpsys)","file_integrity",1
+"DUMPSYS (/system/bin/dumpsys)","fingerprint",10
+"DUMPSYS (/system/bin/dumpsys)","font",626
+"DUMPSYS (/system/bin/dumpsys)","game",1
+"DUMPSYS (/system/bin/dumpsys)","gfxinfo",1837
+"DUMPSYS (/system/bin/dumpsys)","gpu",75
+"DUMPSYS (/system/bin/dumpsys)","graphicsstats",392
+"DUMPSYS (/system/bin/dumpsys)","hardware_properties",29
+"DUMPSYS (/system/bin/dumpsys)","imms",1
+"DUMPSYS (/system/bin/dumpsys)","incident",1
+"DUMPSYS (/system/bin/dumpsys)","incidentcompanion",1
+"DUMPSYS (/system/bin/dumpsys)","input_method",1972
+"DUMPSYS (/system/bin/dumpsys)","inputflinger",2
+"DUMPSYS (/system/bin/dumpsys)","installd",13
+"DUMPSYS (/system/bin/dumpsys)","ions",1
+"DUMPSYS (/system/bin/dumpsys)","iphonesubinfo",1
+"DUMPSYS (/system/bin/dumpsys)","ipsec",6
+"DUMPSYS (/system/bin/dumpsys)","isms",112
+"DUMPSYS (/system/bin/dumpsys)","isub",64
+"DUMPSYS (/system/bin/dumpsys)","jobscheduler",9757
+"DUMPSYS (/system/bin/dumpsys)","launcherapps",1
+"DUMPSYS (/system/bin/dumpsys)","legacy_permission",1
+"DUMPSYS (/system/bin/dumpsys)","lights",12
+"DUMPSYS (/system/bin/dumpsys)","location",255
+"DUMPSYS (/system/bin/dumpsys)","location_time_zone_manager",35
+"DUMPSYS (/system/bin/dumpsys)","lock_settings",36
+"DUMPSYS (/system/bin/dumpsys)","looper_stats",4
+"DUMPSYS (/system/bin/dumpsys)","manager",1
+"DUMPSYS (/system/bin/dumpsys)","media.aaudio",18
+"DUMPSYS (/system/bin/dumpsys)","media.audio_flinger",694
+"DUMPSYS (/system/bin/dumpsys)","media.audio_policy",1892
+"DUMPSYS (/system/bin/dumpsys)","media.camera",2957
+"DUMPSYS (/system/bin/dumpsys)","media.camera.proxy",1
+"DUMPSYS (/system/bin/dumpsys)","media.extractor",36
+"DUMPSYS (/system/bin/dumpsys)","media.metrics",1275
+"DUMPSYS (/system/bin/dumpsys)","media.player",1154
+"DUMPSYS (/system/bin/dumpsys)","media.resource_manager",47
+"DUMPSYS (/system/bin/dumpsys)","media.resource_observer",3
+"DUMPSYS (/system/bin/dumpsys)","media_communication",1
+"DUMPSYS (/system/bin/dumpsys)","media_metrics",1
+"DUMPSYS (/system/bin/dumpsys)","media_projection",4
+"DUMPSYS (/system/bin/dumpsys)","media_resource_monitor",1
+"DUMPSYS (/system/bin/dumpsys)","media_router",46
+"DUMPSYS (/system/bin/dumpsys)","media_session",37
+"DUMPSYS (/system/bin/dumpsys)","memtrack.proxy",1
+"DUMPSYS (/system/bin/dumpsys)","midi",4
+"DUMPSYS (/system/bin/dumpsys)","mount",24
+"DUMPSYS (/system/bin/dumpsys)","music_recognition",1
+"DUMPSYS (/system/bin/dumpsys)","netd",194
+"DUMPSYS (/system/bin/dumpsys)","netd_listener",1
+"DUMPSYS (/system/bin/dumpsys)","netpolicy",893
+"DUMPSYS (/system/bin/dumpsys)","netstats",21
+"DUMPSYS (/system/bin/dumpsys)","network_management",17
+"DUMPSYS (/system/bin/dumpsys)","network_score",3
+"DUMPSYS (/system/bin/dumpsys)","network_stack",33
+"DUMPSYS (/system/bin/dumpsys)","network_time_update_service",8
+"DUMPSYS (/system/bin/dumpsys)","network_watchlist",6
+"DUMPSYS (/system/bin/dumpsys)","nfc",63
+"DUMPSYS (/system/bin/dumpsys)","notification",593
+"DUMPSYS (/system/bin/dumpsys)","oem_lock",1
+"DUMPSYS (/system/bin/dumpsys)","otadexopt",1
+"DUMPSYS (/system/bin/dumpsys)","overlay",2189
+"DUMPSYS (/system/bin/dumpsys)","pac_proxy",1
+"DUMPSYS (/system/bin/dumpsys)","package",36282
+"DUMPSYS (/system/bin/dumpsys)","package_native",1
+"DUMPSYS (/system/bin/dumpsys)","people",1
+"DUMPSYS (/system/bin/dumpsys)","performance_hint",4
+"DUMPSYS (/system/bin/dumpsys)","permission",1
+"DUMPSYS (/system/bin/dumpsys)","permission_checker",1
+"DUMPSYS (/system/bin/dumpsys)","permissionmgr",10
+"DUMPSYS (/system/bin/dumpsys)","persistent_data_block",1
+"DUMPSYS (/system/bin/dumpsys)","phone",65
+"DUMPSYS (/system/bin/dumpsys)","pinner",25
+"DUMPSYS (/system/bin/dumpsys)","platform_compat",127
+"DUMPSYS (/system/bin/dumpsys)","platform_compat_native",1
+"DUMPSYS (/system/bin/dumpsys)","power",1419
+"DUMPSYS (/system/bin/dumpsys)","powerstats",1
+"DUMPSYS (/system/bin/dumpsys)","print",33
+"DUMPSYS (/system/bin/dumpsys)","processinfo",1
+"DUMPSYS (/system/bin/dumpsys)","procstats",26346
+"DUMPSYS (/system/bin/dumpsys)","qchook",1
+"DUMPSYS (/system/bin/dumpsys)","qti.ims.ext",1
+"DUMPSYS (/system/bin/dumpsys)","rcs",1
+"DUMPSYS (/system/bin/dumpsys)","reboot_readiness",1
+"DUMPSYS (/system/bin/dumpsys)","recovery",1
+"DUMPSYS (/system/bin/dumpsys)","restrictions",1
+"DUMPSYS (/system/bin/dumpsys)","role",107
+"DUMPSYS (/system/bin/dumpsys)","rollback",7
+"DUMPSYS (/system/bin/dumpsys)","runtime",14
+"DUMPSYS (/system/bin/dumpsys)","scheduling_policy",1
+"DUMPSYS (/system/bin/dumpsys)","search",16
+"DUMPSYS (/system/bin/dumpsys)","search_ui",1
+"DUMPSYS (/system/bin/dumpsys)","sec_key_att_app_id_provider",1
+"DUMPSYS (/system/bin/dumpsys)","secure_element",7
+"DUMPSYS (/system/bin/dumpsys)","sensor_privacy",9
+"DUMPSYS (/system/bin/dumpsys)","serial",1
+"DUMPSYS (/system/bin/dumpsys)","servicediscovery",4
+"DUMPSYS (/system/bin/dumpsys)","settings",318
+"DUMPSYS (/system/bin/dumpsys)","shortcut",921
+"DUMPSYS (/system/bin/dumpsys)","simphonebook",1
+"DUMPSYS (/system/bin/dumpsys)","sip",1
+"DUMPSYS (/system/bin/dumpsys)","slice",1
+"DUMPSYS (/system/bin/dumpsys)","smartspace",1
+"DUMPSYS (/system/bin/dumpsys)","soundtrigger",1
+"DUMPSYS (/system/bin/dumpsys)","soundtrigger_middleware",17
+"DUMPSYS (/system/bin/dumpsys)","speech_recognition",1
+"DUMPSYS (/system/bin/dumpsys)","stats",2
+"DUMPSYS (/system/bin/dumpsys)","statscompanion",2
+"DUMPSYS (/system/bin/dumpsys)","statsmanager",1
+"DUMPSYS (/system/bin/dumpsys)","statusbar",8
+"DUMPSYS (/system/bin/dumpsys)","storaged",229
+"DUMPSYS (/system/bin/dumpsys)","storaged_pri",1
+"DUMPSYS (/system/bin/dumpsys)","storagestats",1
+"DUMPSYS (/system/bin/dumpsys)","suspend_control",1
+"DUMPSYS (/system/bin/dumpsys)","suspend_control_internal",59
+"DUMPSYS (/system/bin/dumpsys)","system_config",1
+"DUMPSYS (/system/bin/dumpsys)","system_server_dumper",158
+"DUMPSYS (/system/bin/dumpsys)","system_update",1
+"DUMPSYS (/system/bin/dumpsys)","telecom",49
+"DUMPSYS (/system/bin/dumpsys)","telephony.registry",131
+"DUMPSYS (/system/bin/dumpsys)","telephony_ims",1
+"DUMPSYS (/system/bin/dumpsys)","testharness",1
+"DUMPSYS (/system/bin/dumpsys)","tethering",92
+"DUMPSYS (/system/bin/dumpsys)","textclassification",25
+"DUMPSYS (/system/bin/dumpsys)","textservices",183
+"DUMPSYS (/system/bin/dumpsys)","texttospeech",1
+"DUMPSYS (/system/bin/dumpsys)","thermalservice",62
+"DUMPSYS (/system/bin/dumpsys)","time_detector",18
+"DUMPSYS (/system/bin/dumpsys)","time_zone_detector",19
+"DUMPSYS (/system/bin/dumpsys)","tracing.proxy",1
+"DUMPSYS (/system/bin/dumpsys)","translation",11
+"DUMPSYS (/system/bin/dumpsys)","trust",6
+"DUMPSYS (/system/bin/dumpsys)","uimode",9
+"DUMPSYS (/system/bin/dumpsys)","updatelock",2
+"DUMPSYS (/system/bin/dumpsys)","uri_grants",1
+"DUMPSYS (/system/bin/dumpsys)","usagestats",2933
+"DUMPSYS (/system/bin/dumpsys)","usb",171
+"DUMPSYS (/system/bin/dumpsys)","user",342
+"DUMPSYS (/system/bin/dumpsys)","vcn_management",67
+"DUMPSYS (/system/bin/dumpsys)","vibrator_manager",37
+"DUMPSYS (/system/bin/dumpsys)","voiceinteraction",57
+"DUMPSYS (/system/bin/dumpsys)","vpn_management",3
+"DUMPSYS (/system/bin/dumpsys)","vrmanager",32
+"DUMPSYS (/system/bin/dumpsys)","wallpaper",29
+"DUMPSYS (/system/bin/dumpsys)","webviewupdate",18
+"DUMPSYS (/system/bin/dumpsys)","wifi",1167
+"DUMPSYS (/system/bin/dumpsys)","wifiaware",96
+"DUMPSYS (/system/bin/dumpsys)","wifinl80211",23
+"DUMPSYS (/system/bin/dumpsys)","wifip2p",28
+"DUMPSYS (/system/bin/dumpsys)","wifirtt",17
+"DUMPSYS (/system/bin/dumpsys)","wifiscanner",219
+"DUMPSYS CRITICAL (/system/bin/dumpsys)","SurfaceFlinger",1324
+"DUMPSYS CRITICAL (/system/bin/dumpsys)","activity",3319
+"DUMPSYS CRITICAL (/system/bin/dumpsys)","cpuinfo",101
+"DUMPSYS CRITICAL (/system/bin/dumpsys)","input",418
+"DUMPSYS CRITICAL (/system/bin/dumpsys)","input_method",260
+"DUMPSYS CRITICAL (/system/bin/dumpsys)","notification",260
+"DUMPSYS CRITICAL (/system/bin/dumpsys)","power",1382
+"DUMPSYS CRITICAL (/system/bin/dumpsys)","sensorservice",270
+"DUMPSYS CRITICAL (/system/bin/dumpsys)","window",885
+"DUMPSYS HIGH (/system/bin/dumpsys)","meminfo",3864
+"DUMPSYS HIGH (/system/bin/dumpsys)","network_stack",33
+"DUMPSYS HIGH (/system/bin/dumpsys)","tethering",92
+"Dmabuf dump (dmabuf_dump)","[NULL]",290
+"Dmabuf per-buffer/per-exporter/per-device stats (dmabuf_dump -b)","[NULL]",1
+"EBPF MAP STATS (/system/bin/dumpsys -T 30000 netd trafficcontroller)","[NULL]",266
+"EVENT LOG (logcat -b events -v threadtime -v printable -v uid -d *:v)","[NULL]",2410
+"EXTERNAL FRAGMENTATION INFO","[NULL]",3
+"FILESYSTEMS & FREE SPACE (df)","[NULL]",35
+"HARDWARE HALS (/system/xbin/su root lshal --all --types=all)","[NULL]",359
+"IOTOP (iotop -n 1 -m 100)","[NULL]",103
+"IP RULES (ip rule show)","[NULL]",12
+"IP RULES v6 (ip -6 rule show)","[NULL]",12
+"IP6TABLES (ip6tables -L -nvx)","[NULL]",108
+"IP6TABLES MANGLE (ip6tables -t mangle -L -nvx)","[NULL]",97
+"IP6TABLES RAW (ip6tables -t raw -L -nvx)","[NULL]",29
+"IPTABLES (iptables -L -nvx)","[NULL]",129
+"IPTABLES MANGLE (iptables -t mangle -L -nvx)","[NULL]",97
+"IPTABLES NAT (iptables -t nat -L -nvx)","[NULL]",19
+"IPTABLES RAW (iptables -t raw -L -nvx)","[NULL]",29
+"IPv4 ADDRESSES (ip -4 addr show)","[NULL]",3
+"IPv6 ADDRESSES (ip -6 addr show)","[NULL]",9
+"KERNEL CPUFREQ (/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state)","[NULL]",19
+"KERNEL LOG (logcat -b kernel -v threadtime -v printable -v uid -d *:v)","[NULL]",4894
+"KERNEL WAKE SOURCES (/d/wakeup_sources)","[NULL]",392
+"LAST LOGCAT (logcat -L -b all -v threadtime -v printable -v uid -d *:v)","[NULL]",1
+"LIBRANK (/system/xbin/su root librank)","[NULL]",42470
+"LIST OF OPEN FILES (/system/xbin/su root lsof)","[NULL]",42793
+"LOG STATISTICS (logcat -b all -S)","[NULL]",225
+"LPDUMP (lpdump --all)","[NULL]",71
+"LSMOD (lsmod)","[NULL]",14
+"MEMORY INFO (/proc/meminfo)","[NULL]",40
+"MODULES INFO (/system/xbin/su root sh -c cat /proc/modules | cut -d' ' -f1 |     while read MOD ; do echo modinfo:$MOD ; modinfo $MOD ; done)","[NULL]",152
+"MULTICAST ADDRESSES (ip maddr)","[NULL]",105
+"NETSTAT (netstat -nW)","[NULL]",628
+"NETWORK DEV INFO (/proc/net/dev)","[NULL]",34
+"NETWORK INTERFACES (ip link)","[NULL]",62
+"PAGETYPEINFO (/proc/pagetypeinfo)","[NULL]",21
+"PRINTENV (printenv)","[NULL]",16
+"PROCESS TIMES (pid cmd user system iowait+percentage)","[NULL]",256
+"PROCESSES AND THREADS (ps -A -T -Z -O pri,nice,rtprio,sched,pcy,time)","[NULL]",2783
+"PROCRANK (/system/xbin/su root procrank)","[NULL]",173
+"PSI cpu (/proc/pressure/cpu)","[NULL]",2
+"PSI io (/proc/pressure/io)","[NULL]",3
+"PSI memory (/proc/pressure/memory)","[NULL]",3
+"RADIO LOG (logcat -b radio -v threadtime -v printable -v uid -d *:v)","[NULL]",2012
+"ROUTE TABLE IPv4 (ip -4 route show table 1003)","[NULL]",1
+"ROUTE TABLE IPv4 (ip -4 route show table 255)","[NULL]",4
+"ROUTE TABLE IPv6 (ip -6 route show table 1003)","[NULL]",2
+"ROUTE TABLE IPv6 (ip -6 route show table 1010)","[NULL]",2
+"ROUTE TABLE IPv6 (ip -6 route show table 255)","[NULL]",5
+"RT_TABLES (/data/misc/net/rt_tables: 2021-08-24 23:32:34)","[NULL]",8
+"SERVICE HIGH connectivity (/system/bin/dumpsys -T 10000 connectivity --dump-priority HIGH)","[NULL]",98
+"SHOW MAP 1 (/system/bin/init) (/system/xbin/su root showmap -q 1)","[NULL]",344
+"SHOW MAP 10 ([rcuop/0]) (/system/xbin/su root showmap -q 10)","[NULL]",8
+"SHOW MAP 100 ([irq/125-tsens-u]) (/system/xbin/su root showmap -q 100)","[NULL]",8
+"SHOW MAP 101 ([irq/126-tsens-c]) (/system/xbin/su root showmap -q 101)","[NULL]",8
+"SHOW MAP 1013 (/system/bin/hw/android.hidl.allocator@1.0-service) (/system/xbin/su root showmap -q 1013)","[NULL]",54
+"SHOW MAP 1014 (/vendor/bin/hw/android.hardware.neuralnetworks@1.0-service-paintbox) (/system/xbin/su root showmap -q 1014)","[NULL]",112
+"SHOW MAP 1015 (/vendor/bin/hw/android.hardware.audio.service) (/system/xbin/su root showmap -q 1015)","[NULL]",169
+"SHOW MAP 1016 (/vendor/bin/hw/android.hardware.bluetooth@1.0-service-qti) (/system/xbin/su root showmap -q 1016)","[NULL]",75
+"SHOW MAP 1017 (/vendor/bin/hw/android.hardware.camera.provider@2.4-service_64) (/system/xbin/su root showmap -q 1017)","[NULL]",276
+"SHOW MAP 1019 (/vendor/bin/hw/android.hardware.cas@1.2-service) (/system/xbin/su root showmap -q 1019)","[NULL]",65
+"SHOW MAP 102 ([irq/127-tsens-u]) (/system/xbin/su root showmap -q 102)","[NULL]",8
+"SHOW MAP 1020 (/vendor/bin/hw/android.hardware.confirmationui@1.0-service-google) (/system/xbin/su root showmap -q 1020)","[NULL]",86
+"SHOW MAP 1023 (/vendor/bin/hw/android.hardware.contexthub@1.2-service.generic) (/system/xbin/su root showmap -q 1023)","[NULL]",59
+"SHOW MAP 1025 (/vendor/bin/hw/android.hardware.drm@1.0-service) (/system/xbin/su root showmap -q 1025)","[NULL]",67
+"SHOW MAP 1026 (/vendor/bin/hw/android.hardware.drm@1.3-service.widevine) (/system/xbin/su root showmap -q 1026)","[NULL]",68
+"SHOW MAP 1028 (/vendor/bin/hw/android.hardware.drm@1.4-service.clearkey) (/system/xbin/su root showmap -q 1028)","[NULL]",68
+"SHOW MAP 103 ([irq/128-tsens-c]) (/system/xbin/su root showmap -q 103)","[NULL]",8
+"SHOW MAP 1030 (/vendor/bin/hw/android.hardware.gnss@1.1-service-qti) (/system/xbin/su root showmap -q 1030)","[NULL]",114
+"SHOW MAP 1031 ([kworker/u16:10]) (/system/xbin/su root showmap -q 1031)","[NULL]",8
+"SHOW MAP 1032 (/vendor/bin/hw/android.hardware.health@2.0-service.crosshatch) (/system/xbin/su root showmap -q 1032)","[NULL]",74
+"SHOW MAP 1036 (/vendor/bin/hw/android.hardware.identity@1.0-service.citadel) (/system/xbin/su root showmap -q 1036)","[NULL]",89
+"SHOW MAP 1037 (/vendor/bin/hw/android.hardware.neuralnetworks@1.3-service-qti) (/system/xbin/su root showmap -q 1037)","[NULL]",116
+"SHOW MAP 1038 (/vendor/bin/hw/android.hardware.nfc@1.2-service) (/system/xbin/su root showmap -q 1038)","[NULL]",75
+"SHOW MAP 1039 (/vendor/bin/hw/android.hardware.power.stats@1.0-service.pixel) (/system/xbin/su root showmap -q 1039)","[NULL]",64
+"SHOW MAP 104 ([edac-poller]) (/system/xbin/su root showmap -q 104)","[NULL]",8
+"SHOW MAP 1040 (/vendor/bin/hw/android.hardware.sensors@2.0-service) (/system/xbin/su root showmap -q 1040)","[NULL]",103
+"SHOW MAP 1044 (/vendor/bin/hw/android.hardware.usb@1.3-service.crosshatch) (/system/xbin/su root showmap -q 1044)","[NULL]",68
+"SHOW MAP 1045 (/vendor/bin/hw/android.hardware.weaver@1.0-service.citadel) (/system/xbin/su root showmap -q 1045)","[NULL]",83
+"SHOW MAP 10459 ([kworker/2:0]) (/system/xbin/su root showmap -q 10459)","[NULL]",8
+"SHOW MAP 10460 ([kworker/2:4]) (/system/xbin/su root showmap -q 10460)","[NULL]",8
+"SHOW MAP 1047 (/vendor/bin/pixelstats-vendor) (/system/xbin/su root showmap -q 1047)","[NULL]",67
+"SHOW MAP 10474 ([kworker/5:2]) (/system/xbin/su root showmap -q 10474)","[NULL]",8
+"SHOW MAP 10480 ([kworker/4:0]) (/system/xbin/su root showmap -q 10480)","[NULL]",8
+"SHOW MAP 105 ([system]) (/system/xbin/su root showmap -q 105)","[NULL]",8
+"SHOW MAP 1050 (/vendor/bin/hw/small_hals.crosshatch-service) (/system/xbin/su root showmap -q 1050)","[NULL]",66
+"SHOW MAP 1055 (/vendor/bin/hw/vendor.google.google_battery@1.1-service-vendor) (/system/xbin/su root showmap -q 1055)","[NULL]",57
+"SHOW MAP 1059 (/vendor/bin/hw/vendor.google.radioext@1.0-service) (/system/xbin/su root showmap -q 1059)","[NULL]",92
+"SHOW MAP 106 ([ipa_power_mgmt]) (/system/xbin/su root showmap -q 106)","[NULL]",8
+"SHOW MAP 1060 (/vendor/bin/hw/vendor.google.wifi_ext@1.0-service-vendor) (/system/xbin/su root showmap -q 1060)","[NULL]",80
+"SHOW MAP 1062 (/vendor/bin/hw/vendor.google.wireless_charger@1.3-service-vendor) (/system/xbin/su root showmap -q 1062)","[NULL]",63
+"SHOW MAP 1063 ([cdsp_IPCRTR]) (/system/xbin/su root showmap -q 1063)","[NULL]",8
+"SHOW MAP 1065 (/vendor/bin/hw/vendor.qti.hardware.tui_comm@1.0-service-qti) (/system/xbin/su root showmap -q 1065)","[NULL]",60
+"SHOW MAP 1069 (/vendor/bin/hw/vendor.qti.media.c2@1.0-service) (/system/xbin/su root showmap -q 1069)","[NULL]",152
+"SHOW MAP 107 ([transport_power]) (/system/xbin/su root showmap -q 107)","[NULL]",8
+"SHOW MAP 1070 (/vendor/bin/pm-service) (/system/xbin/su root showmap -q 1070)","[NULL]",73
+"SHOW MAP 1073 ([kworker/u16:11]) (/system/xbin/su root showmap -q 1073)","[NULL]",8
+"SHOW MAP 1075 ([kworker/u16:12]) (/system/xbin/su root showmap -q 1075)","[NULL]",8
+"SHOW MAP 1079 (/vendor/bin/rmt_storage) (/system/xbin/su root showmap -q 1079)","[NULL]",67
+"SHOW MAP 108 ([ipa_rm_wq]) (/system/xbin/su root showmap -q 108)","[NULL]",8
+"SHOW MAP 1085 (/vendor/bin/tftp_server) (/system/xbin/su root showmap -q 1085)","[NULL]",45
+"SHOW MAP 1086 (/vendor/bin/modem_svc) (/system/xbin/su root showmap -q 1086)","[NULL]",76
+"SHOW MAP 1088 (/vendor/bin/pd-mapper) (/system/xbin/su root showmap -q 1088)","[NULL]",64
+"SHOW MAP 109 ([devfreq_wq]) (/system/xbin/su root showmap -q 109)","[NULL]",8
+"SHOW MAP 1090 (/vendor/bin/msm_irqbalance) (/system/xbin/su root showmap -q 1090)","[NULL]",53
+"SHOW MAP 1093 (/system/bin/audioserver) (/system/xbin/su root showmap -q 1093)","[NULL]",225
+"SHOW MAP 1095 (/system/bin/credstore) (/system/xbin/su root showmap -q 1095)","[NULL]",69
+"SHOW MAP 1097 (/system/bin/gpuservice) (/system/xbin/su root showmap -q 1097)","[NULL]",92
+"SHOW MAP 1099 (/vendor/bin/sensors.qti) (/system/xbin/su root showmap -q 1099)","[NULL]",76
+"SHOW MAP 11 ([rcuos/0]) (/system/xbin/su root showmap -q 11)","[NULL]",8
+"SHOW MAP 110 ([governor_msm_ad]) (/system/xbin/su root showmap -q 110)","[NULL]",8
+"SHOW MAP 111 ([cfg80211]) (/system/xbin/su root showmap -q 111)","[NULL]",8
+"SHOW MAP 1110 ([qmi_hndl0000000]) (/system/xbin/su root showmap -q 1110)","[NULL]",8
+"SHOW MAP 1112 ([qmi_hndl0000000]) (/system/xbin/su root showmap -q 1112)","[NULL]",8
+"SHOW MAP 1115 ([qmi_hndl0000000]) (/system/xbin/su root showmap -q 1115)","[NULL]",8
+"SHOW MAP 1117 ([modem]) (/system/xbin/su root showmap -q 1117)","[NULL]",8
+"SHOW MAP 1118 ([adsp]) (/system/xbin/su root showmap -q 1118)","[NULL]",8
+"SHOW MAP 1119 ([kworker/u16:13]) (/system/xbin/su root showmap -q 1119)","[NULL]",8
+"SHOW MAP 112 ([irq/541-ibb-sc-]) (/system/xbin/su root showmap -q 112)","[NULL]",8
+"SHOW MAP 11249 (/vendor/bin/diag_mdlog) (/system/xbin/su root showmap -q 11249)","[NULL]",55
+"SHOW MAP 1126 (/vendor/bin/ramdump) (/system/xbin/su root showmap -q 1126)","[NULL]",70
+"SHOW MAP 113 ([irq/542-lab-sc-]) (/system/xbin/su root showmap -q 113)","[NULL]",8
+"SHOW MAP 1134 ([kworker/u17:1]) (/system/xbin/su root showmap -q 1134)","[NULL]",8
+"SHOW MAP 114 ([kworker/1:2]) (/system/xbin/su root showmap -q 114)","[NULL]",8
+"SHOW MAP 115 ([irq/75-qpnp_wle]) (/system/xbin/su root showmap -q 115)","[NULL]",8
+"SHOW MAP 1150 ([lpass_IPCRTR]) (/system/xbin/su root showmap -q 1150)","[NULL]",8
+"SHOW MAP 1153 ([qmi_hndl0000000]) (/system/xbin/su root showmap -q 1153)","[NULL]",8
+"SHOW MAP 1155 ([qmi_hndl0000000]) (/system/xbin/su root showmap -q 1155)","[NULL]",8
+"SHOW MAP 1156 ([qmi_hndl0000000]) (/system/xbin/su root showmap -q 1156)","[NULL]",8
+"SHOW MAP 1157 ([kworker/u17:2]) (/system/xbin/su root showmap -q 1157)","[NULL]",8
+"SHOW MAP 1159 (/vendor/bin/subsystem_ramdump) (/system/xbin/su root showmap -q 1159)","[NULL]",67
+"SHOW MAP 116 ([irq/76-qpnp_wle]) (/system/xbin/su root showmap -q 116)","[NULL]",8
+"SHOW MAP 1160 (/vendor/bin/mm-pp-dpps) (/system/xbin/su root showmap -q 1160)","[NULL]",68
+"SHOW MAP 1168 ([msm_slim_qmi_cl]) (/system/xbin/su root showmap -q 1168)","[NULL]",8
+"SHOW MAP 1169 ([msm_slim_qmi_cl]) (/system/xbin/su root showmap -q 1169)","[NULL]",8
+"SHOW MAP 117 ([watchdogd]) (/system/xbin/su root showmap -q 117)","[NULL]",8
+"SHOW MAP 1170 ([qmi_hndl0000000]) (/system/xbin/su root showmap -q 1170)","[NULL]",8
+"SHOW MAP 1171 ([qmi_hndl0000000]) (/system/xbin/su root showmap -q 1171)","[NULL]",8
+"SHOW MAP 1177 (/system/bin/drmserver) (/system/xbin/su root showmap -q 1177)","[NULL]",84
+"SHOW MAP 1178 (logcat) (/system/xbin/su root showmap -q 1178)","[NULL]",48
+"SHOW MAP 118 ([kworker/2:1]) (/system/xbin/su root showmap -q 118)","[NULL]",8
+"SHOW MAP 1180 (/system/bin/traced_probes) (/system/xbin/su root showmap -q 1180)","[NULL]",43
+"SHOW MAP 1182 ([irq/211-arm-smm]) (/system/xbin/su root showmap -q 1182)","[NULL]",8
+"SHOW MAP 119 ([kworker/4:1]) (/system/xbin/su root showmap -q 119)","[NULL]",8
+"SHOW MAP 1194 ([irq/212-arm-smm]) (/system/xbin/su root showmap -q 1194)","[NULL]",8
+"SHOW MAP 1195 ([irq/213-arm-smm]) (/system/xbin/su root showmap -q 1195)","[NULL]",8
+"SHOW MAP 1196 (/system/bin/traced) (/system/xbin/su root showmap -q 1196)","[NULL]",42
+"SHOW MAP 12 ([rcuob/0]) (/system/xbin/su root showmap -q 12)","[NULL]",8
+"SHOW MAP 1231 (/vendor/bin/adsprpcd) (/system/xbin/su root showmap -q 1231)","[NULL]",50
+"SHOW MAP 1234 (/vendor/bin/cdsprpcd) (/system/xbin/su root showmap -q 1234)","[NULL]",49
+"SHOW MAP 1236 (/vendor/bin/imsqmidaemon) (/system/xbin/su root showmap -q 1236)","[NULL]",62
+"SHOW MAP 1240 (/vendor/bin/cnd) (/system/xbin/su root showmap -q 1240)","[NULL]",103
+"SHOW MAP 1243 (/vendor/bin/netmgrd) (/system/xbin/su root showmap -q 1243)","[NULL]",180
+"SHOW MAP 1244 (/vendor/bin/port-bridge) (/system/xbin/su root showmap -q 1244)","[NULL]",55
+"SHOW MAP 1245 (/vendor/bin/ipacm) (/system/xbin/su root showmap -q 1245)","[NULL]",70
+"SHOW MAP 1247 (/system/bin/cameraserver) (/system/xbin/su root showmap -q 1247)","[NULL]",220
+"SHOW MAP 1248 ([irq/311-wcd9xxx]) (/system/xbin/su root showmap -q 1248)","[NULL]",8
+"SHOW MAP 1258 (/system/bin/incidentd) (/system/xbin/su root showmap -q 1258)","[NULL]",59
+"SHOW MAP 1261 (/system/bin/installd) (/system/xbin/su root showmap -q 1261)","[NULL]",64
+"SHOW MAP 1264 (media.extractor) (/system/xbin/su root showmap -q 1264)","[NULL]",210
+"SHOW MAP 1268 (media.metrics) (/system/xbin/su root showmap -q 1268)","[NULL]",87
+"SHOW MAP 1272 (/system/bin/mediaserver) (/system/xbin/su root showmap -q 1272)","[NULL]",219
+"SHOW MAP 1275 (/system/bin/storaged) (/system/xbin/su root showmap -q 1275)","[NULL]",65
+"SHOW MAP 1277 (/system/bin/wificond) (/system/xbin/su root showmap -q 1277)","[NULL]",67
+"SHOW MAP 1279 (media.codec) (/system/xbin/su root showmap -q 1279)","[NULL]",133
+"SHOW MAP 1283 (/vendor/bin/easelmanagerd) (/system/xbin/su root showmap -q 1283)","[NULL]",68
+"SHOW MAP 1295 (/vendor/bin/hw/qcrild) (/system/xbin/su root showmap -q 1295)","[NULL]",201
+"SHOW MAP 13 ([migration/0]) (/system/xbin/su root showmap -q 13)","[NULL]",8
+"SHOW MAP 1304 ([kworker/u16:14]) (/system/xbin/su root showmap -q 1304)","[NULL]",8
+"SHOW MAP 1306 (media.swcodec) (/system/xbin/su root showmap -q 1306)","[NULL]",184
+"SHOW MAP 1307 ([qmi_wq]) (/system/xbin/su root showmap -q 1307)","[NULL]",8
+"SHOW MAP 1311 (/vendor/bin/cnss-daemon) (/system/xbin/su root showmap -q 1311)","[NULL]",76
+"SHOW MAP 1313 (/vendor/bin/loc_launcher) (/system/xbin/su root showmap -q 1313)","[NULL]",57
+"SHOW MAP 1315 (/system/bin/gatekeeperd) (/system/xbin/su root showmap -q 1315)","[NULL]",66
+"SHOW MAP 1319 (/system/bin/update_engine) (/system/xbin/su root showmap -q 1319)","[NULL]",88
+"SHOW MAP 1321 (/vendor/bin/hw/android.hardware.biometrics.fingerprint@2.1-service.fpc) (/system/xbin/su root showmap -q 1321)","[NULL]",64
+"SHOW MAP 1323 (/vendor/bin/chre) (/system/xbin/su root showmap -q 1323)","[NULL]",65
+"SHOW MAP 1339 (/vendor/bin/imsdatadaemon) (/system/xbin/su root showmap -q 1339)","[NULL]",98
+"SHOW MAP 1345 (lowi-server) (/system/xbin/su root showmap -q 1345)","[NULL]",75
+"SHOW MAP 1346 (xtra-daemon) (/system/xbin/su root showmap -q 1346)","[NULL]",80
+"SHOW MAP 1347 (/system/bin/llkd) (/system/xbin/su root showmap -q 1347)","[NULL]",45
+"SHOW MAP 1356 ([kworker/u16:15]) (/system/xbin/su root showmap -q 1356)","[NULL]",8
+"SHOW MAP 1367 ([qmi_hndl0000000]) (/system/xbin/su root showmap -q 1367)","[NULL]",8
+"SHOW MAP 1387 ([kworker/u16:16]) (/system/xbin/su root showmap -q 1387)","[NULL]",8
+"SHOW MAP 14 ([lru-add-drain]) (/system/xbin/su root showmap -q 14)","[NULL]",8
+"SHOW MAP 144 ([kswapd0]) (/system/xbin/su root showmap -q 144)","[NULL]",8
+"SHOW MAP 145 ([vmstat]) (/system/xbin/su root showmap -q 145)","[NULL]",8
+"SHOW MAP 146 ([ecryptfs-kthrea]) (/system/xbin/su root showmap -q 146)","[NULL]",8
+"SHOW MAP 1468 ([kworker/u16:17]) (/system/xbin/su root showmap -q 1468)","[NULL]",8
+"SHOW MAP 1469 ([kworker/u16:18]) (/system/xbin/su root showmap -q 1469)","[NULL]",8
+"SHOW MAP 1471 ([kworker/u16:19]) (/system/xbin/su root showmap -q 1471)","[NULL]",8
+"SHOW MAP 15 ([cpuhp/0]) (/system/xbin/su root showmap -q 15)","[NULL]",8
+"SHOW MAP 16 ([cpuhp/1]) (/system/xbin/su root showmap -q 16)","[NULL]",8
+"SHOW MAP 1603 ([wdsp_glink_wq]) (/system/xbin/su root showmap -q 1603)","[NULL]",8
+"SHOW MAP 17 ([migration/1]) (/system/xbin/su root showmap -q 17)","[NULL]",8
+"SHOW MAP 1702 (/vendor/bin/hw/android.hardware.thermal@2.0-service.pixel) (/system/xbin/su root showmap -q 1702)","[NULL]",71
+"SHOW MAP 1723 ([mpss_IPCRTR]) (/system/xbin/su root showmap -q 1723)","[NULL]",8
+"SHOW MAP 1724 ([qmi_hndl0000001]) (/system/xbin/su root showmap -q 1724)","[NULL]",8
+"SHOW MAP 1726 ([qmi_hndl0000001]) (/system/xbin/su root showmap -q 1726)","[NULL]",8
+"SHOW MAP 1759 ([qmi_hndl0000001]) (/system/xbin/su root showmap -q 1759)","[NULL]",8
+"SHOW MAP 1764 ([qmi_hndl0000001]) (/system/xbin/su root showmap -q 1764)","[NULL]",8
+"SHOW MAP 1768 ([kworker/u16:20]) (/system/xbin/su root showmap -q 1768)","[NULL]",8
+"SHOW MAP 1773 ([kworker/u16:22]) (/system/xbin/su root showmap -q 1773)","[NULL]",8
+"SHOW MAP 18 ([ksoftirqd/1]) (/system/xbin/su root showmap -q 18)","[NULL]",8
+"SHOW MAP 1831 ([qmi_hndl0000001]) (/system/xbin/su root showmap -q 1831)","[NULL]",8
+"SHOW MAP 1835 (system_server) (/system/xbin/su root showmap -q 1835)","[NULL]",1155
+"SHOW MAP 1841 ([kworker/u16:23]) (/system/xbin/su root showmap -q 1841)","[NULL]",8
+"SHOW MAP 1893 ([ipawq34]) (/system/xbin/su root showmap -q 1893)","[NULL]",8
+"SHOW MAP 1894 ([iparepwq34]) (/system/xbin/su root showmap -q 1894)","[NULL]",8
+"SHOW MAP 1895 ([ipawq35]) (/system/xbin/su root showmap -q 1895)","[NULL]",8
+"SHOW MAP 1896 ([iparepwq35]) (/system/xbin/su root showmap -q 1896)","[NULL]",8
+"SHOW MAP 19 ([kworker/1:0]) (/system/xbin/su root showmap -q 19)","[NULL]",8
+"SHOW MAP 2 ([kthreadd]) (/system/xbin/su root showmap -q 2)","[NULL]",8
+"SHOW MAP 20 ([kworker/1:0H]) (/system/xbin/su root showmap -q 20)","[NULL]",8
+"SHOW MAP 200 ([irq/174-arm-smm]) (/system/xbin/su root showmap -q 200)","[NULL]",8
+"SHOW MAP 201 ([irq/175-arm-smm]) (/system/xbin/su root showmap -q 201)","[NULL]",8
+"SHOW MAP 202 ([irq/91-eud_irq]) (/system/xbin/su root showmap -q 202)","[NULL]",8
+"SHOW MAP 203 ([glink_ssr_wq]) (/system/xbin/su root showmap -q 203)","[NULL]",8
+"SHOW MAP 204 ([glink_lbsrv]) (/system/xbin/su root showmap -q 204)","[NULL]",8
+"SHOW MAP 205 ([spi_wdsp]) (/system/xbin/su root showmap -q 205)","[NULL]",8
+"SHOW MAP 206 ([wdsp_spi_glink_]) (/system/xbin/su root showmap -q 206)","[NULL]",8
+"SHOW MAP 207 ([glink_xprt_wq]) (/system/xbin/su root showmap -q 207)","[NULL]",8
+"SHOW MAP 208 ([IPCRTR_mpss_sme]) (/system/xbin/su root showmap -q 208)","[NULL]",8
+"SHOW MAP 209 ([IPCRTR_lpass_sm]) (/system/xbin/su root showmap -q 209)","[NULL]",8
+"SHOW MAP 21 ([rcuop/1]) (/system/xbin/su root showmap -q 21)","[NULL]",8
+"SHOW MAP 210 ([IPCRTR_dsps_sme]) (/system/xbin/su root showmap -q 210)","[NULL]",8
+"SHOW MAP 211 ([IPCRTR_cdsp_sme]) (/system/xbin/su root showmap -q 211)","[NULL]",8
+"SHOW MAP 212 ([glink_pkt_wq]) (/system/xbin/su root showmap -q 212)","[NULL]",8
+"SHOW MAP 2122 (/vendor/bin/ims_rtp_daemon) (/system/xbin/su root showmap -q 2122)","[NULL]",102
+"SHOW MAP 2123 (/vendor/bin/imsrcsd) (/system/xbin/su root showmap -q 2123)","[NULL]",97
+"SHOW MAP 213 ([irq/176-arm-smm]) (/system/xbin/su root showmap -q 213)","[NULL]",8
+"SHOW MAP 214 ([qmi_svc_event_w]) (/system/xbin/su root showmap -q 214)","[NULL]",8
+"SHOW MAP 215 ([msm_ipc_router]) (/system/xbin/su root showmap -q 215)","[NULL]",8
+"SHOW MAP 2150 ([cds_mc_thread]) (/system/xbin/su root showmap -q 2150)","[NULL]",8
+"SHOW MAP 2151 ([cds_ol_rx_threa]) (/system/xbin/su root showmap -q 2151)","[NULL]",8
+"SHOW MAP 2152 ([wmi_rx_event_wo]) (/system/xbin/su root showmap -q 2152)","[NULL]",8
+"SHOW MAP 2154 ([ipawq13]) (/system/xbin/su root showmap -q 2154)","[NULL]",8
+"SHOW MAP 2155 ([iparepwq13]) (/system/xbin/su root showmap -q 2155)","[NULL]",8
+"SHOW MAP 2156 ([ipawq15]) (/system/xbin/su root showmap -q 2156)","[NULL]",8
+"SHOW MAP 2157 ([iparepwq15]) (/system/xbin/su root showmap -q 2157)","[NULL]",8
+"SHOW MAP 216 ([servloc_wq]) (/system/xbin/su root showmap -q 216)","[NULL]",8
+"SHOW MAP 217 ([irq/177-arm-smm]) (/system/xbin/su root showmap -q 217)","[NULL]",8
+"SHOW MAP 218 ([irq/178-arm-smm]) (/system/xbin/su root showmap -q 218)","[NULL]",8
+"SHOW MAP 219 ([hwrng]) (/system/xbin/su root showmap -q 219)","[NULL]",8
+"SHOW MAP 22 ([rcuos/1]) (/system/xbin/su root showmap -q 22)","[NULL]",8
+"SHOW MAP 221 ([diag_real_time_]) (/system/xbin/su root showmap -q 221)","[NULL]",8
+"SHOW MAP 222 ([diag_wq]) (/system/xbin/su root showmap -q 222)","[NULL]",8
+"SHOW MAP 223 ([DIAG_USB_diag]) (/system/xbin/su root showmap -q 223)","[NULL]",8
+"SHOW MAP 224 ([diag_cntl_wq]) (/system/xbin/su root showmap -q 224)","[NULL]",8
+"SHOW MAP 225 ([diag_dci_wq]) (/system/xbin/su root showmap -q 225)","[NULL]",8
+"SHOW MAP 2254 (/vendor/bin/hw/wpa_supplicant) (/system/xbin/su root showmap -q 2254)","[NULL]",76
+"SHOW MAP 226 ([MODEM_CNTL]) (/system/xbin/su root showmap -q 226)","[NULL]",8
+"SHOW MAP 2261 (com.android.bluetooth) (/system/xbin/su root showmap -q 2261)","[NULL]",624
+"SHOW MAP 227 ([MODEM_DATA]) (/system/xbin/su root showmap -q 227)","[NULL]",8
+"SHOW MAP 228 ([MODEM_CMD]) (/system/xbin/su root showmap -q 228)","[NULL]",8
+"SHOW MAP 2281 (com.android.systemui) (/system/xbin/su root showmap -q 2281)","[NULL]",705
+"SHOW MAP 229 ([MODEM_DCI]) (/system/xbin/su root showmap -q 229)","[NULL]",8
+"SHOW MAP 23 ([rcuob/1]) (/system/xbin/su root showmap -q 23)","[NULL]",8
+"SHOW MAP 230 ([MODEM_DCI_CMD]) (/system/xbin/su root showmap -q 230)","[NULL]",8
+"SHOW MAP 231 ([LPASS_CNTL]) (/system/xbin/su root showmap -q 231)","[NULL]",8
+"SHOW MAP 232 ([LPASS_DATA]) (/system/xbin/su root showmap -q 232)","[NULL]",8
+"SHOW MAP 233 ([LPASS_CMD]) (/system/xbin/su root showmap -q 233)","[NULL]",8
+"SHOW MAP 234 ([LPASS_DCI]) (/system/xbin/su root showmap -q 234)","[NULL]",8
+"SHOW MAP 235 ([LPASS_DCI_CMD]) (/system/xbin/su root showmap -q 235)","[NULL]",8
+"SHOW MAP 236 ([WCNSS_CNTL]) (/system/xbin/su root showmap -q 236)","[NULL]",8
+"SHOW MAP 237 ([WCNSS_DATA]) (/system/xbin/su root showmap -q 237)","[NULL]",8
+"SHOW MAP 238 ([WCNSS_CMD]) (/system/xbin/su root showmap -q 238)","[NULL]",8
+"SHOW MAP 2385 (com.breel.wallpapers18) (/system/xbin/su root showmap -q 2385)","[NULL]",567
+"SHOW MAP 239 ([WCNSS_DCI]) (/system/xbin/su root showmap -q 239)","[NULL]",8
+"SHOW MAP 24 ([cpuhp/2]) (/system/xbin/su root showmap -q 24)","[NULL]",8
+"SHOW MAP 240 ([WCNSS_DCI_CMD]) (/system/xbin/su root showmap -q 240)","[NULL]",8
+"SHOW MAP 2407 (com.android.networkstack.process) (/system/xbin/su root showmap -q 2407)","[NULL]",570
+"SHOW MAP 241 ([SENSORS_CNTL]) (/system/xbin/su root showmap -q 241)","[NULL]",8
+"SHOW MAP 242 ([SENSORS_DATA]) (/system/xbin/su root showmap -q 242)","[NULL]",8
+"SHOW MAP 243 ([SENSORS_CMD]) (/system/xbin/su root showmap -q 243)","[NULL]",8
+"SHOW MAP 2436 (.dataservices) (/system/xbin/su root showmap -q 2436)","[NULL]",568
+"SHOW MAP 244 ([SENSORS_DCI]) (/system/xbin/su root showmap -q 244)","[NULL]",8
+"SHOW MAP 2449 (webview_zygote) (/system/xbin/su root showmap -q 2449)","[NULL]",530
+"SHOW MAP 245 ([SENSORS_DCI_CMD]) (/system/xbin/su root showmap -q 245)","[NULL]",8
+"SHOW MAP 246 ([DIAG_CTRL]) (/system/xbin/su root showmap -q 246)","[NULL]",8
+"SHOW MAP 247 ([DIAG_DATA]) (/system/xbin/su root showmap -q 247)","[NULL]",8
+"SHOW MAP 248 ([DIAG_CMD]) (/system/xbin/su root showmap -q 248)","[NULL]",8
+"SHOW MAP 249 ([DIAG_DCI_DATA]) (/system/xbin/su root showmap -q 249)","[NULL]",8
+"SHOW MAP 25 ([migration/2]) (/system/xbin/su root showmap -q 25)","[NULL]",8
+"SHOW MAP 250 ([DIAG_DCI_CMD]) (/system/xbin/su root showmap -q 250)","[NULL]",8
+"SHOW MAP 2502 (com.qualcomm.qti.telephonyservice) (/system/xbin/su root showmap -q 2502)","[NULL]",560
+"SHOW MAP 2503 (com.google.android.grilservice) (/system/xbin/su root showmap -q 2503)","[NULL]",547
+"SHOW MAP 2505 (com.android.se) (/system/xbin/su root showmap -q 2505)","[NULL]",555
+"SHOW MAP 251 ([CDSP_CNTL]) (/system/xbin/su root showmap -q 251)","[NULL]",8
+"SHOW MAP 252 ([CDSP_DATA]) (/system/xbin/su root showmap -q 252)","[NULL]",8
+"SHOW MAP 253 ([CDSP_CMD]) (/system/xbin/su root showmap -q 253)","[NULL]",8
+"SHOW MAP 2532 (com.android.phone) (/system/xbin/su root showmap -q 2532)","[NULL]",636
+"SHOW MAP 254 ([CDSP_DCI]) (/system/xbin/su root showmap -q 254)","[NULL]",8
+"SHOW MAP 255 ([CDSP_DCI_CMD]) (/system/xbin/su root showmap -q 255)","[NULL]",8
+"SHOW MAP 256 ([DIAG_CNTL_SOCKE]) (/system/xbin/su root showmap -q 256)","[NULL]",8
+"SHOW MAP 257 ([DIAG_GLINK_DIAG]) (/system/xbin/su root showmap -q 257)","[NULL]",8
+"SHOW MAP 2578 (com.google.android.ext.services) (/system/xbin/su root showmap -q 2578)","[NULL]",550
+"SHOW MAP 258 ([DIAG_GLINK_DIAG]) (/system/xbin/su root showmap -q 258)","[NULL]",8
+"SHOW MAP 259 ([DIAG_GLINK_DIAG]) (/system/xbin/su root showmap -q 259)","[NULL]",8
+"SHOW MAP 26 ([ksoftirqd/2]) (/system/xbin/su root showmap -q 26)","[NULL]",8
+"SHOW MAP 260 ([DIAG_GLINK_DIAG]) (/system/xbin/su root showmap -q 260)","[NULL]",8
+"SHOW MAP 261 ([DIAG_GLINK_DIAG]) (/system/xbin/su root showmap -q 261)","[NULL]",8
+"SHOW MAP 262 ([dsi_dimming_wor]) (/system/xbin/su root showmap -q 262)","[NULL]",8
+"SHOW MAP 263 ([kworker/u16:1]) (/system/xbin/su root showmap -q 263)","[NULL]",8
+"SHOW MAP 264 ([kgsl-workqueue]) (/system/xbin/su root showmap -q 264)","[NULL]",8
+"SHOW MAP 265 ([kgsl-mementry]) (/system/xbin/su root showmap -q 265)","[NULL]",8
+"SHOW MAP 2655 (com.google.android.apps.nexuslauncher) (/system/xbin/su root showmap -q 2655)","[NULL]",588
+"SHOW MAP 266 ([kgsl_worker_thr]) (/system/xbin/su root showmap -q 266)","[NULL]",8
+"SHOW MAP 267 ([kworker/u16:2]) (/system/xbin/su root showmap -q 267)","[NULL]",8
+"SHOW MAP 268 ([bioset]) (/system/xbin/su root showmap -q 268)","[NULL]",8
+"SHOW MAP 269 ([bioset]) (/system/xbin/su root showmap -q 269)","[NULL]",8
+"SHOW MAP 270 ([bioset]) (/system/xbin/su root showmap -q 270)","[NULL]",8
+"SHOW MAP 271 ([bioset]) (/system/xbin/su root showmap -q 271)","[NULL]",8
+"SHOW MAP 272 ([bioset]) (/system/xbin/su root showmap -q 272)","[NULL]",8
+"SHOW MAP 273 ([bioset]) (/system/xbin/su root showmap -q 273)","[NULL]",8
+"SHOW MAP 274 ([bioset]) (/system/xbin/su root showmap -q 274)","[NULL]",8
+"SHOW MAP 275 ([bioset]) (/system/xbin/su root showmap -q 275)","[NULL]",8
+"SHOW MAP 276 ([bioset]) (/system/xbin/su root showmap -q 276)","[NULL]",8
+"SHOW MAP 277 ([bioset]) (/system/xbin/su root showmap -q 277)","[NULL]",8
+"SHOW MAP 278 ([bioset]) (/system/xbin/su root showmap -q 278)","[NULL]",8
+"SHOW MAP 2783 (com.google.android.ims) (/system/xbin/su root showmap -q 2783)","[NULL]",569
+"SHOW MAP 279 ([bioset]) (/system/xbin/su root showmap -q 279)","[NULL]",8
+"SHOW MAP 28 ([kworker/2:0H]) (/system/xbin/su root showmap -q 28)","[NULL]",8
+"SHOW MAP 280 ([irq/162-arm-smm]) (/system/xbin/su root showmap -q 280)","[NULL]",8
+"SHOW MAP 281 ([bioset]) (/system/xbin/su root showmap -q 281)","[NULL]",8
+"SHOW MAP 282 ([bioset]) (/system/xbin/su root showmap -q 282)","[NULL]",8
+"SHOW MAP 283 ([bioset]) (/system/xbin/su root showmap -q 283)","[NULL]",8
+"SHOW MAP 284 ([kworker/4:2]) (/system/xbin/su root showmap -q 284)","[NULL]",8
+"SHOW MAP 285 ([bioset]) (/system/xbin/su root showmap -q 285)","[NULL]",8
+"SHOW MAP 286 ([irq/163-arm-smm]) (/system/xbin/su root showmap -q 286)","[NULL]",8
+"SHOW MAP 287 ([bioset]) (/system/xbin/su root showmap -q 287)","[NULL]",8
+"SHOW MAP 2877 (com.google.android.euicc) (/system/xbin/su root showmap -q 2877)","[NULL]",562
+"SHOW MAP 288 ([irq/171-arm-smm]) (/system/xbin/su root showmap -q 288)","[NULL]",8
+"SHOW MAP 289 ([bioset]) (/system/xbin/su root showmap -q 289)","[NULL]",8
+"SHOW MAP 29 ([rcuop/2]) (/system/xbin/su root showmap -q 29)","[NULL]",8
+"SHOW MAP 290 ([bioset]) (/system/xbin/su root showmap -q 290)","[NULL]",8
+"SHOW MAP 292 ([bioset]) (/system/xbin/su root showmap -q 292)","[NULL]",8
+"SHOW MAP 293 ([bioset]) (/system/xbin/su root showmap -q 293)","[NULL]",8
+"SHOW MAP 2939 (com.google.android.gms.persistent) (/system/xbin/su root showmap -q 2939)","[NULL]",615
+"SHOW MAP 294 ([bioset]) (/system/xbin/su root showmap -q 294)","[NULL]",8
+"SHOW MAP 295 ([bioset]) (/system/xbin/su root showmap -q 295)","[NULL]",8
+"SHOW MAP 296 ([bioset]) (/system/xbin/su root showmap -q 296)","[NULL]",8
+"SHOW MAP 297 ([bioset]) (/system/xbin/su root showmap -q 297)","[NULL]",8
+"SHOW MAP 298 ([bioset]) (/system/xbin/su root showmap -q 298)","[NULL]",8
+"SHOW MAP 2981 ([loop29]) (/system/xbin/su root showmap -q 2981)","[NULL]",8
+"SHOW MAP 299 ([bioset]) (/system/xbin/su root showmap -q 299)","[NULL]",8
+"SHOW MAP 2995 (/vendor/bin/pm-proxy) (/system/xbin/su root showmap -q 2995)","[NULL]",56
+"SHOW MAP 2999 ([irq/80-1436400.]) (/system/xbin/su root showmap -q 2999)","[NULL]",8
+"SHOW MAP 3 ([kworker/0:0]) (/system/xbin/su root showmap -q 3)","[NULL]",8
+"SHOW MAP 30 ([rcuos/2]) (/system/xbin/su root showmap -q 30)","[NULL]",8
+"SHOW MAP 300 ([bioset]) (/system/xbin/su root showmap -q 300)","[NULL]",8
+"SHOW MAP 3001 ([irq/81-114a000.]) (/system/xbin/su root showmap -q 3001)","[NULL]",8
+"SHOW MAP 3007 (/vendor/bin/thermal-engine) (/system/xbin/su root showmap -q 3007)","[NULL]",106
+"SHOW MAP 301 ([bioset]) (/system/xbin/su root showmap -q 301)","[NULL]",8
+"SHOW MAP 302 ([bioset]) (/system/xbin/su root showmap -q 302)","[NULL]",8
+"SHOW MAP 303 ([bioset]) (/system/xbin/su root showmap -q 303)","[NULL]",8
+"SHOW MAP 304 ([kworker/5:1]) (/system/xbin/su root showmap -q 304)","[NULL]",8
+"SHOW MAP 305 ([bioset]) (/system/xbin/su root showmap -q 305)","[NULL]",8
+"SHOW MAP 306 ([bioset]) (/system/xbin/su root showmap -q 306)","[NULL]",8
+"SHOW MAP 3060 (com.google.android.as) (/system/xbin/su root showmap -q 3060)","[NULL]",618
+"SHOW MAP 307 ([irq/179-arm-smm]) (/system/xbin/su root showmap -q 307)","[NULL]",8
+"SHOW MAP 3076 (com.google.android.googlequicksearchbox:interactor) (/system/xbin/su root showmap -q 3076)","[NULL]",577
+"SHOW MAP 308 ([qseecom-unreg-l]) (/system/xbin/su root showmap -q 308)","[NULL]",8
+"SHOW MAP 309 ([irq/164-arm-smm]) (/system/xbin/su root showmap -q 309)","[NULL]",8
+"SHOW MAP 3098 (com.google.android.providers.media.module) (/system/xbin/su root showmap -q 3098)","[NULL]",571
+"SHOW MAP 31 ([rcuob/2]) (/system/xbin/su root showmap -q 31)","[NULL]",8
+"SHOW MAP 310 ([memory_wq]) (/system/xbin/su root showmap -q 310)","[NULL]",8
+"SHOW MAP 311 ([kworker/u16:3]) (/system/xbin/su root showmap -q 311)","[NULL]",8
+"SHOW MAP 312 ([irq/180-arm-smm]) (/system/xbin/su root showmap -q 312)","[NULL]",8
+"SHOW MAP 3122 (com.android.nfc) (/system/xbin/su root showmap -q 3122)","[NULL]",579
+"SHOW MAP 313 ([irq/181-arm-smm]) (/system/xbin/su root showmap -q 313)","[NULL]",8
+"SHOW MAP 314 ([irq/182-arm-smm]) (/system/xbin/su root showmap -q 314)","[NULL]",8
+"SHOW MAP 3144 (com.android.ims.rcsservice) (/system/xbin/su root showmap -q 3144)","[NULL]",557
+"SHOW MAP 315 ([crtc_commit:111]) (/system/xbin/su root showmap -q 315)","[NULL]",8
+"SHOW MAP 316 ([crtc_event:111]) (/system/xbin/su root showmap -q 316)","[NULL]",8
+"SHOW MAP 3168 (com.google.SSRestartDetector) (/system/xbin/su root showmap -q 3168)","[NULL]",544
+"SHOW MAP 317 ([crtc_commit:164]) (/system/xbin/su root showmap -q 317)","[NULL]",8
+"SHOW MAP 318 ([crtc_event:164]) (/system/xbin/su root showmap -q 318)","[NULL]",8
+"SHOW MAP 319 ([pp_event]) (/system/xbin/su root showmap -q 319)","[NULL]",8
+"SHOW MAP 32 ([cpuhp/3]) (/system/xbin/su root showmap -q 32)","[NULL]",8
+"SHOW MAP 320 ([qcrypto_seq_res]) (/system/xbin/su root showmap -q 320)","[NULL]",8
+"SHOW MAP 321 ([irq/183-arm-smm]) (/system/xbin/su root showmap -q 321)","[NULL]",8
+"SHOW MAP 3210 (com.qualcomm.qti.services.secureui:sui_service) (/system/xbin/su root showmap -q 3210)","[NULL]",555
+"SHOW MAP 3234 (com.android.pixellogger) (/system/xbin/su root showmap -q 3234)","[NULL]",568
+"SHOW MAP 33 ([migration/3]) (/system/xbin/su root showmap -q 33)","[NULL]",8
+"SHOW MAP 3315 (com.google.process.gservices) (/system/xbin/su root showmap -q 3315)","[NULL]",569
+"SHOW MAP 3326 (com.google.vr.vrcore) (/system/xbin/su root showmap -q 3326)","[NULL]",554
+"SHOW MAP 3397 (com.google.android.gms) (/system/xbin/su root showmap -q 3397)","[NULL]",640
+"SHOW MAP 34 ([ksoftirqd/3]) (/system/xbin/su root showmap -q 34)","[NULL]",8
+"SHOW MAP 342 ([scsi_eh_0]) (/system/xbin/su root showmap -q 342)","[NULL]",8
+"SHOW MAP 343 ([scsi_tmf_0]) (/system/xbin/su root showmap -q 343)","[NULL]",8
+"SHOW MAP 344 ([ufs_pm_qos_0]) (/system/xbin/su root showmap -q 344)","[NULL]",8
+"SHOW MAP 345 ([ufs_clk_gating_]) (/system/xbin/su root showmap -q 345)","[NULL]",8
+"SHOW MAP 346 ([ufs_mgc_hibern8]) (/system/xbin/su root showmap -q 346)","[NULL]",8
+"SHOW MAP 347 ([ice-set-key]) (/system/xbin/su root showmap -q 347)","[NULL]",8
+"SHOW MAP 348 ([spi10]) (/system/xbin/su root showmap -q 348)","[NULL]",8
+"SHOW MAP 3488 (com.google.android.googlequicksearchbox:search) (/system/xbin/su root showmap -q 3488)","[NULL]",633
+"SHOW MAP 349 ([spi32766]) (/system/xbin/su root showmap -q 349)","[NULL]",8
+"SHOW MAP 35 ([kworker/3:0]) (/system/xbin/su root showmap -q 35)","[NULL]",8
+"SHOW MAP 350 ([spi0]) (/system/xbin/su root showmap -q 350)","[NULL]",8
+"SHOW MAP 351 ([spi32765]) (/system/xbin/su root showmap -q 351)","[NULL]",8
+"SHOW MAP 352 ([bond0]) (/system/xbin/su root showmap -q 352)","[NULL]",8
+"SHOW MAP 353 ([kworker/6:1]) (/system/xbin/su root showmap -q 353)","[NULL]",8
+"SHOW MAP 354 ([kgsl-events]) (/system/xbin/su root showmap -q 354)","[NULL]",8
+"SHOW MAP 355 ([kgsl_devfreq_wq]) (/system/xbin/su root showmap -q 355)","[NULL]",8
+"SHOW MAP 357 ([sharedmem_qmi_w]) (/system/xbin/su root showmap -q 357)","[NULL]",8
+"SHOW MAP 358 ([qmi_hndl0000000]) (/system/xbin/su root showmap -q 358)","[NULL]",8
+"SHOW MAP 36 ([kworker/3:0H]) (/system/xbin/su root showmap -q 36)","[NULL]",8
+"SHOW MAP 362 ([uether]) (/system/xbin/su root showmap -q 362)","[NULL]",8
+"SHOW MAP 363 ([k_ipa_usb]) (/system/xbin/su root showmap -q 363)","[NULL]",8
+"SHOW MAP 366 ([irq/378-soc:fp_]) (/system/xbin/su root showmap -q 366)","[NULL]",8
+"SHOW MAP 367 ([kdbgd]) (/system/xbin/su root showmap -q 367)","[NULL]",8
+"SHOW MAP 368 ([msm_vidc_worker]) (/system/xbin/su root showmap -q 368)","[NULL]",8
+"SHOW MAP 369 ([pm_workerq_venu]) (/system/xbin/su root showmap -q 369)","[NULL]",8
+"SHOW MAP 37 ([rcuop/3]) (/system/xbin/su root showmap -q 37)","[NULL]",8
+"SHOW MAP 370 ([irq/184-arm-smm]) (/system/xbin/su root showmap -q 370)","[NULL]",8
+"SHOW MAP 371 ([irq/185-arm-smm]) (/system/xbin/su root showmap -q 371)","[NULL]",8
+"SHOW MAP 372 ([irq/186-arm-smm]) (/system/xbin/su root showmap -q 372)","[NULL]",8
+"SHOW MAP 373 ([irq/187-arm-smm]) (/system/xbin/su root showmap -q 373)","[NULL]",8
+"SHOW MAP 374 ([rot_commitq_0_0]) (/system/xbin/su root showmap -q 374)","[NULL]",8
+"SHOW MAP 375 ([rot_commitq_0_1]) (/system/xbin/su root showmap -q 375)","[NULL]",8
+"SHOW MAP 376 ([rot_doneq_0_0]) (/system/xbin/su root showmap -q 376)","[NULL]",8
+"SHOW MAP 377 ([rot_doneq_0_1]) (/system/xbin/su root showmap -q 377)","[NULL]",8
+"SHOW MAP 378 ([rot_fenceq_0_0]) (/system/xbin/su root showmap -q 378)","[NULL]",8
+"SHOW MAP 379 ([rot_fenceq_0_1]) (/system/xbin/su root showmap -q 379)","[NULL]",8
+"SHOW MAP 38 ([rcuos/3]) (/system/xbin/su root showmap -q 38)","[NULL]",8
+"SHOW MAP 380 ([rot_fenceq_0_2]) (/system/xbin/su root showmap -q 380)","[NULL]",8
+"SHOW MAP 381 ([rot_fenceq_0_3]) (/system/xbin/su root showmap -q 381)","[NULL]",8
+"SHOW MAP 382 ([rot_fenceq_0_4]) (/system/xbin/su root showmap -q 382)","[NULL]",8
+"SHOW MAP 383 ([rot_fenceq_0_5]) (/system/xbin/su root showmap -q 383)","[NULL]",8
+"SHOW MAP 384 ([rot_fenceq_0_6]) (/system/xbin/su root showmap -q 384)","[NULL]",8
+"SHOW MAP 385 ([rot_fenceq_0_7]) (/system/xbin/su root showmap -q 385)","[NULL]",8
+"SHOW MAP 386 ([rot_fenceq_0_8]) (/system/xbin/su root showmap -q 386)","[NULL]",8
+"SHOW MAP 387 ([rot_fenceq_0_9]) (/system/xbin/su root showmap -q 387)","[NULL]",8
+"SHOW MAP 388 ([rot_fenceq_0_10]) (/system/xbin/su root showmap -q 388)","[NULL]",8
+"SHOW MAP 389 ([rot_fenceq_0_11]) (/system/xbin/su root showmap -q 389)","[NULL]",8
+"SHOW MAP 39 ([rcuob/3]) (/system/xbin/su root showmap -q 39)","[NULL]",8
+"SHOW MAP 390 ([rot_fenceq_0_12]) (/system/xbin/su root showmap -q 390)","[NULL]",8
+"SHOW MAP 391 ([rot_fenceq_0_13]) (/system/xbin/su root showmap -q 391)","[NULL]",8
+"SHOW MAP 392 ([rot_fenceq_0_14]) (/system/xbin/su root showmap -q 392)","[NULL]",8
+"SHOW MAP 393 ([rot_fenceq_0_15]) (/system/xbin/su root showmap -q 393)","[NULL]",8
+"SHOW MAP 394 ([cam-cpas]) (/system/xbin/su root showmap -q 394)","[NULL]",8
+"SHOW MAP 395 ([qcom,cam_virtua]) (/system/xbin/su root showmap -q 395)","[NULL]",8
+"SHOW MAP 396 ([irq/188-arm-smm]) (/system/xbin/su root showmap -q 396)","[NULL]",8
+"SHOW MAP 397 ([qcom,cam170-cpa]) (/system/xbin/su root showmap -q 397)","[NULL]",8
+"SHOW MAP 398 ([irq/189-arm-smm]) (/system/xbin/su root showmap -q 398)","[NULL]",8
+"SHOW MAP 399 ([cam_cci_wq]) (/system/xbin/su root showmap -q 399)","[NULL]",8
+"SHOW MAP 4 ([kworker/0:0H]) (/system/xbin/su root showmap -q 4)","[NULL]",8
+"SHOW MAP 40 ([cpuhp/4]) (/system/xbin/su root showmap -q 40)","[NULL]",8
+"SHOW MAP 400 ([cam_cci_wq]) (/system/xbin/su root showmap -q 400)","[NULL]",8
+"SHOW MAP 401 ([irq/190-arm-smm]) (/system/xbin/su root showmap -q 401)","[NULL]",8
+"SHOW MAP 402 ([irq/191-arm-smm]) (/system/xbin/su root showmap -q 402)","[NULL]",8
+"SHOW MAP 403 ([irq/192-arm-smm]) (/system/xbin/su root showmap -q 403)","[NULL]",8
+"SHOW MAP 404 ([irq/193-arm-smm]) (/system/xbin/su root showmap -q 404)","[NULL]",8
+"SHOW MAP 405 ([bioset]) (/system/xbin/su root showmap -q 405)","[NULL]",8
+"SHOW MAP 406 ([bioset]) (/system/xbin/su root showmap -q 406)","[NULL]",8
+"SHOW MAP 407 ([bioset]) (/system/xbin/su root showmap -q 407)","[NULL]",8
+"SHOW MAP 408 ([bioset]) (/system/xbin/su root showmap -q 408)","[NULL]",8
+"SHOW MAP 409 ([irq/709-chg-err]) (/system/xbin/su root showmap -q 409)","[NULL]",8
+"SHOW MAP 41 ([migration/4]) (/system/xbin/su root showmap -q 41)","[NULL]",8
+"SHOW MAP 410 ([kworker/0:2]) (/system/xbin/su root showmap -q 410)","[NULL]",8
+"SHOW MAP 411 ([irq/710-chg-sta]) (/system/xbin/su root showmap -q 411)","[NULL]",8
+"SHOW MAP 412 ([irq/714-otg-fai]) (/system/xbin/su root showmap -q 412)","[NULL]",8
+"SHOW MAP 413 ([bioset]) (/system/xbin/su root showmap -q 413)","[NULL]",8
+"SHOW MAP 414 ([irq/715-otg-ove]) (/system/xbin/su root showmap -q 414)","[NULL]",8
+"SHOW MAP 415 ([irq/716-otg-oc-]) (/system/xbin/su root showmap -q 415)","[NULL]",8
+"SHOW MAP 416 ([irq/717-testmod]) (/system/xbin/su root showmap -q 416)","[NULL]",8
+"SHOW MAP 417 ([irq/718-bat-tem]) (/system/xbin/su root showmap -q 417)","[NULL]",8
+"SHOW MAP 418 ([irq/719-bat-ocp]) (/system/xbin/su root showmap -q 418)","[NULL]",8
+"SHOW MAP 419 ([irq/720-bat-ov]) (/system/xbin/su root showmap -q 419)","[NULL]",8
+"SHOW MAP 42 ([ksoftirqd/4]) (/system/xbin/su root showmap -q 42)","[NULL]",8
+"SHOW MAP 420 ([bioset]) (/system/xbin/su root showmap -q 420)","[NULL]",8
+"SHOW MAP 421 ([irq/721-bat-low]) (/system/xbin/su root showmap -q 421)","[NULL]",8
+"SHOW MAP 422 ([irq/722-bat-the]) (/system/xbin/su root showmap -q 422)","[NULL]",8
+"SHOW MAP 423 ([irq/723-bat-ter]) (/system/xbin/su root showmap -q 423)","[NULL]",8
+"SHOW MAP 424 ([irq/724-usbin-c]) (/system/xbin/su root showmap -q 424)","[NULL]",8
+"SHOW MAP 425 ([irq/725-usbin-l]) (/system/xbin/su root showmap -q 425)","[NULL]",8
+"SHOW MAP 426 ([bioset]) (/system/xbin/su root showmap -q 426)","[NULL]",8
+"SHOW MAP 427 ([irq/726-usbin-u]) (/system/xbin/su root showmap -q 427)","[NULL]",8
+"SHOW MAP 428 ([irq/727-usbin-o]) (/system/xbin/su root showmap -q 428)","[NULL]",8
+"SHOW MAP 429 ([irq/728-usbin-p]) (/system/xbin/su root showmap -q 429)","[NULL]",8
+"SHOW MAP 4291 (com.google.android.apps.scone) (/system/xbin/su root showmap -q 4291)","[NULL]",568
+"SHOW MAP 430 ([irq/729-usbin-s]) (/system/xbin/su root showmap -q 430)","[NULL]",8
+"SHOW MAP 431 ([irq/730-usbin-i]) (/system/xbin/su root showmap -q 431)","[NULL]",8
+"SHOW MAP 4315 (com.google.android.inputmethod.latin) (/system/xbin/su root showmap -q 4315)","[NULL]",591
+"SHOW MAP 432 ([irq/731-type-c-]) (/system/xbin/su root showmap -q 432)","[NULL]",8
+"SHOW MAP 433 ([bioset]) (/system/xbin/su root showmap -q 433)","[NULL]",8
+"SHOW MAP 434 ([irq/732-dcin-co]) (/system/xbin/su root showmap -q 434)","[NULL]",8
+"SHOW MAP 435 ([irq/733-dcin-lt]) (/system/xbin/su root showmap -q 435)","[NULL]",8
+"SHOW MAP 436 ([irq/734-dcin-uv]) (/system/xbin/su root showmap -q 436)","[NULL]",8
+"SHOW MAP 437 ([irq/735-dcin-ov]) (/system/xbin/su root showmap -q 437)","[NULL]",8
+"SHOW MAP 438 ([irq/736-dcin-pl]) (/system/xbin/su root showmap -q 438)","[NULL]",8
+"SHOW MAP 439 ([bioset]) (/system/xbin/su root showmap -q 439)","[NULL]",8
+"SHOW MAP 44 ([kworker/4:0H]) (/system/xbin/su root showmap -q 44)","[NULL]",8
+"SHOW MAP 440 ([irq/737-div2-en]) (/system/xbin/su root showmap -q 440)","[NULL]",8
+"SHOW MAP 441 ([irq/738-dcin-ic]) (/system/xbin/su root showmap -q 441)","[NULL]",8
+"SHOW MAP 442 ([irq/740-wdog-ba]) (/system/xbin/su root showmap -q 442)","[NULL]",8
+"SHOW MAP 443 ([irq/741-aicl-fa]) (/system/xbin/su root showmap -q 443)","[NULL]",8
+"SHOW MAP 4430 (com.qualcomm.qcrilmsgtunnel) (/system/xbin/su root showmap -q 4430)","[NULL]",554
+"SHOW MAP 444 ([irq/742-aicl-do]) (/system/xbin/su root showmap -q 444)","[NULL]",8
+"SHOW MAP 445 ([irq/743-high-du]) (/system/xbin/su root showmap -q 445)","[NULL]",8
+"SHOW MAP 446 ([kworker/u16:4]) (/system/xbin/su root showmap -q 446)","[NULL]",8
+"SHOW MAP 447 ([kworker/2:2]) (/system/xbin/su root showmap -q 447)","[NULL]",8
+"SHOW MAP 448 ([kworker/u16:5]) (/system/xbin/su root showmap -q 448)","[NULL]",8
+"SHOW MAP 449 ([irq/744-input-c]) (/system/xbin/su root showmap -q 449)","[NULL]",8
+"SHOW MAP 45 ([rcuop/4]) (/system/xbin/su root showmap -q 45)","[NULL]",8
+"SHOW MAP 450 ([irq/745-tempera]) (/system/xbin/su root showmap -q 450)","[NULL]",8
+"SHOW MAP 452 ([irq/746-switche]) (/system/xbin/su root showmap -q 452)","[NULL]",8
+"SHOW MAP 454 ([kworker/u16:6]) (/system/xbin/su root showmap -q 454)","[NULL]",8
+"SHOW MAP 455 ([irq/29-i2c_pmic]) (/system/xbin/su root showmap -q 455)","[NULL]",8
+"SHOW MAP 456 ([irq/381-p9221-i]) (/system/xbin/su root showmap -q 456)","[NULL]",8
+"SHOW MAP 457 ([kworker/u16:7]) (/system/xbin/su root showmap -q 457)","[NULL]",8
+"SHOW MAP 458 ([kworker/u16:8]) (/system/xbin/su root showmap -q 458)","[NULL]",8
+"SHOW MAP 459 ([kworker/2:3]) (/system/xbin/su root showmap -q 459)","[NULL]",8
+"SHOW MAP 46 ([rcuos/4]) (/system/xbin/su root showmap -q 46)","[NULL]",8
+"SHOW MAP 460 ([irq/70-bcl-high]) (/system/xbin/su root showmap -q 460)","[NULL]",8
+"SHOW MAP 461 ([irq/72-bcl-low-]) (/system/xbin/su root showmap -q 461)","[NULL]",8
+"SHOW MAP 462 ([irq/543-limits_]) (/system/xbin/su root showmap -q 462)","[NULL]",8
+"SHOW MAP 463 ([irq/319-max1720]) (/system/xbin/su root showmap -q 463)","[NULL]",8
+"SHOW MAP 464 ([irq/544-limits_]) (/system/xbin/su root showmap -q 464)","[NULL]",8
+"SHOW MAP 465 ([qmi_tmd_wq]) (/system/xbin/su root showmap -q 465)","[NULL]",8
+"SHOW MAP 466 ([dm_bufio_cache]) (/system/xbin/su root showmap -q 466)","[NULL]",8
+"SHOW MAP 467 ([irq/99-KRYO3XX ]) (/system/xbin/su root showmap -q 467)","[NULL]",8
+"SHOW MAP 468 ([irq/287-s2mpb04]) (/system/xbin/su root showmap -q 468)","[NULL]",8
+"SHOW MAP 469 ([irq/371-s2mpb04]) (/system/xbin/su root showmap -q 469)","[NULL]",8
+"SHOW MAP 47 ([rcuob/4]) (/system/xbin/su root showmap -q 47)","[NULL]",8
+"SHOW MAP 470 ([irq/52-vendor:e]) (/system/xbin/su root showmap -q 470)","[NULL]",8
+"SHOW MAP 471 ([irq/194-arm-smm]) (/system/xbin/su root showmap -q 471)","[NULL]",8
+"SHOW MAP 472 ([uaudio_svc]) (/system/xbin/su root showmap -q 472)","[NULL]",8
+"SHOW MAP 473 ([qmi_hndl0000000]) (/system/xbin/su root showmap -q 473)","[NULL]",8
+"SHOW MAP 474 ([apr_driver]) (/system/xbin/su root showmap -q 474)","[NULL]",8
+"SHOW MAP 475 ([ipv6_addrconf]) (/system/xbin/su root showmap -q 475)","[NULL]",8
+"SHOW MAP 476 ([irq/880-adsp]) (/system/xbin/su root showmap -q 476)","[NULL]",8
+"SHOW MAP 477 ([sysmon_wq]) (/system/xbin/su root showmap -q 477)","[NULL]",8
+"SHOW MAP 478 ([irq/912-slpi]) (/system/xbin/su root showmap -q 478)","[NULL]",8
+"SHOW MAP 479 ([irq/944-cdsp]) (/system/xbin/su root showmap -q 479)","[NULL]",8
+"SHOW MAP 48 ([cpuhp/5]) (/system/xbin/su root showmap -q 48)","[NULL]",8
+"SHOW MAP 480 ([irq/848-modem]) (/system/xbin/su root showmap -q 480)","[NULL]",8
+"SHOW MAP 487 ([irq/38-sig-tx]) (/system/xbin/su root showmap -q 487)","[NULL]",8
+"SHOW MAP 488 ([irq/64-sig-rx]) (/system/xbin/su root showmap -q 488)","[NULL]",8
+"SHOW MAP 489 ([usbpd0]) (/system/xbin/su root showmap -q 489)","[NULL]",8
+"SHOW MAP 49 ([migration/5]) (/system/xbin/su root showmap -q 49)","[NULL]",8
+"SHOW MAP 490 ([usbpd0]) (/system/xbin/su root showmap -q 490)","[NULL]",8
+"SHOW MAP 491 ([irq/1040-soc-up]) (/system/xbin/su root showmap -q 491)","[NULL]",8
+"SHOW MAP 492 ([irq/1041-soc-re]) (/system/xbin/su root showmap -q 492)","[NULL]",8
+"SHOW MAP 493 ([irq/1042-bsoc-d]) (/system/xbin/su root showmap -q 493)","[NULL]",8
+"SHOW MAP 494 ([irq/1043-msoc-d]) (/system/xbin/su root showmap -q 494)","[NULL]",8
+"SHOW MAP 495 ([irq/1044-msoc-l]) (/system/xbin/su root showmap -q 495)","[NULL]",8
+"SHOW MAP 496 ([irq/1045-msoc-e]) (/system/xbin/su root showmap -q 496)","[NULL]",8
+"SHOW MAP 497 ([irq/1046-msoc-h]) (/system/xbin/su root showmap -q 497)","[NULL]",8
+"SHOW MAP 498 ([irq/1047-msoc-f]) (/system/xbin/su root showmap -q 498)","[NULL]",8
+"SHOW MAP 499 ([irq/1048-vbatt-]) (/system/xbin/su root showmap -q 499)","[NULL]",8
+"SHOW MAP 5 ([kworker/u16:0]) (/system/xbin/su root showmap -q 5)","[NULL]",8
+"SHOW MAP 50 ([ksoftirqd/5]) (/system/xbin/su root showmap -q 50)","[NULL]",8
+"SHOW MAP 500 ([irq/1049-vbatt-]) (/system/xbin/su root showmap -q 500)","[NULL]",8
+"SHOW MAP 501 ([irq/1050-esr-de]) (/system/xbin/su root showmap -q 501)","[NULL]",8
+"SHOW MAP 502 ([irq/1051-batt-m]) (/system/xbin/su root showmap -q 502)","[NULL]",8
+"SHOW MAP 503 ([irq/1052-batt-t]) (/system/xbin/su root showmap -q 503)","[NULL]",8
+"SHOW MAP 504 ([irq/1053-ima-rd]) (/system/xbin/su root showmap -q 504)","[NULL]",8
+"SHOW MAP 505 ([irq/1054-mem-xc]) (/system/xbin/su root showmap -q 505)","[NULL]",8
+"SHOW MAP 506 ([irq/1055-dma-gr]) (/system/xbin/su root showmap -q 506)","[NULL]",8
+"SHOW MAP 507 ([irq/36-qcom,tem]) (/system/xbin/su root showmap -q 507)","[NULL]",8
+"SHOW MAP 508 ([irq/536-pwr_eve]) (/system/xbin/su root showmap -q 508)","[NULL]",8
+"SHOW MAP 509 ([irq/535-dp_hs_p]) (/system/xbin/su root showmap -q 509)","[NULL]",8
+"SHOW MAP 51 ([kworker/5:0]) (/system/xbin/su root showmap -q 51)","[NULL]",8
+"SHOW MAP 510 ([irq/538-dm_hs_p]) (/system/xbin/su root showmap -q 510)","[NULL]",8
+"SHOW MAP 511 ([irq/537-ss_phy_]) (/system/xbin/su root showmap -q 511)","[NULL]",8
+"SHOW MAP 512 ([irq/195-arm-smm]) (/system/xbin/su root showmap -q 512)","[NULL]",8
+"SHOW MAP 513 ([usb_bam_wq]) (/system/xbin/su root showmap -q 513)","[NULL]",8
+"SHOW MAP 514 ([core_ctl/0]) (/system/xbin/su root showmap -q 514)","[NULL]",8
+"SHOW MAP 515 ([core_ctl/4]) (/system/xbin/su root showmap -q 515)","[NULL]",8
+"SHOW MAP 52 ([kworker/5:0H]) (/system/xbin/su root showmap -q 52)","[NULL]",8
+"SHOW MAP 528 ([rq_stats]) (/system/xbin/su root showmap -q 528)","[NULL]",8
+"SHOW MAP 529 ([irq/196-arm-smm]) (/system/xbin/su root showmap -q 529)","[NULL]",8
+"SHOW MAP 53 ([rcuop/5]) (/system/xbin/su root showmap -q 53)","[NULL]",8
+"SHOW MAP 530 ([irq/197-arm-smm]) (/system/xbin/su root showmap -q 530)","[NULL]",8
+"SHOW MAP 531 ([irq/198-arm-smm]) (/system/xbin/su root showmap -q 531)","[NULL]",8
+"SHOW MAP 532 ([irq/199-arm-smm]) (/system/xbin/su root showmap -q 532)","[NULL]",8
+"SHOW MAP 533 ([irq/200-arm-smm]) (/system/xbin/su root showmap -q 533)","[NULL]",8
+"SHOW MAP 534 ([irq/201-arm-smm]) (/system/xbin/su root showmap -q 534)","[NULL]",8
+"SHOW MAP 535 ([irq/202-arm-smm]) (/system/xbin/su root showmap -q 535)","[NULL]",8
+"SHOW MAP 5350 (com.android.settings) (/system/xbin/su root showmap -q 5350)","[NULL]",596
+"SHOW MAP 536 ([irq/203-arm-smm]) (/system/xbin/su root showmap -q 536)","[NULL]",8
+"SHOW MAP 537 ([irq/204-arm-smm]) (/system/xbin/su root showmap -q 537)","[NULL]",8
+"SHOW MAP 538 ([irq/205-arm-smm]) (/system/xbin/su root showmap -q 538)","[NULL]",8
+"SHOW MAP 539 ([irq/206-arm-smm]) (/system/xbin/su root showmap -q 539)","[NULL]",8
+"SHOW MAP 54 ([rcuos/5]) (/system/xbin/su root showmap -q 54)","[NULL]",8
+"SHOW MAP 540 ([set_state_work]) (/system/xbin/su root showmap -q 540)","[NULL]",8
+"SHOW MAP 541 ([sb-1]) (/system/xbin/su root showmap -q 541)","[NULL]",8
+"SHOW MAP 542 ([ngd_rx_thread1]) (/system/xbin/su root showmap -q 542)","[NULL]",8
+"SHOW MAP 543 ([irq/309-mnh-rea]) (/system/xbin/su root showmap -q 543)","[NULL]",8
+"SHOW MAP 5431 ([kworker/3:3]) (/system/xbin/su root showmap -q 5431)","[NULL]",8
+"SHOW MAP 5435 (com.google.android.apps.turbo) (/system/xbin/su root showmap -q 5435)","[NULL]",571
+"SHOW MAP 544 ([ngd_notify_sl1]) (/system/xbin/su root showmap -q 544)","[NULL]",8
+"SHOW MAP 545 ([sb-3]) (/system/xbin/su root showmap -q 545)","[NULL]",8
+"SHOW MAP 546 ([ngd_rx_thread3]) (/system/xbin/su root showmap -q 546)","[NULL]",8
+"SHOW MAP 547 ([ngd_notify_sl3]) (/system/xbin/su root showmap -q 547)","[NULL]",8
+"SHOW MAP 548 ([irq/79-qpnp_fla]) (/system/xbin/su root showmap -q 548)","[NULL]",8
+"SHOW MAP 549 ([irq/78-qpnp_fla]) (/system/xbin/su root showmap -q 549)","[NULL]",8
+"SHOW MAP 55 ([rcuob/5]) (/system/xbin/su root showmap -q 55)","[NULL]",8
+"SHOW MAP 550 ([irq/77-qpnp_fla]) (/system/xbin/su root showmap -q 550)","[NULL]",8
+"SHOW MAP 551 ([irq/1057-mnh_pc]) (/system/xbin/su root showmap -q 551)","[NULL]",8
+"SHOW MAP 552 ([irq/1058-mnh_pc]) (/system/xbin/su root showmap -q 552)","[NULL]",8
+"SHOW MAP 553 ([irq/1059-mnh_pc]) (/system/xbin/su root showmap -q 553)","[NULL]",8
+"SHOW MAP 554 ([irq/1060-mnh_pc]) (/system/xbin/su root showmap -q 554)","[NULL]",8
+"SHOW MAP 555 ([irq/1061-mnh_pc]) (/system/xbin/su root showmap -q 555)","[NULL]",8
+"SHOW MAP 556 ([irq/1064-mnh_pc]) (/system/xbin/su root showmap -q 556)","[NULL]",8
+"SHOW MAP 557 ([irq/1065-mnh_pc]) (/system/xbin/su root showmap -q 557)","[NULL]",8
+"SHOW MAP 558 ([irq/207-arm-smm]) (/system/xbin/su root showmap -q 558)","[NULL]",8
+"SHOW MAP 559 ([kworker/4:1H]) (/system/xbin/su root showmap -q 559)","[NULL]",8
+"SHOW MAP 56 ([cpuhp/6]) (/system/xbin/su root showmap -q 56)","[NULL]",8
+"SHOW MAP 563 ([jbd2/sda20-8]) (/system/xbin/su root showmap -q 563)","[NULL]",8
+"SHOW MAP 564 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 564)","[NULL]",8
+"SHOW MAP 566 ([kdmflush]) (/system/xbin/su root showmap -q 566)","[NULL]",8
+"SHOW MAP 567 ([bioset]) (/system/xbin/su root showmap -q 567)","[NULL]",8
+"SHOW MAP 569 ([kdmflush]) (/system/xbin/su root showmap -q 569)","[NULL]",8
+"SHOW MAP 57 ([migration/6]) (/system/xbin/su root showmap -q 57)","[NULL]",8
+"SHOW MAP 570 ([bioset]) (/system/xbin/su root showmap -q 570)","[NULL]",8
+"SHOW MAP 5714 (com.google.android.permissioncontroller) (/system/xbin/su root showmap -q 5714)","[NULL]",563
+"SHOW MAP 572 ([kdmflush]) (/system/xbin/su root showmap -q 572)","[NULL]",8
+"SHOW MAP 573 ([bioset]) (/system/xbin/su root showmap -q 573)","[NULL]",8
+"SHOW MAP 5734 (com.google.android.setupwizard) (/system/xbin/su root showmap -q 5734)","[NULL]",576
+"SHOW MAP 575 ([kdmflush]) (/system/xbin/su root showmap -q 575)","[NULL]",8
+"SHOW MAP 576 ([bioset]) (/system/xbin/su root showmap -q 576)","[NULL]",8
+"SHOW MAP 578 ([kdmflush]) (/system/xbin/su root showmap -q 578)","[NULL]",8
+"SHOW MAP 579 ([bioset]) (/system/xbin/su root showmap -q 579)","[NULL]",8
+"SHOW MAP 58 ([ksoftirqd/6]) (/system/xbin/su root showmap -q 58)","[NULL]",8
+"SHOW MAP 580 ([kverityd]) (/system/xbin/su root showmap -q 580)","[NULL]",8
+"SHOW MAP 581 ([bioset]) (/system/xbin/su root showmap -q 581)","[NULL]",8
+"SHOW MAP 582 ([bioset]) (/system/xbin/su root showmap -q 582)","[NULL]",8
+"SHOW MAP 583 ([bioset]) (/system/xbin/su root showmap -q 583)","[NULL]",8
+"SHOW MAP 584 ([kworker/0:1H]) (/system/xbin/su root showmap -q 584)","[NULL]",8
+"SHOW MAP 585 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 585)","[NULL]",8
+"SHOW MAP 586 ([kworker/u16:9]) (/system/xbin/su root showmap -q 586)","[NULL]",8
+"SHOW MAP 588 ([kdmflush]) (/system/xbin/su root showmap -q 588)","[NULL]",8
+"SHOW MAP 589 ([bioset]) (/system/xbin/su root showmap -q 589)","[NULL]",8
+"SHOW MAP 59 ([kworker/6:0]) (/system/xbin/su root showmap -q 59)","[NULL]",8
+"SHOW MAP 590 ([kverityd]) (/system/xbin/su root showmap -q 590)","[NULL]",8
+"SHOW MAP 591 ([bioset]) (/system/xbin/su root showmap -q 591)","[NULL]",8
+"SHOW MAP 592 ([bioset]) (/system/xbin/su root showmap -q 592)","[NULL]",8
+"SHOW MAP 5928 (com.google.android.gms.unstable) (/system/xbin/su root showmap -q 5928)","[NULL]",575
+"SHOW MAP 593 ([bioset]) (/system/xbin/su root showmap -q 593)","[NULL]",8
+"SHOW MAP 594 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 594)","[NULL]",8
+"SHOW MAP 596 ([kdmflush]) (/system/xbin/su root showmap -q 596)","[NULL]",8
+"SHOW MAP 597 ([bioset]) (/system/xbin/su root showmap -q 597)","[NULL]",8
+"SHOW MAP 598 ([kverityd]) (/system/xbin/su root showmap -q 598)","[NULL]",8
+"SHOW MAP 599 ([bioset]) (/system/xbin/su root showmap -q 599)","[NULL]",8
+"SHOW MAP 6 ([ksoftirqd/0]) (/system/xbin/su root showmap -q 6)","[NULL]",8
+"SHOW MAP 60 ([kworker/6:0H]) (/system/xbin/su root showmap -q 60)","[NULL]",8
+"SHOW MAP 600 ([bioset]) (/system/xbin/su root showmap -q 600)","[NULL]",8
+"SHOW MAP 601 ([bioset]) (/system/xbin/su root showmap -q 601)","[NULL]",8
+"SHOW MAP 602 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 602)","[NULL]",8
+"SHOW MAP 604 ([kdmflush]) (/system/xbin/su root showmap -q 604)","[NULL]",8
+"SHOW MAP 605 ([bioset]) (/system/xbin/su root showmap -q 605)","[NULL]",8
+"SHOW MAP 606 ([kverityd]) (/system/xbin/su root showmap -q 606)","[NULL]",8
+"SHOW MAP 607 ([bioset]) (/system/xbin/su root showmap -q 607)","[NULL]",8
+"SHOW MAP 608 ([bioset]) (/system/xbin/su root showmap -q 608)","[NULL]",8
+"SHOW MAP 609 ([bioset]) (/system/xbin/su root showmap -q 609)","[NULL]",8
+"SHOW MAP 61 ([rcuop/6]) (/system/xbin/su root showmap -q 61)","[NULL]",8
+"SHOW MAP 610 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 610)","[NULL]",8
+"SHOW MAP 611 ([kworker/5:1H]) (/system/xbin/su root showmap -q 611)","[NULL]",8
+"SHOW MAP 612 ([kworker/3:2]) (/system/xbin/su root showmap -q 612)","[NULL]",8
+"SHOW MAP 613 ([kworker/1:1H]) (/system/xbin/su root showmap -q 613)","[NULL]",8
+"SHOW MAP 614 ([kworker/2:1H]) (/system/xbin/su root showmap -q 614)","[NULL]",8
+"SHOW MAP 615 ([kworker/3:1H]) (/system/xbin/su root showmap -q 615)","[NULL]",8
+"SHOW MAP 616 ([kworker/7:1H]) (/system/xbin/su root showmap -q 616)","[NULL]",8
+"SHOW MAP 617 ([kworker/7:1]) (/system/xbin/su root showmap -q 617)","[NULL]",8
+"SHOW MAP 618 ([kworker/6:1H]) (/system/xbin/su root showmap -q 618)","[NULL]",8
+"SHOW MAP 62 ([rcuos/6]) (/system/xbin/su root showmap -q 62)","[NULL]",8
+"SHOW MAP 620 (/system/bin/init) (/system/xbin/su root showmap -q 620)","[NULL]",79
+"SHOW MAP 622 (/system/bin/ueventd) (/system/xbin/su root showmap -q 622)","[NULL]",75
+"SHOW MAP 624 ([kworker/0:3]) (/system/xbin/su root showmap -q 624)","[NULL]",8
+"SHOW MAP 63 ([rcuob/6]) (/system/xbin/su root showmap -q 63)","[NULL]",8
+"SHOW MAP 635 ([bioset]) (/system/xbin/su root showmap -q 635)","[NULL]",8
+"SHOW MAP 636 ([bioset]) (/system/xbin/su root showmap -q 636)","[NULL]",8
+"SHOW MAP 637 ([bioset]) (/system/xbin/su root showmap -q 637)","[NULL]",8
+"SHOW MAP 638 ([bioset]) (/system/xbin/su root showmap -q 638)","[NULL]",8
+"SHOW MAP 639 ([bioset]) (/system/xbin/su root showmap -q 639)","[NULL]",8
+"SHOW MAP 64 ([cpuhp/7]) (/system/xbin/su root showmap -q 64)","[NULL]",8
+"SHOW MAP 640 ([bioset]) (/system/xbin/su root showmap -q 640)","[NULL]",8
+"SHOW MAP 641 ([bioset]) (/system/xbin/su root showmap -q 641)","[NULL]",8
+"SHOW MAP 642 ([bioset]) (/system/xbin/su root showmap -q 642)","[NULL]",8
+"SHOW MAP 643 ([bioset]) (/system/xbin/su root showmap -q 643)","[NULL]",8
+"SHOW MAP 644 ([bioset]) (/system/xbin/su root showmap -q 644)","[NULL]",8
+"SHOW MAP 645 ([bioset]) (/system/xbin/su root showmap -q 645)","[NULL]",8
+"SHOW MAP 646 ([bioset]) (/system/xbin/su root showmap -q 646)","[NULL]",8
+"SHOW MAP 647 ([bioset]) (/system/xbin/su root showmap -q 647)","[NULL]",8
+"SHOW MAP 648 ([bioset]) (/system/xbin/su root showmap -q 648)","[NULL]",8
+"SHOW MAP 649 ([bioset]) (/system/xbin/su root showmap -q 649)","[NULL]",8
+"SHOW MAP 65 ([migration/7]) (/system/xbin/su root showmap -q 65)","[NULL]",8
+"SHOW MAP 650 ([bioset]) (/system/xbin/su root showmap -q 650)","[NULL]",8
+"SHOW MAP 651 ([bioset]) (/system/xbin/su root showmap -q 651)","[NULL]",8
+"SHOW MAP 652 ([bioset]) (/system/xbin/su root showmap -q 652)","[NULL]",8
+"SHOW MAP 653 ([bioset]) (/system/xbin/su root showmap -q 653)","[NULL]",8
+"SHOW MAP 654 ([bioset]) (/system/xbin/su root showmap -q 654)","[NULL]",8
+"SHOW MAP 655 ([bioset]) (/system/xbin/su root showmap -q 655)","[NULL]",8
+"SHOW MAP 656 ([bioset]) (/system/xbin/su root showmap -q 656)","[NULL]",8
+"SHOW MAP 657 ([bioset]) (/system/xbin/su root showmap -q 657)","[NULL]",8
+"SHOW MAP 658 ([bioset]) (/system/xbin/su root showmap -q 658)","[NULL]",8
+"SHOW MAP 659 ([bioset]) (/system/xbin/su root showmap -q 659)","[NULL]",8
+"SHOW MAP 66 ([ksoftirqd/7]) (/system/xbin/su root showmap -q 66)","[NULL]",8
+"SHOW MAP 660 ([bioset]) (/system/xbin/su root showmap -q 660)","[NULL]",8
+"SHOW MAP 661 ([bioset]) (/system/xbin/su root showmap -q 661)","[NULL]",8
+"SHOW MAP 662 ([bioset]) (/system/xbin/su root showmap -q 662)","[NULL]",8
+"SHOW MAP 663 ([bioset]) (/system/xbin/su root showmap -q 663)","[NULL]",8
+"SHOW MAP 668 ([loop0]) (/system/xbin/su root showmap -q 668)","[NULL]",8
+"SHOW MAP 669 ([loop1]) (/system/xbin/su root showmap -q 669)","[NULL]",8
+"SHOW MAP 670 ([loop2]) (/system/xbin/su root showmap -q 670)","[NULL]",8
+"SHOW MAP 671 ([loop3]) (/system/xbin/su root showmap -q 671)","[NULL]",8
+"SHOW MAP 672 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 672)","[NULL]",8
+"SHOW MAP 673 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 673)","[NULL]",8
+"SHOW MAP 674 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 674)","[NULL]",8
+"SHOW MAP 675 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 675)","[NULL]",8
+"SHOW MAP 68 ([kworker/7:0H]) (/system/xbin/su root showmap -q 68)","[NULL]",8
+"SHOW MAP 683 ([jbd2/sdf2-8]) (/system/xbin/su root showmap -q 683)","[NULL]",8
+"SHOW MAP 684 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 684)","[NULL]",8
+"SHOW MAP 69 ([rcuop/7]) (/system/xbin/su root showmap -q 69)","[NULL]",8
+"SHOW MAP 690 (/system/bin/logd) (/system/xbin/su root showmap -q 690)","[NULL]",63
+"SHOW MAP 691 (/system/bin/lmkd) (/system/xbin/su root showmap -q 691)","[NULL]",49
+"SHOW MAP 692 (/system/bin/servicemanager) (/system/xbin/su root showmap -q 692)","[NULL]",59
+"SHOW MAP 693 (/system/bin/hwservicemanager) (/system/xbin/su root showmap -q 693)","[NULL]",63
+"SHOW MAP 694 (/vendor/bin/vndservicemanager) (/system/xbin/su root showmap -q 694)","[NULL]",56
+"SHOW MAP 695 ([psimon]) (/system/xbin/su root showmap -q 695)","[NULL]",8
+"SHOW MAP 696 (/vendor/bin/qseecomd) (/system/xbin/su root showmap -q 696)","[NULL]",83
+"SHOW MAP 697 (/vendor/bin/hw/android.hardware.keymaster@4.0-service-qti) (/system/xbin/su root showmap -q 697)","[NULL]",69
+"SHOW MAP 7 ([rcu_preempt]) (/system/xbin/su root showmap -q 7)","[NULL]",8
+"SHOW MAP 70 ([rcuos/7]) (/system/xbin/su root showmap -q 70)","[NULL]",8
+"SHOW MAP 701 ([kauditd]) (/system/xbin/su root showmap -q 701)","[NULL]",8
+"SHOW MAP 702 (/vendor/bin/hw/citadeld) (/system/xbin/su root showmap -q 702)","[NULL]",73
+"SHOW MAP 703 (/vendor/bin/hw/android.hardware.keymaster@4.1-service.citadel) (/system/xbin/su root showmap -q 703)","[NULL]",82
+"SHOW MAP 705 ([sugov:0]) (/system/xbin/su root showmap -q 705)","[NULL]",8
+"SHOW MAP 706 ([sugov:4]) (/system/xbin/su root showmap -q 706)","[NULL]",8
+"SHOW MAP 71 ([rcuob/7]) (/system/xbin/su root showmap -q 71)","[NULL]",8
+"SHOW MAP 718 (/system/bin/vold) (/system/xbin/su root showmap -q 718)","[NULL]",100
+"SHOW MAP 72 ([netns]) (/system/xbin/su root showmap -q 72)","[NULL]",8
+"SHOW MAP 73 ([ksuspend_mon]) (/system/xbin/su root showmap -q 73)","[NULL]",8
+"SHOW MAP 732 (/system/bin/hw/android.system.suspend@1.0-service) (/system/xbin/su root showmap -q 732)","[NULL]",62
+"SHOW MAP 733 (/system/bin/keystore2) (/system/xbin/su root showmap -q 733)","[NULL]",106
+"SHOW MAP 734 (/vendor/bin/hw/android.hardware.boot@1.0-service) (/system/xbin/su root showmap -q 734)","[NULL]",63
+"SHOW MAP 735 (/vendor/bin/hw/android.hardware.gatekeeper@1.0-service-qti) (/system/xbin/su root showmap -q 735)","[NULL]",66
+"SHOW MAP 736 (/vendor/bin/sscrpcd) (/system/xbin/su root showmap -q 736)","[NULL]",52
+"SHOW MAP 74 ([kworker/0:1]) (/system/xbin/su root showmap -q 74)","[NULL]",8
+"SHOW MAP 741 (/system/bin/surfaceflinger) (/system/xbin/su root showmap -q 741)","[NULL]",178
+"SHOW MAP 743 (/vendor/bin/hw/android.hardware.graphics.composer@2.3-service) (/system/xbin/su root showmap -q 743)","[NULL]",168
+"SHOW MAP 744 (/vendor/bin/hw/android.hardware.configstore@1.1-service) (/system/xbin/su root showmap -q 744)","[NULL]",62
+"SHOW MAP 746 (/vendor/bin/hw/vendor.qti.hardware.display.allocator@1.0-service) (/system/xbin/su root showmap -q 746)","[NULL]",73
+"SHOW MAP 747 (/vendor/bin/hw/hardware.google.light@1.0-service) (/system/xbin/su root showmap -q 747)","[NULL]",61
+"SHOW MAP 7475 (com.qualcomm.telephony) (/system/xbin/su root showmap -q 7475)","[NULL]",551
+"SHOW MAP 75 ([ipa_usb_wq]) (/system/xbin/su root showmap -q 75)","[NULL]",8
+"SHOW MAP 76 ([kworker/1:1]) (/system/xbin/su root showmap -q 76)","[NULL]",8
+"SHOW MAP 766 ([kdmflush]) (/system/xbin/su root showmap -q 766)","[NULL]",8
+"SHOW MAP 767 ([bioset]) (/system/xbin/su root showmap -q 767)","[NULL]",8
+"SHOW MAP 77 ([msm_watchdog]) (/system/xbin/su root showmap -q 77)","[NULL]",8
+"SHOW MAP 78 ([smem_native_sps]) (/system/xbin/su root showmap -q 78)","[NULL]",8
+"SHOW MAP 780 ([f2fs_flush-253:]) (/system/xbin/su root showmap -q 780)","[NULL]",8
+"SHOW MAP 781 ([f2fs_discard-25]) (/system/xbin/su root showmap -q 781)","[NULL]",8
+"SHOW MAP 79 ([spss_mailbox_gl]) (/system/xbin/su root showmap -q 79)","[NULL]",8
+"SHOW MAP 790 ([irq/165-arm-smm]) (/system/xbin/su root showmap -q 790)","[NULL]",8
+"SHOW MAP 8 ([rcu_sched]) (/system/xbin/su root showmap -q 8)","[NULL]",8
+"SHOW MAP 80 ([qmp_aop]) (/system/xbin/su root showmap -q 80)","[NULL]",8
+"SHOW MAP 801 ([f2fs_gc-253:8]) (/system/xbin/su root showmap -q 801)","[NULL]",8
+"SHOW MAP 8035 (com.google.android.connectivitymonitor) (/system/xbin/su root showmap -q 8035)","[NULL]",554
+"SHOW MAP 804 (/vendor/bin/time_daemon) (/system/xbin/su root showmap -q 804)","[NULL]",72
+"SHOW MAP 806 (/vendor/bin/hw/android.hardware.power-service.pixel-libperfmgr) (/system/xbin/su root showmap -q 806)","[NULL]",65
+"SHOW MAP 81 ([smem_native_mps]) (/system/xbin/su root showmap -q 81)","[NULL]",8
+"SHOW MAP 8139 (com.google.android.apps.tips) (/system/xbin/su root showmap -q 8139)","[NULL]",559
+"SHOW MAP 8164 (com.google.android.configupdater) (/system/xbin/su root showmap -q 8164)","[NULL]",551
+"SHOW MAP 82 ([mpss_smem_glink]) (/system/xbin/su root showmap -q 82)","[NULL]",8
+"SHOW MAP 8228 (/apex/com.android.adbd/bin/adbd) (/system/xbin/su root showmap -q 8228)","[NULL]",71
+"SHOW MAP 8236 (android.process.media) (/system/xbin/su root showmap -q 8236)","[NULL]",558
+"SHOW MAP 8251 (com.google.android.projection.gearhead:shared) (/system/xbin/su root showmap -q 8251)","[NULL]",568
+"SHOW MAP 8279 (com.google.android.projection.gearhead:car) (/system/xbin/su root showmap -q 8279)","[NULL]",559
+"SHOW MAP 83 ([smem_native_lpa]) (/system/xbin/su root showmap -q 83)","[NULL]",8
+"SHOW MAP 831 ([cds_recovery_wo]) (/system/xbin/su root showmap -q 831)","[NULL]",8
+"SHOW MAP 832 ([wlan_logging_th]) (/system/xbin/su root showmap -q 832)","[NULL]",8
+"SHOW MAP 835 (/system/bin/tombstoned) (/system/xbin/su root showmap -q 835)","[NULL]",42
+"SHOW MAP 8353 (com.android.ramdump) (/system/xbin/su root showmap -q 8353)","[NULL]",543
+"SHOW MAP 8376 (com.google.android.settings.intelligence) (/system/xbin/su root showmap -q 8376)","[NULL]",560
+"SHOW MAP 84 ([lpass_smem_glin]) (/system/xbin/su root showmap -q 84)","[NULL]",8
+"SHOW MAP 8405 ([kworker/0:4]) (/system/xbin/su root showmap -q 8405)","[NULL]",8
+"SHOW MAP 8407 ([kworker/0:5]) (/system/xbin/su root showmap -q 8407)","[NULL]",8
+"SHOW MAP 8408 ([kworker/0:6]) (/system/xbin/su root showmap -q 8408)","[NULL]",8
+"SHOW MAP 8409 ([kworker/0:7]) (/system/xbin/su root showmap -q 8409)","[NULL]",8
+"SHOW MAP 8416 (com.google.android.apps.turbo:aab) (/system/xbin/su root showmap -q 8416)","[NULL]",571
+"SHOW MAP 8462 (bugreportz) (/system/xbin/su root showmap -q 8462)","[NULL]",42
+"SHOW MAP 8464 (/system/bin/dumpstate) (/system/xbin/su root showmap -q 8464)","[NULL]",83
+"SHOW MAP 8468 (com.android.traceur) (/system/xbin/su root showmap -q 8468)","[NULL]",551
+"SHOW MAP 849 ([kworker/7:2]) (/system/xbin/su root showmap -q 849)","[NULL]",8
+"SHOW MAP 85 ([smem_native_dsp]) (/system/xbin/su root showmap -q 85)","[NULL]",8
+"SHOW MAP 859 ([irq/372-cs35l36]) (/system/xbin/su root showmap -q 859)","[NULL]",8
+"SHOW MAP 86 ([dsps_smem_glink]) (/system/xbin/su root showmap -q 86)","[NULL]",8
+"SHOW MAP 862 ([irq/297-cs35l36]) (/system/xbin/su root showmap -q 862)","[NULL]",8
+"SHOW MAP 867 ([loop4]) (/system/xbin/su root showmap -q 867)","[NULL]",8
+"SHOW MAP 869 ([dsps_IPCRTR]) (/system/xbin/su root showmap -q 869)","[NULL]",8
+"SHOW MAP 87 ([smem_native_cds]) (/system/xbin/su root showmap -q 87)","[NULL]",8
+"SHOW MAP 870 ([loop5]) (/system/xbin/su root showmap -q 870)","[NULL]",8
+"SHOW MAP 871 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 871)","[NULL]",8
+"SHOW MAP 872 ([loop6]) (/system/xbin/su root showmap -q 872)","[NULL]",8
+"SHOW MAP 873 ([qmi_hndl0000000]) (/system/xbin/su root showmap -q 873)","[NULL]",8
+"SHOW MAP 874 ([loop7]) (/system/xbin/su root showmap -q 874)","[NULL]",8
+"SHOW MAP 875 ([qmi_hndl0000000]) (/system/xbin/su root showmap -q 875)","[NULL]",8
+"SHOW MAP 876 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 876)","[NULL]",8
+"SHOW MAP 877 ([loop8]) (/system/xbin/su root showmap -q 877)","[NULL]",8
+"SHOW MAP 878 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 878)","[NULL]",8
+"SHOW MAP 879 ([loop9]) (/system/xbin/su root showmap -q 879)","[NULL]",8
+"SHOW MAP 88 ([cdsp_smem_glink]) (/system/xbin/su root showmap -q 88)","[NULL]",8
+"SHOW MAP 880 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 880)","[NULL]",8
+"SHOW MAP 881 ([loop10]) (/system/xbin/su root showmap -q 881)","[NULL]",8
+"SHOW MAP 882 ([loop11]) (/system/xbin/su root showmap -q 882)","[NULL]",8
+"SHOW MAP 883 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 883)","[NULL]",8
+"SHOW MAP 884 ([loop12]) (/system/xbin/su root showmap -q 884)","[NULL]",8
+"SHOW MAP 885 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 885)","[NULL]",8
+"SHOW MAP 886 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 886)","[NULL]",8
+"SHOW MAP 887 ([loop13]) (/system/xbin/su root showmap -q 887)","[NULL]",8
+"SHOW MAP 888 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 888)","[NULL]",8
+"SHOW MAP 889 ([loop14]) (/system/xbin/su root showmap -q 889)","[NULL]",8
+"SHOW MAP 89 ([khungtaskd]) (/system/xbin/su root showmap -q 89)","[NULL]",8
+"SHOW MAP 890 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 890)","[NULL]",8
+"SHOW MAP 891 ([loop15]) (/system/xbin/su root showmap -q 891)","[NULL]",8
+"SHOW MAP 892 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 892)","[NULL]",8
+"SHOW MAP 893 ([loop16]) (/system/xbin/su root showmap -q 893)","[NULL]",8
+"SHOW MAP 894 ([loop17]) (/system/xbin/su root showmap -q 894)","[NULL]",8
+"SHOW MAP 895 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 895)","[NULL]",8
+"SHOW MAP 896 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 896)","[NULL]",8
+"SHOW MAP 897 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 897)","[NULL]",8
+"SHOW MAP 898 ([loop18]) (/system/xbin/su root showmap -q 898)","[NULL]",8
+"SHOW MAP 9 ([rcu_bh]) (/system/xbin/su root showmap -q 9)","[NULL]",8
+"SHOW MAP 90 ([oom_reaper]) (/system/xbin/su root showmap -q 90)","[NULL]",8
+"SHOW MAP 902 ([loop19]) (/system/xbin/su root showmap -q 902)","[NULL]",8
+"SHOW MAP 903 ([loop20]) (/system/xbin/su root showmap -q 903)","[NULL]",8
+"SHOW MAP 904 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 904)","[NULL]",8
+"SHOW MAP 905 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 905)","[NULL]",8
+"SHOW MAP 906 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 906)","[NULL]",8
+"SHOW MAP 907 ([loop21]) (/system/xbin/su root showmap -q 907)","[NULL]",8
+"SHOW MAP 908 ([loop22]) (/system/xbin/su root showmap -q 908)","[NULL]",8
+"SHOW MAP 909 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 909)","[NULL]",8
+"SHOW MAP 91 ([writeback]) (/system/xbin/su root showmap -q 91)","[NULL]",8
+"SHOW MAP 910 ([loop23]) (/system/xbin/su root showmap -q 910)","[NULL]",8
+"SHOW MAP 911 ([loop24]) (/system/xbin/su root showmap -q 911)","[NULL]",8
+"SHOW MAP 912 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 912)","[NULL]",8
+"SHOW MAP 913 ([loop25]) (/system/xbin/su root showmap -q 913)","[NULL]",8
+"SHOW MAP 914 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 914)","[NULL]",8
+"SHOW MAP 915 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 915)","[NULL]",8
+"SHOW MAP 916 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 916)","[NULL]",8
+"SHOW MAP 917 ([loop26]) (/system/xbin/su root showmap -q 917)","[NULL]",8
+"SHOW MAP 918 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 918)","[NULL]",8
+"SHOW MAP 919 ([loop27]) (/system/xbin/su root showmap -q 919)","[NULL]",8
+"SHOW MAP 92 ([kcompactd0]) (/system/xbin/su root showmap -q 92)","[NULL]",8
+"SHOW MAP 921 ([loop28]) (/system/xbin/su root showmap -q 921)","[NULL]",8
+"SHOW MAP 922 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 922)","[NULL]",8
+"SHOW MAP 923 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 923)","[NULL]",8
+"SHOW MAP 924 ([ext4-rsv-conver]) (/system/xbin/su root showmap -q 924)","[NULL]",8
+"SHOW MAP 93 ([crypto]) (/system/xbin/su root showmap -q 93)","[NULL]",8
+"SHOW MAP 931 ([tbn_event_proce]) (/system/xbin/su root showmap -q 931)","[NULL]",8
+"SHOW MAP 932 ([qmi_hndl0000000]) (/system/xbin/su root showmap -q 932)","[NULL]",8
+"SHOW MAP 936 ([irq/382-sec_ts]) (/system/xbin/su root showmap -q 936)","[NULL]",8
+"SHOW MAP 94 ([bioset]) (/system/xbin/su root showmap -q 94)","[NULL]",8
+"SHOW MAP 945 (/system/bin/netd) (/system/xbin/su root showmap -q 945)","[NULL]",94
+"SHOW MAP 946 (/vendor/bin/hw/android.hardware.rebootescrow-service.citadel) (/system/xbin/su root showmap -q 946)","[NULL]",84
+"SHOW MAP 948 ([irq/208-arm-smm]) (/system/xbin/su root showmap -q 948)","[NULL]",8
+"SHOW MAP 949 ([irq/209-arm-smm]) (/system/xbin/su root showmap -q 949)","[NULL]",8
+"SHOW MAP 95 ([kblockd]) (/system/xbin/su root showmap -q 95)","[NULL]",8
+"SHOW MAP 950 ([irq/210-arm-smm]) (/system/xbin/su root showmap -q 950)","[NULL]",8
+"SHOW MAP 957 ([ipa_interrupt_w]) (/system/xbin/su root showmap -q 957)","[NULL]",8
+"SHOW MAP 958 ([ipawq36]) (/system/xbin/su root showmap -q 958)","[NULL]",8
+"SHOW MAP 959 ([iparepwq36]) (/system/xbin/su root showmap -q 959)","[NULL]",8
+"SHOW MAP 96 ([irq/160-arm-smm]) (/system/xbin/su root showmap -q 96)","[NULL]",8
+"SHOW MAP 960 ([ipawq33]) (/system/xbin/su root showmap -q 960)","[NULL]",8
+"SHOW MAP 961 ([iparepwq33]) (/system/xbin/su root showmap -q 961)","[NULL]",8
+"SHOW MAP 962 ([ipawq32]) (/system/xbin/su root showmap -q 962)","[NULL]",8
+"SHOW MAP 963 ([iparepwq32]) (/system/xbin/su root showmap -q 963)","[NULL]",8
+"SHOW MAP 964 ([ipa_ut_dbgfs]) (/system/xbin/su root showmap -q 964)","[NULL]",8
+"SHOW MAP 966 ([ipa_A7_svc]) (/system/xbin/su root showmap -q 966)","[NULL]",8
+"SHOW MAP 967 ([qmi_hndl0000000]) (/system/xbin/su root showmap -q 967)","[NULL]",8
+"SHOW MAP 968 ([clnt_req]) (/system/xbin/su root showmap -q 968)","[NULL]",8
+"SHOW MAP 969 ([clnt_resp]) (/system/xbin/su root showmap -q 969)","[NULL]",8
+"SHOW MAP 9696 ([cmd]) (/system/xbin/su root showmap -q 9696)","[NULL]",8
+"SHOW MAP 97 ([irq/161-arm-smm]) (/system/xbin/su root showmap -q 97)","[NULL]",8
+"SHOW MAP 970 ([clnt_req]) (/system/xbin/su root showmap -q 970)","[NULL]",8
+"SHOW MAP 976 (/system/bin/iptables-restore) (/system/xbin/su root showmap -q 976)","[NULL]",38
+"SHOW MAP 977 (/system/bin/ip6tables-restore) (/system/xbin/su root showmap -q 977)","[NULL]",38
+"SHOW MAP 979 (/apex/com.android.os.statsd/bin/statsd) (/system/xbin/su root showmap -q 979)","[NULL]",63
+"SHOW MAP 98 ([irq/170-arm-smm]) (/system/xbin/su root showmap -q 98)","[NULL]",8
+"SHOW MAP 980 (zygote64) (/system/xbin/su root showmap -q 980)","[NULL]",524
+"SHOW MAP 981 (zygote) (/system/xbin/su root showmap -q 981)","[NULL]",522
+"SHOW MAP 9811 (com.android.printspooler) (/system/xbin/su root showmap -q 9811)","[NULL]",550
+"SHOW MAP 99 ([kworker/u17:0]) (/system/xbin/su root showmap -q 99)","[NULL]",8
+"SHOW MAP 990 ([at_usb0]) (/system/xbin/su root showmap -q 990)","[NULL]",8
+"SHOW MAP 991 ([at_usb1]) (/system/xbin/su root showmap -q 991)","[NULL]",8
+"SHOW MAP 992 ([qdss]) (/system/xbin/su root showmap -q 992)","[NULL]",8
+"SLAB INFO (/proc/slabinfo)","[NULL]",106
+"STATSDSTATS (/system/bin/dumpsys -T 30000 stats --metadata)","[NULL]",591
+"STORAGED IO INFO (storaged -u -p)","[NULL]",124
+"SYSTEM LOG (logcat -v threadtime -v printable -v uid -d *:v -T 2021-08-24 23:35:43.000)","[NULL]",3553
+"SYSTEM LOG (logcat -v threadtime -v printable -v uid -d *:v)","[NULL]",37062
+"SYSTEM PROPERTIES (getprop)","[NULL]",1149
+"UPTIME (uptime)","[NULL]",1
+"VIRTUAL MEMORY STATS (/proc/vmstat)","[NULL]",100
+"VM TRACES JUST NOW (/data/user_de/0/com.android.shell/files/bugreports/dumptrace_ymdts4: 2021-08-24 23:35:55)","[NULL]",36690
+"VMALLOC INFO (/proc/vmallocinfo)","[NULL]",4276
+"XFRM STATS (/proc/net/xfrm_stat)","[NULL]",29
+"ZONEINFO (/proc/zoneinfo)","[NULL]",171
diff --git a/test/trace_processor/diff_tests/android/android_bugreport_dumpsys_test.out b/test/trace_processor/diff_tests/android/android_bugreport_dumpsys_test.out
new file mode 100644
index 0000000..cb47b8e
--- /dev/null
+++ b/test/trace_processor/diff_tests/android/android_bugreport_dumpsys_test.out
@@ -0,0 +1,39 @@
+"section","service","line"
+"DUMPSYS (/system/bin/dumpsys)","color_display","DUMP OF SERVICE color_display:"
+"DUMPSYS (/system/bin/dumpsys)","color_display","COLOR DISPLAY MANAGER dumpsys (color_display)"
+"DUMPSYS (/system/bin/dumpsys)","color_display","Night display:"
+"DUMPSYS (/system/bin/dumpsys)","color_display","    Activated: false"
+"DUMPSYS (/system/bin/dumpsys)","color_display","    Color temp: 2850"
+"DUMPSYS (/system/bin/dumpsys)","color_display","Global saturation:"
+"DUMPSYS (/system/bin/dumpsys)","color_display","    Activated: false"
+"DUMPSYS (/system/bin/dumpsys)","color_display","App Saturation: "
+"DUMPSYS (/system/bin/dumpsys)","color_display","    com.android.pixellogger:"
+"DUMPSYS (/system/bin/dumpsys)","color_display","        0:"
+"DUMPSYS (/system/bin/dumpsys)","color_display","            mSaturationLevels: {}"
+"DUMPSYS (/system/bin/dumpsys)","color_display","            mControllerRefs count: 1"
+"DUMPSYS (/system/bin/dumpsys)","color_display","    com.android.settings:"
+"DUMPSYS (/system/bin/dumpsys)","color_display","        0:"
+"DUMPSYS (/system/bin/dumpsys)","color_display","            mSaturationLevels: {}"
+"DUMPSYS (/system/bin/dumpsys)","color_display","            mControllerRefs count: 3"
+"DUMPSYS (/system/bin/dumpsys)","color_display","    com.android.systemui:"
+"DUMPSYS (/system/bin/dumpsys)","color_display","        0:"
+"DUMPSYS (/system/bin/dumpsys)","color_display","            mSaturationLevels: {}"
+"DUMPSYS (/system/bin/dumpsys)","color_display","            mControllerRefs count: 1"
+"DUMPSYS (/system/bin/dumpsys)","color_display","    com.android.yadayada:"
+"DUMPSYS (/system/bin/dumpsys)","color_display","        0:"
+"DUMPSYS (/system/bin/dumpsys)","color_display","            mSaturationLevels: {}"
+"DUMPSYS (/system/bin/dumpsys)","color_display","            mControllerRefs count: 1"
+"DUMPSYS (/system/bin/dumpsys)","color_display","    com.google.android.apps.nexuslauncher:"
+"DUMPSYS (/system/bin/dumpsys)","color_display","        0:"
+"DUMPSYS (/system/bin/dumpsys)","color_display","            mSaturationLevels: {}"
+"DUMPSYS (/system/bin/dumpsys)","color_display","            mControllerRefs count: 2"
+"DUMPSYS (/system/bin/dumpsys)","color_display","    com.google.android.gms:"
+"DUMPSYS (/system/bin/dumpsys)","color_display","        0:"
+"DUMPSYS (/system/bin/dumpsys)","color_display","            mSaturationLevels: {}"
+"DUMPSYS (/system/bin/dumpsys)","color_display","            mControllerRefs count: 8"
+"DUMPSYS (/system/bin/dumpsys)","color_display","Display white balance:"
+"DUMPSYS (/system/bin/dumpsys)","color_display","    Not available"
+"DUMPSYS (/system/bin/dumpsys)","color_display","Reduce bright colors:"
+"DUMPSYS (/system/bin/dumpsys)","color_display","    Activated: false"
+"DUMPSYS (/system/bin/dumpsys)","color_display","    mStrength = 50"
+"DUMPSYS (/system/bin/dumpsys)","color_display","Color mode: 3"
diff --git a/test/trace_processor/diff_tests/android/android_bugreport_logs_test.out b/test/trace_processor/diff_tests/android/android_bugreport_logs_test.out
new file mode 100644
index 0000000..95af605
--- /dev/null
+++ b/test/trace_processor/diff_tests/android/android_bugreport_logs_test.out
@@ -0,0 +1,201 @@
+"cnt","ts","prio","tag","msg"
+45028,1609462800670000000,5,"auditd","type=2000 audit(0.0:1): initialized"
+45028,1609462800670000000,4,"auditd","type=2000 audit(0.0:1): initialized"
+45028,1609463611529000000,4,"","c0      0 Booting Linux on physical CPU 0x0"
+45028,1609463611529000000,4,"","c0      0 Linux version 4.9.270-g862f51bac900-ab7613625 (android-build@abfarm-east4-101) (Android (7284624, based on r416183b) clang version 12.0.5 (https://android.googlesource.com/toolchain/llvm-project c935d99d7cf2016289302412d708641d52d2f7ee)) #0 SMP PREEMPT Thu Aug 5 07:04:42 UTC 2021"
+45028,1609463611529000000,4,"","c0      0 Boot CPU: AArch64 Processor [517f803c]"
+45028,1609463611529000000,4,"","c0      0 Machine: Google Inc. MSM sdm845 C1 EVT v1.0"
+45028,1609463611529000000,4,"","c0      0 Reserved memory: created CMA memory pool at 0x00000000fec00000, size 16 MiB"
+45028,1609463611529000000,4,"","c0      0 OF: reserved mem: initialized node adsp_region, compatible id shared-dma-pool"
+45028,1609463611529000000,4,"","c0      0 Reserved memory: created CMA memory pool at 0x00000000fdc00000, size 16 MiB"
+45028,1609463611529000000,4,"","c0      0 OF: reserved mem: initialized node qseecom_ta_region, compatible id shared-dma-pool"
+45028,1609463611529000000,4,"","c0      0 Reserved memory: created CMA memory pool at 0x00000000f4c00000, size 144 MiB"
+45028,1609463611529000000,4,"","c0      0 OF: reserved mem: initialized node secure_display_region, compatible id shared-dma-pool"
+45028,1609463611529000000,4,"","c0      0 Reserved memory: created CMA memory pool at 0x000000017b800000, size 36 MiB"
+45028,1609463611529000000,4,"","c0      0 OF: reserved mem: initialized node mem_dump_region, compatible id shared-dma-pool"
+45028,1609463611529000000,4,"","c0      0 Reserved memory: created CMA memory pool at 0x00000000f4400000, size 8 MiB"
+45028,1609463611529000000,4,"","c0      0 OF: reserved mem: initialized node secure_sp_region, compatible id shared-dma-pool"
+45028,1609463611529000000,4,"","c0      0 Reserved memory: created CMA memory pool at 0x00000000f2400000, size 32 MiB"
+45028,1609463611529000000,4,"","c0      0 OF: reserved mem: initialized node linux,cma, compatible id shared-dma-pool"
+45028,1609463611529000000,4,"","c0      0 Reserved memory: created DMA memory pool at 0x000000008ab00000, size 20 MiB"
+45028,1609463611529000000,4,"","c0      0 OF: reserved mem: initialized node qseecom_region@0x8ab00000, compatible id shared-dma-pool"
+45028,1609463611529000000,4,"","c0      0 Removed memory: created DMA memory pool at 0x000000008bf00000, size 5 MiB"
+45028,1609463611529000000,4,"","c0      0 OF: reserved mem: initialized node camera_region@0x8bf00000, compatible id removed-dma-pool"
+45028,1609463611529000000,4,"","c0      0 Removed memory: created DMA memory pool at 0x000000008c400000, size 0 MiB"
+45028,1609463611529000000,4,"","c0      0 OF: reserved mem: initialized node ips_fw_region@0x8c400000, compatible id removed-dma-pool"
+45028,1609463611529000000,4,"","c0      0 Removed memory: created DMA memory pool at 0x000000008c410000, size 0 MiB"
+45028,1609463611529000000,4,"","c0      0 OF: reserved mem: initialized node ipa_gsi_region@0x8c410000, compatible id removed-dma-pool"
+45028,1609463611529000000,4,"","c0      0 Removed memory: created DMA memory pool at 0x000000008c415000, size 0 MiB"
+45028,1609463611529000000,4,"","c0      0 OF: reserved mem: initialized node gpu_region@0x8c415000, compatible id removed-dma-pool"
+45028,1609463611529000000,4,"","c0      0 Removed memory: created DMA memory pool at 0x000000008c500000, size 26 MiB"
+45028,1609463611529000000,4,"","c0      0 OF: reserved mem: initialized node adsp_region@0x8c500000, compatible id removed-dma-pool"
+45028,1609463611529000000,4,"","c0      0 Removed memory: created DMA memory pool at 0x000000008df00000, size 1 MiB"
+45028,1609463611529000000,4,"","c0      0 OF: reserved mem: initialized node wlan_fw_region@0x8df00000, compatible id removed-dma-pool"
+45028,1609463611529000000,4,"","c0      0 Removed memory: created DMA memory pool at 0x000000008e000000, size 152 MiB"
+45028,1609463611529000000,4,"","c0      0 OF: reserved mem: initialized node modem_region@0x8e000000, compatible id removed-dma-pool"
+45028,1609463611529000000,4,"","c0      0 Removed memory: created DMA memory pool at 0x0000000097800000, size 5 MiB"
+45028,1609463611529000000,4,"","c0      0 OF: reserved mem: initialized node video_region@0x95800000, compatible id removed-dma-pool"
+45028,1609463611529000000,4,"","c0      0 Removed memory: created DMA memory pool at 0x0000000097d00000, size 8 MiB"
+45028,1609463611529000000,4,"","c0      0 OF: reserved mem: initialized node cdsp_region@0x95d00000, compatible id removed-dma-pool"
+45028,1609463611529000000,4,"","c0      0 Removed memory: created DMA memory pool at 0x0000000098500000, size 2 MiB"
+45028,1609463611529000000,4,"","c0      0 OF: reserved mem: initialized node mba_region@0x96500000, compatible id removed-dma-pool"
+45028,1609463611529000000,4,"","c0      0 Removed memory: created DMA memory pool at 0x0000000098700000, size 20 MiB"
+45028,1609463611529000000,4,"","c0      0 OF: reserved mem: initialized node slpi_region@0x96700000, compatible id removed-dma-pool"
+45028,1609463611529000000,4,"","c0      0 Removed memory: created DMA memory pool at 0x0000000099b00000, size 1 MiB"
+45028,1609463611529000000,4,"","c0      0 OF: reserved mem: initialized node pil_spss_region@0x97b00000, compatible id removed-dma-pool"
+45028,1609463611529000000,4,"","c0      0 Removed memory: created DMA memory pool at 0x00000000a1800000, size 0 MiB"
+45028,1609463611529000000,4,"","c0      0 OF: reserved mem: initialized node debug_info@0, compatible id removed-dma-pool"
+45028,1609463611529000000,4,"","c0      0 Removed memory: created DMA memory pool at 0x00000000a1810000, size 2 MiB"
+45028,1609463611529000000,4,"","c0      0 OF: reserved mem: initialized node ramoops_region@a1810000, compatible id removed-dma-pool"
+45028,1609463611529000000,4,"","c0      0 Removed memory: created DMA memory pool at 0x00000000a1a10000, size 2 MiB"
+45028,1609463611529000000,4,"","c0      0 OF: reserved mem: initialized node alt_ramoops_region@a1a10000, compatible id removed-dma-pool"
+45028,1609463611529000000,4,"","c0      0 Removed memory: created DMA memory pool at 0x00000000a1c10000, size 0 MiB"
+45028,1609463611529000000,4,"","c0      0 OF: reserved mem: initialized node ramoops_meta_region@a1c10000, compatible id removed-dma-pool"
+45028,1609463611529000000,3,"","c0      0 On node 0 totalpages: 963896"
+45028,1609463611529000000,3,"","c0      0   DMA zone: 6999 pages used for memmap"
+45028,1609463611529000000,3,"","c0      0   DMA zone: 0 pages reserved"
+45028,1609463611529000000,3,"","c0      0   DMA zone: 447896 pages, LIFO batch:31"
+45028,1609463611529000000,3,"","c0      0   Normal zone: 8063 pages used for memmap"
+45028,1609463611529000000,3,"","c0      0   Normal zone: 516000 pages, LIFO batch:31"
+45028,1609463611529000000,4,"","c0      0 psci: probing for conduit method from DT."
+45028,1609463611529000000,4,"","c0      0 psci: PSCIv1.1 detected in firmware."
+45028,1609463611529000000,4,"","c0      0 psci: Using standard PSCI v0.2 function IDs"
+45028,1609463611529000000,4,"","c0      0 psci: MIGRATE_INFO_TYPE not supported."
+45028,1609463611529000000,4,"","c0      0 psci: SMC Calling Convention v1.0"
+45028,1609463611529000000,4,"","c0      0 random: fast init done"
+45028,1609463611529000000,4,"","c0      0 percpu: Embedded 23 pages/cpu s54296 r8192 d31720 u94208"
+45028,1609463611529000000,3,"","c0      0 pcpu-alloc: s54296 r8192 d31720 u94208 alloc=23*4096"
+45028,1609463611529000000,3,"","c0      0 pcpu-alloc: [0] 0 [0] 1 [0] 2 [0] 3 [0] 4 [0] 5 [0] 6 [0] 7"
+45028,1609463611529000000,4,"","c0      0 Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 948834"
+45028,1609463611529000000,4,"","c0      0 Kernel command line: rcupdate.rcu_expedited=1 rootwait ro init=/init androidboot.bootdevice=1d84000.ufshc androidboot.baseband=sdm androidboot.keymaster=1 msm_drm.dsi_display0=dsi_s6e3ha8_cmd_display::timing0 androidboot.force_normal_boot=1 androidboot.serialno=84TY005M7 androidboot.slot_suffix=_a androidboot.slot_retry_count=2 androidboot.slot_successful=yes androidboot.hardware.platform=sdm845 androidboot.hardware=crosshatch androidboot.revision=EVT1.0 androidboot.bootloader=b1c1-0.4-7617406 androidboot.hardware.sku=G013C androidboot.hardware.radio.subtype=0 androidboot.hardware.dsds=0 androidboot.secure_boot=NONE androidboot.cdt_hwid=0x05010A00 androidboot.hardware.majorid=0x01 androidboot.dtb_idx=0 androidboot.dtbo_idx=10 androidboot.mode=normal androidboot.bootreason=reboot androidboot.hardware.ddr=4GB,Samsung,LPDDR4X androidboot.ddr_info=Samsung androidboot.ddr_size=4GB androidboot.hardware.ufs=64GB,Samsung androidboot.cid=00000000 androidboot.boottime=0BLE:59,1BLL:22,"
+45028,1609463611529000000,4,"","c0      0 PID hash table entries: 4096 (order: 3, 32768 bytes)"
+45028,1609463611529000000,4,"","c0      0 Dentry cache hash table entries: 524288 (order: 10, 4194304 bytes)"
+45028,1609463611529000000,4,"","c0      0 Inode-cache hash table entries: 262144 (order: 9, 2097152 bytes)"
+45028,1609463611529000000,4,"","c0      0 software IO TLB: mapped [mem 0xee400000-0xf2400000] (64MB)"
+45028,1609463611529000000,4,"","c0      0 Memory: 3347020K/3855584K available (26108K kernel code, 3324K rwdata, 8608K rodata, 8192K init, 9407K bss, 250516K reserved, 258048K cma-reserved)"
+45028,1609463611529000000,4,"","c0      0 Virtual kernel memory layout:"
+45028,1609463611529000000,4,"","c0      0     modules : 0xffffff8000000000 - 0xffffff8008000000   (   128 MB)"
+45028,1609463611529000000,4,"","c0      0     vmalloc : 0xffffff8008000000 - 0xffffffbebfff0000   (   250 GB)"
+45028,1609463611529000000,4,"","c0      0       .text : 0x        (ptrval) - 0x        (ptrval)   ( 26112 KB)"
+45028,1609463611529000000,4,"","c0      0     .rodata : 0x        (ptrval) - 0x        (ptrval)   ( 10240 KB)"
+45028,1609463611529000000,4,"","c0      0       .init : 0x        (ptrval) - 0x        (ptrval)   (  8192 KB)"
+45028,1609463611529000000,4,"","c0      0       .data : 0x        (ptrval) - 0x        (ptrval)   (  3324 KB)"
+45028,1609463611529000000,4,"","c0      0        .bss : 0x        (ptrval) - 0x        (ptrval)   (  9408 KB)"
+45028,1609463611529000000,4,"","c0      0     fixed   : 0xffffffbefe7fb000 - 0xffffffbefec00000   (  4116 KB)"
+45028,1609463611529000000,4,"","c0      0     PCI I/O : 0xffffffbefee00000 - 0xffffffbeffe00000   (    16 MB)"
+45028,1609463611529000000,4,"","c0      0     vmemmap : 0xffffffbf00000000 - 0xffffffc000000000   (     4 GB maximum)"
+45028,1609463611529000000,4,"","c0      0               0xffffffbf7b000000 - 0xffffffbf7ef7e800   (    63 MB actual)"
+45028,1609463611529000000,4,"","c0      0     memory  : 0xffffffdec0000000 - 0xffffffdfbdfa0000   (  4063 MB)"
+45028,1609463611529000000,4,"","c0      0 SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=8, Nodes=1"
+45028,1609463611529000000,4,"","c0      0 Preemptible hierarchical RCU implementation."
+45028,1609463611529000000,4,"","c0      0 	RCU dyntick-idle grace-period acceleration is enabled."
+45028,1609463611529000000,4,"","c0      0 NR_IRQS:64 nr_irqs:64 0"
+45028,1609463611529000000,4,"","c0      0 PDC SDM845 v2 initialized"
+45028,1609463611529000000,4,"","c0      0 	Offload RCU callbacks from all CPUs"
+45028,1609463611529000000,4,"","c0      0 	Offload RCU callbacks from CPUs: 0-7."
+45028,1609463611529000000,4,"","c0      0 arm_arch_timer: Architected cp15 and mmio timer(s) running at 19.20MHz (virt/virt)."
+45028,1609463611529000000,4,"","c0      0 clocksource: arch_sys_counter: mask: 0xffffffffffffff max_cycles: 0x46d987e47, max_idle_ns: 440795202767 ns"
+45028,1609463611529000000,4,"","c0      0 sched_clock: 56 bits at 19MHz, resolution 52ns, wraps every 4398046511078ns"
+45028,1609463611529000000,4,"","c0      0 clocksource: Switched to clocksource arch_sys_counter"
+45028,1609463611531000000,4,"","c0      0 Console: colour dummy device 80x25"
+45028,1609463611531000000,4,"","c0      0 Calibrating delay loop (skipped), value calculated using timer frequency.. 38.00 BogoMIPS (lpj=64000)"
+45028,1629848355866000000,4,"","c3    698 logd: logdr: UID=2000 GID=2000 PID=12111 n tail=0 logMask=99 pid=0 start=1629844543000000000ns deadline=0ns"
+45028,1629848355796000000,3,"dumpstate","Duration of 'DUMPSTATE': 199.82s"
+45028,1629848355680000000,5,"IorapForwardingService","No service published for: iorapd"
+45028,1629848355606000000,3,"dumpstate","Adding dir /sys/fs/cgroup (recursive: 1)"
+45028,1629848355577000000,3,"dumpstate","Adding dir /linkerconfig (recursive: 1)"
+45028,1629848355566000000,3,"ProcessState","Binder ioctl to enable oneway spam detection failed: Invalid argument"
+45028,1629848355566000000,4,"","c1  12109 binder: 12109:12109 ioctl 40046210 7fcfdfc5c4 returned -22"
+45028,1629848355540000000,3,"ProcessState","Binder ioctl to enable oneway spam detection failed: Invalid argument"
+45028,1629848355540000000,4,"","c0  12107 binder: 12107:12107 ioctl 40046210 7fcff784d4 returned -22"
+45028,1629848355514000000,3,"ProcessState","Binder ioctl to enable oneway spam detection failed: Invalid argument"
+45028,1629848355513000000,4,"","c0  12105 binder: 12105:12105 ioctl 40046210 7fcac734d4 returned -22"
+45028,1629848355475000000,3,"dumpstate","Duration of 'APP PROVIDERS NON-PLATFORM': 1.22s"
+45028,1629848355023000000,6,"earchbox:searc","Resource 7f030033 is a complex map type."
+45028,1629848355023000000,6,"earchbox:searc","Resource 7f030038 is a complex map type."
+45028,1629848355022000000,6,"earchbox:searc","Resource 7f030013 is a complex map type."
+45028,1629848355022000000,6,"earchbox:searc","Resource 7f030014 is a complex map type."
+45028,1629848355022000000,6,"earchbox:searc","Resource 7f03001b is a complex map type."
+45028,1629848355022000000,6,"earchbox:searc","Resource 7f03001c is a complex map type."
+45028,1629848355022000000,6,"earchbox:searc","Resource 7f03001f is a complex map type."
+45028,1629848355022000000,6,"earchbox:searc","Resource 7f03002c is a complex map type."
+45028,1629848355022000000,6,"earchbox:searc","Resource 7f030032 is a complex map type."
+45028,1629848355021000000,6,"earchbox:searc","Resource 7f030000 is a complex map type."
+45028,1629848355021000000,6,"earchbox:searc","Resource 7f030012 is a complex map type."
+45028,1629848354946000000,4,"PCP.MediaManager","Dumping pcp media manager events"
+45028,1629848354944000000,4,"PCP.ImplV0","on dump start"
+45028,1629848354932000000,4,"A","Loaded shared library: nativecrashreporter"
+45028,1629848354910000000,4,"A","[null"
+45028,1629848354910000000,4,"A",", GlobalData"
+45028,1629848354910000000,4,"A","]"
+45028,1629848354892000000,4,"A","[AccountId{id=1}"
+45028,1629848354892000000,4,"A",", SharedProactiveData"
+45028,1629848354892000000,4,"A",", ClientProactiveData"
+45028,1629848354892000000,4,"A",", LastClientDataQuery"
+45028,1629848354892000000,4,"A","]"
+45028,1629848354683000000,4,"AmbBrdcstRcvrServClient","created monitors of size 4"
+45028,1629848354682000000,4,"AmbBrdcstRcvrServClient","mapping trigger monitor: MEDIA_SESSION"
+45028,1629848354682000000,4,"AmbBrdcstRcvrServClient","mapping trigger monitor: INFERENCE_START"
+45028,1629848354682000000,4,"AmbBrdcstRcvrServClient","mapping trigger monitor: SCREEN_STATE"
+45028,1629848354679000000,4,"AmbBrdcstRcvrServClient","mapping trigger monitor: HEADSET_STATE"
+45028,1629848354677000000,5,"IorapForwardingService","No service published for: iorapd"
+45028,1629848354557000000,4,"PeopleDatabaseHelper","cleanUpNonGplusAccounts done."
+45028,1629848354413000000,5,"Icing","Record file /data/user/0/com.google.android.gms/files/AppDataSearch/main/query-record-log.tmp not found, ignoring"
+45028,1629848354323000000,4,"","c3      1 init: Service 'apexd' (pid 11731) exited with status 0 oneshot service took 3.818000 seconds in background"
+45028,1629848354323000000,4,"","c3      1 init: Sending signal 9 to service 'apexd' (pid 11731) process group..."
+45028,1629848354323000000,4,"","c3      1 libprocessgroup: Successfully killed process cgroup uid 0 pid 11731 in 0ms"
+45028,1629848354316000000,4,"BpBinder","onLastStrongRef automatically unlinking death recipients: <uncached descriptor>"
+45028,1629848354316000000,4,"","c3  11732 AidlLazyServiceRegistrar: Unregistered all clients and exiting"
+45028,1629848354315000000,4,"","c0  11732 AidlLazyServiceRegistrar: Process has 1 (of 1 available) client(s) in use after notification apexservice has clients: 1"
+45028,1629848354315000000,4,"","c0  11732 AidlLazyServiceRegistrar: Process has 0 (of 1 available) client(s) in use after notification apexservice has clients: 0"
+45028,1629848354315000000,4,"","c0  11732 AidlLazyServiceRegistrar: Trying to shut down the service. No clients in use for any service in process."
+45028,1629848354314000000,4,"servicemanager","Notifying apexservice they have clients: 1"
+45028,1629848354314000000,4,"servicemanager","Notifying apexservice they have clients: 0"
+45028,1629848354273000000,3,"ProcessState","Binder ioctl to enable oneway spam detection failed: Invalid argument"
+45028,1629848354272000000,4,"","c2  11994 binder: 11994:11994 ioctl 40046210 7fde058b34 returned -22"
+45028,1629848354251000000,3,"dumpstate","Adjusting max progress from 9795 to 10791"
+45028,1629848354130000000,3,"ProcessState","Binder ioctl to enable oneway spam detection failed: Invalid argument"
+45028,1629848354130000000,4,"","c2  11947 binder: 11947:11947 ioctl 40046210 7fff3be7c4 returned -22"
+45028,1629848354106000000,3,"dumpstate","Duration of 'APP SERVICES NON-PLATFORM': 1.36s"
+45028,1629848353981000000,4,"putmethod.lati","Explicit concurrent copying GC freed 64704(3613KB) AllocSpace objects, 6(112KB) LOS objects, 67% free, 4033KB/11MB, paused 89us total 70.172ms"
+45028,1629848353798000000,4,"FallbackOnDeviceRecognizerModule","FallbackOnDeviceRecognizerModule.dump():36 dump()"
+45028,1629848353792000000,4,"Environment","Environment.isPackageInstalled():311 com.bitstrips.imoji is not installed"
+45028,1629848353675000000,5,"IorapForwardingService","No service published for: iorapd"
+45028,1629848353458000000,3,"ActivityThread","Loading provider com.google.android.gms.location.preferences: com.google.android.gms.location.preferences.LocationPreferencesContentProvider"
+45028,1629848353340000000,5,"GCM","GCM FAILED TO INITIALIZE - missing checkin"
+45028,1629848353339000000,6,"GCM","Missing checkin config file"
+45028,1629848353180000000,3,"CompatibilityChangeReporter","Compat change id reported: 132649864; UID 10130; state: DISABLED"
+45028,1629848353179000000,3,"CompatibilityChangeReporter","Compat change id reported: 149924527; UID 10130; state: ENABLED"
+45028,1629848352782000000,3,"ProcessState","Binder ioctl to enable oneway spam detection failed: Invalid argument"
+45028,1629848352782000000,4,"","c1  11872 binder: 11872:11872 ioctl 40046210 7ff18c3184 returned -22"
+45028,1629848352706000000,4,"BufferEulogizer","Not eulogizing buffers; they are 452734 hours old"
+45028,1629848352671000000,5,"IorapForwardingService","No service published for: iorapd"
+45028,1629848352661000000,5,"BroadcastQueue","Background execution not allowed: receiving Intent { act=android.intent.action.DROPBOX_ENTRY_ADDED flg=0x10 (has extras) } to com.google.android.gms/.stats.service.DropBoxEntryAddedReceiver"
+45028,1629848352661000000,5,"BroadcastQueue","Background execution not allowed: receiving Intent { act=android.intent.action.DROPBOX_ENTRY_ADDED flg=0x10 (has extras) } to com.google.android.gms/.chimera.GmsIntentOperationService$PersistentTrustedReceiver"
+45028,1629848352512000000,3,"TelephonyProvider","Using old permission behavior for telephony provider compat"
+45028,1629848352470000000,3,"TelephonyProvider","Using old permission behavior for telephony provider compat"
+45028,1629848352462000000,3,"TelephonyProvider","Using old permission behavior for telephony provider compat"
+45028,1629848352453000000,3,"TelephonyProvider","Using old permission behavior for telephony provider compat"
+45028,1629848352445000000,3,"TelephonyProvider","Using old permission behavior for telephony provider compat"
+45028,1629848352438000000,3,"TelephonyProvider","Using old permission behavior for telephony provider compat"
+45028,1629848352430000000,3,"TelephonyProvider","Using old permission behavior for telephony provider compat"
+45028,1629848352421000000,3,"TelephonyProvider","Using old permission behavior for telephony provider compat"
+45028,1629848352414000000,3,"TelephonyProvider","Using old permission behavior for telephony provider compat"
+45028,1629848352402000000,3,"TelephonyProvider","Using old permission behavior for telephony provider compat"
+45028,1629848352350000000,3,"ProcessState","Binder ioctl to enable oneway spam detection failed: Invalid argument"
+45028,1629848352350000000,4,"","c1  11839 binder: 11839:11839 ioctl 40046210 7feb290494 returned -22"
+45028,1629848352273000000,3,"ProcessState","Binder ioctl to enable oneway spam detection failed: Invalid argument"
+45028,1629848352272000000,4,"","c0  11835 binder: 11835:11835 ioctl 40046210 7ffa868704 returned -22"
+45028,1629848352178000000,3,"dumpstate","Adding dir /data/misc/bluetooth/logs (recursive: 1)"
+45028,1629848352025000000,3,"dumpstate","Duration of 'DUMPSYS': 8.76s"
+45028,1629848352014000000,3,"WificondScannerImpl","Latest native scan results nowMs = 411505"
+45028,1629848352010000000,3,"WifiScanningService","Latest scan results nowMs = 411501"
+45028,1629848351951000000,3,"PermissionCache","checking android.permission.DUMP for uid=2000 => granted (2356 us)"
+45028,1629848351814000000,4,"","c0    698 logd: logdr: UID=1000 GID=1000 PID=11826 n tail=127 logMask=80 pid=0 start=0ns deadline=0ns"
+45028,1629848351673000000,4,"","c1    698 logd: logdr: UID=1000 GID=1000 PID=11823 n tail=127 logMask=19 pid=0 start=0ns deadline=0ns"
+45028,1629848351670000000,5,"IorapForwardingService","No service published for: iorapd"
+45028,1629848351641000000,4,"","c1   2150 R0: [cds_mc_thread][8121993061] [22:39:11.641914]  wlan: [2150:I :WMI] Sent WMI_DEBUG_MESG_FLUSH_CMDID to FW"
+45028,1629848351640000000,4,"","c6   1060 R0: [wifi_ext@1.0-se][8121970301] [22:39:11.640728]  wlan: [1060:I :HDD] __wlan_hdd_cfg80211_wifi_logger_get_ring_data: 6571: Bug report triggered by framework"
+45028,1629848351640000000,4,"","c6   1060 R0: [wifi_ext@1.0-se][8121971425] [22:39:11.640787]  wlan: [1060:I :QDF] cds_flush_logs: Triggering bug report: type:0, indicator=1 reason_code=0"
+45028,1629848351640000000,4,"","c6   1060 R0: [wifi_ext@1.0-se][8121971808] [22:39:11.640807]  wlan: [1060:I :SYS] DPT: Total Records: 113, Head: 0, Tail: 112"
+45028,1629848351539000000,2,"UserManagerService","dumpPackageWhitelistProblems(): using mode ENFORCE|IMPLICIT_WHITELIST|IMPLICIT_WHITELIST_SYSTEM"
diff --git a/test/trace_processor/android/android_system_property_slice.out b/test/trace_processor/diff_tests/android/android_system_property_slice.out
similarity index 100%
rename from test/trace_processor/android/android_system_property_slice.out
rename to test/trace_processor/diff_tests/android/android_system_property_slice.out
diff --git a/test/trace_processor/android/game_intervention_list_test.out b/test/trace_processor/diff_tests/android/game_intervention_list_test.out
similarity index 100%
rename from test/trace_processor/android/game_intervention_list_test.out
rename to test/trace_processor/diff_tests/android/game_intervention_list_test.out
diff --git a/test/trace_processor/diff_tests/android/tests.py b/test/trace_processor/diff_tests/android/tests.py
new file mode 100644
index 0000000..e67a165
--- /dev/null
+++ b/test/trace_processor/diff_tests/android/tests.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class Android(TestSuite):
+
+  def test_android_system_property_counter(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          timestamp: 1000
+          android_system_property {
+            values {
+              name: "debug.tracing.screen_state"
+              value: "2"
+            }
+            values {
+              name: "debug.tracing.device_state"
+              value: "some_state_from_sysprops"
+            }
+          }
+        }
+        packet {
+          ftrace_events {
+            cpu: 1
+            event {
+              timestamp: 2000
+              pid: 1
+              print {
+                buf: "C|1000|ScreenState|1\n"
+              }
+            }
+            event {
+              timestamp: 3000
+              pid: 1
+              print {
+                buf: "N|1000|DeviceStateChanged|some_state_from_atrace\n"
+              }
+            }
+          }
+        }
+        """),
+        query="""
+        SELECT t.id, t.type, t.name, c.id, c.ts, c.type, c.value
+        FROM counter_track t JOIN counter c ON t.id = c.track_id
+        WHERE name = 'ScreenState';
+        """,
+        out=Csv("""
+        "id","type","name","id","ts","type","value"
+        0,"counter_track","ScreenState",0,1000,"counter",2.000000
+        0,"counter_track","ScreenState",1,2000,"counter",1.000000
+        """))
+
+  def test_android_system_property_slice(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          timestamp: 1000
+          android_system_property {
+            values {
+              name: "debug.tracing.screen_state"
+              value: "2"
+            }
+            values {
+              name: "debug.tracing.device_state"
+              value: "some_state_from_sysprops"
+            }
+          }
+        }
+        packet {
+          ftrace_events {
+            cpu: 1
+            event {
+              timestamp: 2000
+              pid: 1
+              print {
+                buf: "C|1000|ScreenState|1\n"
+              }
+            }
+            event {
+              timestamp: 3000
+              pid: 1
+              print {
+                buf: "N|1000|DeviceStateChanged|some_state_from_atrace\n"
+              }
+            }
+          }
+        }
+        """),
+        query="""
+        SELECT t.id, t.type, t.name, s.id, s.ts, s.dur, s.type, s.name
+        FROM track t JOIN slice s ON s.track_id = t.id
+        WHERE t.name = 'DeviceStateChanged';
+        """,
+        out=Path('android_system_property_slice.out'))
diff --git a/test/trace_processor/diff_tests/android/tests_bugreport.py b/test/trace_processor/diff_tests/android/tests_bugreport.py
new file mode 100644
index 0000000..790b529
--- /dev/null
+++ b/test/trace_processor/diff_tests/android/tests_bugreport.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class AndroidBugreport(TestSuite):
+
+  def test_android_bugreport_logs(self):
+    return DiffTestBlueprint(
+        trace=DataPath('bugreport-crosshatch-SPB5.zip'),
+        query="""
+        WITH
+        initial AS (SELECT
+            (SELECT count(*) FROM android_logs) AS cnt,
+            ts, prio, tag, msg FROM android_logs
+          ORDER BY ts ASC
+          LIMIT 100
+        ),
+        latest AS (SELECT
+            (SELECT count(*) FROM android_logs) AS cnt,
+            ts, prio, tag, msg FROM android_logs
+          ORDER BY ts DESC
+          LIMIT 100
+        )
+        SELECT * FROM initial UNION ALL SELECT * FROM latest;
+        """,
+        out=Path('android_bugreport_logs_test.out'))
+
+  def test_android_bugreport_dumpstate(self):
+    return DiffTestBlueprint(
+        trace=DataPath('bugreport-crosshatch-SPB5.zip'),
+        query="""
+        SELECT section, service, count(line) AS linecount FROM android_dumpstate
+        GROUP BY section, service;
+        """,
+        out=Path('android_bugreport_dumpstate_test.out'))
+
+  def test_android_bugreport_dumpsys(self):
+    return DiffTestBlueprint(
+        trace=DataPath('bugreport-crosshatch-SPB5.zip'),
+        query="""
+        SELECT section, service, line FROM android_dumpstate
+        WHERE service = 'color_display';
+        """,
+        out=Path('android_bugreport_dumpsys_test.out'))
diff --git a/test/trace_processor/diff_tests/android/tests_games.py b/test/trace_processor/diff_tests/android/tests_games.py
new file mode 100644
index 0000000..daa9a8d
--- /dev/null
+++ b/test/trace_processor/diff_tests/android/tests_games.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class AndroidGames(TestSuite):
+  # Ensure Android game intervention list are parsed correctly
+  def test_game_intervention_list(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+            android_game_intervention_list {
+              parse_error: false
+              read_error: false
+              game_packages {
+                name: "com.test.game1"
+                uid: 1001
+                current_mode: 1
+                game_mode_info {
+                  mode: 1
+                  use_angle: true
+                  resolution_downscale: 1.0
+                  fps: 0.0
+                }
+                game_mode_info {
+                  mode: 2
+                  use_angle: false
+                  resolution_downscale: 1.0
+                  fps: 60.0
+                }
+                game_mode_info {
+                  mode: 3
+                  use_angle: true
+                  resolution_downscale: 0.75
+                  fps: 120.0
+                }
+              }
+              game_packages {
+                name: "com.test.game2"
+                uid: 1002
+                current_mode: 3
+                game_mode_info {
+                  mode: 1
+                  use_angle: false
+                  resolution_downscale: 1.0
+                  fps: 0.0
+                }
+                game_mode_info {
+                  mode: 3
+                  use_angle: false
+                  resolution_downscale:  0.95
+                  fps: 45.0
+                }
+              }
+            }
+        }
+        """),
+        query="""
+        SELECT
+          package_name,
+          uid,
+          current_mode,
+          standard_mode_supported,
+          standard_mode_downscale,
+          standard_mode_use_angle,
+          standard_mode_fps,
+          perf_mode_supported,
+          perf_mode_downscale,
+          perf_mode_use_angle,
+          perf_mode_fps,
+          battery_mode_supported,
+          battery_mode_downscale,
+          battery_mode_use_angle,
+          battery_mode_fps
+        FROM android_game_intervention_list
+        ORDER BY package_name;
+        """,
+        out=Path('game_intervention_list_test.out'))
diff --git a/test/trace_processor/diff_tests/android/tests_general.py b/test/trace_processor/diff_tests/android/tests_general.py
new file mode 100644
index 0000000..fd555a5
--- /dev/null
+++ b/test/trace_processor/diff_tests/android/tests_general.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import DiffTestModule
+
+
+class AndroidGeneral(DiffTestModule):
+
+  def test_game_intervention_list(self):
+    return DiffTestBlueprint(
+        trace=Path('game_intervention_list_test.textproto'),
+        query="""
+SELECT
+  package_name,
+  uid,
+  current_mode,
+  standard_mode_supported,
+  standard_mode_downscale,
+  standard_mode_use_angle,
+  standard_mode_fps,
+  perf_mode_supported,
+  perf_mode_downscale,
+  perf_mode_use_angle,
+  perf_mode_fps,
+  battery_mode_supported,
+  battery_mode_downscale,
+  battery_mode_use_angle,
+  battery_mode_fps
+FROM android_game_intervention_list
+ORDER BY package_name;
+""",
+        out=Path('game_intervention_list_test.out'))
+
+  def test_android_system_property_counter(self):
+    return DiffTestBlueprint(
+        trace=Path('android_system_property.textproto'),
+        query="""
+SELECT t.id, t.type, t.name, c.id, c.ts, c.type, c.value
+FROM counter_track t JOIN counter c ON t.id = c.track_id
+WHERE name = 'ScreenState';
+""",
+        out=Csv("""
+"id","type","name","id","ts","type","value"
+0,"counter_track","ScreenState",0,1000,"counter",2.000000
+0,"counter_track","ScreenState",1,2000,"counter",1.000000
+"""))
+
+  def test_android_system_property_slice(self):
+    return DiffTestBlueprint(
+        trace=Path('android_system_property.textproto'),
+        query="""
+SELECT t.id, t.type, t.name, s.id, s.ts, s.dur, s.type, s.name
+FROM track t JOIN slice s ON s.track_id = t.id
+WHERE t.name = 'DeviceStateChanged';
+""",
+        out=Path('android_system_property_slice.out'))
diff --git a/test/trace_processor/atrace/android_b2b_async_begin.textproto b/test/trace_processor/diff_tests/atrace/android_b2b_async_begin.textproto
similarity index 100%
rename from test/trace_processor/atrace/android_b2b_async_begin.textproto
rename to test/trace_processor/diff_tests/atrace/android_b2b_async_begin.textproto
diff --git a/test/trace_processor/atrace/async_track_atrace.py b/test/trace_processor/diff_tests/atrace/async_track_atrace.py
similarity index 100%
rename from test/trace_processor/atrace/async_track_atrace.py
rename to test/trace_processor/diff_tests/atrace/async_track_atrace.py
diff --git a/test/trace_processor/atrace/bad_print.systrace b/test/trace_processor/diff_tests/atrace/bad_print.systrace
similarity index 100%
rename from test/trace_processor/atrace/bad_print.systrace
rename to test/trace_processor/diff_tests/atrace/bad_print.systrace
diff --git a/test/trace_processor/atrace/bad_print.textproto b/test/trace_processor/diff_tests/atrace/bad_print.textproto
similarity index 100%
rename from test/trace_processor/atrace/bad_print.textproto
rename to test/trace_processor/diff_tests/atrace/bad_print.textproto
diff --git a/test/trace_processor/atrace/instant_async_atrace.py b/test/trace_processor/diff_tests/atrace/instant_async_atrace.py
similarity index 100%
rename from test/trace_processor/atrace/instant_async_atrace.py
rename to test/trace_processor/diff_tests/atrace/instant_async_atrace.py
diff --git a/test/trace_processor/atrace/instant_atrace.py b/test/trace_processor/diff_tests/atrace/instant_atrace.py
similarity index 100%
rename from test/trace_processor/atrace/instant_atrace.py
rename to test/trace_processor/diff_tests/atrace/instant_atrace.py
diff --git a/test/trace_processor/atrace/process_track_slices_android_async_slice.out b/test/trace_processor/diff_tests/atrace/process_track_slices_android_async_slice.out
similarity index 100%
rename from test/trace_processor/atrace/process_track_slices_android_async_slice.out
rename to test/trace_processor/diff_tests/atrace/process_track_slices_android_async_slice.out
diff --git a/test/trace_processor/diff_tests/atrace/sys_write_and_atrace.py b/test/trace_processor/diff_tests/atrace/sys_write_and_atrace.py
new file mode 100644
index 0000000..7b96618
--- /dev/null
+++ b/test/trace_processor/diff_tests/atrace/sys_write_and_atrace.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python3
+# Copyright (C) 2022 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
+
+import synth_common
+
+trace = synth_common.create_trace()
+# sys_write id is 64 on arm64
+trace.add_system_info(arch="aarch64")
+
+trace.add_ftrace_packet(cpu=0)
+# expect: one normal sys_write slice
+trace.add_sys_enter(ts=100, tid=42, id=64)
+trace.add_sys_exit(ts=200, tid=42, id=64, ret=0)
+# expect: truncated sys_write slice due to nesting workarounds
+trace.add_sys_enter(ts=300, tid=42, id=64)
+trace.add_sys_exit(ts=400, tid=42, id=64, ret=0)
+# expect: truncated sys_write slice due to nesting workarounds
+trace.add_sys_enter(ts=600, tid=42, id=64)
+trace.add_sys_exit(ts=700, tid=42, id=64, ret=0)
+
+trace.add_atrace_begin(ts=350, tid=42, pid=42, buf='test')
+trace.add_atrace_end(ts=650, tid=42, pid=42)
+
+sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/diff_tests/atrace/tests.py b/test/trace_processor/diff_tests/atrace/tests.py
new file mode 100644
index 0000000..0f892c8
--- /dev/null
+++ b/test/trace_processor/diff_tests/atrace/tests.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class Atrace(TestSuite):
+  # Match legacy Catapult behaviour when we see multiple S events b2b with the
+  # cookie name and upid.
+  def test_android_b2b_async_begin_list_slices(self):
+    return DiffTestBlueprint(
+        trace=Path('android_b2b_async_begin.textproto'),
+        query="""
+        SELECT ts, dur, name
+        FROM slice;
+        """,
+        out=Csv("""
+        "ts","dur","name"
+        1000,30,"multistart"
+        1015,45,"multistart"
+        1030,20,"multistart"
+        """))
+
+  # Android userspace async slices
+  def test_process_track_slices_android_async_slice(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          ftrace_events {
+            cpu: 3
+            event {
+              timestamp: 74289018336
+              pid: 4064
+              print {
+                ip: 18446743562018522420
+                buf: "S|1204|launching: com.android.chrome|0\n"
+              }
+            }
+          }
+        }
+        packet {
+          ftrace_events {
+            cpu: 2
+            event {
+              timestamp: 74662603008
+              pid: 1257
+              print {
+                ip: 18446743562018522420
+                buf: "F|1204|launching: com.android.chrome|0\n"
+              }
+            }
+          }
+        }
+        """),
+        query="""
+        SELECT
+          ts,
+          dur,
+          pid,
+          slice.name AS slice_name,
+          process_track.name AS track_name
+        FROM slice
+        JOIN process_track ON slice.track_id = process_track.id
+        JOIN process USING (upid);
+        """,
+        out=Path('process_track_slices_android_async_slice.out'))
+
+  def test_async_track_atrace_process_track_slices(self):
+    return DiffTestBlueprint(
+        trace=Path('async_track_atrace.py'),
+        query="""
+        SELECT
+          ts,
+          dur,
+          pid,
+          slice.name AS slice_name,
+          process_track.name AS track_name
+        FROM slice
+        JOIN process_track ON slice.track_id = process_track.id
+        JOIN process USING (upid);
+        """,
+        out=Csv("""
+        "ts","dur","pid","slice_name","track_name"
+        50,25,1,"ev","track"
+        55,15,1,"ev","track"
+        60,5,2,"ev","track"
+        """))
+
+  # Resolving slice nesting issues when tracing both atrace and sys_write
+  def test_sys_write_and_atrace(self):
+    return DiffTestBlueprint(
+        trace=Path('sys_write_and_atrace.py'),
+        query="""
+        SELECT slice.ts, slice.dur, slice.name, slice.depth
+        FROM slice
+        JOIN thread_track ON (slice.track_id = thread_track.id)
+        JOIN thread USING (utid)
+        WHERE tid = 42;
+        """,
+        out=Csv("""
+        "ts","dur","name","depth"
+        100,100,"sys_write",0
+        300,50,"sys_write",0
+        350,300,"test",0
+        600,50,"sys_write",1
+        """))
diff --git a/test/trace_processor/diff_tests/atrace/tests_error_handling.py b/test/trace_processor/diff_tests/atrace/tests_error_handling.py
new file mode 100644
index 0000000..3b215bf
--- /dev/null
+++ b/test/trace_processor/diff_tests/atrace/tests_error_handling.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class AtraceErrorHandling(TestSuite):
+  # Check error handling when parsing print events.
+  def test_bad_print_textproto_list_slices(self):
+    return DiffTestBlueprint(
+        trace=Path('bad_print.textproto'),
+        query="""
+        SELECT ts, dur, name
+        FROM slice;
+        """,
+        out=Csv("""
+        "ts","dur","name"
+        74662603048,2,"valid_print"
+        """))
+
+  def test_bad_print_systrace_list_slices(self):
+    return DiffTestBlueprint(
+        trace=Path('bad_print.systrace'),
+        query="""
+        SELECT ts, dur, name
+        FROM slice;
+        """,
+        out=Csv("""
+        "ts","dur","name"
+        10852771242000,3000,"some event"
+        """))
+
+  def test_instant_atrace_instant_with_thread(self):
+    return DiffTestBlueprint(
+        trace=Path('instant_atrace.py'),
+        query="""
+        SELECT 
+            thread.name AS thread_name, 
+            instant.name AS track_name, 
+            instant.ts
+        FROM slice instant
+        JOIN thread_track ON instant.track_id = thread_track.id
+        JOIN thread USING (utid)
+        WHERE dur = 0;
+        """,
+        out=Csv("""
+        "thread_name","track_name","ts"
+        "t2","t2_event",51
+        "t1","t1_event",53
+        """))
+
+  def test_instant_async_atrace_instant_async(self):
+    return DiffTestBlueprint(
+        trace=Path('instant_async_atrace.py'),
+        query="""
+        SELECT
+          process.name AS process_name,
+          process_track.name AS track_name,
+          instant.name AS instant_name,
+          ts
+        FROM slice instant
+        JOIN process_track ON instant.track_id = process_track.id
+        JOIN process USING (upid)
+        WHERE dur = 0;
+        """,
+        out=Csv("""
+        "process_name","track_name","instant_name","ts"
+        "p2","track_p2","ev1",51
+        "p1","track_p1","ev2",53
+        """))
diff --git a/test/trace_processor/diff_tests/atrace/tests_general.py b/test/trace_processor/diff_tests/atrace/tests_general.py
new file mode 100644
index 0000000..59417c0
--- /dev/null
+++ b/test/trace_processor/diff_tests/atrace/tests_general.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import DiffTestModule
+
+
+class AtraceGeneral(DiffTestModule):
+
+  def test_android_b2b_async_begin_list_slices(self):
+    return DiffTestBlueprint(
+        trace=Path('android_b2b_async_begin.textproto'),
+        query="""
+SELECT ts, dur, name
+FROM slice;
+""",
+        out=Csv("""
+"ts","dur","name"
+1000,30,"multistart"
+1015,45,"multistart"
+1030,20,"multistart"
+"""))
+
+  def test_process_track_slices_android_async_slice(self):
+    return DiffTestBlueprint(
+        trace=Path('android_async_slice.textproto'),
+        query="""
+SELECT
+  ts,
+  dur,
+  pid,
+  slice.name AS slice_name,
+  process_track.name AS track_name
+FROM slice
+JOIN process_track ON slice.track_id = process_track.id
+JOIN process USING (upid);
+""",
+        out=Path('process_track_slices_android_async_slice.out'))
+
+  def test_async_track_atrace_process_track_slices(self):
+    return DiffTestBlueprint(
+        trace=Path('async_track_atrace.py'),
+        query="""
+SELECT
+  ts,
+  dur,
+  pid,
+  slice.name AS slice_name,
+  process_track.name AS track_name
+FROM slice
+JOIN process_track ON slice.track_id = process_track.id
+JOIN process USING (upid);
+""",
+        out=Csv("""
+"ts","dur","pid","slice_name","track_name"
+50,25,1,"ev","track"
+55,15,1,"ev","track"
+60,5,2,"ev","track"
+"""))
+
+  def test_sys_write_and_atrace(self):
+    return DiffTestBlueprint(
+        trace=Path('sys_write_and_atrace.py'),
+        query="""
+SELECT slice.ts, slice.dur, slice.name, slice.depth
+FROM slice
+JOIN thread_track ON (slice.track_id = thread_track.id)
+JOIN thread USING (utid)
+WHERE tid = 42;
+""",
+        out=Csv("""
+"ts","dur","name","depth"
+100,100,"sys_write",0
+300,50,"sys_write",0
+350,300,"test",0
+600,50,"sys_write",1
+"""))
diff --git a/test/trace_processor/camera/camera-ion-mem-trace_android_camera_unagg.out b/test/trace_processor/diff_tests/camera/camera-ion-mem-trace_android_camera_unagg.out
similarity index 100%
rename from test/trace_processor/camera/camera-ion-mem-trace_android_camera_unagg.out
rename to test/trace_processor/diff_tests/camera/camera-ion-mem-trace_android_camera_unagg.out
diff --git a/test/trace_processor/diff_tests/camera/tests.py b/test/trace_processor/diff_tests/camera/tests.py
new file mode 100644
index 0000000..f08e10a
--- /dev/null
+++ b/test/trace_processor/diff_tests/camera/tests.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class Camera(TestSuite):
+
+  def test_camera_ion_mem_trace_android_camera(self):
+    return DiffTestBlueprint(
+        trace=DataPath('camera-ion-mem-trace'),
+        query=Metric('android_camera'),
+        out=TextProto(r"""
+        android_camera {
+          gc_rss_and_dma {
+            min: 47779840.0
+            max: 2529583104.0
+            avg: 1459479416.3297353
+          }
+        }
+        """))
+
+  def test_camera_ion_mem_trace_android_camera_unagg(self):
+    return DiffTestBlueprint(
+        trace=DataPath('camera-ion-mem-trace'),
+        query=Metric('android_camera_unagg'),
+        out=Path('camera-ion-mem-trace_android_camera_unagg.out'))
diff --git a/test/trace_processor/diff_tests/camera/tests_general.py b/test/trace_processor/diff_tests/camera/tests_general.py
new file mode 100644
index 0000000..7277796
--- /dev/null
+++ b/test/trace_processor/diff_tests/camera/tests_general.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import DiffTestModule
+
+
+class CameraGeneral(DiffTestModule):
+
+  def test_camera_ion_mem_trace_android_camera(self):
+    return DiffTestBlueprint(
+        trace=DataPath('camera-ion-mem-trace'),
+        query=Metric('android_camera'),
+        out=TextProto(r"""
+android_camera {
+  gc_rss_and_dma {
+    min: 47779840.0
+    max: 2529583104.0
+    avg: 1459479416.3297353
+  }
+}
+"""))
+
+  def test_camera_ion_mem_trace_android_camera_unagg(self):
+    return DiffTestBlueprint(
+        trace=DataPath('camera-ion-mem-trace'),
+        query=Metric('android_camera_unagg'),
+        out=Path('camera-ion-mem-trace_android_camera_unagg.out'))
diff --git a/test/trace_processor/chrome/actual_power_by_combined_rail_mode.py b/test/trace_processor/diff_tests/chrome/actual_power_by_combined_rail_mode.py
similarity index 100%
rename from test/trace_processor/chrome/actual_power_by_combined_rail_mode.py
rename to test/trace_processor/diff_tests/chrome/actual_power_by_combined_rail_mode.py
diff --git a/test/trace_processor/diff_tests/chrome/chrome_dropped_frames_metric_test.sql b/test/trace_processor/diff_tests/chrome/chrome_dropped_frames_metric_test.sql
new file mode 100644
index 0000000..550a34f
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/chrome_dropped_frames_metric_test.sql
@@ -0,0 +1,18 @@
+--
+-- Copyright 2021 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('experimental/chrome_dropped_frames.sql');
+
+SELECT * FROM dropped_frames_with_process_info;
diff --git a/test/trace_processor/chrome/chrome_input_to_browser_intervals.out b/test/trace_processor/diff_tests/chrome/chrome_input_to_browser_intervals.out
similarity index 100%
rename from test/trace_processor/chrome/chrome_input_to_browser_intervals.out
rename to test/trace_processor/diff_tests/chrome/chrome_input_to_browser_intervals.out
diff --git a/test/trace_processor/diff_tests/chrome/chrome_log_message_args_test.sql b/test/trace_processor/diff_tests/chrome/chrome_log_message_args_test.sql
new file mode 100644
index 0000000..371b95d
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/chrome_log_message_args_test.sql
@@ -0,0 +1,22 @@
+--
+-- Copyright 2022 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
+  EXTRACT_ARG(s.arg_set_id, 'track_event.log_message') AS log_message,
+  EXTRACT_ARG(s.arg_set_id, 'track_event.log_message.function_name') AS function_name,
+  EXTRACT_ARG(s.arg_set_id, 'track_event.log_message.file_name') AS file_name,
+  EXTRACT_ARG(s.arg_set_id, 'track_event.log_message.line_number') AS line_number
+FROM
+  slice s;
diff --git a/test/trace_processor/diff_tests/chrome/chrome_long_tasks_delaying_input_processing_compare_default_test.sql b/test/trace_processor/diff_tests/chrome/chrome_long_tasks_delaying_input_processing_compare_default_test.sql
new file mode 100644
index 0000000..fa25165
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/chrome_long_tasks_delaying_input_processing_compare_default_test.sql
@@ -0,0 +1,32 @@
+--
+-- Copyright 2022 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.
+
+-- This test runs the on a trace that includes both LongTask tracking traces
+-- and top-level traces. This test verifies that the default input processing
+-- delay metric can be calculated while both scenarios are enabled. The output
+-- should be consistent (same tasks) as the test for the LongTask version of the
+-- metric - chrome_long_tasks_delaying_input_processing_test.sql
+
+SELECT RUN_METRIC(
+  'chrome/chrome_tasks_delaying_input_processing.sql',
+  'duration_causing_jank_ms', '4'
+);
+
+SELECT
+  full_name,
+  duration_ms,
+  slice_id
+FROM chrome_tasks_delaying_input_processing
+ORDER BY slice_id;
\ No newline at end of file
diff --git a/test/trace_processor/chrome/chrome_processes_android_systrace.out b/test/trace_processor/diff_tests/chrome/chrome_processes_android_systrace.out
similarity index 100%
rename from test/trace_processor/chrome/chrome_processes_android_systrace.out
rename to test/trace_processor/diff_tests/chrome/chrome_processes_android_systrace.out
diff --git a/test/trace_processor/chrome/chrome_processes_type_android_systrace.out b/test/trace_processor/diff_tests/chrome/chrome_processes_type_android_systrace.out
similarity index 100%
rename from test/trace_processor/chrome/chrome_processes_type_android_systrace.out
rename to test/trace_processor/diff_tests/chrome/chrome_processes_type_android_systrace.out
diff --git a/test/trace_processor/diff_tests/chrome/chrome_reliable_range.textproto b/test/trace_processor/diff_tests/chrome/chrome_reliable_range.textproto
new file mode 100644
index 0000000..d1115cf
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/chrome_reliable_range.textproto
@@ -0,0 +1,106 @@
+# We have 3 threads, with 1 event each. The start time of the events are 11,
+# 12, and 13. The third thread has first_packet_on_sequence, so it should be
+# ignored for the reliable range computation.
+
+# Track descriptors for each thread.
+packet {
+  timestamp: 1
+  incremental_state_cleared: true
+  trusted_packet_sequence_id: 1
+  track_descriptor {
+    uuid: 1
+    thread {
+      pid: 1
+      tid: 1
+    }
+    parent_uuid: 0
+  }
+}
+packet {
+  timestamp: 2
+  incremental_state_cleared: true
+  trusted_packet_sequence_id: 2
+  track_descriptor {
+    uuid: 2
+    thread {
+      pid: 2
+      tid: 2
+    }
+    parent_uuid: 0
+  }
+}
+packet {
+  timestamp: 3
+  incremental_state_cleared: true
+  first_packet_on_sequence: true
+  trusted_packet_sequence_id: 3
+  track_descriptor {
+    uuid: 3
+    thread {
+      pid: 3
+      tid: 3
+    }
+    parent_uuid: 0
+  }
+}
+
+# Slice begin events
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 11
+  track_event {
+    track_uuid: 1
+    categories: "cat"
+    type: 1
+    name: "slice1"
+  }
+}
+packet {
+  trusted_packet_sequence_id: 2
+  timestamp: 12
+  track_event {
+    track_uuid: 2
+    categories: "cat"
+    type: 1
+    name: "slice1"
+  }
+}
+packet {
+  trusted_packet_sequence_id: 3
+  timestamp: 13
+  track_event {
+    track_uuid: 3
+    categories: "cat"
+    type: 1
+    name: "slice3"
+  }
+}
+
+# Slice end events
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 1011
+  track_event {
+    track_uuid: 1
+    categories: "cat"
+    type: 2
+  }
+}
+packet {
+  trusted_packet_sequence_id: 2
+  timestamp: 1012
+  track_event {
+    track_uuid: 2
+    categories: "cat"
+    type: 2
+  }
+}
+packet {
+  trusted_packet_sequence_id: 3
+  timestamp: 1013
+  track_event {
+    track_uuid: 3
+    categories: "cat"
+    type: 2
+  }
+}
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/chrome/chrome_reliable_range_cropping.textproto b/test/trace_processor/diff_tests/chrome/chrome_reliable_range_cropping.textproto
new file mode 100644
index 0000000..f02a3e6
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/chrome_reliable_range_cropping.textproto
@@ -0,0 +1,113 @@
+# This test is similar to chrome_reliable_range.textproto, but it has a cropping
+# packet, which takes precedence.
+
+packet {
+  timestamp: 0
+  trusted_packet_sequence_id: 1
+  incremental_state_cleared: true
+  track_event_range_of_interest {
+    start_us: 10
+  }
+}
+
+# Track descriptors for each thread.
+packet {
+  timestamp: 1
+  trusted_packet_sequence_id: 1
+  track_descriptor {
+    uuid: 1
+    thread {
+      pid: 1
+      tid: 1
+    }
+    parent_uuid: 0
+  }
+}
+packet {
+  timestamp: 2
+  incremental_state_cleared: true
+  trusted_packet_sequence_id: 2
+  track_descriptor {
+    uuid: 2
+    thread {
+      pid: 2
+      tid: 2
+    }
+    parent_uuid: 0
+  }
+}
+packet {
+  timestamp: 3
+  incremental_state_cleared: true
+  first_packet_on_sequence: true
+  trusted_packet_sequence_id: 3
+  track_descriptor {
+    uuid: 3
+    thread {
+      pid: 3
+      tid: 3
+    }
+    parent_uuid: 0
+  }
+}
+
+# Slice begin events
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 11000
+  track_event {
+    track_uuid: 1
+    categories: "cat"
+    type: 1
+    name: "slice1"
+  }
+}
+packet {
+  trusted_packet_sequence_id: 2
+  timestamp: 12000
+  track_event {
+    track_uuid: 2
+    categories: "cat"
+    type: 1
+    name: "slice1"
+  }
+}
+packet {
+  trusted_packet_sequence_id: 3
+  timestamp: 13000
+  track_event {
+    track_uuid: 3
+    categories: "cat"
+    type: 1
+    name: "slice3"
+  }
+}
+
+# Slice end events
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 1011000
+  track_event {
+    track_uuid: 1
+    categories: "cat"
+    type: 2
+  }
+}
+packet {
+  trusted_packet_sequence_id: 2
+  timestamp: 1012000
+  track_event {
+    track_uuid: 2
+    categories: "cat"
+    type: 2
+  }
+}
+packet {
+  trusted_packet_sequence_id: 3
+  timestamp: 1013000
+  track_event {
+    track_uuid: 3
+    categories: "cat"
+    type: 2
+  }
+}
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/chrome/chrome_reliable_range_missing_browser_main.out b/test/trace_processor/diff_tests/chrome/chrome_reliable_range_missing_browser_main.out
new file mode 100644
index 0000000..5fa75a7
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/chrome_reliable_range_missing_browser_main.out
@@ -0,0 +1,3 @@
+
+"start","reason","debug_limiting_upid","debug_limiting_utid"
+1011,"Missing main thread for upid=1",1,1
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/chrome/chrome_reliable_range_missing_browser_main.textproto b/test/trace_processor/diff_tests/chrome/chrome_reliable_range_missing_browser_main.textproto
new file mode 100644
index 0000000..b7dac0b
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/chrome_reliable_range_missing_browser_main.textproto
@@ -0,0 +1,50 @@
+# There is a Browser process, but there's no Browser main thread.
+# The entire trace is unreliable.
+
+packet {
+  timestamp: 1
+  incremental_state_cleared: true
+  trusted_packet_sequence_id: 1
+  track_descriptor {
+    uuid: 1
+    thread {
+      pid: 1
+      tid: 1
+    }
+    parent_uuid: 0
+  }
+}
+packet {
+    timestamp: 2
+    track_descriptor {
+        uuid: 2
+        process {
+            pid: 1
+        }
+        chrome_process {
+            process_type: PROCESS_BROWSER
+        }
+    }
+}
+
+# Slice begin event
+packet {
+  trusted_packet_sequence_id: 2
+  timestamp: 11
+  track_event {
+    track_uuid: 1
+    categories: "cat"
+    type: 1
+    name: "slice1"
+  }
+}
+# Slice end event
+packet {
+  trusted_packet_sequence_id: 2
+  timestamp: 1011
+  track_event {
+    track_uuid: 1
+    categories: "cat"
+    type: 2
+  }
+}
diff --git a/test/trace_processor/diff_tests/chrome/chrome_reliable_range_missing_gpu_main.textproto b/test/trace_processor/diff_tests/chrome/chrome_reliable_range_missing_gpu_main.textproto
new file mode 100644
index 0000000..4465a30
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/chrome_reliable_range_missing_gpu_main.textproto
@@ -0,0 +1,50 @@
+# There is a Gpu process, but there's no Gpu main thread.
+# The entire trace is unreliable.
+
+packet {
+  timestamp: 1
+  incremental_state_cleared: true
+  trusted_packet_sequence_id: 1
+  track_descriptor {
+    uuid: 1
+    thread {
+      pid: 1
+      tid: 1
+    }
+    parent_uuid: 0
+  }
+}
+packet {
+    timestamp: 2
+    track_descriptor {
+        uuid: 2
+        process {
+            pid: 1
+        }
+        chrome_process {
+            process_type: PROCESS_GPU
+        }
+    }
+}
+
+# Slice begin event
+packet {
+  trusted_packet_sequence_id: 2
+  timestamp: 11
+  track_event {
+    track_uuid: 1
+    categories: "cat"
+    type: 1
+    name: "slice1"
+  }
+}
+# Slice end event
+packet {
+  trusted_packet_sequence_id: 2
+  timestamp: 1011
+  track_event {
+    track_uuid: 1
+    categories: "cat"
+    type: 2
+  }
+}
diff --git a/test/trace_processor/diff_tests/chrome/chrome_reliable_range_missing_processes.textproto b/test/trace_processor/diff_tests/chrome/chrome_reliable_range_missing_processes.textproto
new file mode 100644
index 0000000..ffc093c
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/chrome_reliable_range_missing_processes.textproto
@@ -0,0 +1,53 @@
+packet {
+  timestamp: 0
+  incremental_state_cleared: true
+  trusted_packet_sequence_id: 1
+  track_event {
+    type: TYPE_INSTANT
+    name: "ActiveProcesses"
+    [perfetto.protos.ChromeTrackEvent.active_processes]: {
+      pid: 1
+      # Process 2 is marked as active, but its process descriptors are not
+      # present. Therefore, there is data loss until the end of the trace and
+      # the reliable range starts at the last packet's timestamp.
+      pid: 2
+    }
+  }
+}
+packet {
+  timestamp: 1
+  trusted_packet_sequence_id: 2
+  track_descriptor {
+    uuid: 1
+    thread {
+      pid: 1
+      tid: 1
+    }
+    process {
+        pid: 1
+    }
+    parent_uuid: 0
+  }
+}
+
+# Slice begin event
+packet {
+  trusted_packet_sequence_id: 2
+  timestamp: 11
+  track_event {
+    track_uuid: 1
+    categories: "cat"
+    type: 1
+    name: "slice1"
+  }
+}
+# Slice end event
+packet {
+  trusted_packet_sequence_id: 2
+  timestamp: 1011
+  track_event {
+    track_uuid: 1
+    categories: "cat"
+    type: 2
+  }
+}
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/chrome/chrome_reliable_range_missing_renderer_main.out b/test/trace_processor/diff_tests/chrome/chrome_reliable_range_missing_renderer_main.out
new file mode 100644
index 0000000..5fa75a7
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/chrome_reliable_range_missing_renderer_main.out
@@ -0,0 +1,3 @@
+
+"start","reason","debug_limiting_upid","debug_limiting_utid"
+1011,"Missing main thread for upid=1",1,1
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/chrome/chrome_reliable_range_missing_renderer_main.textproto b/test/trace_processor/diff_tests/chrome/chrome_reliable_range_missing_renderer_main.textproto
new file mode 100644
index 0000000..42cb741
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/chrome_reliable_range_missing_renderer_main.textproto
@@ -0,0 +1,50 @@
+# There is a Renderer process, but there's no Renderer main thread.
+# The entire trace is unreliable.
+
+packet {
+  timestamp: 1
+  incremental_state_cleared: true
+  trusted_packet_sequence_id: 1
+  track_descriptor {
+    uuid: 1
+    thread {
+      pid: 1
+      tid: 1
+    }
+    parent_uuid: 0
+  }
+}
+packet {
+    timestamp: 2
+    track_descriptor {
+        uuid: 2
+        process {
+            pid: 1
+        }
+        chrome_process {
+            process_type: PROCESS_RENDERER
+        }
+    }
+}
+
+# Slice begin event
+packet {
+  trusted_packet_sequence_id: 2
+  timestamp: 11
+  track_event {
+    track_uuid: 1
+    categories: "cat"
+    type: 1
+    name: "slice1"
+  }
+}
+# Slice end event
+packet {
+  trusted_packet_sequence_id: 2
+  timestamp: 1011
+  track_event {
+    track_uuid: 1
+    categories: "cat"
+    type: 2
+  }
+}
diff --git a/test/trace_processor/diff_tests/chrome/chrome_reliable_range_non_chrome_process.out b/test/trace_processor/diff_tests/chrome/chrome_reliable_range_non_chrome_process.out
new file mode 100644
index 0000000..f184dea
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/chrome_reliable_range_non_chrome_process.out
@@ -0,0 +1,3 @@
+
+"start","reason","debug_limiting_upid","debug_limiting_utid"
+0,"[NULL]","[NULL]","[NULL]"
diff --git a/test/trace_processor/diff_tests/chrome/chrome_reliable_range_test.sql b/test/trace_processor/diff_tests/chrome/chrome_reliable_range_test.sql
new file mode 100644
index 0000000..22e475c
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/chrome_reliable_range_test.sql
@@ -0,0 +1,15 @@
+-- Copyright 2022 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('chrome/chrome_reliable_range.sql');
+SELECT start, reason, debug_limiting_upid, debug_limiting_utid FROM chrome_reliable_range;
diff --git a/test/trace_processor/diff_tests/chrome/chrome_scroll_jank_caused_by_scheduling_test.out b/test/trace_processor/diff_tests/chrome/chrome_scroll_jank_caused_by_scheduling_test.out
new file mode 100644
index 0000000..968ff01
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/chrome_scroll_jank_caused_by_scheduling_test.out
@@ -0,0 +1,3 @@
+
+"full_name","total_duration_ms","total_thread_duration_ms","count","window_start_ts","window_end_ts","scroll_type"
+"viz.mojom.CompositorFrameSinkClient message (hash=50871626),viz.mojom.CompositorFrameSinkClient message (hash=3114070324),SingleThreadProxy::BeginMainFrame(java_views=ToolbarLayout),blink.mojom.WidgetInputHandler reply (hash=3392143105),cc.mojom.RenderFrameMetadataObserverClient message (hash=330497194),RunTask(posted_from=cc/scheduler/scheduler.cc:ScheduleBeginImplFrameDeadline),RunTask(posted_from=cc/scheduler/scheduler.cc:PostPendingBeginFrameTask),viz.mojom.FrameSinkManagerClient message (hash=532012934)",7.568000,6.745000,11,666960999011,666972176011,"regular"
diff --git a/test/trace_processor/chrome/chrome_stack_samples_for_task_test.out b/test/trace_processor/diff_tests/chrome/chrome_stack_samples_for_task_test.out
similarity index 100%
rename from test/trace_processor/chrome/chrome_stack_samples_for_task_test.out
rename to test/trace_processor/diff_tests/chrome/chrome_stack_samples_for_task_test.out
diff --git a/test/trace_processor/chrome/chrome_tasks.out b/test/trace_processor/diff_tests/chrome/chrome_tasks.out
similarity index 100%
rename from test/trace_processor/chrome/chrome_tasks.out
rename to test/trace_processor/diff_tests/chrome/chrome_tasks.out
diff --git a/test/trace_processor/diff_tests/chrome/chrome_tasks_delaying_input_processing_test.out b/test/trace_processor/diff_tests/chrome/chrome_tasks_delaying_input_processing_test.out
new file mode 100644
index 0000000..9edba72
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/chrome_tasks_delaying_input_processing_test.out
@@ -0,0 +1,5 @@
+
+"full_name","duration_ms","thread_dur_ms"
+"FrameHost::BeginNavigation (unknown frame type)",16.111000,10.451000
+"Looper.dispatch: android.net.ConnectivityManager$CallbackHandler(null)",10.507000,0.878000
+"blink.mojom.PresentationService message (hash=3202951471)",22.524000,9.992000
diff --git a/test/trace_processor/chrome/chrome_threads.out b/test/trace_processor/diff_tests/chrome/chrome_threads.out
similarity index 100%
rename from test/trace_processor/chrome/chrome_threads.out
rename to test/trace_processor/diff_tests/chrome/chrome_threads.out
diff --git a/test/trace_processor/chrome/chrome_threads_android_systrace.out b/test/trace_processor/diff_tests/chrome/chrome_threads_android_systrace.out
similarity index 100%
rename from test/trace_processor/chrome/chrome_threads_android_systrace.out
rename to test/trace_processor/diff_tests/chrome/chrome_threads_android_systrace.out
diff --git a/test/trace_processor/chrome/combined_rail_modes.py b/test/trace_processor/diff_tests/chrome/combined_rail_modes.py
similarity index 100%
rename from test/trace_processor/chrome/combined_rail_modes.py
rename to test/trace_processor/diff_tests/chrome/combined_rail_modes.py
diff --git a/test/trace_processor/chrome/cpu_time_by_combined_rail_mode.py b/test/trace_processor/diff_tests/chrome/cpu_time_by_combined_rail_mode.py
similarity index 100%
rename from test/trace_processor/chrome/cpu_time_by_combined_rail_mode.py
rename to test/trace_processor/diff_tests/chrome/cpu_time_by_combined_rail_mode.py
diff --git a/test/trace_processor/chrome/estimated_power_by_combined_rail_mode.py b/test/trace_processor/diff_tests/chrome/estimated_power_by_combined_rail_mode.py
similarity index 100%
rename from test/trace_processor/chrome/estimated_power_by_combined_rail_mode.py
rename to test/trace_processor/diff_tests/chrome/estimated_power_by_combined_rail_mode.py
diff --git a/test/trace_processor/diff_tests/chrome/event_latency_scroll_jank.out b/test/trace_processor/diff_tests/chrome/event_latency_scroll_jank.out
new file mode 100644
index 0000000..effa4b0
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/event_latency_scroll_jank.out
@@ -0,0 +1,12 @@
+
+"jank","next_jank","prev_jank","gesture_begin_ts","gesture_end_ts","ts","dur","event_type","next_ts","next_dur","prev_ts","prev_dur"
+1,0,1,3107579952792897,3107581649376897,3107580143766897,35454000,"INERTIAL_GESTURE_SCROLL_UPDATE",3107580152108897,35464000,3107580135417897,27100000
+1,1,0,3107578477792897,3107579903554897,3107578535792897,39440000,"GESTURE_SCROLL_UPDATE",3107578556443897,27153000,3107578527792897,39081000
+1,0,1,3107578477792897,3107579903554897,3107579191373897,35485000,"INERTIAL_GESTURE_SCROLL_UPDATE",3107579199700897,35521000,3107579183046897,27103000
+1,0,1,3107578477792897,3107579903554897,3107579600751897,43838000,"INERTIAL_GESTURE_SCROLL_UPDATE",3107579609175897,43757000,3107579592394897,35480000
+0,0,0,3107579952792897,3107581649376897,3107579952792897,25970000,"FIRST_GESTURE_SCROLL_UPDATE",3107579956792897,30333000,"[NULL]","[NULL]"
+0,0,0,3107579952792897,3107581649376897,3107579956792897,30333000,"GESTURE_SCROLL_UPDATE",3107579968792897,26657000,3107579952792897,25970000
+0,0,0,3107579952792897,3107581649376897,3107579968792897,26657000,"GESTURE_SCROLL_UPDATE",3107579976792897,27003000,3107579956792897,30333000
+0,0,0,3107579952792897,3107581649376897,3107579976792897,27003000,"GESTURE_SCROLL_UPDATE",3107579985792897,26361000,3107579968792897,26657000
+0,0,0,3107579952792897,3107581649376897,3107579985792897,26361000,"GESTURE_SCROLL_UPDATE",3107579993792897,26709000,3107579976792897,27003000
+0,0,0,3107579952792897,3107581649376897,3107579993792897,26709000,"GESTURE_SCROLL_UPDATE",3107579998792897,30183000,3107579985792897,26361000
diff --git a/test/trace_processor/diff_tests/chrome/event_latency_scroll_jank_cause.out b/test/trace_processor/diff_tests/chrome/event_latency_scroll_jank_cause.out
new file mode 100644
index 0000000..6fc5c2f
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/event_latency_scroll_jank_cause.out
@@ -0,0 +1,6 @@
+
+"dur","ts","event_type","next_jank","prev_jank","next_delta_dur_ns","prev_delta_dur_ns","cause_of_jank","max_delta_dur_ns","sub_cause_of_jank"
+39440000,3107578535792897,"GESTURE_SCROLL_UPDATE",1,0,9233000,"[NULL]","SubmitCompositorFrameToPresentationCompositorFrame",9233000,"BufferReadyToLatch"
+35485000,3107579191373897,"INERTIAL_GESTURE_SCROLL_UPDATE",0,1,"[NULL]",7445000,"SubmitCompositorFrameToPresentationCompositorFrame",7445000,"BufferReadyToLatch"
+43838000,3107579600751897,"INERTIAL_GESTURE_SCROLL_UPDATE",0,1,"[NULL]",8059000,"SubmitCompositorFrameToPresentationCompositorFrame",8059000,"BufferReadyToLatch"
+35454000,3107580143766897,"INERTIAL_GESTURE_SCROLL_UPDATE",0,1,"[NULL]",7219000,"SubmitCompositorFrameToPresentationCompositorFrame",7219000,"StartDrawToSwapStart"
diff --git a/test/trace_processor/diff_tests/chrome/event_latency_to_breakdowns.out b/test/trace_processor/diff_tests/chrome/event_latency_to_breakdowns.out
new file mode 100644
index 0000000..099e02b
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/event_latency_to_breakdowns.out
@@ -0,0 +1,32 @@
+
+"event_latency_ts","event_latency_dur","event_type","GenerationToRendererCompositorNs","GenerationToBrowserMainNs","BrowserMainToRendererCompositorNs","RendererCompositorQueueingDelayNs","unknown_stages_seen"
+3107578415792897,17551000,"TOUCH_PRESSED",7945000,"[NULL]","[NULL]",108000,"[NULL]"
+3107578415792897,20411000,"GESTURE_TAP_DOWN",9648000,"[NULL]","[NULL]",33000,"[NULL]"
+3107578418792897,6034000,"TOUCH_MOVED",5803000,"[NULL]","[NULL]",34000,"[NULL]"
+3107578423792897,6439000,"TOUCH_MOVED",6192000,"[NULL]","[NULL]",82000,"[NULL]"
+3107578427792897,4318000,"TOUCH_MOVED",4189000,"[NULL]","[NULL]",34000,"[NULL]"
+3107578431792897,5754000,"TOUCH_MOVED",5407000,"[NULL]","[NULL]",87000,"[NULL]"
+3107578435792897,3983000,"TOUCH_MOVED",3857000,"[NULL]","[NULL]",28000,"[NULL]"
+3107578439792897,4734000,"TOUCH_MOVED",4550000,"[NULL]","[NULL]",57000,"[NULL]"
+3107578448792897,4346000,"TOUCH_MOVED",4085000,"[NULL]","[NULL]",79000,"[NULL]"
+3107578452792897,3863000,"TOUCH_MOVED",3702000,"[NULL]","[NULL]",36000,"[NULL]"
+3107578456792897,4743000,"TOUCH_MOVED",4579000,"[NULL]","[NULL]",50000,"[NULL]"
+3107578460792897,4516000,"TOUCH_MOVED",4389000,"[NULL]","[NULL]",32000,"[NULL]"
+3107578464792897,4709000,"TOUCH_MOVED",4525000,"[NULL]","[NULL]",62000,"[NULL]"
+3107578468792897,4876000,"TOUCH_MOVED",4751000,"[NULL]","[NULL]",31000,"[NULL]"
+3107578473792897,3960000,"TOUCH_MOVED",3792000,"[NULL]","[NULL]",53000,"[NULL]"
+3107578477792897,26678000,"GESTURE_TAP_CANCEL",5674000,"[NULL]","[NULL]",51000,"[NULL]"
+3107578477792897,3956000,"TOUCH_MOVED",3793000,"[NULL]","[NULL]",36000,"[NULL]"
+3107578477792897,6671000,"GESTURE_SCROLL_BEGIN","[NULL]",5477000,871000,24000,"[NULL]"
+3107578477792897,30595000,"FIRST_GESTURE_SCROLL_UPDATE","[NULL]",6101000,1077000,5608000,"[NULL]"
+3107578481792897,5625000,"TOUCH_MOVED",5463000,"[NULL]","[NULL]",45000,"[NULL]"
+3107578481792897,8937000,"GESTURE_SCROLL_UPDATE","[NULL]",5903000,455000,2428000,"[NULL]"
+3107578483992897,3438000,"TOUCH_PRESSED",788000,"[NULL]","[NULL]",14000,"[NULL]"
+3107578485792897,4373000,"TOUCH_MOVED",4213000,"[NULL]","[NULL]",45000,"[NULL]"
+3107578485792897,30948000,"GESTURE_SCROLL_UPDATE","[NULL]",5302000,979000,7100000,"[NULL]"
+3107578489792897,5294000,"TOUCH_MOVED",5191000,"[NULL]","[NULL]",53000,"[NULL]"
+3107578489792897,9525000,"GESTURE_SCROLL_UPDATE","[NULL]",5045000,454000,3882000,"[NULL]"
+3107578494792897,3912000,"TOUCH_MOVED",3821000,"[NULL]","[NULL]",41000,"[NULL]"
+3107578494792897,30309000,"GESTURE_SCROLL_UPDATE","[NULL]",3671000,1262000,7674000,"[NULL]"
+3107578498792897,7457000,"TOUCH_MOVED",7359000,"[NULL]","[NULL]",47000,"[NULL]"
+3107578498792897,8728000,"GESTURE_SCROLL_UPDATE","[NULL]",3560000,4430000,617000,"[NULL]"
diff --git a/test/trace_processor/diff_tests/chrome/experimental_reliable_chrome_tasks_delaying_input_processing_test.out b/test/trace_processor/diff_tests/chrome/experimental_reliable_chrome_tasks_delaying_input_processing_test.out
new file mode 100644
index 0000000..d5bf819
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/experimental_reliable_chrome_tasks_delaying_input_processing_test.out
@@ -0,0 +1,4 @@
+
+"full_name","duration_ms","thread_dur_ms"
+"Looper.dispatch: android.net.ConnectivityManager$CallbackHandler(null)",10.507000,0.878000
+"blink.mojom.PresentationService message (hash=3202951471)",22.524000,9.992000
diff --git a/test/trace_processor/chrome/frame_times_metric.out b/test/trace_processor/diff_tests/chrome/frame_times_metric.out
similarity index 100%
rename from test/trace_processor/chrome/frame_times_metric.out
rename to test/trace_processor/diff_tests/chrome/frame_times_metric.out
diff --git a/test/trace_processor/diff_tests/chrome/frame_times_metric_test.sql b/test/trace_processor/diff_tests/chrome/frame_times_metric_test.sql
new file mode 100644
index 0000000..0eb0ad1
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/frame_times_metric_test.sql
@@ -0,0 +1,18 @@
+--
+-- Copyright 2021 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('experimental/frame_times.sql');
+
+SELECT * FROM AvgSurfaceFps;
diff --git a/test/trace_processor/diff_tests/chrome/index.py b/test/trace_processor/diff_tests/chrome/index.py
new file mode 100644
index 0000000..b90c2b1
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/index.py
@@ -0,0 +1,1413 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import DiffTestModule
+
+
+class DiffTestModule_Chrome(DiffTestModule):
+
+  def test_scroll_jank_general_validation(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
+        query=Path('scroll_jank_general_validation_test.sql'),
+        out=Path('scroll_jank_general_validation.out'))
+
+  def test_scroll_jank(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
+        query="""
+SELECT RUN_METRIC('chrome/scroll_jank.sql');
+
+SELECT
+  gesture_scroll_id,
+  trace_id,
+  jank,
+  ts,
+  dur,
+  jank_budget
+FROM scroll_jank;
+""",
+        out=Path('scroll_jank.out'))
+
+  def test_event_latency_to_breakdowns(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/event_latency_with_args.perfetto-trace'),
+        query="""
+SELECT RUN_METRIC('chrome/event_latency_to_breakdowns.sql');
+
+SELECT
+  event_latency_ts,
+  event_latency_dur,
+  event_type,
+  GenerationToRendererCompositorNs,
+  GenerationToBrowserMainNs,
+  BrowserMainToRendererCompositorNs,
+  RendererCompositorQueueingDelayNs,
+  unknown_stages_seen
+FROM event_latency_to_breakdowns
+ORDER BY event_latency_id
+LIMIT 30;
+""",
+        out=Path('event_latency_to_breakdowns.out'))
+
+  def test_event_latency_scroll_jank(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/event_latency_with_args.perfetto-trace'),
+        query="""
+SELECT RUN_METRIC('chrome/event_latency_scroll_jank.sql');
+
+SELECT
+  jank,
+  next_jank,
+  prev_jank,
+  gesture_begin_ts,
+  gesture_end_ts,
+  ts,
+  dur,
+  event_type,
+  next_ts,
+  next_dur,
+  prev_ts,
+  prev_dur
+FROM scroll_event_latency_jank
+ORDER BY jank DESC
+LIMIT 10;
+""",
+        out=Path('event_latency_scroll_jank.out'))
+
+  def test_event_latency_scroll_jank_cause(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/event_latency_with_args.perfetto-trace'),
+        query="""
+SELECT RUN_METRIC('chrome/event_latency_scroll_jank_cause.sql');
+
+SELECT
+  dur,
+  ts,
+  event_type,
+  next_jank,
+  prev_jank,
+  next_delta_dur_ns,
+  prev_delta_dur_ns,
+  cause_of_jank,
+  max_delta_dur_ns,
+  sub_cause_of_jank
+FROM event_latency_scroll_jank_cause
+ORDER by ts;
+""",
+        out=Path('event_latency_scroll_jank_cause.out'))
+
+  def test_scroll_flow_event(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
+        query="""
+SELECT RUN_METRIC('chrome/scroll_flow_event.sql');
+
+SELECT
+  trace_id,
+  ts,
+  dur,
+  jank,
+  step,
+  ancestor_end,
+  maybe_next_ancestor_ts,
+  next_ts,
+  next_trace_id,
+  next_step
+FROM scroll_flow_event
+ORDER BY gesture_scroll_id, trace_id, ts;
+""",
+        out=Path('scroll_flow_event.out'))
+
+  def test_scroll_flow_event_general_validation(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
+        query="""
+SELECT RUN_METRIC('chrome/scroll_flow_event.sql');
+
+SELECT
+  -- Each trace_id (in our example trace not true in general) has 8 steps. There
+  -- are 139 scrolls. So we expect 1112 rows in total 72 of which are janky.
+  (
+    SELECT
+      COUNT(*)
+    FROM (
+      SELECT
+        trace_id,
+        COUNT(*)
+      FROM scroll_flow_event
+      GROUP BY trace_id
+    )
+  ) AS total_scroll_updates,
+  (
+    SELECT COUNT(*) FROM scroll_flow_event
+  ) AS total_flow_event_steps,
+  (
+    SELECT COUNT(*) FROM scroll_flow_event WHERE jank
+  ) AS total_janky_flow_event_steps,
+  (
+    SELECT COUNT(*) FROM (SELECT step FROM scroll_flow_event GROUP BY step)
+  ) AS number_of_unique_steps;
+""",
+        out=Path('scroll_flow_event_general_validation.out'))
+
+  def test_scroll_jank_cause(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
+        query="""
+SELECT RUN_METRIC('chrome/scroll_jank_cause.sql');
+
+SELECT
+  COUNT(*) AS total,
+  SUM(jank) AS total_jank,
+  SUM(explained_jank + unexplained_jank) AS sum_explained_and_unexplained,
+  SUM(
+    CASE WHEN explained_jank THEN
+      unexplained_jank
+      ELSE
+        CASE WHEN jank AND NOT unexplained_jank THEN
+          1
+          ELSE
+            0
+        END
+    END
+  ) AS error_rows
+FROM scroll_jank_cause;
+""",
+        out=Csv("""
+"total","total_jank","sum_explained_and_unexplained","error_rows"
+139,7,7,0
+"""))
+
+  def test_scroll_flow_event_queuing_delay(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
+        query="""
+SELECT RUN_METRIC('chrome/scroll_flow_event_queuing_delay.sql');
+
+SELECT
+  trace_id,
+  jank,
+  step,
+  next_step,
+  ancestor_end,
+  maybe_next_ancestor_ts,
+  queuing_time_ns
+FROM scroll_flow_event_queuing_delay
+WHERE trace_id = 2954 OR trace_id = 2956 OR trace_id = 2960
+ORDER BY trace_id, ts;
+""",
+        out=Path('scroll_flow_event_queuing_delay.out'))
+
+  def test_scroll_flow_event_general_validation_2(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
+        query=Path(
+            'scroll_flow_event_queuing_delay_general_validation_test.sql'),
+        out=Path('scroll_flow_event_general_validation.out'))
+
+  def test_scroll_jank_cause_queuing_delay(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
+        query="""
+SELECT RUN_METRIC('chrome/scroll_jank_cause_queuing_delay.sql');
+
+SELECT
+  process_name,
+  thread_name,
+  trace_id,
+  jank,
+  dur_overlapping_ns,
+  metric_name
+FROM scroll_jank_cause_queuing_delay
+WHERE trace_id = 2918 OR trace_id = 2926
+ORDER BY trace_id ASC, ts ASC;
+""",
+        out=Path('scroll_jank_cause_queuing_delay.out'))
+
+  def test_scroll_jank_cause_queuing_delay_restricted(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
+        query="""
+SELECT RUN_METRIC('chrome/scroll_jank_cause_queuing_delay.sql');
+
+SELECT
+  process_name,
+  thread_name,
+  trace_id,
+  jank,
+  dur_overlapping_ns,
+  restricted_metric_name
+FROM scroll_jank_cause_queuing_delay
+WHERE trace_id = 2918 OR trace_id = 2926
+ORDER BY trace_id ASC, ts ASC;
+""",
+        out=Path('scroll_jank_cause_queuing_delay_restricted.out'))
+
+  def test_scroll_jank_cause_queuing_delay_general_validation(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
+        query="""
+SELECT RUN_METRIC('chrome/scroll_jank_cause_queuing_delay.sql');
+
+SELECT
+  COUNT(*) AS total,
+  (
+    SELECT DISTINCT
+      (avg_no_jank_dur_overlapping_ns)
+    FROM scroll_jank_cause_queuing_delay
+    WHERE
+      location = "LatencyInfo.Flow"
+      AND jank
+  ) AS janky_latency_info_non_jank_avg_dur,
+  (
+    SELECT DISTINCT
+      (avg_no_jank_dur_overlapping_ns)
+    FROM scroll_jank_cause_queuing_delay
+    WHERE
+      location = "LatencyInfo.Flow"
+      AND NOT jank
+  ) AS non_janky_latency_info_non_jank_avg_dur
+FROM (
+  SELECT
+    trace_id
+  FROM scroll_jank_cause_queuing_delay
+  GROUP BY trace_id
+);
+""",
+        out=Path('scroll_jank_cause_queuing_delay_general_validation.out'))
+
+  def test_chrome_thread_slice(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
+        query="""
+SELECT RUN_METRIC('chrome/chrome_thread_slice.sql');
+
+SELECT
+  EXTRACT_ARG(arg_set_id, 'chrome_latency_info.trace_id') AS trace_id,
+  dur,
+  thread_dur
+FROM chrome_thread_slice
+WHERE
+  name = 'LatencyInfo.Flow'
+  AND EXTRACT_ARG(arg_set_id, 'chrome_latency_info.trace_id') = 2734;
+""",
+        out=Csv("""
+"trace_id","dur","thread_dur"
+2734,25000,25000
+2734,1000,2000
+2734,2000,2000
+2734,258000,171000
+2734,1000,1000
+"""))
+
+  def test_chrome_input_to_browser_intervals(self):
+    return DiffTestBlueprint(
+        trace=Path(
+            '../../data/scrolling_with_blocked_nonblocked_frames.pftrace'),
+        query="""
+SELECT RUN_METRIC('chrome/chrome_input_to_browser_intervals.sql');
+
+SELECT
+  *
+FROM chrome_input_to_browser_intervals
+WHERE window_start_ts >= 60934320005158
+  AND window_start_ts <= 60934338798158;
+""",
+        out=Path('chrome_input_to_browser_intervals.out'))
+
+  def test_chrome_scroll_jank_caused_by_scheduling_test(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/fling_with_input_delay.pftrace'),
+        query="""
+SELECT RUN_METRIC('chrome/chrome_scroll_jank_caused_by_scheduling.sql',
+  'dur_causes_jank_ms',
+/* dur_causes_jank_ms = */ '5');
+
+SELECT
+  full_name,
+  total_duration_ms,
+  total_thread_duration_ms,
+  count,
+  window_start_ts,
+  window_end_ts,
+  scroll_type
+FROM chrome_scroll_jank_caused_by_scheduling;
+""",
+        out=Path('chrome_scroll_jank_caused_by_scheduling_test.out'))
+
+  def test_chrome_tasks_delaying_input_processing_test(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/fling_with_input_delay.pftrace'),
+        query="""
+SELECT RUN_METRIC('chrome/chrome_tasks_delaying_input_processing.sql',
+  'duration_causing_jank_ms',
+ /* duration_causing_jank_ms = */ '8');
+
+SELECT
+  full_name,
+  duration_ms,
+  thread_dur_ms
+FROM chrome_tasks_delaying_input_processing;
+""",
+        out=Path('chrome_tasks_delaying_input_processing_test.out'))
+
+  def test_long_task_tracking_trace_chrome_long_tasks_delaying_input_processing_test(
+      self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/long_task_tracking_trace'),
+        query="""
+SELECT RUN_METRIC('chrome/chrome_long_tasks_delaying_input_processing.sql');
+
+SELECT
+  full_name,
+  duration_ms,
+  slice_id
+FROM chrome_tasks_delaying_input_processing
+ORDER BY slice_id;
+""",
+        out=Path(
+            'long_task_tracking_trace_chrome_long_tasks_delaying_input_processing_test.out'
+        ))
+
+  def test_experimental_reliable_chrome_tasks_delaying_input_processing_test(
+      self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/fling_with_input_delay.pftrace'),
+        query="""
+SELECT RUN_METRIC(
+    'chrome/experimental_reliable_chrome_tasks_delaying_input_processing.sql',
+    'duration_causing_jank_ms', '8');
+
+SELECT
+  full_name,
+  duration_ms,
+  thread_dur_ms
+FROM chrome_tasks_delaying_input_processing;
+""",
+        out=Path(
+            'experimental_reliable_chrome_tasks_delaying_input_processing_test.out'
+        ))
+
+  def test_chrome_scroll_inputs_per_frame_test(self):
+    return DiffTestBlueprint(
+        trace=Path(
+            '../../data/scrolling_with_blocked_nonblocked_frames.pftrace'),
+        query="""
+SELECT RUN_METRIC('chrome/chrome_scroll_inputs_per_frame.sql');
+
+SELECT
+  count_for_frame,
+  ts
+FROM chrome_scroll_inputs_per_frame
+WHERE ts = 60934316798158;
+""",
+        out=Csv("""
+"count_for_frame","ts"
+4,60934316798158
+"""))
+
+  def test_chrome_thread_slice_repeated(self):
+    return DiffTestBlueprint(
+        trace=Path('../track_event/track_event_counters.textproto'),
+        query="""
+SELECT RUN_METRIC('chrome/chrome_thread_slice.sql');
+
+SELECT
+  name,
+  ts,
+  dur,
+  thread_dur
+FROM chrome_thread_slice;
+""",
+        out=Csv("""
+"name","ts","dur","thread_dur"
+"event1_on_t1",1000,100,10000
+"event2_on_t1",2000,200,30000
+"event3_on_t1",2000,200,10000
+"event4_on_t1",4000,0,0
+"float_counter_on_t1",4300,0,"[NULL]"
+"float_counter_on_t1",4500,0,"[NULL]"
+"event1_on_t3",4000,100,5000
+"""))
+
+  def test_frame_times_metric(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_rendering_desktop.pftrace'),
+        query=Metric('frame_times'),
+        out=Path('frame_times_metric.out'))
+
+  def test_chrome_dropped_frames_metric(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_rendering_desktop.pftrace'),
+        query=Metric('chrome_dropped_frames'),
+        out=TextProto(r"""
+[perfetto.protos.chrome_dropped_frames]: {
+  dropped_frame: {
+    ts: 166479338462000
+    process_name: "Renderer"
+    pid: 12743
+  }
+  dropped_frame: {
+    ts: 166479355302000
+    process_name: "Renderer"
+    pid: 12743
+  }
+}"""))
+
+  def test_chrome_long_latency_metric(self):
+    return DiffTestBlueprint(
+        trace=Path('../chrome/long_event_latency.textproto'),
+        query="""
+SELECT RUN_METRIC('experimental/chrome_long_latency.sql');
+
+SELECT * FROM long_latency_with_process_info;
+""",
+        out=Csv("""
+"ts","event_type","process_name","process_id"
+200111000,"FirstGestureScrollUpdate,GestureScrollUpdate","Renderer",1001
+200111000,"GestureScrollUpdate","Renderer",1002
+280111001,"GestureScrollUpdate","Renderer",1001
+"""))
+
+  def test_scroll_jank_mojo_simple_watcher(self):
+    return DiffTestBlueprint(
+        trace=Path('scroll_jank_mojo_simple_watcher.py'),
+        query="""
+SELECT RUN_METRIC('chrome/scroll_jank_cause_queuing_delay.sql');
+
+SELECT
+  trace_id,
+  jank,
+  dur_overlapping_ns,
+  metric_name
+FROM scroll_jank_cause_queuing_delay
+ORDER BY trace_id ASC, ts ASC;
+""",
+        out=Path('scroll_jank_mojo_simple_watcher.out'))
+
+  def test_scroll_jank_gpu_check(self):
+    return DiffTestBlueprint(
+        trace=Path('scroll_jank_gpu_check.py'),
+        query="""
+SELECT RUN_METRIC('chrome/scroll_jank.sql');
+
+SELECT ts, jank
+FROM scroll_jank
+ORDER BY ts ASC;
+""",
+        out=Csv("""
+"ts","jank"
+15000000,0
+30000000,1
+115000000,0
+"""))
+
+  def test_touch_jank(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_touch_gesture_scroll.pftrace'),
+        query="""
+SELECT RUN_METRIC('chrome/touch_jank.sql');
+
+SELECT
+  touch_id,
+  trace_id,
+  jank,
+  ts,
+  dur,
+  jank_budget
+FROM touch_jank;
+""",
+        out=Path('touch_jank.out'))
+
+  def test_touch_flow_event(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_touch_gesture_scroll.pftrace'),
+        query="""
+SELECT RUN_METRIC('chrome/touch_flow_event.sql');
+
+SELECT
+  trace_id,
+  ts,
+  dur,
+  jank,
+  step,
+  ancestor_end,
+  maybe_next_ancestor_ts,
+  next_ts,
+  next_trace_id,
+  next_step
+FROM touch_flow_event
+ORDER BY touch_id, trace_id, ts;
+""",
+        out=Path('touch_flow_event.out'))
+
+  def test_touch_flow_event_queuing_delay(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_touch_gesture_scroll.pftrace'),
+        query="""
+SELECT RUN_METRIC('chrome/touch_flow_event_queuing_delay.sql');
+
+SELECT
+  trace_id,
+  jank,
+  step,
+  next_step,
+  ancestor_end,
+  maybe_next_ancestor_ts,
+  queuing_time_ns
+FROM touch_flow_event_queuing_delay
+WHERE trace_id = 6915 OR trace_id = 6911 OR trace_id = 6940
+ORDER BY trace_id, ts;
+""",
+        out=Path('touch_flow_event_queuing_delay.out'))
+
+  def test_touch_jank_synth(self):
+    return DiffTestBlueprint(
+        trace=Path('touch_jank.py'),
+        query="""
+SELECT RUN_METRIC('chrome/touch_jank.sql');
+
+SELECT
+  touch_id,
+  trace_id,
+  jank,
+  ts,
+  dur,
+  jank_budget
+FROM touch_jank;
+""",
+        out=Csv("""
+"touch_id","trace_id","jank","ts","dur","jank_budget"
+87654,34577,0,0,10000000,-31333333.350000
+87654,34578,1,16000000,33000000,14666666.650000
+87654,34579,0,55000000,33000000,-8333333.350000
+"""))
+
+  def test_touch_flow_event_synth(self):
+    return DiffTestBlueprint(
+        trace=Path('touch_jank.py'),
+        query="""
+SELECT RUN_METRIC('chrome/touch_flow_event.sql');
+
+SELECT
+  trace_id,
+  ts,
+  dur,
+  jank,
+  step,
+  ancestor_end,
+  maybe_next_ancestor_ts,
+  next_ts,
+  next_trace_id,
+  next_step
+FROM touch_flow_event
+ORDER BY touch_id, trace_id, ts;
+""",
+        out=Path('touch_flow_event_synth.out'))
+
+  def test_touch_flow_event_queuing_delay_synth(self):
+    return DiffTestBlueprint(
+        trace=Path('touch_jank.py'),
+        query="""
+SELECT RUN_METRIC('chrome/touch_flow_event_queuing_delay.sql');
+
+SELECT
+  trace_id,
+  jank,
+  step,
+  next_step,
+  ancestor_end,
+  maybe_next_ancestor_ts,
+  queuing_time_ns
+FROM touch_flow_event_queuing_delay
+ORDER BY trace_id, ts;
+""",
+        out=Path('touch_flow_event_queuing_delay_synth.out'))
+
+  def test_memory_snapshot_general_validation(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_memory_snapshot.pftrace'),
+        query="""
+SELECT
+  (
+    SELECT COUNT(*) FROM memory_snapshot
+  ) AS total_snapshots,
+  (
+    SELECT COUNT(*) FROM process
+  ) AS total_processes,
+  (
+    SELECT COUNT(*) FROM process_memory_snapshot
+  ) AS total_process_snapshots,
+  (
+    SELECT COUNT(*) FROM memory_snapshot_node
+  ) AS total_nodes,
+  (
+    SELECT COUNT(*) FROM memory_snapshot_edge
+  ) AS total_edges,
+  (
+    SELECT COUNT(DISTINCT args.id)
+    FROM args
+    JOIN memory_snapshot_node
+      ON args.arg_set_id = memory_snapshot_node.arg_set_id
+  ) AS total_node_args,
+  (
+    SELECT COUNT(*) FROM profiler_smaps
+    JOIN memory_snapshot ON timestamp = ts
+  ) AS total_smaps;
+""",
+        out=Path('memory_snapshot_general_validation.out'))
+
+  def test_memory_snapshot_os_dump_events(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_memory_snapshot.pftrace'),
+        query="""
+SELECT
+  p.upid,
+  pid,
+  p.name,
+  timestamp,
+  detail_level,
+  pf.value AS private_footprint_kb,
+  prs.value AS peak_resident_set_kb,
+  EXTRACT_ARG(p.arg_set_id, 'is_peak_rss_resettable') AS is_peak_rss_resettable
+FROM process p
+LEFT JOIN memory_snapshot
+LEFT JOIN (
+  SELECT id, upid
+  FROM process_counter_track
+  WHERE name = 'chrome.private_footprint_kb'
+  ) AS pct_pf
+  ON p.upid = pct_pf.upid
+LEFT JOIN counter pf ON timestamp = pf.ts AND pct_pf.id = pf.track_id
+LEFT JOIN (
+  SELECT id, upid
+  FROM process_counter_track
+  WHERE name = 'chrome.peak_resident_set_kb'
+  ) AS pct_prs
+  ON p.upid = pct_prs.upid
+LEFT JOIN counter prs ON timestamp = prs.ts AND pct_prs.id = prs.track_id
+ORDER BY timestamp;
+""",
+        out=Path('memory_snapshot_os_dump_events.out'))
+
+  def test_memory_snapshot_chrome_dump_events(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_memory_snapshot.pftrace'),
+        query="""
+SELECT
+  pms.id AS process_snapshot_id,
+  upid,
+  snapshot_id,
+  timestamp,
+  detail_level
+FROM memory_snapshot ms
+LEFT JOIN process_memory_snapshot pms
+  ON ms.id = pms.snapshot_id;
+""",
+        out=Path('memory_snapshot_chrome_dump_events.out'))
+
+  def test_memory_snapshot_nodes(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_memory_snapshot.pftrace'),
+        query="""
+SELECT
+  id,
+  process_snapshot_id,
+  parent_node_id,
+  path,
+  size,
+  effective_size
+FROM memory_snapshot_node
+LIMIT 20;
+""",
+        out=Path('memory_snapshot_nodes.out'))
+
+  def test_memory_snapshot_edges(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_memory_snapshot.pftrace'),
+        query="""
+SELECT
+  id,
+  source_node_id,
+  target_node_id,
+  importance
+FROM memory_snapshot_edge
+LIMIT 20;
+""",
+        out=Path('memory_snapshot_edges.out'))
+
+  def test_memory_snapshot_node_args(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_memory_snapshot.pftrace'),
+        query="""
+SELECT
+  node.id AS node_id,
+  key,
+  value_type,
+  int_value,
+  string_value
+FROM memory_snapshot_node node
+JOIN args ON node.arg_set_id = args.arg_set_id
+LIMIT 20;
+""",
+        out=Path('memory_snapshot_node_args.out'))
+
+  def test_memory_snapshot_smaps(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_memory_snapshot.pftrace'),
+        query="""
+SELECT
+  process.upid,
+  process.name,
+  smap.ts,
+  path,
+  size_kb,
+  private_dirty_kb,
+  swap_kb,
+  file_name,
+  start_address,
+  module_timestamp,
+  module_debugid,
+  module_debug_path,
+  protection_flags,
+  private_clean_resident_kb,
+  shared_dirty_resident_kb,
+  shared_clean_resident_kb,
+  locked_kb,
+  proportional_resident_kb
+FROM process
+JOIN profiler_smaps smap ON process.upid = smap.upid
+JOIN memory_snapshot ms ON ms.timestamp = smap.ts
+LIMIT 20;
+""",
+        out=Path('memory_snapshot_smaps.out'))
+
+  def test_combined_rail_modes(self):
+    return DiffTestBlueprint(
+        trace=Path('combined_rail_modes.py'),
+        query="""
+SELECT RUN_METRIC('chrome/rail_modes.sql');
+SELECT * FROM combined_overall_rail_slices;
+""",
+        out=Csv("""
+"id","ts","dur","rail_mode"
+1,0,10000,"response"
+2,10000,25000,"animation"
+3,35000,10000,"background"
+"""))
+
+  def test_cpu_time_by_combined_rail_mode(self):
+    return DiffTestBlueprint(
+        trace=Path('cpu_time_by_combined_rail_mode.py'),
+        query="""
+SELECT RUN_METRIC('chrome/cpu_time_by_rail_mode.sql');
+SELECT * FROM cpu_time_by_rail_mode;
+""",
+        out=Csv("""
+"id","ts","dur","rail_mode","cpu_dur"
+1,0,10000,"response",26000
+2,10000,20000,"animation",20000
+3,30000,5000,"background",8000
+4,35000,10000,"animation",21000
+5,45000,10000,"background",1000
+"""))
+
+  def test_actual_power_by_combined_rail_mode(self):
+    return DiffTestBlueprint(
+        trace=Path('actual_power_by_combined_rail_mode.py'),
+        query="""
+SELECT RUN_METRIC('chrome/actual_power_by_rail_mode.sql');
+SELECT * FROM real_power_by_rail_mode;
+""",
+        out=Csv("""
+"id","ts","dur","rail_mode","subsystem","joules","drain_w"
+1,0,10000000,"response","cellular",0.000000,0.000000
+1,0,10000000,"response","cpu_little",0.000140,0.014000
+2,10000000,20000000,"animation","cellular",0.000350,0.017500
+2,10000000,20000000,"animation","cpu_little",0.000140,0.007000
+3,30000000,5000000,"background","cellular",0.000018,0.003500
+3,30000000,5000000,"background","cpu_little",0.000007,0.001400
+4,35000000,10000000,"animation","cellular",0.000021,0.002100
+4,35000000,10000000,"animation","cpu_little",0.000070,0.007000
+5,45000000,10000000,"background","cellular",0.000003,0.000350
+5,45000000,10000000,"background","cpu_little",0.000070,0.007000
+"""))
+
+  def test_estimated_power_by_combined_rail_mode(self):
+    return DiffTestBlueprint(
+        trace=Path('estimated_power_by_combined_rail_mode.py'),
+        query="""
+SELECT RUN_METRIC('chrome/estimated_power_by_rail_mode.sql');
+SELECT * FROM power_by_rail_mode;
+""",
+        out=Csv("""
+"id","ts","dur","rail_mode","mas","ma"
+1,0,10000000,"response",0.554275,55.427500
+2,10000000,20000000,"animation",0.284850,14.242500
+3,30000000,5000000,"background",0.076233,15.246667
+4,35000000,10000000,"animation",0.536850,53.685000
+5,45000000,10000000,"background",0.071580,7.158000
+"""))
+
+  def test_modified_rail_modes(self):
+    return DiffTestBlueprint(
+        trace=Path('modified_rail_modes.py'),
+        query="""
+SELECT RUN_METRIC('chrome/rail_modes.sql');
+SELECT * FROM modified_rail_slices;
+""",
+        out=Csv("""
+"id","ts","dur","mode"
+2,0,1000000000,"response"
+3,1000000000,1950000000,"foreground_idle"
+4,2950000000,333333324,"animation"
+5,3283333324,216666676,"foreground_idle"
+6,3500000000,1000000000,"background"
+"""))
+
+  def test_modified_rail_modes_no_vsyncs(self):
+    return DiffTestBlueprint(
+        trace=Path('modified_rail_modes_no_vsyncs.py'),
+        query="""
+SELECT RUN_METRIC('chrome/rail_modes.sql');
+SELECT * FROM modified_rail_slices;
+""",
+        out=Csv("""
+"id","ts","dur","mode"
+2,0,1000000000,"response"
+3,1000000000,2500000000,"foreground_idle"
+4,3500000000,1000000000,"background"
+"""))
+
+  def test_modified_rail_modes_with_input(self):
+    return DiffTestBlueprint(
+        trace=Path('modified_rail_modes_with_input.py'),
+        query="""
+SELECT RUN_METRIC('chrome/rail_modes.sql');
+SELECT * FROM modified_rail_slices;
+""",
+        out=Csv("""
+"id","ts","dur","mode"
+2,0,1000000000,"response"
+3,1000000000,1950000000,"foreground_idle"
+4,2950000000,50000000,"animation"
+5,3000000000,66666674,"response"
+6,3066666674,216666650,"animation"
+7,3283333324,216666676,"foreground_idle"
+8,3500000000,1000000000,"background"
+"""))
+
+  def test_modified_rail_modes_long(self):
+    return DiffTestBlueprint(
+        trace=Path('modified_rail_modes_long.py'),
+        query="""
+SELECT RUN_METRIC('chrome/rail_modes.sql');
+SELECT * FROM modified_rail_slices;
+""",
+        out=Csv("""
+"id","ts","dur","mode"
+2,0,1000000000,"response"
+3,1000000000,1,"background"
+"""))
+
+  def test_modified_rail_modes_extra_long(self):
+    return DiffTestBlueprint(
+        trace=Path('modified_rail_modes_extra_long.py'),
+        query="""
+SELECT RUN_METRIC('chrome/rail_modes.sql');
+SELECT * FROM modified_rail_slices;
+""",
+        out=Csv("""
+"id","ts","dur","mode"
+"""))
+
+  def test_chrome_processes(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
+        query="""
+SELECT RUN_METRIC('chrome/chrome_processes.sql');
+SELECT pid, name, process_type FROM chrome_process;
+""",
+        out=Csv("""
+"pid","name","process_type"
+18250,"Renderer","Renderer"
+17547,"Browser","Browser"
+18277,"GPU Process","Gpu"
+17578,"Browser","Browser"
+"""))
+
+  def test_chrome_processes_android_systrace(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_android_systrace.pftrace'),
+        query="""
+SELECT RUN_METRIC('chrome/chrome_processes.sql');
+SELECT pid, name, process_type FROM chrome_process;
+""",
+        out=Path('chrome_processes_android_systrace.out'))
+
+  def test_chrome_threads(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
+        query="""
+SELECT RUN_METRIC('chrome/chrome_processes.sql');
+SELECT tid, name, is_main_thread, canonical_name
+FROM chrome_thread
+ORDER BY tid, name;
+""",
+        out=Path('chrome_threads.out'))
+
+  def test_chrome_threads_android_systrace(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_android_systrace.pftrace'),
+        query="""
+SELECT RUN_METRIC('chrome/chrome_processes.sql');
+SELECT tid, name, is_main_thread, canonical_name
+FROM chrome_thread
+ORDER BY tid, name;
+""",
+        out=Path('chrome_threads_android_systrace.out'))
+
+  def test_chrome_processes_type(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
+        query="""
+SELECT pid, name, string_value AS chrome_process_type
+FROM
+  process
+JOIN
+  (SELECT * FROM args WHERE key = "chrome.process_type") chrome_process_args
+  ON
+    process.arg_set_id = chrome_process_args.arg_set_id
+ORDER BY pid;
+""",
+        out=Csv("""
+"pid","name","chrome_process_type"
+17547,"Browser","Browser"
+17578,"Browser","Browser"
+18250,"Renderer","Renderer"
+18277,"GPU Process","Gpu"
+"""))
+
+  def test_chrome_processes_type_android_systrace(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_android_systrace.pftrace'),
+        query="""
+SELECT pid, name, string_value AS chrome_process_type
+FROM
+  process
+JOIN
+  (SELECT * FROM args WHERE key = "chrome.process_type") chrome_process_args
+  ON
+    process.arg_set_id = chrome_process_args.arg_set_id
+ORDER BY pid;
+""",
+        out=Path('chrome_processes_type_android_systrace.out'))
+
+  def test_track_with_chrome_process(self):
+    return DiffTestBlueprint(
+        trace=Path('track_with_chrome_process.textproto'),
+        query="""
+SELECT pid, name, string_value AS chrome_process_type
+FROM
+  process
+JOIN
+  (SELECT * FROM args WHERE key = "chrome.process_type") chrome_process_args
+  ON
+    process.arg_set_id = chrome_process_args.arg_set_id
+ORDER BY pid;
+""",
+        out=Csv("""
+"pid","name","chrome_process_type"
+5,"p5","[NULL]"
+"""))
+
+  def test_chrome_histogram_hashes(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_histogram_hashes.textproto'),
+        query=Metric('chrome_histogram_hashes'),
+        out=TextProto(r"""
+[perfetto.protos.chrome_histogram_hashes]: {
+  hash: 10
+  hash: 20
+}
+"""))
+
+  def test_chrome_user_event_hashes(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_user_event_hashes.textproto'),
+        query=Metric('chrome_user_event_hashes'),
+        out=TextProto(r"""
+[perfetto.protos.chrome_user_event_hashes]: {
+  action_hash: 10
+  action_hash: 20
+}
+
+"""))
+
+  def test_chrome_performance_mark_hashes(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_performance_mark_hashes.textproto'),
+        query=Metric('chrome_performance_mark_hashes'),
+        out=TextProto(r"""
+[perfetto.protos.chrome_performance_mark_hashes]: {
+  site_hash: 10
+  site_hash: 20
+  mark_hash: 100
+  mark_hash: 200
+}
+"""))
+
+  def test_chrome_reliable_range(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_reliable_range.textproto'),
+        query=Path('chrome_reliable_range_test.sql'),
+        out=Csv("""
+"start","reason","debug_limiting_upid","debug_limiting_utid"
+12,"First slice for utid=2","[NULL]",2
+"""))
+
+  def test_chrome_reliable_range_cropping(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_reliable_range_cropping.textproto'),
+        query=Path('chrome_reliable_range_test.sql'),
+        out=Csv("""
+"start","reason","debug_limiting_upid","debug_limiting_utid"
+10000,"Range of interest packet","[NULL]",2
+"""))
+
+  def test_chrome_reliable_range_missing_processes(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_reliable_range_missing_processes.textproto'),
+        query=Path('chrome_reliable_range_test.sql'),
+        out=Csv("""
+"start","reason","debug_limiting_upid","debug_limiting_utid"
+1011,"Missing process data for upid=2",2,1
+"""))
+
+  def test_chrome_reliable_range_missing_browser_main(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_reliable_range_missing_browser_main.textproto'),
+        query=Path('chrome_reliable_range_test.sql'),
+        out=Csv("""
+"start","reason","debug_limiting_upid","debug_limiting_utid"
+1011,"Missing main thread for upid=1",1,1
+"""))
+
+  def test_chrome_reliable_range_missing_renderer_main(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_reliable_range_missing_renderer_main.textproto'),
+        query=Path('chrome_reliable_range_test.sql'),
+        out=Csv("""
+"start","reason","debug_limiting_upid","debug_limiting_utid"
+1011,"Missing main thread for upid=1",1,1
+"""))
+
+  def test_chrome_reliable_range_non_chrome_process(self):
+    return DiffTestBlueprint(
+        # We need a trace with a large number of non-chrome slices, so that the
+        # reliable range is affected by their filtering.
+        trace=Path('../../data/example_android_trace_30s.pb'),
+        query=Path('chrome_reliable_range_test.sql'),
+        out=Csv("""
+  "start","reason","debug_limiting_upid","debug_limiting_utid"
+  0,"[NULL]","[NULL]","[NULL]"
+  """))
+
+  def test_chrome_slice_names(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_slice_names.textproto'),
+        query=Metric('chrome_slice_names'),
+        out=TextProto(r"""
+[perfetto.protos.chrome_slice_names]: {
+  chrome_version_code: 123
+  slice_name: "Looper.Dispatch: class1"
+  slice_name: "name2"
+}
+"""))
+
+  def test_chrome_tasks(self):
+    return DiffTestBlueprint(
+        trace=Path(
+            '../../data/chrome_page_load_all_categories_not_extended.pftrace.gz'
+        ),
+        query="""
+SELECT RUN_METRIC('chrome/chrome_tasks.sql');
+
+SELECT full_name, task_type, count() AS count
+FROM chrome_tasks
+GROUP BY full_name, task_type
+ORDER BY count DESC
+LIMIT 50;
+""",
+        out=Path('chrome_tasks.out'))
+
+  def test_top_level_java_choreographer_slices_top_level_java_chrome_tasks_test(
+      self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/top_level_java_choreographer_slices'),
+        query="""
+SELECT RUN_METRIC(
+  'chrome/chrome_tasks_template.sql',
+  'slice_table_name', 'slice',
+  'function_prefix', ''
+);
+
+SELECT
+  full_name,
+  task_type
+FROM chrome_tasks
+WHERE category = "toplevel,Java"
+AND ts < 263904000000000
+GROUP BY full_name, task_type;
+""",
+        out=Path(
+            'top_level_java_choreographer_slices_top_level_java_chrome_tasks_test.out'
+        ))
+
+  def test_chrome_stack_samples_for_task_test(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_stack_traces_symbolized_trace.pftrace'),
+        query="""
+SELECT RUN_METRIC('chrome/chrome_stack_samples_for_task.sql',
+    'target_duration_ms', '0.000001',
+    'thread_name', '"CrBrowserMain"',
+    'task_name', '"sendTouchEvent"');
+
+SELECT
+  sample.description,
+  sample.ts,
+  sample.depth
+FROM chrome_stack_samples_for_task sample
+JOIN (
+    SELECT
+      ts,
+      dur
+    FROM slice
+    WHERE ts = 696373965001470
+) test_slice
+ON sample.ts >= test_slice.ts
+  AND sample.ts <= test_slice.ts + test_slice.dur
+ORDER BY sample.ts, sample.depth;
+""",
+        out=Path('chrome_stack_samples_for_task_test.out'))
+
+  def test_unsymbolized_args(self):
+    return DiffTestBlueprint(
+        trace=Path('unsymbolized_args.textproto'),
+        query=Metric('chrome_unsymbolized_args'),
+        out=TextProto(r"""
+[perfetto.protos.chrome_unsymbolized_args]: {
+  args {
+     module: "/liblib.so"
+     build_id: "6275696c642d6964"
+     address: 123
+     google_lookup_id: "6275696c642d6964"
+   }
+   args {
+     module: "/libmonochrome_64.so"
+     build_id: "7f0715c286f8b16c10e4ad349cda3b9b56c7a773"
+     address: 234
+     google_lookup_id: "c215077ff8866cb110e4ad349cda3b9b0"
+   }
+}"""))
+
+  def test_async_trace_1_count_slices(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/async-trace-1.json'),
+        query="""
+SELECT COUNT(1) FROM slice;
+""",
+        out=Csv("""
+"COUNT(1)"
+16
+"""))
+
+  def test_async_trace_2_count_slices(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/async-trace-2.json'),
+        query="""
+SELECT COUNT(1) FROM slice;
+""",
+        out=Csv("""
+"COUNT(1)"
+35
+"""))
+
+  def test_chrome_args_class_names(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_args_class_names.textproto'),
+        query=Metric('chrome_args_class_names'),
+        out=TextProto(r"""
+
+[perfetto.protos.chrome_args_class_names] {
+  class_names_per_version {
+    class_name: "abc"
+    class_name: "def"
+    class_name: "ghi"
+    class_name: "jkl"
+  }
+}
+"""))
+
+  def test_chrome_log_message(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_log_message.textproto'),
+        query="""
+SELECT utid, tag, msg FROM android_logs;
+""",
+        out=Csv("""
+"utid","tag","msg"
+1,"foo.cc:123","log message"
+"""))
+
+  def test_chrome_log_message_args(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_log_message.textproto'),
+        query=Path('chrome_log_message_args_test.sql'),
+        out=Csv("""
+"log_message","function_name","file_name","line_number"
+"log message","func","foo.cc",123
+"""))
+
+  def test_chrome_missing_processes_default_trace(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
+        query="""
+SELECT upid, pid, reliable_from
+FROM
+  experimental_missing_chrome_processes
+JOIN
+  process
+  USING(upid)
+ORDER BY upid;
+""",
+        out=Csv("""
+"upid","pid","reliable_from"
+"""))
+
+  def test_chrome_missing_processes(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_missing_processes.textproto'),
+        query="""
+SELECT upid, pid, reliable_from
+FROM
+  experimental_missing_chrome_processes
+JOIN
+  process
+  USING(upid)
+ORDER BY upid;
+""",
+        out=Csv("""
+"upid","pid","reliable_from"
+2,100,1000000000
+3,1000,"[NULL]"
+"""))
+
+  def test_chrome_missing_processes_args(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_missing_processes.textproto'),
+        query="""
+SELECT arg_set_id, key, int_value
+FROM
+  slice
+JOIN
+  args
+  USING(arg_set_id)
+ORDER BY arg_set_id, key;
+""",
+        out=Csv("""
+"arg_set_id","key","int_value"
+2,"chrome_active_processes.pid[0]",10
+2,"chrome_active_processes.pid[1]",100
+2,"chrome_active_processes.pid[2]",1000
+"""))
+
+  def test_chrome_missing_processes_2(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_missing_processes_extension.textproto'),
+        query="""
+SELECT upid, pid, reliable_from
+FROM
+  experimental_missing_chrome_processes
+JOIN
+  process
+  USING(upid)
+ORDER BY upid;
+""",
+        out=Csv("""
+"upid","pid","reliable_from"
+2,100,1000000000
+3,1000,"[NULL]"
+"""))
+
+  def test_chrome_missing_processes_extension_args(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_missing_processes_extension.textproto'),
+        query="""
+SELECT arg_set_id, key, int_value
+FROM
+  slice
+JOIN
+  args
+  USING(arg_set_id)
+ORDER BY arg_set_id, key;
+""",
+        out=Csv("""
+"arg_set_id","key","int_value"
+2,"active_processes.pid[0]",10
+2,"active_processes.pid[1]",100
+2,"active_processes.pid[2]",1000
+"""))
+
+  def test_chrome_custom_navigation_tasks(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_custom_navigation_trace.gz'),
+        query="""
+SELECT RUN_METRIC('chrome/chrome_tasks.sql');
+
+SELECT full_name, task_type, count() AS count
+FROM chrome_tasks
+WHERE full_name GLOB 'FrameHost::BeginNavigation*'
+  OR full_name GLOB 'FrameHost::DidCommitProvisionalLoad*'
+  OR full_name GLOB 'FrameHost::DidCommitSameDocumentNavigation*'
+  OR full_name GLOB 'FrameHost::DidStopLoading*'
+GROUP BY full_name, task_type
+ORDER BY count DESC
+LIMIT 50;
+""",
+        out=Csv("""
+"full_name","task_type","count"
+"FrameHost::BeginNavigation (SUBFRAME)","navigation_task",5
+"FrameHost::DidStopLoading (SUBFRAME)","navigation_task",3
+"FrameHost::BeginNavigation (PRIMARY_MAIN_FRAME)","navigation_task",1
+"FrameHost::DidCommitProvisionalLoad (SUBFRAME)","navigation_task",1
+"""))
+
+  def test_proto_content(self):
+    return DiffTestBlueprint(
+        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
+        query="""
+SELECT path, SUM(total_size) as total_size
+FROM experimental_proto_content as content 
+JOIN experimental_proto_path as frame ON content.path_id = frame.id
+GROUP BY path
+ORDER BY total_size DESC, path
+LIMIT 10;
+""",
+        out=Path('proto_content.out'))
diff --git a/test/trace_processor/chrome/long_event_latency.textproto b/test/trace_processor/diff_tests/chrome/long_event_latency.textproto
similarity index 100%
rename from test/trace_processor/chrome/long_event_latency.textproto
rename to test/trace_processor/diff_tests/chrome/long_event_latency.textproto
diff --git a/test/trace_processor/diff_tests/chrome/long_task_tracking_trace_chrome_long_tasks_delaying_input_processing_compare_default_test.out b/test/trace_processor/diff_tests/chrome/long_task_tracking_trace_chrome_long_tasks_delaying_input_processing_compare_default_test.out
new file mode 100644
index 0000000..9f3f1f6
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/long_task_tracking_trace_chrome_long_tasks_delaying_input_processing_compare_default_test.out
@@ -0,0 +1,16 @@
+
+"full_name","duration_ms","slice_id"
+"cc.mojom.RenderFrameMetadataObserverClient message (hash=330497194)",4.054000,5054
+"FrameHost::BeginNavigation (unknown frame type)",9.647000,15011
+"FrameHost::BeginNavigation (unknown frame type)",12.937000,15448
+"network.mojom.NetworkService reply (hash=1419877769)",14.115000,15900
+"content.mojom.NavigationClient reply (hash=479951742)",4.935000,21525
+"RunTask(posted_from=cc/trees/single_thread_proxy.cc:DidReceiveCompositorFrameAckOnImplThread)",4.292000,42834
+"content.mojom.FrameHost message (hash=2324894379)",4.038000,70576
+"FrameHost::BeginNavigation (unknown frame type)",15.509000,70929
+"cc.mojom.RenderFrameMetadataObserverClient message (hash=330497194)",4.940000,89185
+"content.mojom.NavigationClient reply (hash=479951742)",5.413000,91211
+"FrameHost::BeginNavigation (unknown frame type)",10.327000,98059
+"content.mojom.NavigationRendererCancellationListener message (hash=281273569)",7.873000,99939
+"FrameHost::BeginNavigation (unknown frame type)",9.579000,124733
+"network.mojom.NetworkService reply (hash=1419877769)",10.132000,126133
diff --git a/test/trace_processor/diff_tests/chrome/long_task_tracking_trace_chrome_long_tasks_delaying_input_processing_test.out b/test/trace_processor/diff_tests/chrome/long_task_tracking_trace_chrome_long_tasks_delaying_input_processing_test.out
new file mode 100644
index 0000000..79b5036
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/long_task_tracking_trace_chrome_long_tasks_delaying_input_processing_test.out
@@ -0,0 +1,16 @@
+
+"full_name","duration_ms","slice_id"
+"cc.mojom.RenderFrameMetadataObserverClient message (hash=330497194)",4.061000,5055
+"FrameHost::BeginNavigation (unknown frame type)",9.651000,15012
+"FrameHost::BeginNavigation (unknown frame type)",12.943000,15449
+"network.mojom.NetworkService reply (hash=1419877769)",14.118000,15899
+"content.mojom.NavigationClient reply (hash=479951742)",4.937000,21526
+"RunTask(posted_from=cc/trees/single_thread_proxy.cc:DidReceiveCompositorFrameAckOnImplThread)",4.298000,42835
+"content.mojom.FrameHost message (hash=2324894379)",4.046000,70575
+"FrameHost::BeginNavigation (unknown frame type)",15.514000,70930
+"cc.mojom.RenderFrameMetadataObserverClient message (hash=330497194)",4.950000,89184
+"content.mojom.NavigationClient reply (hash=479951742)",5.418000,91212
+"FrameHost::BeginNavigation (unknown frame type)",10.333000,98058
+"content.mojom.NavigationRendererCancellationListener message (hash=281273569)",7.876000,99938
+"FrameHost::BeginNavigation (unknown frame type)",9.585000,124732
+"network.mojom.NetworkService reply (hash=1419877769)",10.134000,126134
diff --git a/test/trace_processor/chrome/memory_snapshot_chrome_dump_events.out b/test/trace_processor/diff_tests/chrome/memory_snapshot_chrome_dump_events.out
similarity index 100%
rename from test/trace_processor/chrome/memory_snapshot_chrome_dump_events.out
rename to test/trace_processor/diff_tests/chrome/memory_snapshot_chrome_dump_events.out
diff --git a/test/trace_processor/chrome/memory_snapshot_edges.out b/test/trace_processor/diff_tests/chrome/memory_snapshot_edges.out
similarity index 100%
rename from test/trace_processor/chrome/memory_snapshot_edges.out
rename to test/trace_processor/diff_tests/chrome/memory_snapshot_edges.out
diff --git a/test/trace_processor/chrome/memory_snapshot_general_validation.out b/test/trace_processor/diff_tests/chrome/memory_snapshot_general_validation.out
similarity index 100%
rename from test/trace_processor/chrome/memory_snapshot_general_validation.out
rename to test/trace_processor/diff_tests/chrome/memory_snapshot_general_validation.out
diff --git a/test/trace_processor/chrome/memory_snapshot_node_args.out b/test/trace_processor/diff_tests/chrome/memory_snapshot_node_args.out
similarity index 100%
rename from test/trace_processor/chrome/memory_snapshot_node_args.out
rename to test/trace_processor/diff_tests/chrome/memory_snapshot_node_args.out
diff --git a/test/trace_processor/chrome/memory_snapshot_nodes.out b/test/trace_processor/diff_tests/chrome/memory_snapshot_nodes.out
similarity index 100%
rename from test/trace_processor/chrome/memory_snapshot_nodes.out
rename to test/trace_processor/diff_tests/chrome/memory_snapshot_nodes.out
diff --git a/test/trace_processor/chrome/memory_snapshot_os_dump_events.out b/test/trace_processor/diff_tests/chrome/memory_snapshot_os_dump_events.out
similarity index 100%
rename from test/trace_processor/chrome/memory_snapshot_os_dump_events.out
rename to test/trace_processor/diff_tests/chrome/memory_snapshot_os_dump_events.out
diff --git a/test/trace_processor/diff_tests/chrome/memory_snapshot_os_dump_events_test.sql b/test/trace_processor/diff_tests/chrome/memory_snapshot_os_dump_events_test.sql
new file mode 100644
index 0000000..4a18b36
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/memory_snapshot_os_dump_events_test.sql
@@ -0,0 +1,45 @@
+--
+-- 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
+  p.upid,
+  pid,
+  p.name,
+  timestamp,
+  detail_level,
+  pf.value AS private_footprint_kb,
+  prs.value AS peak_resident_set_kb,
+  EXTRACT_ARG(p.arg_set_id, 'is_peak_rss_resettable')
+    AS is_peak_rss_resettable
+FROM process p
+LEFT JOIN memory_snapshot
+LEFT JOIN (
+  SELECT id, upid
+  FROM process_counter_track
+  WHERE name = 'chrome.private_footprint_kb'
+  ) AS pct_pf
+  ON p.upid = pct_pf.upid
+LEFT JOIN counter pf 
+  ON timestamp = pf.ts AND pct_pf.id = pf.track_id
+LEFT JOIN (
+  SELECT id, upid
+  FROM process_counter_track
+  WHERE name = 'chrome.peak_resident_set_kb'
+  ) AS pct_prs
+  ON p.upid = pct_prs.upid
+LEFT JOIN counter prs 
+  ON timestamp = prs.ts AND pct_prs.id = prs.track_id
+ORDER BY timestamp;
diff --git a/test/trace_processor/chrome/memory_snapshot_smaps.out b/test/trace_processor/diff_tests/chrome/memory_snapshot_smaps.out
similarity index 100%
rename from test/trace_processor/chrome/memory_snapshot_smaps.out
rename to test/trace_processor/diff_tests/chrome/memory_snapshot_smaps.out
diff --git a/test/trace_processor/chrome/modified_rail_modes.py b/test/trace_processor/diff_tests/chrome/modified_rail_modes.py
similarity index 100%
rename from test/trace_processor/chrome/modified_rail_modes.py
rename to test/trace_processor/diff_tests/chrome/modified_rail_modes.py
diff --git a/test/trace_processor/chrome/modified_rail_modes_extra_long.py b/test/trace_processor/diff_tests/chrome/modified_rail_modes_extra_long.py
similarity index 100%
rename from test/trace_processor/chrome/modified_rail_modes_extra_long.py
rename to test/trace_processor/diff_tests/chrome/modified_rail_modes_extra_long.py
diff --git a/test/trace_processor/chrome/modified_rail_modes_long.py b/test/trace_processor/diff_tests/chrome/modified_rail_modes_long.py
similarity index 100%
rename from test/trace_processor/chrome/modified_rail_modes_long.py
rename to test/trace_processor/diff_tests/chrome/modified_rail_modes_long.py
diff --git a/test/trace_processor/chrome/modified_rail_modes_no_vsyncs.py b/test/trace_processor/diff_tests/chrome/modified_rail_modes_no_vsyncs.py
similarity index 100%
rename from test/trace_processor/chrome/modified_rail_modes_no_vsyncs.py
rename to test/trace_processor/diff_tests/chrome/modified_rail_modes_no_vsyncs.py
diff --git a/test/trace_processor/chrome/modified_rail_modes_with_input.py b/test/trace_processor/diff_tests/chrome/modified_rail_modes_with_input.py
similarity index 100%
rename from test/trace_processor/chrome/modified_rail_modes_with_input.py
rename to test/trace_processor/diff_tests/chrome/modified_rail_modes_with_input.py
diff --git a/test/trace_processor/diff_tests/chrome/proto_content.out b/test/trace_processor/diff_tests/chrome/proto_content.out
new file mode 100644
index 0000000..6c37864
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/proto_content.out
@@ -0,0 +1,11 @@
+"path","total_size"
+"TracePacket",364932
+"TracePacket.#track_event.TrackEvent",254590
+"TracePacket.#trusted_uid.int32",95260
+"TracePacket.#track_event.TrackEvent.#debug_annotations.DebugAnnotation.#string_value.string",73911
+"TracePacket.#timestamp.uint64",66933
+"TracePacket.#trusted_packet_sequence_id.uint32",39091
+"TracePacket.#sequence_flags.uint32",39020
+"TracePacket.#track_event.TrackEvent.#extra_counter_values.int64",36079
+"TracePacket.#track_event.TrackEvent.#chrome_latency_info.ChromeLatencyInfo",33452
+"TracePacket.#track_event.TrackEvent.#type.enum",32937
diff --git a/test/trace_processor/diff_tests/chrome/proto_content_path_test.sql b/test/trace_processor/diff_tests/chrome/proto_content_path_test.sql
new file mode 100644
index 0000000..5aafd97
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/proto_content_path_test.sql
@@ -0,0 +1,23 @@
+--
+-- Copyright 2022 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 content.total_size,
+  frame.field_type, frame.field_name,
+  frame.parent_id,
+  EXTRACT_ARG(frame.arg_set_id, 'event.category') AS event_category,
+  EXTRACT_ARG(frame.arg_set_id, 'event.name') AS event_name
+FROM experimental_proto_path AS frame JOIN experimental_proto_content AS content ON content.path_id = frame.id
+ORDER BY total_size DESC, path
+LIMIT 10;
diff --git a/test/trace_processor/diff_tests/chrome/proto_content_test.sql b/test/trace_processor/diff_tests/chrome/proto_content_test.sql
new file mode 100644
index 0000000..a0baba0
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/proto_content_test.sql
@@ -0,0 +1,20 @@
+--
+-- Copyright 2022 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 path, SUM(total_size) as total_size
+FROM experimental_proto_content as content JOIN experimental_proto_path as frame ON content.path_id = frame.id
+GROUP BY path
+ORDER BY total_size DESC, path
+LIMIT 10;
diff --git a/test/trace_processor/chrome/scroll_flow_event.out b/test/trace_processor/diff_tests/chrome/scroll_flow_event.out
similarity index 100%
rename from test/trace_processor/chrome/scroll_flow_event.out
rename to test/trace_processor/diff_tests/chrome/scroll_flow_event.out
diff --git a/test/trace_processor/chrome/scroll_flow_event_general_validation.out b/test/trace_processor/diff_tests/chrome/scroll_flow_event_general_validation.out
similarity index 100%
rename from test/trace_processor/chrome/scroll_flow_event_general_validation.out
rename to test/trace_processor/diff_tests/chrome/scroll_flow_event_general_validation.out
diff --git a/test/trace_processor/chrome/scroll_flow_event_queuing_delay.out b/test/trace_processor/diff_tests/chrome/scroll_flow_event_queuing_delay.out
similarity index 100%
rename from test/trace_processor/chrome/scroll_flow_event_queuing_delay.out
rename to test/trace_processor/diff_tests/chrome/scroll_flow_event_queuing_delay.out
diff --git a/test/trace_processor/diff_tests/chrome/scroll_flow_event_queuing_delay_general_validation_test.sql b/test/trace_processor/diff_tests/chrome/scroll_flow_event_queuing_delay_general_validation_test.sql
new file mode 100644
index 0000000..656d0f8
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/scroll_flow_event_queuing_delay_general_validation_test.sql
@@ -0,0 +1,39 @@
+--
+-- 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('chrome/scroll_flow_event_queuing_delay.sql');
+
+SELECT
+  -- Each trace_id (in our example trace not true in general) has 8 steps. There
+  -- are 139 scrolls. So we expect 1112 rows in total 72 of which are janky.
+  (
+    SELECT
+      COUNT(*)
+    FROM (
+      SELECT
+        trace_id,
+        COUNT(*)
+      FROM scroll_flow_event_queuing_delay
+      GROUP BY trace_id
+    )
+  ) AS total_scroll_updates,
+  (
+    SELECT COUNT(*) FROM scroll_flow_event_queuing_delay
+  ) AS total_flow_event_steps,
+  (
+    SELECT COUNT(*) FROM scroll_flow_event_queuing_delay WHERE jank
+  ) AS total_janky_flow_event_steps,
+  (
+    SELECT COUNT(*) FROM (SELECT step FROM scroll_flow_event_queuing_delay GROUP BY step)
+  ) AS number_of_unique_steps;
diff --git a/test/trace_processor/chrome/scroll_jank.out b/test/trace_processor/diff_tests/chrome/scroll_jank.out
similarity index 100%
rename from test/trace_processor/chrome/scroll_jank.out
rename to test/trace_processor/diff_tests/chrome/scroll_jank.out
diff --git a/test/trace_processor/chrome/scroll_jank_cause_queuing_delay.out b/test/trace_processor/diff_tests/chrome/scroll_jank_cause_queuing_delay.out
similarity index 100%
rename from test/trace_processor/chrome/scroll_jank_cause_queuing_delay.out
rename to test/trace_processor/diff_tests/chrome/scroll_jank_cause_queuing_delay.out
diff --git a/test/trace_processor/chrome/scroll_jank_cause_queuing_delay_general_validation.out b/test/trace_processor/diff_tests/chrome/scroll_jank_cause_queuing_delay_general_validation.out
similarity index 100%
rename from test/trace_processor/chrome/scroll_jank_cause_queuing_delay_general_validation.out
rename to test/trace_processor/diff_tests/chrome/scroll_jank_cause_queuing_delay_general_validation.out
diff --git a/test/trace_processor/chrome/scroll_jank_cause_queuing_delay_restricted.out b/test/trace_processor/diff_tests/chrome/scroll_jank_cause_queuing_delay_restricted.out
similarity index 100%
rename from test/trace_processor/chrome/scroll_jank_cause_queuing_delay_restricted.out
rename to test/trace_processor/diff_tests/chrome/scroll_jank_cause_queuing_delay_restricted.out
diff --git a/test/trace_processor/chrome/scroll_jank_general_validation.out b/test/trace_processor/diff_tests/chrome/scroll_jank_general_validation.out
similarity index 100%
rename from test/trace_processor/chrome/scroll_jank_general_validation.out
rename to test/trace_processor/diff_tests/chrome/scroll_jank_general_validation.out
diff --git a/test/trace_processor/diff_tests/chrome/scroll_jank_general_validation_test.sql b/test/trace_processor/diff_tests/chrome/scroll_jank_general_validation_test.sql
new file mode 100644
index 0000000..23372ea
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/scroll_jank_general_validation_test.sql
@@ -0,0 +1,81 @@
+--
+-- 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('chrome/scroll_jank.sql');
+
+SELECT (
+  -- There are only two valid scrolls (one additional scroll is missing a begin
+  -- and the other is missing an end).
+  SELECT COUNT(*) FROM joined_scroll_begin_and_end
+) AS total,
+(
+  -- Of the two valid scrolls
+  -- gesture_scroll_id: 2708
+  --     starts at: 544958000403
+  --     ends at:   545859896829
+  --     adds dur:     901896426 nanoseconds of scrolling.
+  --
+  -- gesture_scroll_id: 2917
+  --     starts at: 546027000403
+  --     ends at:   546753574829
+  --     adds dur:     726574426 nanoseconds of scrolling.
+  -- This means we should have scroll_dur = 1628470852
+  SELECT SUM(scroll_dur) FROM (
+    SELECT
+      gesture_scroll_id, max(maybe_gesture_end) - begin_ts AS scroll_dur
+    FROM scroll_jank
+    GROUP BY gesture_scroll_id
+  )
+) AS scroll_dur,
+(
+  -- This can be verified by the following simple query to ensure the end result
+  -- in scroll_jank table is sane. The result should be 139.
+  -- SELECT
+  --   COUNT(*)
+  -- FROM slice
+  -- WHERE
+  --    name = "InputLatency::GestureScrollUpdate" AND
+  --    NOT EXTRACT_ARG(arg_set_id, 'chrome_latency_info.is_coalesced') AND
+  --    (
+  --    EXTRACT_ARG(arg_set_id, 'chrome_latency_info.gesture_scroll_id') = 2708
+  --    OR
+  --    EXTRACT_ARG(arg_set_id, 'chrome_latency_info.gesture_scroll_id') = 2917
+  --    )
+  SELECT COUNT(*) FROM scroll_jank
+) AS non_coalesced_updates,
+(
+  -- This can be verified by the following simple query as above but replace
+  -- COUNT(*) with SUM(dur). The result should be 3974685214.
+  SELECT SUM(dur) FROM scroll_jank
+) AS non_coalesced_dur,
+(
+  -- This was found by running the previous metric before porting on the
+  -- example trace.
+  SELECT COUNT(*) FROM scroll_jank WHERE jank
+) AS non_coalesced_janky_updates,
+(
+  -- This was found by running the previous metric before porting on the
+  -- example trace, and also manually summing them.
+  SELECT SUM(dur) FROM scroll_jank WHERE jank
+) AS non_coalesced_janky_dur,
+(
+  -- This is floor((non_coalesced_janky_dur/non_coalesced_dur) * 100) in SQLite.
+  SELECT
+    CAST((CAST((SELECT SUM(dur) FROM scroll_jank WHERE jank) AS FLOAT)
+      / CAST((SELECT SUM(dur) FROM scroll_jank) AS FLOAT)) * 100 AS INT)
+) AS janky_percentage,
+(
+  SELECT avg_vsync_interval FROM joined_scroll_begin_and_end
+) AS avg_vsync_interval;
diff --git a/test/trace_processor/chrome/scroll_jank_gpu_check.py b/test/trace_processor/diff_tests/chrome/scroll_jank_gpu_check.py
similarity index 100%
rename from test/trace_processor/chrome/scroll_jank_gpu_check.py
rename to test/trace_processor/diff_tests/chrome/scroll_jank_gpu_check.py
diff --git a/test/trace_processor/chrome/scroll_jank_mojo_simple_watcher.out b/test/trace_processor/diff_tests/chrome/scroll_jank_mojo_simple_watcher.out
similarity index 100%
rename from test/trace_processor/chrome/scroll_jank_mojo_simple_watcher.out
rename to test/trace_processor/diff_tests/chrome/scroll_jank_mojo_simple_watcher.out
diff --git a/test/trace_processor/chrome/scroll_jank_mojo_simple_watcher.py b/test/trace_processor/diff_tests/chrome/scroll_jank_mojo_simple_watcher.py
similarity index 100%
rename from test/trace_processor/chrome/scroll_jank_mojo_simple_watcher.py
rename to test/trace_processor/diff_tests/chrome/scroll_jank_mojo_simple_watcher.py
diff --git a/test/trace_processor/diff_tests/chrome/tests.py b/test/trace_processor/diff_tests/chrome/tests.py
new file mode 100644
index 0000000..ec4181e
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/tests.py
@@ -0,0 +1,473 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class Chrome(TestSuite):
+  # Tests related to Chrome's use of Perfetto. Chrome histogram hashes
+  def test_chrome_histogram_hashes(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          trusted_packet_sequence_id: 1
+          timestamp: 0
+          incremental_state_cleared: true
+          track_event {
+            categories: "cat1"
+            type: 3
+            name_iid: 1
+            chrome_histogram_sample {
+              name_hash: 10
+              sample: 100
+            }
+          }
+        }
+        packet {
+          trusted_packet_sequence_id: 1
+          timestamp: 0
+          incremental_state_cleared: true
+          track_event {
+            categories: "cat2"
+            type: 3
+            name_iid: 2
+            chrome_histogram_sample {
+              name_hash: 20
+            }
+          }
+        }
+        """),
+        query=Metric('chrome_histogram_hashes'),
+        out=TextProto(r"""
+        [perfetto.protos.chrome_histogram_hashes]: {
+          hash: 10
+          hash: 20
+        }
+        """))
+
+  # Chrome user events
+  def test_chrome_user_event_hashes(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          trusted_packet_sequence_id: 1
+          timestamp: 0
+          incremental_state_cleared: true
+          track_event {
+            categories: "cat1"
+            type: 3
+            name_iid: 1
+            chrome_user_event {
+              action_hash: 10
+            }
+          }
+        }
+        packet {
+          trusted_packet_sequence_id: 1
+          timestamp: 0
+          incremental_state_cleared: true
+          track_event {
+            categories: "cat2"
+            type: 3
+            name_iid: 2
+            chrome_user_event {
+              action_hash: 20
+            }
+          }
+        }
+        """),
+        query=Metric('chrome_user_event_hashes'),
+        out=TextProto(r"""
+        [perfetto.protos.chrome_user_event_hashes]: {
+          action_hash: 10
+          action_hash: 20
+        }
+        """))
+
+  # Chrome performance mark
+  def test_chrome_performance_mark_hashes(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          trusted_packet_sequence_id: 1
+          timestamp: 0
+          incremental_state_cleared: true
+          track_event {
+            categories: "cat1"
+            type: 3
+            name: "name1"
+            [perfetto.protos.ChromeTrackEvent.chrome_hashed_performance_mark] {
+              site_hash: 10
+              mark_hash: 100
+            }
+          }
+        }
+        packet {
+          trusted_packet_sequence_id: 1
+          timestamp: 0
+          incremental_state_cleared: true
+          track_event {
+            categories: "cat2"
+            type: 3
+            name: "name2"
+            [perfetto.protos.ChromeTrackEvent.chrome_hashed_performance_mark] {
+              site_hash: 20
+              mark_hash: 200
+            }
+          }
+        }
+        """),
+        query=Metric('chrome_performance_mark_hashes'),
+        out=TextProto(r"""
+        [perfetto.protos.chrome_performance_mark_hashes]: {
+          site_hash: 10
+          site_hash: 20
+          mark_hash: 100
+          mark_hash: 200
+        }
+        """))
+
+  # Chrome reliable range
+  def test_chrome_reliable_range(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_reliable_range.textproto'),
+        query=Path('chrome_reliable_range_test.sql'),
+        out=Csv("""
+        "start","reason","debug_limiting_upid","debug_limiting_utid"
+        12,"First slice for utid=2","[NULL]",2
+        """))
+
+  def test_chrome_reliable_range_cropping(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_reliable_range_cropping.textproto'),
+        query=Path('chrome_reliable_range_test.sql'),
+        out=Csv("""
+        "start","reason","debug_limiting_upid","debug_limiting_utid"
+        10000,"Range of interest packet","[NULL]",2
+        """))
+
+  def test_chrome_reliable_range_missing_processes(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_reliable_range_missing_processes.textproto'),
+        query=Path('chrome_reliable_range_test.sql'),
+        out=Csv("""
+        "start","reason","debug_limiting_upid","debug_limiting_utid"
+        1011,"Missing process data for upid=2",2,1
+        """))
+
+  def test_chrome_reliable_range_missing_browser_main(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_reliable_range_missing_browser_main.textproto'),
+        query=Path('chrome_reliable_range_test.sql'),
+        out=Csv("""
+        "start","reason","debug_limiting_upid","debug_limiting_utid"
+        1011,"Missing main thread for upid=1",1,1
+        """))
+
+  def test_chrome_reliable_range_missing_gpu_main(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_reliable_range_missing_gpu_main.textproto'),
+        query=Path('chrome_reliable_range_test.sql'),
+        out=Csv("""
+        "start","reason","debug_limiting_upid","debug_limiting_utid"
+        1011,"Missing main thread for upid=1",1,1
+        """))
+
+  def test_chrome_reliable_range_missing_renderer_main(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_reliable_range_missing_renderer_main.textproto'),
+        query=Path('chrome_reliable_range_test.sql'),
+        out=Csv("""
+        "start","reason","debug_limiting_upid","debug_limiting_utid"
+        1011,"Missing main thread for upid=1",1,1
+        """))
+
+  def test_chrome_reliable_range_non_chrome_process(self):
+    return DiffTestBlueprint(
+        # We need a trace with a large number of non-chrome slices, so that the
+        # reliable range is affected by their filtering.
+        trace=DataPath('example_android_trace_30s.pb'),
+        query=Path('chrome_reliable_range_test.sql'),
+        out=Csv("""
+        "start","reason","debug_limiting_upid","debug_limiting_utid"
+        0,"[NULL]","[NULL]","[NULL]"
+        """))
+
+  # Chrome slices
+  def test_chrome_slice_names(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          trusted_packet_sequence_id: 1
+          timestamp: 1000
+          track_event {
+            categories: "cat"
+            name: "Looper.Dispatch: class1"
+            type: 3
+          }
+        }
+        packet {
+          trusted_packet_sequence_id: 1
+          timestamp: 2000
+          track_event {
+            categories: "cat"
+            name: "name2"
+            type: 3
+          }
+        }
+        packet {
+          chrome_metadata {
+            chrome_version_code: 123
+          }
+        }
+        """),
+        query=Metric('chrome_slice_names'),
+        out=TextProto(r"""
+        [perfetto.protos.chrome_slice_names]: {
+          chrome_version_code: 123
+          slice_name: "Looper.Dispatch: class1"
+          slice_name: "name2"
+        }
+        """))
+
+  # Chrome tasks.
+  def test_chrome_tasks(self):
+    return DiffTestBlueprint(
+        trace=DataPath(
+            'chrome_page_load_all_categories_not_extended.pftrace.gz'),
+        query="""
+        SELECT RUN_METRIC('chrome/chrome_tasks.sql');
+
+        SELECT full_name, task_type, count() AS count
+        FROM chrome_tasks
+        GROUP BY full_name, task_type
+        ORDER BY count DESC
+        LIMIT 50;
+        """,
+        out=Path('chrome_tasks.out'))
+
+  def test_top_level_java_choreographer_slices_top_level_java_chrome_tasks(
+      self):
+    return DiffTestBlueprint(
+        trace=DataPath('top_level_java_choreographer_slices'),
+        query="""
+        SELECT RUN_METRIC(
+          'chrome/chrome_tasks_template.sql',
+          'slice_table_name', 'slice',
+          'function_prefix', ''
+        );
+
+        SELECT
+          full_name,
+          task_type
+        FROM chrome_tasks
+        WHERE category = "toplevel,Java"
+        AND ts < 263904000000000
+        GROUP BY full_name, task_type;
+        """,
+        out=Path(
+            'top_level_java_choreographer_slices_top_level_java_chrome_tasks_test.out'
+        ))
+
+  # Chrome stack samples.
+  def test_chrome_stack_samples_for_task(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_stack_traces_symbolized_trace.pftrace'),
+        query="""
+        SELECT RUN_METRIC('chrome/chrome_stack_samples_for_task.sql',
+            'target_duration_ms', '0.000001',
+            'thread_name', '"CrBrowserMain"',
+            'task_name', '"sendTouchEvent"');
+
+        SELECT
+          sample.description,
+          sample.ts,
+          sample.depth
+        FROM chrome_stack_samples_for_task sample
+        JOIN (
+            SELECT
+              ts,
+              dur
+            FROM slice
+            WHERE ts = 696373965001470
+        ) test_slice
+        ON sample.ts >= test_slice.ts
+          AND sample.ts <= test_slice.ts + test_slice.dur
+        ORDER BY sample.ts, sample.depth;
+        """,
+        out=Path('chrome_stack_samples_for_task_test.out'))
+
+  # Log messages.
+  def test_chrome_log_message(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          timestamp: 0
+          incremental_state_cleared: true
+          trusted_packet_sequence_id: 1
+          track_descriptor {
+            uuid: 12345
+            thread {
+              pid: 123
+              tid: 345
+            }
+            parent_uuid: 0
+            chrome_thread {
+              thread_type: THREAD_POOL_FG_WORKER
+            }
+          }
+        }
+
+        packet {
+          trusted_packet_sequence_id: 1
+          timestamp: 10
+          track_event {
+            track_uuid: 12345
+            categories: "cat1"
+            type: TYPE_INSTANT
+            name: "slice1"
+            log_message {
+                body_iid: 1
+                source_location_iid: 3
+            }
+          }
+          interned_data {
+            log_message_body {
+                iid: 1
+                body: "log message"
+            }
+            source_locations {
+                iid: 3
+                function_name: "func"
+                file_name: "foo.cc"
+                line_number: 123
+            }
+          }
+        }
+        """),
+        query="""
+        SELECT utid, tag, msg FROM android_logs;
+        """,
+        out=Csv("""
+        "utid","tag","msg"
+        1,"foo.cc:123","log message"
+        """))
+
+  def test_chrome_log_message_args(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          timestamp: 0
+          incremental_state_cleared: true
+          trusted_packet_sequence_id: 1
+          track_descriptor {
+            uuid: 12345
+            thread {
+              pid: 123
+              tid: 345
+            }
+            parent_uuid: 0
+            chrome_thread {
+              thread_type: THREAD_POOL_FG_WORKER
+            }
+          }
+        }
+
+        packet {
+          trusted_packet_sequence_id: 1
+          timestamp: 10
+          track_event {
+            track_uuid: 12345
+            categories: "cat1"
+            type: TYPE_INSTANT
+            name: "slice1"
+            log_message {
+                body_iid: 1
+                source_location_iid: 3
+            }
+          }
+          interned_data {
+            log_message_body {
+                iid: 1
+                body: "log message"
+            }
+            source_locations {
+                iid: 3
+                function_name: "func"
+                file_name: "foo.cc"
+                line_number: 123
+            }
+          }
+        }
+        """),
+        query=Path('chrome_log_message_args_test.sql'),
+        out=Csv("""
+        "log_message","function_name","file_name","line_number"
+        "log message","func","foo.cc",123
+        """))
+
+  # Chrome custom navigation event names
+  def test_chrome_custom_navigation_tasks(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_custom_navigation_trace.gz'),
+        query="""
+        SELECT RUN_METRIC('chrome/chrome_tasks.sql');
+
+        SELECT full_name, task_type, count() AS count
+        FROM chrome_tasks
+        WHERE full_name GLOB 'FrameHost::BeginNavigation*'
+          OR full_name GLOB 'FrameHost::DidCommitProvisionalLoad*'
+          OR full_name GLOB 'FrameHost::DidCommitSameDocumentNavigation*'
+          OR full_name GLOB 'FrameHost::DidStopLoading*'
+        GROUP BY full_name, task_type
+        ORDER BY count DESC
+        LIMIT 50;
+        """,
+        out=Csv("""
+        "full_name","task_type","count"
+        "FrameHost::BeginNavigation (SUBFRAME)","navigation_task",5
+        "FrameHost::DidStopLoading (SUBFRAME)","navigation_task",3
+        "FrameHost::BeginNavigation (PRIMARY_MAIN_FRAME)","navigation_task",1
+        "FrameHost::DidCommitProvisionalLoad (SUBFRAME)","navigation_task",1
+        """))
+
+  # Trace proto content
+  def test_proto_content(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_scroll_without_vsync.pftrace'),
+        query=Path('proto_content_test.sql'),
+        out=Path('proto_content.out'))
+
+  # TODO(mayzner): Uncomment when it works
+  # def test_proto_content_path(self):
+  #   return DiffTestBlueprint(
+  #       trace=DataPath('chrome_scroll_without_vsync.pftrace'),
+  #       query=Path('proto_content_path_test.sql'),
+  #       out=Csv("""
+  #       "total_size","field_type","field_name","parent_id","event_category","event_name"
+  #       137426,"TracePacket","[NULL]","[NULL]","[NULL]","[NULL]"
+  #       59475,"TrackEvent","#track_event",415,"[NULL]","[NULL]"
+  #       37903,"TrackEvent","#track_event",17,"[NULL]","[NULL]"
+  #       35904,"int32","#trusted_uid",17,"[NULL]","[NULL]"
+  #       35705,"TracePacket","[NULL]","[NULL]","input,benchmark","LatencyInfo.Flow"
+  #       29403,"TracePacket","[NULL]","[NULL]","cc,input","[NULL]"
+  #       24703,"ChromeLatencyInfo","#chrome_latency_info",18,"[NULL]","[NULL]"
+  #       22620,"uint64","#time_us",26,"[NULL]","[NULL]"
+  #       18711,"TrackEvent","#track_event",1467,"[NULL]","[NULL]"
+  #       15606,"uint64","#timestamp",17,"[NULL]","[NULL]"
+  #       """))
diff --git a/test/trace_processor/diff_tests/chrome/tests_args.py b/test/trace_processor/diff_tests/chrome/tests_args.py
new file mode 100644
index 0000000..9495525
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/tests_args.py
@@ -0,0 +1,131 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class ChromeArgs(TestSuite):
+  # Unsymbolized args.
+  def test_unsymbolized_args(self):
+    return DiffTestBlueprint(
+        trace=Path('unsymbolized_args.textproto'),
+        query=Metric('chrome_unsymbolized_args'),
+        out=TextProto(r"""
+        [perfetto.protos.chrome_unsymbolized_args]: {
+          args {
+             module: "/liblib.so"
+             build_id: "6275696c642d6964"
+             address: 123
+             google_lookup_id: "6275696c642d6964"
+           }
+           args {
+             module: "/libmonochrome_64.so"
+             build_id: "7f0715c286f8b16c10e4ad349cda3b9b56c7a773"
+             address: 234
+             google_lookup_id: "c215077ff8866cb110e4ad349cda3b9b0"
+           }
+        }
+        """))
+
+  def test_async_trace_1_count_slices(self):
+    return DiffTestBlueprint(
+        trace=DataPath('async-trace-1.json'),
+        query="""
+        SELECT COUNT(1) FROM slice;
+        """,
+        out=Csv("""
+        "COUNT(1)"
+        16
+        """))
+
+  def test_async_trace_2_count_slices(self):
+    return DiffTestBlueprint(
+        trace=DataPath('async-trace-2.json'),
+        query="""
+        SELECT COUNT(1) FROM slice;
+        """,
+        out=Csv("""
+        "COUNT(1)"
+        35
+        """))
+
+  # Chrome args class names
+  def test_chrome_args_class_names(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          timestamp: 0
+          incremental_state_cleared: true
+          trusted_packet_sequence_id: 1
+          track_descriptor {
+            uuid: 12345
+            thread {
+              pid: 123
+              tid: 345
+            }
+            parent_uuid: 0
+            chrome_thread {
+              thread_type: THREAD_POOL_FG_WORKER
+            }
+          }
+        }
+
+        packet {
+          trusted_packet_sequence_id: 1
+          timestamp: 0
+          incremental_state_cleared: true
+          track_event {
+            track_uuid: 12345
+            categories: "cat1"
+            type: 3
+            name: "name1"
+            [perfetto.protos.ChromeTrackEvent.android_view_dump] {
+              activity {
+                name: "A"
+                view {
+                  class_name: "abc"
+                },
+                view {
+                  class_name: "def"
+                },
+                view {
+                  class_name: "ghi"
+                }
+              }
+              activity {
+                name: "B"
+                view {
+                  class_name: "jkl"
+                }
+              }
+            }
+          }
+        }
+        """),
+        query=Metric('chrome_args_class_names'),
+        out=TextProto(r"""
+
+        [perfetto.protos.chrome_args_class_names] {
+          class_names_per_version {
+            class_name: "abc"
+            class_name: "def"
+            class_name: "ghi"
+            class_name: "jkl"
+          }
+        }
+        """))
diff --git a/test/trace_processor/diff_tests/chrome/tests_general.py b/test/trace_processor/diff_tests/chrome/tests_general.py
new file mode 100644
index 0000000..99938b8
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/tests_general.py
@@ -0,0 +1,212 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import DiffTestModule
+
+
+class ChromeGeneral(DiffTestModule):
+
+  def test_chrome_histogram_hashes(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_histogram_hashes.textproto'),
+        query=Metric('chrome_histogram_hashes'),
+        out=TextProto(r"""
+[perfetto.protos.chrome_histogram_hashes]: {
+  hash: 10
+  hash: 20
+}
+"""))
+
+  def test_chrome_user_event_hashes(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_user_event_hashes.textproto'),
+        query=Metric('chrome_user_event_hashes'),
+        out=TextProto(r"""
+[perfetto.protos.chrome_user_event_hashes]: {
+  action_hash: 10
+  action_hash: 20
+}
+
+"""))
+
+  def test_chrome_performance_mark_hashes(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_performance_mark_hashes.textproto'),
+        query=Metric('chrome_performance_mark_hashes'),
+        out=TextProto(r"""
+[perfetto.protos.chrome_performance_mark_hashes]: {
+  site_hash: 10
+  site_hash: 20
+  mark_hash: 100
+  mark_hash: 200
+}
+"""))
+
+  def test_chrome_reliable_range(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_reliable_range.textproto'),
+        query=Path('chrome_reliable_range_test.sql'),
+        out=Csv("""
+"start","reason","debug_limiting_upid","debug_limiting_utid"
+12,"First slice for utid=2","[NULL]",2
+"""))
+
+  def test_chrome_reliable_range_cropping(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_reliable_range_cropping.textproto'),
+        query=Path('chrome_reliable_range_test.sql'),
+        out=Csv("""
+"start","reason","debug_limiting_upid","debug_limiting_utid"
+10000,"Range of interest packet","[NULL]",2
+"""))
+
+  def test_chrome_reliable_range_missing_processes(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_reliable_range_missing_processes.textproto'),
+        query=Path('chrome_reliable_range_test.sql'),
+        out=Csv("""
+"start","reason","debug_limiting_upid","debug_limiting_utid"
+1011,"Missing process data for upid=2",2,1
+"""))
+
+  def test_chrome_slice_names(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_slice_names.textproto'),
+        query=Metric('chrome_slice_names'),
+        out=TextProto(r"""
+[perfetto.protos.chrome_slice_names]: {
+  chrome_version_code: 123
+  slice_name: "Looper.Dispatch: class1"
+  slice_name: "name2"
+}
+"""))
+
+  def test_chrome_tasks(self):
+    return DiffTestBlueprint(
+        trace=DataPath(
+            'chrome_page_load_all_categories_not_extended.pftrace.gz'),
+        query="""
+SELECT RUN_METRIC('chrome/chrome_tasks.sql');
+
+SELECT full_name, task_type, count() AS count
+FROM chrome_tasks
+GROUP BY full_name, task_type
+ORDER BY count DESC
+LIMIT 50;
+""",
+        out=Path('chrome_tasks.out'))
+
+  def test_top_level_java_choreographer_slices_top_level_java_chrome_tasks(
+      self):
+    return DiffTestBlueprint(
+        trace=DataPath('top_level_java_choreographer_slices'),
+        query="""
+SELECT RUN_METRIC(
+  'chrome/chrome_tasks_template.sql',
+  'slice_table_name', 'slice',
+  'function_prefix', ''
+);
+
+SELECT
+  full_name,
+  task_type
+FROM chrome_tasks
+WHERE category = "toplevel,Java"
+AND ts < 263904000000000
+GROUP BY full_name, task_type;
+""",
+        out=Path(
+            'top_level_java_choreographer_slices_top_level_java_chrome_tasks_test.out'
+        ))
+
+  def test_chrome_stack_samples_for_task(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_stack_traces_symbolized_trace.pftrace'),
+        query="""
+SELECT RUN_METRIC('chrome/chrome_stack_samples_for_task.sql',
+    'target_duration_ms', '0.000001',
+    'thread_name', '"CrBrowserMain"',
+    'task_name', '"sendTouchEvent"');
+
+SELECT
+  sample.description,
+  sample.ts,
+  sample.depth
+FROM chrome_stack_samples_for_task sample
+JOIN (
+    SELECT
+      ts,
+      dur
+    FROM slice
+    WHERE ts = 696373965001470
+) test_slice
+ON sample.ts >= test_slice.ts
+  AND sample.ts <= test_slice.ts + test_slice.dur
+ORDER BY sample.ts, sample.depth;
+""",
+        out=Path('chrome_stack_samples_for_task_test.out'))
+
+  def test_chrome_log_message(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_log_message.textproto'),
+        query="""
+SELECT utid, tag, msg FROM android_logs;
+""",
+        out=Csv("""
+"utid","tag","msg"
+1,"foo.cc:123","log message"
+"""))
+
+  def test_chrome_log_message_args(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_log_message.textproto'),
+        query=Path('chrome_log_message_args_test.sql'),
+        out=Csv("""
+"log_message","function_name","file_name","line_number"
+"log message","func","foo.cc",123
+"""))
+
+  def test_chrome_custom_navigation_tasks(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_custom_navigation_trace.gz'),
+        query="""
+SELECT RUN_METRIC('chrome/chrome_tasks.sql');
+
+SELECT full_name, task_type, count() AS count
+FROM chrome_tasks
+WHERE full_name GLOB 'FrameHost::BeginNavigation*'
+  OR full_name GLOB 'FrameHost::DidCommitProvisionalLoad*'
+  OR full_name GLOB 'FrameHost::DidCommitSameDocumentNavigation*'
+  OR full_name GLOB 'FrameHost::DidStopLoading*'
+GROUP BY full_name, task_type
+ORDER BY count DESC
+LIMIT 50;
+""",
+        out=Csv("""
+"full_name","task_type","count"
+"FrameHost::BeginNavigation (SUBFRAME)","navigation_task",5
+"FrameHost::DidStopLoading (SUBFRAME)","navigation_task",3
+"FrameHost::BeginNavigation (PRIMARY_MAIN_FRAME)","navigation_task",1
+"FrameHost::DidCommitProvisionalLoad (SUBFRAME)","navigation_task",1
+"""))
+
+  def test_proto_content(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_scroll_without_vsync.pftrace'),
+        query=Path('proto_content_test.sql'),
+        out=Path('proto_content.out'))
diff --git a/test/trace_processor/diff_tests/chrome/tests_memory_snapshots.py b/test/trace_processor/diff_tests/chrome/tests_memory_snapshots.py
new file mode 100644
index 0000000..07e5341
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/tests_memory_snapshots.py
@@ -0,0 +1,153 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class ChromeMemorySnapshots(TestSuite):
+
+  def test_memory_snapshot_general_validation(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_memory_snapshot.pftrace'),
+        query="""
+        SELECT
+          (
+            SELECT COUNT(*) FROM memory_snapshot
+          ) AS total_snapshots,
+          (
+            SELECT COUNT(*) FROM process
+          ) AS total_processes,
+          (
+            SELECT COUNT(*) FROM process_memory_snapshot
+          ) AS total_process_snapshots,
+          (
+            SELECT COUNT(*) FROM memory_snapshot_node
+          ) AS total_nodes,
+          (
+            SELECT COUNT(*) FROM memory_snapshot_edge
+          ) AS total_edges,
+          (
+            SELECT COUNT(DISTINCT args.id)
+            FROM args
+            JOIN memory_snapshot_node
+              ON args.arg_set_id = memory_snapshot_node.arg_set_id
+          ) AS total_node_args,
+          (
+            SELECT COUNT(*) FROM profiler_smaps
+            JOIN memory_snapshot ON timestamp = ts
+          ) AS total_smaps;
+        """,
+        out=Path('memory_snapshot_general_validation.out'))
+
+  def test_memory_snapshot_os_dump_events(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_memory_snapshot.pftrace'),
+        query=Path('memory_snapshot_os_dump_events_test.sql'),
+        out=Path('memory_snapshot_os_dump_events.out'))
+
+  def test_memory_snapshot_chrome_dump_events(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_memory_snapshot.pftrace'),
+        query="""
+        SELECT
+          pms.id AS process_snapshot_id,
+          upid,
+          snapshot_id,
+          timestamp,
+          detail_level
+        FROM memory_snapshot ms
+        LEFT JOIN process_memory_snapshot pms
+          ON ms.id = pms.snapshot_id;
+        """,
+        out=Path('memory_snapshot_chrome_dump_events.out'))
+
+  def test_memory_snapshot_nodes(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_memory_snapshot.pftrace'),
+        query="""
+        SELECT
+          id,
+          process_snapshot_id,
+          parent_node_id,
+          path,
+          size,
+          effective_size
+        FROM memory_snapshot_node
+        LIMIT 20;
+        """,
+        out=Path('memory_snapshot_nodes.out'))
+
+  def test_memory_snapshot_edges(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_memory_snapshot.pftrace'),
+        query="""
+        SELECT
+          id,
+          source_node_id,
+          target_node_id,
+          importance
+        FROM memory_snapshot_edge
+        LIMIT 20;
+        """,
+        out=Path('memory_snapshot_edges.out'))
+
+  def test_memory_snapshot_node_args(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_memory_snapshot.pftrace'),
+        query="""
+        SELECT
+          node.id AS node_id,
+          key,
+          value_type,
+          int_value,
+          string_value
+        FROM memory_snapshot_node node
+        JOIN args ON node.arg_set_id = args.arg_set_id
+        LIMIT 20;
+        """,
+        out=Path('memory_snapshot_node_args.out'))
+
+  def test_memory_snapshot_smaps(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_memory_snapshot.pftrace'),
+        query="""
+        SELECT
+          process.upid,
+          process.name,
+          smap.ts,
+          path,
+          size_kb,
+          private_dirty_kb,
+          swap_kb,
+          file_name,
+          start_address,
+          module_timestamp,
+          module_debugid,
+          module_debug_path,
+          protection_flags,
+          private_clean_resident_kb,
+          shared_dirty_resident_kb,
+          shared_clean_resident_kb,
+          locked_kb,
+          proportional_resident_kb
+        FROM process
+        JOIN profiler_smaps smap ON process.upid = smap.upid
+        JOIN memory_snapshot ms ON ms.timestamp = smap.ts
+        LIMIT 20;
+        """,
+        out=Path('memory_snapshot_smaps.out'))
diff --git a/test/trace_processor/diff_tests/chrome/tests_processes.py b/test/trace_processor/diff_tests/chrome/tests_processes.py
new file mode 100644
index 0000000..1bb75c5
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/tests_processes.py
@@ -0,0 +1,379 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class ChromeProcesses(TestSuite):
+
+  def test_chrome_processes(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_scroll_without_vsync.pftrace'),
+        query="""
+        SELECT RUN_METRIC('chrome/chrome_processes.sql');
+        SELECT pid, name, process_type FROM chrome_process;
+        """,
+        out=Csv("""
+        "pid","name","process_type"
+        18250,"Renderer","Renderer"
+        17547,"Browser","Browser"
+        18277,"GPU Process","Gpu"
+        17578,"Browser","Browser"
+        """))
+
+  def test_chrome_processes_android_systrace(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_android_systrace.pftrace'),
+        query="""
+        SELECT RUN_METRIC('chrome/chrome_processes.sql');
+        SELECT pid, name, process_type FROM chrome_process;
+        """,
+        out=Path('chrome_processes_android_systrace.out'))
+
+  def test_chrome_threads(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_scroll_without_vsync.pftrace'),
+        query="""
+        SELECT RUN_METRIC('chrome/chrome_processes.sql');
+        SELECT tid, name, is_main_thread, canonical_name
+        FROM chrome_thread
+        ORDER BY tid, name;
+        """,
+        out=Path('chrome_threads.out'))
+
+  def test_chrome_threads_android_systrace(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_android_systrace.pftrace'),
+        query="""
+        SELECT RUN_METRIC('chrome/chrome_processes.sql');
+        SELECT tid, name, is_main_thread, canonical_name
+        FROM chrome_thread
+        ORDER BY tid, name;
+        """,
+        out=Path('chrome_threads_android_systrace.out'))
+
+  def test_chrome_processes_type(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_scroll_without_vsync.pftrace'),
+        query="""
+        SELECT pid, name, string_value AS chrome_process_type
+        FROM
+          process
+        JOIN
+          (SELECT * FROM args WHERE key = "chrome.process_type") chrome_process_args
+          ON
+            process.arg_set_id = chrome_process_args.arg_set_id
+        ORDER BY pid;
+        """,
+        out=Csv("""
+        "pid","name","chrome_process_type"
+        17547,"Browser","Browser"
+        17578,"Browser","Browser"
+        18250,"Renderer","Renderer"
+        18277,"GPU Process","Gpu"
+        """))
+
+  def test_chrome_processes_type_android_systrace(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_android_systrace.pftrace'),
+        query="""
+        SELECT pid, name, string_value AS chrome_process_type
+        FROM
+          process
+        JOIN
+          (SELECT * FROM args WHERE key = "chrome.process_type") chrome_process_args
+          ON
+            process.arg_set_id = chrome_process_args.arg_set_id
+        ORDER BY pid;
+        """,
+        out=Path('chrome_processes_type_android_systrace.out'))
+
+  def test_track_with_chrome_process(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          trusted_packet_sequence_id: 1
+          incremental_state_cleared: true
+          timestamp: 0
+          track_descriptor {
+            uuid: 10
+            process {
+              pid: 5
+              process_name: "p5"
+            }
+            # Empty Chrome process. This is similar to a process descriptor emitted by
+            # Chrome for a process with an unknown Chrome process_type. This process
+            # should still receive a "chrome_process_type" arg in the args table, but
+            # with a NULL value.
+            chrome_process {}
+          }
+        }
+        """),
+        query="""
+        SELECT pid, name, string_value AS chrome_process_type
+        FROM
+          process
+        JOIN
+          (SELECT * FROM args WHERE key = "chrome.process_type") chrome_process_args
+          ON
+            process.arg_set_id = chrome_process_args.arg_set_id
+        ORDER BY pid;
+        """,
+        out=Csv("""
+        "pid","name","chrome_process_type"
+        5,"p5","[NULL]"
+        """))
+
+  # Missing processes.
+  def test_chrome_missing_processes_default_trace(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_scroll_without_vsync.pftrace'),
+        query="""
+        SELECT upid, pid, reliable_from
+        FROM
+          experimental_missing_chrome_processes
+        JOIN
+          process
+          USING(upid)
+        ORDER BY upid;
+        """,
+        out=Csv("""
+        "upid","pid","reliable_from"
+        """))
+
+  def test_chrome_missing_processes(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          timestamp: 1
+          incremental_state_cleared: true
+          trusted_packet_sequence_id: 1
+          track_event {
+            type: TYPE_INSTANT
+            name: "ActiveProcesses"
+            chrome_active_processes {
+              pid: 10
+              pid: 100
+              pid: 1000
+            }
+          }
+        }
+        packet {
+          timestamp: 1
+          trusted_packet_sequence_id: 2
+          track_descriptor {
+            uuid: 1
+            process {
+              pid: 10
+            }
+            parent_uuid: 0
+          }
+        }
+        packet {
+          timestamp: 1000000000
+          trusted_packet_sequence_id: 3
+          track_descriptor {
+            uuid: 2
+            process {
+              pid: 100
+            }
+            parent_uuid: 0
+          }
+        }
+        """),
+        query="""
+        SELECT upid, pid, reliable_from
+        FROM
+          experimental_missing_chrome_processes
+        JOIN
+          process
+          USING(upid)
+        ORDER BY upid;
+        """,
+        out=Csv("""
+        "upid","pid","reliable_from"
+        2,100,1000000000
+        3,1000,"[NULL]"
+        """))
+
+  def test_chrome_missing_processes_args(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          timestamp: 1
+          incremental_state_cleared: true
+          trusted_packet_sequence_id: 1
+          track_event {
+            type: TYPE_INSTANT
+            name: "ActiveProcesses"
+            chrome_active_processes {
+              pid: 10
+              pid: 100
+              pid: 1000
+            }
+          }
+        }
+        packet {
+          timestamp: 1
+          trusted_packet_sequence_id: 2
+          track_descriptor {
+            uuid: 1
+            process {
+              pid: 10
+            }
+            parent_uuid: 0
+          }
+        }
+        packet {
+          timestamp: 1000000000
+          trusted_packet_sequence_id: 3
+          track_descriptor {
+            uuid: 2
+            process {
+              pid: 100
+            }
+            parent_uuid: 0
+          }
+        }
+        """),
+        query="""
+        SELECT arg_set_id, key, int_value
+        FROM
+          slice
+        JOIN
+          args
+          USING(arg_set_id)
+        ORDER BY arg_set_id, key;
+        """,
+        out=Csv("""
+        "arg_set_id","key","int_value"
+        2,"chrome_active_processes.pid[0]",10
+        2,"chrome_active_processes.pid[1]",100
+        2,"chrome_active_processes.pid[2]",1000
+        """))
+
+  def test_chrome_missing_processes_2(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          timestamp: 1
+          incremental_state_cleared: true
+          trusted_packet_sequence_id: 1
+          track_event {
+            type: TYPE_INSTANT
+            name: "ActiveProcesses"
+            [perfetto.protos.ChromeTrackEvent.active_processes]: {
+              pid: 10
+              pid: 100
+              pid: 1000
+            }
+          }
+        }
+        packet {
+          timestamp: 1
+          trusted_packet_sequence_id: 2
+          track_descriptor {
+            uuid: 1
+            process {
+              pid: 10
+            }
+            parent_uuid: 0
+          }
+        }
+        packet {
+          timestamp: 1000000000
+          trusted_packet_sequence_id: 3
+          track_descriptor {
+            uuid: 2
+            process {
+              pid: 100
+            }
+            parent_uuid: 0
+          }
+        }
+        """),
+        query="""
+        SELECT upid, pid, reliable_from
+        FROM
+          experimental_missing_chrome_processes
+        JOIN
+          process
+          USING(upid)
+        ORDER BY upid;
+        """,
+        out=Csv("""
+        "upid","pid","reliable_from"
+        2,100,1000000000
+        3,1000,"[NULL]"
+        """))
+
+  def test_chrome_missing_processes_extension_args(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          timestamp: 1
+          incremental_state_cleared: true
+          trusted_packet_sequence_id: 1
+          track_event {
+            type: TYPE_INSTANT
+            name: "ActiveProcesses"
+            [perfetto.protos.ChromeTrackEvent.active_processes]: {
+              pid: 10
+              pid: 100
+              pid: 1000
+            }
+          }
+        }
+        packet {
+          timestamp: 1
+          trusted_packet_sequence_id: 2
+          track_descriptor {
+            uuid: 1
+            process {
+              pid: 10
+            }
+            parent_uuid: 0
+          }
+        }
+        packet {
+          timestamp: 1000000000
+          trusted_packet_sequence_id: 3
+          track_descriptor {
+            uuid: 2
+            process {
+              pid: 100
+            }
+            parent_uuid: 0
+          }
+        }
+        """),
+        query="""
+        SELECT arg_set_id, key, int_value
+        FROM
+          slice
+        JOIN
+          args
+          USING(arg_set_id)
+        ORDER BY arg_set_id, key;
+        """,
+        out=Csv("""
+        "arg_set_id","key","int_value"
+        2,"active_processes.pid[0]",10
+        2,"active_processes.pid[1]",100
+        2,"active_processes.pid[2]",1000
+        """))
diff --git a/test/trace_processor/diff_tests/chrome/tests_rail_modes.py b/test/trace_processor/diff_tests/chrome/tests_rail_modes.py
new file mode 100644
index 0000000..1e3df14
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/tests_rail_modes.py
@@ -0,0 +1,161 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class ChromeRailModes(TestSuite):
+
+  def test_combined_rail_modes(self):
+    return DiffTestBlueprint(
+        trace=Path('combined_rail_modes.py'),
+        query="""
+        SELECT RUN_METRIC('chrome/rail_modes.sql');
+        SELECT * FROM combined_overall_rail_slices;
+        """,
+        out=Csv("""
+        "id","ts","dur","rail_mode"
+        1,0,10000,"response"
+        2,10000,25000,"animation"
+        3,35000,10000,"background"
+        """))
+
+  def test_cpu_time_by_combined_rail_mode(self):
+    return DiffTestBlueprint(
+        trace=Path('cpu_time_by_combined_rail_mode.py'),
+        query="""
+        SELECT RUN_METRIC('chrome/cpu_time_by_rail_mode.sql');
+        SELECT * FROM cpu_time_by_rail_mode;
+        """,
+        out=Csv("""
+        "id","ts","dur","rail_mode","cpu_dur"
+        1,0,10000,"response",26000
+        2,10000,20000,"animation",20000
+        3,30000,5000,"background",8000
+        4,35000,10000,"animation",21000
+        5,45000,10000,"background",1000
+        """))
+
+  def test_actual_power_by_combined_rail_mode(self):
+    return DiffTestBlueprint(
+        trace=Path('actual_power_by_combined_rail_mode.py'),
+        query="""
+        SELECT RUN_METRIC('chrome/actual_power_by_rail_mode.sql');
+        SELECT * FROM real_power_by_rail_mode;
+        """,
+        out=Csv("""
+        "id","ts","dur","rail_mode","subsystem","joules","drain_w"
+        1,0,10000000,"response","cellular",0.000000,0.000000
+        1,0,10000000,"response","cpu_little",0.000140,0.014000
+        2,10000000,20000000,"animation","cellular",0.000350,0.017500
+        2,10000000,20000000,"animation","cpu_little",0.000140,0.007000
+        3,30000000,5000000,"background","cellular",0.000018,0.003500
+        3,30000000,5000000,"background","cpu_little",0.000007,0.001400
+        4,35000000,10000000,"animation","cellular",0.000021,0.002100
+        4,35000000,10000000,"animation","cpu_little",0.000070,0.007000
+        5,45000000,10000000,"background","cellular",0.000003,0.000350
+        5,45000000,10000000,"background","cpu_little",0.000070,0.007000
+        """))
+
+  def test_estimated_power_by_combined_rail_mode(self):
+    return DiffTestBlueprint(
+        trace=Path('estimated_power_by_combined_rail_mode.py'),
+        query="""
+        SELECT RUN_METRIC('chrome/estimated_power_by_rail_mode.sql');
+        SELECT * FROM power_by_rail_mode;
+        """,
+        out=Csv("""
+        "id","ts","dur","rail_mode","mas","ma"
+        1,0,10000000,"response",0.554275,55.427500
+        2,10000000,20000000,"animation",0.284850,14.242500
+        3,30000000,5000000,"background",0.076233,15.246667
+        4,35000000,10000000,"animation",0.536850,53.685000
+        5,45000000,10000000,"background",0.071580,7.158000
+        """))
+
+  def test_modified_rail_modes(self):
+    return DiffTestBlueprint(
+        trace=Path('modified_rail_modes.py'),
+        query="""
+        SELECT RUN_METRIC('chrome/rail_modes.sql');
+        SELECT * FROM modified_rail_slices;
+        """,
+        out=Csv("""
+        "id","ts","dur","mode"
+        2,0,1000000000,"response"
+        3,1000000000,1950000000,"foreground_idle"
+        4,2950000000,333333324,"animation"
+        5,3283333324,216666676,"foreground_idle"
+        6,3500000000,1000000000,"background"
+        """))
+
+  def test_modified_rail_modes_no_vsyncs(self):
+    return DiffTestBlueprint(
+        trace=Path('modified_rail_modes_no_vsyncs.py'),
+        query="""
+        SELECT RUN_METRIC('chrome/rail_modes.sql');
+        SELECT * FROM modified_rail_slices;
+        """,
+        out=Csv("""
+        "id","ts","dur","mode"
+        2,0,1000000000,"response"
+        3,1000000000,2500000000,"foreground_idle"
+        4,3500000000,1000000000,"background"
+        """))
+
+  def test_modified_rail_modes_with_input(self):
+    return DiffTestBlueprint(
+        trace=Path('modified_rail_modes_with_input.py'),
+        query="""
+        SELECT RUN_METRIC('chrome/rail_modes.sql');
+        SELECT * FROM modified_rail_slices;
+        """,
+        out=Csv("""
+        "id","ts","dur","mode"
+        2,0,1000000000,"response"
+        3,1000000000,1950000000,"foreground_idle"
+        4,2950000000,50000000,"animation"
+        5,3000000000,66666674,"response"
+        6,3066666674,216666650,"animation"
+        7,3283333324,216666676,"foreground_idle"
+        8,3500000000,1000000000,"background"
+        """))
+
+  def test_modified_rail_modes_long(self):
+    return DiffTestBlueprint(
+        trace=Path('modified_rail_modes_long.py'),
+        query="""
+        SELECT RUN_METRIC('chrome/rail_modes.sql');
+        SELECT * FROM modified_rail_slices;
+        """,
+        out=Csv("""
+        "id","ts","dur","mode"
+        2,0,1000000000,"response"
+        3,1000000000,1,"background"
+        """))
+
+  def test_modified_rail_modes_extra_long(self):
+    return DiffTestBlueprint(
+        trace=Path('modified_rail_modes_extra_long.py'),
+        query="""
+        SELECT RUN_METRIC('chrome/rail_modes.sql');
+        SELECT * FROM modified_rail_slices;
+        """,
+        out=Csv("""
+        "id","ts","dur","mode"
+        """))
diff --git a/test/trace_processor/diff_tests/chrome/tests_scroll_jank.py b/test/trace_processor/diff_tests/chrome/tests_scroll_jank.py
new file mode 100644
index 0000000..c5fd3bc
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/tests_scroll_jank.py
@@ -0,0 +1,520 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class ChromeScrollJank(TestSuite):
+  # Scroll jank metrics
+  def test_scroll_jank_general_validation(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_scroll_without_vsync.pftrace'),
+        query=Path('scroll_jank_general_validation_test.sql'),
+        out=Path('scroll_jank_general_validation.out'))
+
+  def test_scroll_jank(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_scroll_without_vsync.pftrace'),
+        query="""
+        SELECT RUN_METRIC('chrome/scroll_jank.sql');
+
+        SELECT
+          gesture_scroll_id,
+          trace_id,
+          jank,
+          ts,
+          dur,
+          jank_budget
+        FROM scroll_jank;
+        """,
+        out=Path('scroll_jank.out'))
+
+  def test_event_latency_to_breakdowns(self):
+    return DiffTestBlueprint(
+        trace=DataPath('event_latency_with_args.perfetto-trace'),
+        query="""
+        SELECT RUN_METRIC('chrome/event_latency_to_breakdowns.sql');
+
+        SELECT
+          event_latency_ts,
+          event_latency_dur,
+          event_type,
+          GenerationToRendererCompositorNs,
+          GenerationToBrowserMainNs,
+          BrowserMainToRendererCompositorNs,
+          RendererCompositorQueueingDelayNs,
+          unknown_stages_seen
+        FROM event_latency_to_breakdowns
+        ORDER BY event_latency_id
+        LIMIT 30;
+        """,
+        out=Path('event_latency_to_breakdowns.out'))
+
+  def test_event_latency_scroll_jank(self):
+    return DiffTestBlueprint(
+        trace=DataPath('event_latency_with_args.perfetto-trace'),
+        query="""
+        SELECT RUN_METRIC('chrome/event_latency_scroll_jank.sql');
+
+        SELECT
+          jank,
+          next_jank,
+          prev_jank,
+          gesture_begin_ts,
+          gesture_end_ts,
+          ts,
+          dur,
+          event_type,
+          next_ts,
+          next_dur,
+          prev_ts,
+          prev_dur
+        FROM scroll_event_latency_jank
+        ORDER BY jank DESC
+        LIMIT 10;
+        """,
+        out=Path('event_latency_scroll_jank.out'))
+
+  def test_event_latency_scroll_jank_cause(self):
+    return DiffTestBlueprint(
+        trace=DataPath('event_latency_with_args.perfetto-trace'),
+        query="""
+        SELECT RUN_METRIC('chrome/event_latency_scroll_jank_cause.sql');
+
+        SELECT
+          dur,
+          ts,
+          event_type,
+          next_jank,
+          prev_jank,
+          next_delta_dur_ns,
+          prev_delta_dur_ns,
+          cause_of_jank,
+          max_delta_dur_ns,
+          sub_cause_of_jank
+        FROM event_latency_scroll_jank_cause
+        ORDER by ts;
+        """,
+        out=Path('event_latency_scroll_jank_cause.out'))
+
+  def test_scroll_flow_event(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_scroll_without_vsync.pftrace'),
+        query="""
+        SELECT RUN_METRIC('chrome/scroll_flow_event.sql');
+
+        SELECT
+          trace_id,
+          ts,
+          dur,
+          jank,
+          step,
+          ancestor_end,
+          maybe_next_ancestor_ts,
+          next_ts,
+          next_trace_id,
+          next_step
+        FROM scroll_flow_event
+        ORDER BY gesture_scroll_id, trace_id, ts;
+        """,
+        out=Path('scroll_flow_event.out'))
+
+  def test_scroll_flow_event_general_validation(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_scroll_without_vsync.pftrace'),
+        query="""
+        SELECT RUN_METRIC('chrome/scroll_flow_event.sql');
+
+        SELECT
+          -- Each trace_id (in our example trace not true in general) has 8 steps. There
+          -- are 139 scrolls. So we expect 1112 rows in total 72 of which are janky.
+          (
+            SELECT
+              COUNT(*)
+            FROM (
+              SELECT
+                trace_id,
+                COUNT(*)
+              FROM scroll_flow_event
+              GROUP BY trace_id
+            )
+          ) AS total_scroll_updates,
+          (
+            SELECT COUNT(*) FROM scroll_flow_event
+          ) AS total_flow_event_steps,
+          (
+            SELECT COUNT(*) FROM scroll_flow_event WHERE jank
+          ) AS total_janky_flow_event_steps,
+          (
+            SELECT COUNT(*) FROM (SELECT step FROM scroll_flow_event GROUP BY step)
+          ) AS number_of_unique_steps;
+        """,
+        out=Path('scroll_flow_event_general_validation.out'))
+
+  def test_scroll_jank_cause(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_scroll_without_vsync.pftrace'),
+        query="""
+        SELECT RUN_METRIC('chrome/scroll_jank_cause.sql');
+
+        SELECT
+          COUNT(*) AS total,
+          SUM(jank) AS total_jank,
+          SUM(explained_jank + unexplained_jank) AS sum_explained_and_unexplained,
+          SUM(
+            CASE WHEN explained_jank THEN
+              unexplained_jank
+              ELSE
+                CASE WHEN jank AND NOT unexplained_jank THEN
+                  1
+                  ELSE
+                    0
+                END
+            END
+          ) AS error_rows
+        FROM scroll_jank_cause;
+        """,
+        out=Csv("""
+        "total","total_jank","sum_explained_and_unexplained","error_rows"
+        139,7,7,0
+        """))
+
+  def test_scroll_flow_event_queuing_delay(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_scroll_without_vsync.pftrace'),
+        query="""
+        SELECT RUN_METRIC('chrome/scroll_flow_event_queuing_delay.sql');
+
+        SELECT
+          trace_id,
+          jank,
+          step,
+          next_step,
+          ancestor_end,
+          maybe_next_ancestor_ts,
+          queuing_time_ns
+        FROM scroll_flow_event_queuing_delay
+        WHERE trace_id = 2954 OR trace_id = 2956 OR trace_id = 2960
+        ORDER BY trace_id, ts;
+        """,
+        out=Path('scroll_flow_event_queuing_delay.out'))
+
+  def test_scroll_flow_event_general_validation_2(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_scroll_without_vsync.pftrace'),
+        query=Path(
+            'scroll_flow_event_queuing_delay_general_validation_test.sql'),
+        out=Path('scroll_flow_event_general_validation.out'))
+
+  def test_scroll_jank_cause_queuing_delay(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_scroll_without_vsync.pftrace'),
+        query="""
+        SELECT RUN_METRIC('chrome/scroll_jank_cause_queuing_delay.sql');
+
+        SELECT
+          process_name,
+          thread_name,
+          trace_id,
+          jank,
+          dur_overlapping_ns,
+          metric_name
+        FROM scroll_jank_cause_queuing_delay
+        WHERE trace_id = 2918 OR trace_id = 2926
+        ORDER BY trace_id ASC, ts ASC;
+        """,
+        out=Path('scroll_jank_cause_queuing_delay.out'))
+
+  def test_scroll_jank_cause_queuing_delay_restricted(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_scroll_without_vsync.pftrace'),
+        query="""
+        SELECT RUN_METRIC('chrome/scroll_jank_cause_queuing_delay.sql');
+
+        SELECT
+          process_name,
+          thread_name,
+          trace_id,
+          jank,
+          dur_overlapping_ns,
+          restricted_metric_name
+        FROM scroll_jank_cause_queuing_delay
+        WHERE trace_id = 2918 OR trace_id = 2926
+        ORDER BY trace_id ASC, ts ASC;
+        """,
+        out=Path('scroll_jank_cause_queuing_delay_restricted.out'))
+
+  def test_scroll_jank_cause_queuing_delay_general_validation(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_scroll_without_vsync.pftrace'),
+        query="""
+        SELECT RUN_METRIC('chrome/scroll_jank_cause_queuing_delay.sql');
+
+        SELECT
+          COUNT(*) AS total,
+          (
+            SELECT DISTINCT
+              (avg_no_jank_dur_overlapping_ns)
+            FROM scroll_jank_cause_queuing_delay
+            WHERE
+              location = "LatencyInfo.Flow"
+              AND jank
+          ) AS janky_latency_info_non_jank_avg_dur,
+          (
+            SELECT DISTINCT
+              (avg_no_jank_dur_overlapping_ns)
+            FROM scroll_jank_cause_queuing_delay
+            WHERE
+              location = "LatencyInfo.Flow"
+              AND NOT jank
+          ) AS non_janky_latency_info_non_jank_avg_dur
+        FROM (
+          SELECT
+            trace_id
+          FROM scroll_jank_cause_queuing_delay
+          GROUP BY trace_id
+        );
+        """,
+        out=Path('scroll_jank_cause_queuing_delay_general_validation.out'))
+
+  def test_chrome_thread_slice(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_scroll_without_vsync.pftrace'),
+        query="""
+        SELECT RUN_METRIC('chrome/chrome_thread_slice.sql');
+
+        SELECT
+          EXTRACT_ARG(arg_set_id, 'chrome_latency_info.trace_id') AS trace_id,
+          dur,
+          thread_dur
+        FROM chrome_thread_slice
+        WHERE
+          name = 'LatencyInfo.Flow'
+          AND EXTRACT_ARG(arg_set_id, 'chrome_latency_info.trace_id') = 2734;
+        """,
+        out=Csv("""
+        "trace_id","dur","thread_dur"
+        2734,25000,25000
+        2734,1000,2000
+        2734,2000,2000
+        2734,258000,171000
+        2734,1000,1000
+        """))
+
+  def test_chrome_input_to_browser_intervals(self):
+    return DiffTestBlueprint(
+        trace=DataPath('scrolling_with_blocked_nonblocked_frames.pftrace'),
+        query="""
+        SELECT RUN_METRIC('chrome/chrome_input_to_browser_intervals.sql');
+
+        SELECT
+          *
+        FROM chrome_input_to_browser_intervals
+        WHERE window_start_ts >= 60934320005158
+          AND window_start_ts <= 60934338798158;
+        """,
+        out=Path('chrome_input_to_browser_intervals.out'))
+
+  def test_chrome_scroll_jank_caused_by_scheduling(self):
+    return DiffTestBlueprint(
+        trace=DataPath('fling_with_input_delay.pftrace'),
+        query="""
+        SELECT RUN_METRIC('chrome/chrome_scroll_jank_caused_by_scheduling.sql',
+          'dur_causes_jank_ms',
+        /* dur_causes_jank_ms = */ '5');
+
+        SELECT
+          full_name,
+          total_duration_ms,
+          total_thread_duration_ms,
+          count,
+          window_start_ts,
+          window_end_ts,
+          scroll_type
+        FROM chrome_scroll_jank_caused_by_scheduling;
+        """,
+        out=Path('chrome_scroll_jank_caused_by_scheduling_test.out'))
+
+  def test_chrome_tasks_delaying_input_processing(self):
+    return DiffTestBlueprint(
+        trace=DataPath('fling_with_input_delay.pftrace'),
+        query="""
+        SELECT RUN_METRIC('chrome/chrome_tasks_delaying_input_processing.sql',
+          'duration_causing_jank_ms',
+         /* duration_causing_jank_ms = */ '8');
+
+        SELECT
+          full_name,
+          duration_ms,
+          thread_dur_ms
+        FROM chrome_tasks_delaying_input_processing;
+        """,
+        out=Path('chrome_tasks_delaying_input_processing_test.out'))
+
+  def test_long_task_tracking_trace_chrome_long_tasks_delaying_input_processing(
+      self):
+    return DiffTestBlueprint(
+        trace=DataPath('long_task_tracking_trace'),
+        query="""
+        SELECT RUN_METRIC('chrome/chrome_long_tasks_delaying_input_processing.sql');
+
+        SELECT
+          full_name,
+          duration_ms,
+          slice_id
+        FROM chrome_tasks_delaying_input_processing
+        ORDER BY slice_id;
+        """,
+        out=Path(
+            'long_task_tracking_trace_chrome_long_tasks_delaying_input_processing_test.out'
+        ))
+
+  # TODO(b/264520610): Uncomment once fixed
+  # chrome_long_tasks_delaying_input_processing_compare_default_test.sql
+  # long_task_tracking_trace_chrome_long_tasks_delaying_input_processing_compare_default_test.out
+  def test_experimental_reliable_chrome_tasks_delaying_input_processing(self):
+    return DiffTestBlueprint(
+        trace=DataPath('fling_with_input_delay.pftrace'),
+        query="""
+        SELECT RUN_METRIC(
+            'chrome/experimental_reliable_chrome_tasks_delaying_input_processing.sql',
+            'duration_causing_jank_ms', '8');
+
+        SELECT
+          full_name,
+          duration_ms,
+          thread_dur_ms
+        FROM chrome_tasks_delaying_input_processing;
+        """,
+        out=Path(
+            'experimental_reliable_chrome_tasks_delaying_input_processing_test.out'
+        ))
+
+  def test_chrome_scroll_inputs_per_frame(self):
+    return DiffTestBlueprint(
+        trace=DataPath('scrolling_with_blocked_nonblocked_frames.pftrace'),
+        query="""
+        SELECT RUN_METRIC('chrome/chrome_scroll_inputs_per_frame.sql');
+
+        SELECT
+          count_for_frame,
+          ts
+        FROM chrome_scroll_inputs_per_frame
+        WHERE ts = 60934316798158;
+        """,
+        out=Csv("""
+        "count_for_frame","ts"
+        4,60934316798158
+        """))
+
+  def test_chrome_thread_slice_repeated(self):
+    return DiffTestBlueprint(
+        trace=Path('../track_event/track_event_counters.textproto'),
+        query="""
+        SELECT RUN_METRIC('chrome/chrome_thread_slice.sql');
+
+        SELECT
+          name,
+          ts,
+          dur,
+          thread_dur
+        FROM chrome_thread_slice;
+        """,
+        out=Csv("""
+        "name","ts","dur","thread_dur"
+        "event1_on_t1",1000,100,10000
+        "event2_on_t1",2000,200,30000
+        "event3_on_t1",2000,200,10000
+        "event4_on_t1",4000,0,0
+        "float_counter_on_t1",4300,0,"[NULL]"
+        "float_counter_on_t1",4500,0,"[NULL]"
+        "event1_on_t3",4000,100,5000
+        """))
+
+  def test_frame_times_metric(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_rendering_desktop.pftrace'),
+        query=Metric('frame_times'),
+        out=Path('frame_times_metric.out'))
+
+  def test_chrome_dropped_frames_metric(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_rendering_desktop.pftrace'),
+        query=Metric('chrome_dropped_frames'),
+        out=TextProto(r"""
+        [perfetto.protos.chrome_dropped_frames]: {
+          dropped_frame: {
+            ts: 166479338462000
+            process_name: "Renderer"
+            pid: 12743
+          }
+          dropped_frame: {
+            ts: 166479355302000
+            process_name: "Renderer"
+            pid: 12743
+          }
+        }
+        """))
+
+  def test_chrome_long_latency_metric(self):
+    return DiffTestBlueprint(
+        trace=Path('../chrome/long_event_latency.textproto'),
+        query="""
+        SELECT RUN_METRIC('experimental/chrome_long_latency.sql');
+
+        SELECT * FROM long_latency_with_process_info;
+        """,
+        out=Csv("""
+        "ts","event_type","process_name","process_id"
+        200111000,"FirstGestureScrollUpdate,GestureScrollUpdate","Renderer",1001
+        200111000,"GestureScrollUpdate","Renderer",1002
+        280111001,"GestureScrollUpdate","Renderer",1001
+        """))
+
+  def test_scroll_jank_mojo_simple_watcher(self):
+    return DiffTestBlueprint(
+        trace=Path('scroll_jank_mojo_simple_watcher.py'),
+        query="""
+        SELECT RUN_METRIC('chrome/scroll_jank_cause_queuing_delay.sql');
+
+        SELECT
+          trace_id,
+          jank,
+          dur_overlapping_ns,
+          metric_name
+        FROM scroll_jank_cause_queuing_delay
+        ORDER BY trace_id ASC, ts ASC;
+        """,
+        out=Path('scroll_jank_mojo_simple_watcher.out'))
+
+  def test_scroll_jank_gpu_check(self):
+    return DiffTestBlueprint(
+        trace=Path('scroll_jank_gpu_check.py'),
+        query="""
+        SELECT RUN_METRIC('chrome/scroll_jank.sql');
+
+        SELECT ts, jank
+        FROM scroll_jank
+        ORDER BY ts ASC;
+        """,
+        out=Csv("""
+        "ts","jank"
+        15000000,0
+        30000000,1
+        115000000,0
+        """))
diff --git a/test/trace_processor/diff_tests/chrome/tests_touch_gesture.py b/test/trace_processor/diff_tests/chrome/tests_touch_gesture.py
new file mode 100644
index 0000000..245cbff
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/tests_touch_gesture.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class ChromeTouchGesture(TestSuite):
+
+  def test_touch_jank(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_touch_gesture_scroll.pftrace'),
+        query="""
+        SELECT RUN_METRIC('chrome/touch_jank.sql');
+
+        SELECT
+          touch_id,
+          trace_id,
+          jank,
+          ts,
+          dur,
+          jank_budget
+        FROM touch_jank;
+        """,
+        out=Path('touch_jank.out'))
+
+  def test_touch_flow_event(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_touch_gesture_scroll.pftrace'),
+        query="""
+        SELECT RUN_METRIC('chrome/touch_flow_event.sql');
+
+        SELECT
+          trace_id,
+          ts,
+          dur,
+          jank,
+          step,
+          ancestor_end,
+          maybe_next_ancestor_ts,
+          next_ts,
+          next_trace_id,
+          next_step
+        FROM touch_flow_event
+        ORDER BY touch_id, trace_id, ts;
+        """,
+        out=Path('touch_flow_event.out'))
+
+  def test_touch_flow_event_queuing_delay(self):
+    return DiffTestBlueprint(
+        trace=DataPath('chrome_touch_gesture_scroll.pftrace'),
+        query="""
+        SELECT RUN_METRIC('chrome/touch_flow_event_queuing_delay.sql');
+
+        SELECT
+          trace_id,
+          jank,
+          step,
+          next_step,
+          ancestor_end,
+          maybe_next_ancestor_ts,
+          queuing_time_ns
+        FROM touch_flow_event_queuing_delay
+        WHERE trace_id = 6915 OR trace_id = 6911 OR trace_id = 6940
+        ORDER BY trace_id, ts;
+        """,
+        out=Path('touch_flow_event_queuing_delay.out'))
+
+  def test_touch_jank_synth(self):
+    return DiffTestBlueprint(
+        trace=Path('touch_jank.py'),
+        query="""
+        SELECT RUN_METRIC('chrome/touch_jank.sql');
+
+        SELECT
+          touch_id,
+          trace_id,
+          jank,
+          ts,
+          dur,
+          jank_budget
+        FROM touch_jank;
+        """,
+        out=Csv("""
+        "touch_id","trace_id","jank","ts","dur","jank_budget"
+        87654,34577,0,0,10000000,-31333333.350000
+        87654,34578,1,16000000,33000000,14666666.650000
+        87654,34579,0,55000000,33000000,-8333333.350000
+        """))
+
+  def test_touch_flow_event_synth(self):
+    return DiffTestBlueprint(
+        trace=Path('touch_jank.py'),
+        query="""
+        SELECT RUN_METRIC('chrome/touch_flow_event.sql');
+
+        SELECT
+          trace_id,
+          ts,
+          dur,
+          jank,
+          step,
+          ancestor_end,
+          maybe_next_ancestor_ts,
+          next_ts,
+          next_trace_id,
+          next_step
+        FROM touch_flow_event
+        ORDER BY touch_id, trace_id, ts;
+        """,
+        out=Path('touch_flow_event_synth.out'))
+
+  def test_touch_flow_event_queuing_delay_synth(self):
+    return DiffTestBlueprint(
+        trace=Path('touch_jank.py'),
+        query="""
+        SELECT RUN_METRIC('chrome/touch_flow_event_queuing_delay.sql');
+
+        SELECT
+          trace_id,
+          jank,
+          step,
+          next_step,
+          ancestor_end,
+          maybe_next_ancestor_ts,
+          queuing_time_ns
+        FROM touch_flow_event_queuing_delay
+        ORDER BY trace_id, ts;
+        """,
+        out=Path('touch_flow_event_queuing_delay_synth.out'))
diff --git a/test/trace_processor/diff_tests/chrome/top_level_java_choreographer_slices_top_level_java_chrome_tasks_test.out b/test/trace_processor/diff_tests/chrome/top_level_java_choreographer_slices_top_level_java_chrome_tasks_test.out
new file mode 100644
index 0000000..81aa80c
--- /dev/null
+++ b/test/trace_processor/diff_tests/chrome/top_level_java_choreographer_slices_top_level_java_chrome_tasks_test.out
@@ -0,0 +1,31 @@
+
+"full_name","task_type"
+"Choreographer(java_views=)","choreographer"
+"Choreographer(java_views=BrowserControlsManager.onAndroidVisibilityChanged)","choreographer"
+"Choreographer(java_views=BrowserControlsManager.onAndroidVisibilityChanged,ToolbarLayout)","choreographer"
+"Choreographer(java_views=ChromeKeyboardVisibilityDelegate.calculateKeyboardHeight,ToolbarLayout,ToolbarPhone.layoutLocationBar,ToolbarPhone.updateLocationBarLayoutForExpansionAnimation)","choreographer"
+"Looper.dispatch: android.app.ActivityThread$H(android.app.-$$Lambda$LoadedApk$ReceiverDispatcher$Args$YBWo_pyjHgkQEJgfA2r9yWoWRQA@16385c5)","other"
+"Looper.dispatch: android.app.ActivityThread$H(android.app.-$$Lambda$LoadedApk$ReceiverDispatcher$Args$YBWo_pyjHgkQEJgfA2r9yWoWRQA@420fe54)","other"
+"Looper.dispatch: android.app.ActivityThread$H(android.app.-$$Lambda$SharedPreferencesImpl$EditorImpl$xMt3av_jX0oSy8XrWpULfBhYg_E@132943f)","other"
+"Looper.dispatch: android.app.ActivityThread$H(null)","other"
+"Looper.dispatch: android.app.job.JobServiceEngine$JobHandler(null)","other"
+"Looper.dispatch: android.os.Handler(IR1@8b9abc0)","other"
+"Looper.dispatch: android.os.Handler(Lr3@f41c710)","other"
+"Looper.dispatch: android.os.Handler(Pu@78aeef2)","other"
+"Looper.dispatch: android.os.Handler(Qc@ba01266)","other"
+"Looper.dispatch: android.os.Handler(Qc@e41d2ad)","other"
+"Looper.dispatch: android.os.Handler(Qc@f0599e2)","other"
+"Looper.dispatch: android.os.Handler(cV0@6624567)","other"
+"Looper.dispatch: android.os.Handler(ir@3576081)","other"
+"Looper.dispatch: android.os.Handler(ir@9611880)","other"
+"Looper.dispatch: android.os.Handler(ir@ed97b8b)","other"
+"Looper.dispatch: android.os.Handler(k22@1291fec)","other"
+"Looper.dispatch: android.os.Handler(k22@2c4afb5)","other"
+"Looper.dispatch: android.os.Handler(k22@882459)","other"
+"Looper.dispatch: android.os.Handler(ki@5802b1a)","other"
+"Looper.dispatch: android.os.Handler(oe@88211fd)","other"
+"Looper.dispatch: android.view.ViewRootImpl$ViewRootHandler(SZ1@8c1ddb2)","other"
+"Looper.dispatch: android.view.ViewRootImpl$ViewRootHandler(android.view.View$PerformClick@c5b536)","other"
+"Looper.dispatch: android.view.ViewRootImpl$ViewRootHandler(null)","other"
+"Looper.dispatch: js3(aW1@185a62f)","other"
+"Looper.dispatch: xP1(null)","other"
diff --git a/test/trace_processor/chrome/touch_flow_event.out b/test/trace_processor/diff_tests/chrome/touch_flow_event.out
similarity index 100%
rename from test/trace_processor/chrome/touch_flow_event.out
rename to test/trace_processor/diff_tests/chrome/touch_flow_event.out
diff --git a/test/trace_processor/chrome/touch_flow_event_queuing_delay.out b/test/trace_processor/diff_tests/chrome/touch_flow_event_queuing_delay.out
similarity index 100%
rename from test/trace_processor/chrome/touch_flow_event_queuing_delay.out
rename to test/trace_processor/diff_tests/chrome/touch_flow_event_queuing_delay.out
diff --git a/test/trace_processor/chrome/touch_flow_event_queuing_delay_synth.out b/test/trace_processor/diff_tests/chrome/touch_flow_event_queuing_delay_synth.out
similarity index 100%
rename from test/trace_processor/chrome/touch_flow_event_queuing_delay_synth.out
rename to test/trace_processor/diff_tests/chrome/touch_flow_event_queuing_delay_synth.out
diff --git a/test/trace_processor/chrome/touch_flow_event_synth.out b/test/trace_processor/diff_tests/chrome/touch_flow_event_synth.out
similarity index 100%
rename from test/trace_processor/chrome/touch_flow_event_synth.out
rename to test/trace_processor/diff_tests/chrome/touch_flow_event_synth.out
diff --git a/test/trace_processor/chrome/touch_jank.out b/test/trace_processor/diff_tests/chrome/touch_jank.out
similarity index 100%
rename from test/trace_processor/chrome/touch_jank.out
rename to test/trace_processor/diff_tests/chrome/touch_jank.out
diff --git a/test/trace_processor/chrome/touch_jank.py b/test/trace_processor/diff_tests/chrome/touch_jank.py
similarity index 100%
rename from test/trace_processor/chrome/touch_jank.py
rename to test/trace_processor/diff_tests/chrome/touch_jank.py
diff --git a/test/trace_processor/chrome/unsymbolized_args.textproto b/test/trace_processor/diff_tests/chrome/unsymbolized_args.textproto
similarity index 100%
rename from test/trace_processor/chrome/unsymbolized_args.textproto
rename to test/trace_processor/diff_tests/chrome/unsymbolized_args.textproto
diff --git a/test/trace_processor/common/synth_1.py b/test/trace_processor/diff_tests/common/synth_1.py
similarity index 100%
rename from test/trace_processor/common/synth_1.py
rename to test/trace_processor/diff_tests/common/synth_1.py
diff --git a/test/trace_processor/cros/cros_ec_sensorhub_data.out b/test/trace_processor/diff_tests/cros/cros_ec_sensorhub_data.out
similarity index 100%
rename from test/trace_processor/cros/cros_ec_sensorhub_data.out
rename to test/trace_processor/diff_tests/cros/cros_ec_sensorhub_data.out
diff --git a/test/trace_processor/diff_tests/cros/tests.py b/test/trace_processor/diff_tests/cros/tests.py
new file mode 100644
index 0000000..dc71bce
--- /dev/null
+++ b/test/trace_processor/diff_tests/cros/tests.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class Cros(TestSuite):
+  # cros_ec_sensorhub_data
+  def test_cros_ec_sensorhub_data(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          ftrace_events {
+            cpu: 0
+            event {
+              timestamp: 223951135789653
+              pid: 181
+              cros_ec_sensorhub_data {
+                current_time: 223951135778716
+                current_timestamp: 223951052378946
+                delta: -83399770
+                ec_fifo_timestamp: 2128620968
+                ec_sensor_num: 0
+                fifo_timestamp: 223951132978872
+              }
+            }
+          }
+        }
+        """),
+        query="""
+        SELECT
+          t.name,
+          c.ts,
+          c.value,
+          EXTRACT_ARG(c.arg_set_id, 'ec_num') AS ec_num,
+          EXTRACT_ARG(c.arg_set_id, 'ec_delta') AS ec_delta,
+          EXTRACT_ARG(c.arg_set_id, 'sample_ts') AS sample_ts
+        FROM counter c
+        JOIN track t
+          ON c.track_id = t.id
+        WHERE t.name = 'cros_ec.cros_ec_sensorhub_data.0';
+        """,
+        out=Path('cros_ec_sensorhub_data.out'))
diff --git a/test/trace_processor/diff_tests/cros/tests_general.py b/test/trace_processor/diff_tests/cros/tests_general.py
new file mode 100644
index 0000000..5f012db
--- /dev/null
+++ b/test/trace_processor/diff_tests/cros/tests_general.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import DiffTestModule
+
+
+class CrosGeneral(DiffTestModule):
+
+  def test_cros_ec_sensorhub_data(self):
+    return DiffTestBlueprint(
+        trace=Path('cros_ec_sensorhub_data.textproto'),
+        query="""
+SELECT
+  t.name,
+  c.ts,
+  c.value,
+  EXTRACT_ARG(c.arg_set_id, 'ec_num') AS ec_num,
+  EXTRACT_ARG(c.arg_set_id, 'ec_delta') AS ec_delta,
+  EXTRACT_ARG(c.arg_set_id, 'sample_ts') AS sample_ts
+FROM counter c
+JOIN track t
+  ON c.track_id = t.id
+WHERE t.name = 'cros_ec.cros_ec_sensorhub_data.0';
+""",
+        out=Path('cros_ec_sensorhub_data.out'))
diff --git a/test/trace_processor/dynamic/ancestor_slice.out b/test/trace_processor/diff_tests/dynamic/ancestor_slice.out
similarity index 100%
rename from test/trace_processor/dynamic/ancestor_slice.out
rename to test/trace_processor/diff_tests/dynamic/ancestor_slice.out
diff --git a/test/trace_processor/dynamic/connected_flow.out b/test/trace_processor/diff_tests/dynamic/connected_flow.out
similarity index 100%
rename from test/trace_processor/dynamic/connected_flow.out
rename to test/trace_processor/diff_tests/dynamic/connected_flow.out
diff --git a/test/trace_processor/dynamic/connected_flow_data.json b/test/trace_processor/diff_tests/dynamic/connected_flow_data.json
similarity index 100%
rename from test/trace_processor/dynamic/connected_flow_data.json
rename to test/trace_processor/diff_tests/dynamic/connected_flow_data.json
diff --git a/test/trace_processor/diff_tests/dynamic/connected_flow_test.sql b/test/trace_processor/diff_tests/dynamic/connected_flow_test.sql
new file mode 100644
index 0000000..7582bf7
--- /dev/null
+++ b/test/trace_processor/diff_tests/dynamic/connected_flow_test.sql
@@ -0,0 +1,30 @@
+--
+-- 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 "directly_connected" AS type, s.name, s1.name AS start_name, s2.name AS end_name FROM slice s
+JOIN DIRECTLY_CONNECTED_FLOW(s.id) c
+JOIN slice s1 ON s1.id = c.slice_out
+JOIN slice s2 ON s2.id = c.slice_in
+UNION
+SELECT "following" AS type, s.name, s1.name AS start_name, s2.name AS end_name FROM slice s
+JOIN FOLLOWING_FLOW(s.id) c
+JOIN slice s1 ON s1.id = c.slice_out
+JOIN slice s2 ON s2.id = c.slice_in
+UNION
+SELECT "preceding" AS type, s.name, s1.name AS start_name, s2.name AS end_name FROM slice s
+JOIN PRECEDING_FLOW(s.id) c
+JOIN slice s1 ON s1.id = c.slice_out
+JOIN slice s2 ON s2.id = c.slice_in
+ORDER BY type, s.name, s1.name, s2.name ASC;
diff --git a/test/trace_processor/dynamic/descendant_slice.out b/test/trace_processor/diff_tests/dynamic/descendant_slice.out
similarity index 100%
rename from test/trace_processor/dynamic/descendant_slice.out
rename to test/trace_processor/diff_tests/dynamic/descendant_slice.out
diff --git a/test/trace_processor/dynamic/perf_sample_sc_annotated_callstack.out b/test/trace_processor/diff_tests/dynamic/perf_sample_sc_annotated_callstack.out
similarity index 100%
rename from test/trace_processor/dynamic/perf_sample_sc_annotated_callstack.out
rename to test/trace_processor/diff_tests/dynamic/perf_sample_sc_annotated_callstack.out
diff --git a/test/trace_processor/dynamic/relationship_tables.textproto b/test/trace_processor/diff_tests/dynamic/relationship_tables.textproto
similarity index 100%
rename from test/trace_processor/dynamic/relationship_tables.textproto
rename to test/trace_processor/diff_tests/dynamic/relationship_tables.textproto
diff --git a/test/trace_processor/dynamic/slice_stacks.textproto b/test/trace_processor/diff_tests/dynamic/slice_stacks.textproto
similarity index 100%
rename from test/trace_processor/dynamic/slice_stacks.textproto
rename to test/trace_processor/diff_tests/dynamic/slice_stacks.textproto
diff --git a/test/trace_processor/diff_tests/dynamic/tests.py b/test/trace_processor/diff_tests/dynamic/tests.py
new file mode 100644
index 0000000..24589ee
--- /dev/null
+++ b/test/trace_processor/diff_tests/dynamic/tests.py
@@ -0,0 +1,163 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class Dynamic(TestSuite):
+  # Tests for custom dynamic tables. Ancestor slice table.
+  def test_ancestor_slice(self):
+    return DiffTestBlueprint(
+        trace=Path('relationship_tables.textproto'),
+        query="""
+        SELECT slice.name AS currentSliceName, ancestor.name AS ancestorSliceName
+        FROM slice LEFT JOIN ancestor_slice(slice.id) AS ancestor
+        ORDER BY slice.ts ASC, ancestor.ts ASC, slice.name ASC, ancestor.name ASC;
+        """,
+        out=Path('ancestor_slice.out'))
+
+  # Descendant slice table.
+  def test_descendant_slice(self):
+    return DiffTestBlueprint(
+        trace=Path('relationship_tables.textproto'),
+        query="""
+        SELECT slice.name AS currentSliceName, descendant.name AS descendantSliceName
+        FROM slice LEFT JOIN descendant_slice(slice.id) AS descendant
+        ORDER BY slice.ts ASC, descendant.ts ASC, slice.name ASC, descendant.name ASC;
+        """,
+        out=Path('descendant_slice.out'))
+
+  # Ancestor slice by stack table.
+  def test_ancestor_slice_by_stack(self):
+    return DiffTestBlueprint(
+        trace=Path('slice_stacks.textproto'),
+        query="""
+        SELECT ts, name FROM ancestor_slice_by_stack((
+          SELECT stack_id FROM slice
+          WHERE name = 'event_depth_2'
+          LIMIT 1
+          ));
+        """,
+        out=Csv("""
+        "ts","name"
+        1000,"event_depth_0"
+        2000,"event_depth_1"
+        8000,"event_depth_0"
+        9000,"event_depth_1"
+        """))
+
+  # Descendant slice by stack table.
+  def test_descendant_slice_by_stack(self):
+    return DiffTestBlueprint(
+        trace=Path('slice_stacks.textproto'),
+        query="""
+        SELECT ts, name FROM descendant_slice_by_stack((
+          SELECT stack_id FROM slice
+          WHERE name = 'event_depth_0'
+          LIMIT 1
+          ));
+        """,
+        out=Csv("""
+        "ts","name"
+        2000,"event_depth_1"
+        3000,"event_depth_2"
+        9000,"event_depth_1"
+        10000,"event_depth_2"
+        """))
+
+  # Connected/Following/Perceeding flow table.
+  def test_connected_flow(self):
+    return DiffTestBlueprint(
+        trace=Path('connected_flow_data.json'),
+        query=Path('connected_flow_test.sql'),
+        out=Path('connected_flow.out'))
+
+  # Annotated callstacks.
+  def test_perf_sample_sc_annotated_callstack(self):
+    return DiffTestBlueprint(
+        trace=DataPath('perf_sample_sc.pb'),
+        query="""
+        SELECT eac.id, eac.depth, eac.frame_id, eac.annotation,
+               spf.name
+        FROM experimental_annotated_callstack eac
+        JOIN perf_sample ps
+          ON (eac.start_id = ps.callsite_id)
+        JOIN stack_profile_frame spf
+          ON (eac.frame_id = spf.id)
+        ORDER BY eac.start_id ASC, eac.depth ASC;
+        """,
+        out=Path('perf_sample_sc_annotated_callstack.out'))
+
+  # ABS_TIME_STR function
+  def test_various_clocks_abs_time_str(self):
+    return DiffTestBlueprint(
+        trace=Path('various_clocks.textproto'),
+        query="""
+        SELECT
+          ABS_TIME_STR(15) AS t15,
+          ABS_TIME_STR(25) AS t25,
+          ABS_TIME_STR(35) AS t35;
+        """,
+        out=Path('various_clocks_abs_time_str.out'))
+
+  def test_empty_abs_time_str(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+
+        """),
+        query="""
+        SELECT
+          ABS_TIME_STR(15) AS t15,
+          ABS_TIME_STR(25) AS t25,
+          ABS_TIME_STR(35) AS t35;
+        """,
+        out=Csv("""
+        "t15","t25","t35"
+        "[NULL]","[NULL]","[NULL]"
+        """))
+
+  # TO_MONOTONIC function
+  def test_various_clocks_to_monotonic(self):
+    return DiffTestBlueprint(
+        trace=Path('various_clocks.textproto'),
+        query="""
+        SELECT
+          TO_MONOTONIC(25) AS t15,
+          TO_MONOTONIC(35) AS t20,
+          TO_MONOTONIC(50) AS t25;
+        """,
+        out=Csv("""
+        "t15","t20","t25"
+        15,20,25
+        """))
+
+  def test_empty_to_monotonic(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+
+        """),
+        query="""
+        SELECT
+          TO_MONOTONIC(25) AS t15,
+          TO_MONOTONIC(35) AS t20,
+          TO_MONOTONIC(50) AS t25;
+        """,
+        out=Csv("""
+        "t15","t20","t25"
+        "[NULL]","[NULL]","[NULL]"
+        """))
diff --git a/test/trace_processor/diff_tests/dynamic/various_clocks.textproto b/test/trace_processor/diff_tests/dynamic/various_clocks.textproto
new file mode 100644
index 0000000..11c03e9
--- /dev/null
+++ b/test/trace_processor/diff_tests/dynamic/various_clocks.textproto
@@ -0,0 +1,81 @@
+packet {
+  clock_snapshot {
+    clocks {
+      clock_id: 1
+      timestamp: 0
+    }
+    clocks {
+      clock_id: 6
+      timestamp: 10
+    }
+  }
+  trusted_packet_sequence_id: 1
+  timestamp: 10
+}
+packet {
+  clock_snapshot {
+    clocks {
+      clock_id: 1
+      timestamp: 1652904000000000000
+    }
+    clocks {
+      clock_id: 6
+      timestamp: 20
+    }
+  }
+  trusted_packet_sequence_id: 1
+  timestamp: 20
+}
+packet {
+  clock_snapshot {
+    clocks {
+      clock_id: 1
+      timestamp: 1652903999999999995
+    }
+    clocks {
+      clock_id: 6
+      timestamp: 30
+    }
+  }
+  trusted_packet_sequence_id: 1
+  timestamp: 30
+}
+packet {
+  clock_snapshot {
+    clocks {
+      clock_id: 3
+      timestamp: 15
+    }
+    clocks {
+      clock_id: 6
+      timestamp: 25
+    }
+  }
+  trusted_packet_sequence_id: 1
+}
+packet {
+  clock_snapshot {
+    clocks {
+      clock_id: 3
+      timestamp: 20
+    }
+    clocks {
+      clock_id: 6
+      timestamp: 35
+    }
+  }
+  trusted_packet_sequence_id: 1
+}
+packet {
+  clock_snapshot {
+    clocks {
+      clock_id: 3
+      timestamp: 25
+    }
+    clocks {
+      clock_id: 6
+      timestamp: 50
+    }
+  }
+  trusted_packet_sequence_id: 1
+}
diff --git a/test/trace_processor/dynamic/various_clocks_abs_time_str.out b/test/trace_processor/diff_tests/dynamic/various_clocks_abs_time_str.out
similarity index 100%
rename from test/trace_processor/dynamic/various_clocks_abs_time_str.out
rename to test/trace_processor/diff_tests/dynamic/various_clocks_abs_time_str.out
diff --git a/test/trace_processor/fs/f2fs_iostat.out b/test/trace_processor/diff_tests/fs/f2fs_iostat.out
similarity index 100%
rename from test/trace_processor/fs/f2fs_iostat.out
rename to test/trace_processor/diff_tests/fs/f2fs_iostat.out
diff --git a/test/trace_processor/fs/f2fs_iostat.textproto b/test/trace_processor/diff_tests/fs/f2fs_iostat.textproto
similarity index 100%
rename from test/trace_processor/fs/f2fs_iostat.textproto
rename to test/trace_processor/diff_tests/fs/f2fs_iostat.textproto
diff --git a/test/trace_processor/fs/f2fs_iostat_latency.out b/test/trace_processor/diff_tests/fs/f2fs_iostat_latency.out
similarity index 100%
rename from test/trace_processor/fs/f2fs_iostat_latency.out
rename to test/trace_processor/diff_tests/fs/f2fs_iostat_latency.out
diff --git a/test/trace_processor/fs/f2fs_iostat_latency.textproto b/test/trace_processor/diff_tests/fs/f2fs_iostat_latency.textproto
similarity index 100%
rename from test/trace_processor/fs/f2fs_iostat_latency.textproto
rename to test/trace_processor/diff_tests/fs/f2fs_iostat_latency.textproto
diff --git a/test/trace_processor/diff_tests/fs/tests.py b/test/trace_processor/diff_tests/fs/tests.py
new file mode 100644
index 0000000..3fb6b6a
--- /dev/null
+++ b/test/trace_processor/diff_tests/fs/tests.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class Fs(TestSuite):
+
+  def test_f2fs_iostat(self):
+    return DiffTestBlueprint(
+        trace=Path('f2fs_iostat.textproto'),
+        query="""
+        SELECT
+          name,
+          ts,
+          value
+        FROM
+          counter AS c
+        JOIN
+          counter_track AS ct
+          ON c.track_id = ct.id
+        ORDER BY name, ts;
+        """,
+        out=Path('f2fs_iostat.out'))
+
+  def test_f2fs_iostat_latency(self):
+    return DiffTestBlueprint(
+        trace=Path('f2fs_iostat_latency.textproto'),
+        query="""
+        SELECT
+          name,
+          ts,
+          value
+        FROM
+          counter AS c
+        JOIN
+          counter_track AS ct
+          ON c.track_id = ct.id
+        ORDER BY name, ts;
+        """,
+        out=Path('f2fs_iostat_latency.out'))
diff --git a/test/trace_processor/fuchsia/fuchsia_workstation_smoke_slices.out b/test/trace_processor/diff_tests/fuchsia/fuchsia_workstation_smoke_slices.out
similarity index 100%
rename from test/trace_processor/fuchsia/fuchsia_workstation_smoke_slices.out
rename to test/trace_processor/diff_tests/fuchsia/fuchsia_workstation_smoke_slices.out
diff --git a/test/trace_processor/diff_tests/fuchsia/tests.py b/test/trace_processor/diff_tests/fuchsia/tests.py
new file mode 100644
index 0000000..caa44f7
--- /dev/null
+++ b/test/trace_processor/diff_tests/fuchsia/tests.py
@@ -0,0 +1,203 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class Fuchsia(TestSuite):
+  # Contains tests for parsing Fuchsia traces. Smoke test a bunch of different
+  # types.
+  def test_fuchsia_smoke(self):
+    return DiffTestBlueprint(
+        trace=DataPath('fuchsia_trace.fxt'),
+        query="""
+        SELECT
+          ts,
+          cpu,
+          dur,
+          end_state,
+          priority,
+          tid
+        FROM sched
+        JOIN thread USING(utid)
+        ORDER BY ts
+        LIMIT 10;
+        """,
+        out=Csv("""
+        "ts","cpu","dur","end_state","priority","tid"
+        19675868967,2,79022,"S",20,4344
+        19676000188,3,504797,"S",20,6547
+        19676504985,3,42877,"S",20,6525
+        19676582005,0,48467,"S",20,11566
+        19676989045,2,138116,"S",20,9949
+        19677162311,3,48655,"S",20,6525
+        19677305405,3,48814,"S",20,6525
+        19677412330,0,177220,"S",20,4344
+        19677680485,2,91422,"S",20,6537
+        19677791779,3,96082,"S",20,1680
+        """))
+
+  def test_fuchsia_smoke_slices(self):
+    return DiffTestBlueprint(
+        trace=DataPath('fuchsia_trace.fxt'),
+        query="""
+        SELECT track.type AS type, depth, count(*) AS count
+        FROM slice
+        JOIN track ON slice.track_id = track.id
+        GROUP BY track.type, depth
+        ORDER BY track.type, depth;
+        """,
+        out=Csv("""
+        "type","depth","count"
+        "thread_track",0,2153
+        "thread_track",1,1004
+        """))
+
+  def test_fuchsia_smoke_instants(self):
+    return DiffTestBlueprint(
+        trace=DataPath('fuchsia_trace.fxt'),
+        query="""
+        SELECT
+          ts,
+          name
+        FROM slice
+        WHERE
+          dur = 0
+        LIMIT 10;
+        """,
+        out=Csv("""
+        "ts","name"
+        21442756010,"task_start"
+        21446583438,"task_end"
+        21448366538,"task_start"
+        21450363277,"task_end"
+        21454255741,"task_start"
+        21457834528,"task_end"
+        21459006408,"task_start"
+        21460601866,"task_end"
+        21461282720,"task_start"
+        21462998487,"task_end"
+        """))
+
+  def test_fuchsia_smoke_counters(self):
+    return DiffTestBlueprint(
+        trace=DataPath('fuchsia_trace.fxt'),
+        query="""
+        SELECT
+          ts,
+          value,
+          name
+        FROM counters
+        LIMIT 10;
+        """,
+        out=Csv("""
+        "ts","value","name"
+        20329439768,30.331177,"cpu_usage:average_cpu_percentage"
+        21331281870,7.829745,"cpu_usage:average_cpu_percentage"
+        22332302017,9.669818,"cpu_usage:average_cpu_percentage"
+        23332974162,6.421237,"cpu_usage:average_cpu_percentage"
+        24333405767,12.079849,"cpu_usage:average_cpu_percentage"
+        """))
+
+  def test_fuchsia_smoke_flow(self):
+    return DiffTestBlueprint(
+        trace=DataPath('fuchsia_trace.fxt'),
+        query="""
+        SELECT
+          id,
+          slice_out,
+          slice_in
+        FROM flow
+        LIMIT 10;
+        """,
+        out=Csv("""
+        "id","slice_out","slice_in"
+        0,0,1
+        1,2,3
+        2,4,5
+        3,6,7
+        4,8,9
+        5,10,11
+        6,12,13
+        7,14,15
+        8,16,17
+        9,18,19
+        """))
+
+  def test_fuchsia_smoke_type(self):
+    return DiffTestBlueprint(
+        trace=DataPath('fuchsia_trace.fxt'),
+        query="""
+        SELECT
+          id,
+          name,
+          type
+        FROM track
+        LIMIT 10;
+        """,
+        out=Csv("""
+        "id","name","type"
+        0,"[NULL]","thread_track"
+        1,"[NULL]","thread_track"
+        2,"[NULL]","thread_track"
+        3,"[NULL]","thread_track"
+        4,"[NULL]","thread_track"
+        5,"cpu_usage:average_cpu_percentage","process_counter_track"
+        6,"[NULL]","thread_track"
+        7,"[NULL]","thread_track"
+        8,"[NULL]","thread_track"
+        9,"[NULL]","thread_track"
+        """))
+
+  # Smoke test a high-CPU trace.
+  def test_fuchsia_workstation_smoke_slices(self):
+    return DiffTestBlueprint(
+        trace=DataPath('fuchsia_workstation.fxt'),
+        query="""
+        SELECT track.type AS type, depth, count(*) AS count
+        FROM slice
+        JOIN track ON slice.track_id = track.id
+        GROUP BY track.type, depth
+        ORDER BY track.type, depth;
+        """,
+        out=Path('fuchsia_workstation_smoke_slices.out'))
+
+  def test_fuchsia_workstation_smoke_args(self):
+    return DiffTestBlueprint(
+        trace=DataPath('fuchsia_workstation.fxt'),
+        query="""
+        SELECT
+          key,
+          COUNT(*)
+        FROM args
+        GROUP BY key
+        LIMIT 10;
+        """,
+        out=Csv("""
+        "key","COUNT(*)"
+        "Dart Arguments",3
+        "Escher frame number",33
+        "Expected presentation time",17
+        "Frame number",33
+        "MinikinFontsCount",2
+        "Predicted frame duration(ms)",21
+        "Render time(ms)",21
+        "Timestamp",917
+        "Update time(ms)",21
+        "Vsync interval",900
+        """))
diff --git a/test/trace_processor/diff_tests/functions/tests.py b/test/trace_processor/diff_tests/functions/tests.py
new file mode 100644
index 0000000..abab176
--- /dev/null
+++ b/test/trace_processor/diff_tests/functions/tests.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class Functions(TestSuite):
+
+  def test_first_non_null_frame(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+
+        """),
+        query="""
+        CREATE TABLE TEST(id INTEGER, val INTEGER);
+
+        INSERT INTO TEST
+        VALUES (1, 1), (2, NULL), (3, 3), (4, 4), (5, NULL), (6, NULL), (7, NULL);
+
+        SELECT
+          id,
+          LAST_NON_NULL(val)
+          OVER (ORDER BY id ASC ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING) AS val
+        FROM TEST
+        ORDER BY id ASC;
+        """,
+        out=Csv("""
+        "id","val"
+        1,3
+        2,4
+        3,4
+        4,4
+        5,"[NULL]"
+        6,"[NULL]"
+        7,"[NULL]"
+        """))
+
+  def test_first_non_null_partition(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+
+        """),
+        query="""
+        CREATE TABLE TEST(id INTEGER, part TEXT, val INTEGER);
+
+        INSERT INTO TEST
+        VALUES
+        (1, 'A', 1),
+        (2, 'A', NULL),
+        (3, 'A', 3),
+        (4, 'B', NULL),
+        (5, 'B', 5),
+        (6, 'B', NULL),
+        (7, 'B', 7);
+
+        SELECT id, LAST_NON_NULL(val) OVER (PARTITION BY part ORDER BY id ASC) AS val
+        FROM TEST
+        ORDER BY id ASC;
+        """,
+        out=Csv("""
+        "id","val"
+        1,1
+        2,1
+        3,3
+        4,"[NULL]"
+        5,5
+        6,5
+        7,7
+        """))
+
+  def test_first_non_null(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+
+        """),
+        query="""
+        CREATE TABLE TEST(id INTEGER, val INTEGER);
+
+        INSERT INTO TEST
+        VALUES (1, 1), (2, NULL), (3, 3), (4, 4), (5, NULL), (6, NULL), (7, NULL);
+
+        SELECT id, LAST_NON_NULL(val) OVER (ORDER BY id ASC) AS val
+        FROM TEST
+        ORDER BY id ASC;
+        """,
+        out=Csv("""
+        "id","val"
+        1,1
+        2,1
+        3,3
+        4,4
+        5,4
+        6,4
+        7,4
+        """))
diff --git a/test/trace_processor/graphics/actual_frame_timeline_events.out b/test/trace_processor/diff_tests/graphics/actual_frame_timeline_events.out
similarity index 100%
rename from test/trace_processor/graphics/actual_frame_timeline_events.out
rename to test/trace_processor/diff_tests/graphics/actual_frame_timeline_events.out
diff --git a/test/trace_processor/diff_tests/graphics/actual_frame_timeline_events_test.sql b/test/trace_processor/diff_tests/graphics/actual_frame_timeline_events_test.sql
new file mode 100644
index 0000000..a46f6aa
--- /dev/null
+++ b/test/trace_processor/diff_tests/graphics/actual_frame_timeline_events_test.sql
@@ -0,0 +1,24 @@
+--
+-- 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 ts, dur, process.pid AS pid, display_frame_token, surface_frame_token, layer_name,
+  present_type, on_time_finish, gpu_composition, jank_type, prediction_type, jank_tag
+FROM
+  (SELECT t.*, process_track.name AS track_name FROM
+    process_track LEFT JOIN actual_frame_timeline_slice t
+    ON process_track.id = t.track_id) s
+JOIN process USING(upid)
+WHERE s.track_name = 'Actual Timeline'
+ORDER BY ts;
diff --git a/test/trace_processor/graphics/android_jank_cuj.out b/test/trace_processor/diff_tests/graphics/android_jank_cuj.out
similarity index 100%
rename from test/trace_processor/graphics/android_jank_cuj.out
rename to test/trace_processor/diff_tests/graphics/android_jank_cuj.out
diff --git a/test/trace_processor/graphics/android_sysui_cuj.py b/test/trace_processor/diff_tests/graphics/android_jank_cuj.py
similarity index 100%
rename from test/trace_processor/graphics/android_sysui_cuj.py
rename to test/trace_processor/diff_tests/graphics/android_jank_cuj.py
diff --git a/test/trace_processor/graphics/android_jank_cuj_query.out b/test/trace_processor/diff_tests/graphics/android_jank_cuj_query.out
similarity index 100%
rename from test/trace_processor/graphics/android_jank_cuj_query.out
rename to test/trace_processor/diff_tests/graphics/android_jank_cuj_query.out
diff --git a/test/trace_processor/diff_tests/graphics/android_jank_cuj_query_test.sql b/test/trace_processor/diff_tests/graphics/android_jank_cuj_query_test.sql
new file mode 100644
index 0000000..831da00
--- /dev/null
+++ b/test/trace_processor/diff_tests/graphics/android_jank_cuj_query_test.sql
@@ -0,0 +1,66 @@
+--
+-- Copyright 2022 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/android_jank_cuj.sql');
+
+
+-- First query to look at `binder transaction` on the Main Thread
+
+DROP VIEW IF EXISTS android_jank_cuj_query_test_binder;
+CREATE VIEW android_jank_cuj_query_test_binder AS
+SELECT * FROM android_jank_cuj_slice
+WHERE name = 'binder transaction';
+
+SELECT ANDROID_JANK_CORRELATE_FRAME_SLICE('MainThread', 'android_jank_cuj_query_test_binder') AS suppress_query_output;
+
+
+-- Second query to look at `JIT compiling` slices on JIT threadpool
+
+DROP VIEW IF EXISTS android_jank_cuj_query_test_jit;
+CREATE VIEW android_jank_cuj_query_test_jit AS
+SELECT * FROM android_jank_cuj_slice
+WHERE name GLOB 'JIT compiling*';
+
+SELECT ANDROID_JANK_CORRELATE_FRAME_SLICE_IMPL('App threads', 'android_jank_cuj_query_test_jit', 'jank_query_jit') AS suppress_query_output;
+
+--- Third query to look at 'sf binder' slices on SF main thread
+
+DROP VIEW IF EXISTS android_jank_cuj_query_test_sf_binder;
+CREATE VIEW android_jank_cuj_query_test_sf_binder AS
+SELECT * FROM android_jank_cuj_sf_slice
+WHERE name = 'sf binder';
+
+SELECT ANDROID_JANK_CORRELATE_FRAME_SLICE_IMPL('SF MainThread', 'android_jank_cuj_query_test_sf_binder', 'jank_query_sf_binder') AS suppress_query_output;
+
+
+--- Fourth query to look at 'shader compile' slices on SF RenderEngine
+
+DROP VIEW IF EXISTS android_jank_cuj_query_test_re;
+CREATE VIEW android_jank_cuj_query_test_re AS
+SELECT * FROM android_jank_cuj_sf_slice
+WHERE name = 'shader compile';
+
+SELECT ANDROID_JANK_CORRELATE_FRAME_SLICE_IMPL('SF RenderEngine', 'android_jank_cuj_query_test_re', 'jank_query_re') AS suppress_query_output;
+
+
+-- UNION ALL results from all queries.
+SELECT 'JIT compiling' AS slice, * FROM jank_query_jit_slice_in_frame_agg
+UNION ALL
+SELECT 'binder transaction' AS slice, * FROM jank_query_slice_in_frame_agg
+UNION ALL
+SELECT 'sf binder' AS slice, * FROM jank_query_sf_binder_slice_in_frame_agg
+UNION ALL
+SELECT 'shader compile' AS slice, * FROM jank_query_re_slice_in_frame_agg
+ORDER BY slice, cuj_id, vsync;
diff --git a/test/trace_processor/diff_tests/graphics/clock_sync.py b/test/trace_processor/diff_tests/graphics/clock_sync.py
new file mode 100644
index 0000000..657ddd8
--- /dev/null
+++ b/test/trace_processor/diff_tests/graphics/clock_sync.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python3
+# 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 the clock-sync logic. It synthesizes a trace with
+# (i) builtin clocks, (ii) custom global clocks, (iii) sequence-scoped clocks.
+# It uses gpu counters because that is a quite simple packet and doesn't have
+# special treatement. We can't use ftrace because ftrace events use nested
+# per-event timestamps and they are assumed to be in the CLOCK_MONOTONIC
+# domains regardless of the TracePacket's timestamp_clock_id.
+
+from os import sys, path
+
+import synth_common
+from synth_common import CLONE_THREAD
+
+# Clock IDs in the range [64, 128) are sequence-scoped. See comments in
+# clock_snapshot.proto.
+CLOCK_MONOTONIC = 3  # Builtin clock, see clock_snapshot.proto.
+CLOCK_BOOTTIME = 6  # Builtin clock, see clock_snapshot.proto.
+GLOBAL_CLK1 = 128
+GLOBAL_CLK2 = 129
+SEQ_CLOCK1 = 64
+
+trace = synth_common.create_trace()
+
+# See gpu_counter_descriptor.proto
+SECOND = 22
+PIXEL = 26
+
+# Add a counter descriptor for our test counter:
+trace.add_gpu_counter_spec(
+    ts=1,
+    counter_id=42,
+    name="gpu_counter(42)",
+    description="Number of fragments per second",
+    unit_numerators=[PIXEL],
+    unit_denominators=[SECOND])
+
+# The default trace clock domain is CLOCK_BOOTTIME.
+trace.add_gpu_counter(ts=1, counter_id=42, value=3)
+
+# Emit a ClockSnapshot that sets BOOTTIME = MONOTONIC + 100.
+trace.add_clock_snapshot(clocks={CLOCK_MONOTONIC: 1, CLOCK_BOOTTIME: 101})
+
+# Emit a counter synced against the global built-in clock CLOCK_MONOTONIC.
+# This should be translated, at import time, to BOOTTIME = 2 + 100 = 102.
+trace.add_gpu_counter(ts=2, clock_id=CLOCK_MONOTONIC, counter_id=42, value=5)
+
+# Use two global custom clocks. We sync them as follows:
+# BOOTTIME = GLOBAL_CLK1 + 1000
+# GLOBAL_CLK1 = GLOBAL_CLK2 + 1
+# Hence, recursively:
+# BOOTTIME = GLOBAL_CLK2 + 1000 + 1
+trace.add_clock_snapshot(clocks={GLOBAL_CLK1: 1, CLOCK_BOOTTIME: 1001})
+trace.add_clock_snapshot(clocks={GLOBAL_CLK1: 2, GLOBAL_CLK2: 1})
+
+# This counter should be translated, at import time, to BOOTTIME = 3 + 1000
+trace.add_gpu_counter(ts=3, clock_id=GLOBAL_CLK1, counter_id=42, value=7)
+
+# This one instead to BOOTTIME = 4 + 1000 + 1 = 1005
+trace.add_gpu_counter(ts=4, clock_id=GLOBAL_CLK2, counter_id=42, value=9)
+
+# Use a sequence-scoped clock on two differents sequences.
+# On seq 2, BOOTTIME = SEQ_CLOCK1 + 2000
+# On seq 3, BOOTTIME = SEQ_CLOCK1 + 3000
+trace.add_clock_snapshot(seq_id=2, clocks={SEQ_CLOCK1: 1, CLOCK_BOOTTIME: 2001})
+trace.add_clock_snapshot(seq_id=3, clocks={SEQ_CLOCK1: 1, CLOCK_BOOTTIME: 3001})
+
+# This counter should be translated @ BOOTTIME : 3000 + 7
+trace.add_gpu_counter(
+    ts=7, clock_id=SEQ_CLOCK1, counter_id=42, value=14, seq_id=3)
+
+# This counter should be translated @ BOOTTIME : 2000 + 6
+trace.add_gpu_counter(
+    ts=6, clock_id=SEQ_CLOCK1, seq_id=2, counter_id=42, value=11)
+
+# Set default clock for sequence 2.
+defaults_packet = trace.add_packet()
+defaults_packet.trusted_packet_sequence_id = 2
+defaults_packet.trace_packet_defaults.timestamp_clock_id = SEQ_CLOCK1
+
+# This counter should be translated @ BOOTTIME : 2000 + 10
+trace.add_gpu_counter(ts=10, seq_id=2, counter_id=42, value=12)
+
+# Manually specified clock_id overrides the default clock.
+trace.add_gpu_counter(
+    ts=2013, clock_id=CLOCK_BOOTTIME, seq_id=2, counter_id=42, value=13)
+
+# Other sequence's default clock isn't changed, so this should be in BOOTTIME.
+trace.add_gpu_counter(ts=3010, counter_id=42, value=15, seq_id=3)
+
+sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/graphics/composer_execution.py b/test/trace_processor/diff_tests/graphics/composer_execution.py
similarity index 100%
rename from test/trace_processor/graphics/composer_execution.py
rename to test/trace_processor/diff_tests/graphics/composer_execution.py
diff --git a/test/trace_processor/graphics/composition_layer.py b/test/trace_processor/diff_tests/graphics/composition_layer.py
similarity index 100%
rename from test/trace_processor/graphics/composition_layer.py
rename to test/trace_processor/diff_tests/graphics/composition_layer.py
diff --git a/test/trace_processor/diff_tests/graphics/display_metrics.py b/test/trace_processor/diff_tests/graphics/display_metrics.py
new file mode 100644
index 0000000..dbec027
--- /dev/null
+++ b/test/trace_processor/diff_tests/graphics/display_metrics.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+# 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 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
+
+import synth_common
+
+trace = synth_common.create_trace()
+
+trace.add_packet(ts=1)
+trace.add_process(10, 1, "parent_process")
+trace.add_process(11, 10, "child_process")
+
+trace.add_ftrace_packet(1)
+
+trace.add_print(ts=99_000_000, tid=11, buf='C|10|panel_fps|60')
+trace.add_print(ts=100_000_000, tid=11, buf='C|10|panel_fps|90')
+trace.add_print(ts=101_000_000, tid=11, buf='C|10|panel_fps|60')
+trace.add_print(ts=102_000_000, tid=11, buf='C|10|panel_fps|120')
+
+# The duplicated fps will be ignored
+trace.add_print(ts=103_000_000, tid=11, buf='C|10|panel_fps|120')
+
+trace.add_print(ts=104_000_000, tid=11, buf='C|10|panel_fps|90')
+
+# The last fps and its following duplicates will be ignored, and will
+# only be used for the calculation of duration of the previous fps
+trace.add_print(ts=105_000_000, tid=11, buf='C|10|panel_fps|24')
+trace.add_print(ts=106_000_000, tid=11, buf='C|10|panel_fps|24')
+
+trace.add_track_event_slice(
+    "DisplayPowerController#updatePowerState",
+    0,
+    5000000,
+    trusted_sequence_id=1)
+
+trace.add_track_event_slice(
+    "DisplayPowerController#updatePowerState",
+    0,
+    3000000,
+    trusted_sequence_id=1)
+
+sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/graphics/dpu_vote_clock_bw.textproto b/test/trace_processor/diff_tests/graphics/dpu_vote_clock_bw.textproto
similarity index 100%
rename from test/trace_processor/graphics/dpu_vote_clock_bw.textproto
rename to test/trace_processor/diff_tests/graphics/dpu_vote_clock_bw.textproto
diff --git a/test/trace_processor/graphics/drm_dma_fence.textproto b/test/trace_processor/diff_tests/graphics/drm_dma_fence.textproto
similarity index 100%
rename from test/trace_processor/graphics/drm_dma_fence.textproto
rename to test/trace_processor/diff_tests/graphics/drm_dma_fence.textproto
diff --git a/test/trace_processor/graphics/drm_sched.textproto b/test/trace_processor/diff_tests/graphics/drm_sched.textproto
similarity index 100%
rename from test/trace_processor/graphics/drm_sched.textproto
rename to test/trace_processor/diff_tests/graphics/drm_sched.textproto
diff --git a/test/trace_processor/diff_tests/graphics/expected_frame_timeline_events_test.sql b/test/trace_processor/diff_tests/graphics/expected_frame_timeline_events_test.sql
new file mode 100644
index 0000000..ddf3f2f
--- /dev/null
+++ b/test/trace_processor/diff_tests/graphics/expected_frame_timeline_events_test.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 ts, dur, process.pid AS pid, display_frame_token, surface_frame_token, layer_name
+FROM
+  (SELECT t.*, process_track.name AS track_name FROM
+    process_track LEFT JOIN expected_frame_timeline_slice t
+    ON process_track.id = t.track_id) s
+JOIN process USING(upid)
+WHERE s.track_name = 'Expected Timeline'
+ORDER BY ts;
diff --git a/test/trace_processor/graphics/frame_missed.py b/test/trace_processor/diff_tests/graphics/frame_missed.py
similarity index 100%
rename from test/trace_processor/graphics/frame_missed.py
rename to test/trace_processor/diff_tests/graphics/frame_missed.py
diff --git a/test/trace_processor/graphics/frame_timeline_events.py b/test/trace_processor/diff_tests/graphics/frame_timeline_events.py
similarity index 100%
rename from test/trace_processor/graphics/frame_timeline_events.py
rename to test/trace_processor/diff_tests/graphics/frame_timeline_events.py
diff --git a/test/trace_processor/graphics/g2d_metrics.out b/test/trace_processor/diff_tests/graphics/g2d_metrics.out
similarity index 100%
rename from test/trace_processor/graphics/g2d_metrics.out
rename to test/trace_processor/diff_tests/graphics/g2d_metrics.out
diff --git a/test/trace_processor/graphics/g2d_metrics.textproto b/test/trace_processor/diff_tests/graphics/g2d_metrics.textproto
similarity index 100%
rename from test/trace_processor/graphics/g2d_metrics.textproto
rename to test/trace_processor/diff_tests/graphics/g2d_metrics.textproto
diff --git a/test/trace_processor/graphics/gpu_counter_specs.textproto b/test/trace_processor/diff_tests/graphics/gpu_counter_specs.textproto
similarity index 100%
rename from test/trace_processor/graphics/gpu_counter_specs.textproto
rename to test/trace_processor/diff_tests/graphics/gpu_counter_specs.textproto
diff --git a/test/trace_processor/graphics/gpu_counters.py b/test/trace_processor/diff_tests/graphics/gpu_counters.py
similarity index 100%
rename from test/trace_processor/graphics/gpu_counters.py
rename to test/trace_processor/diff_tests/graphics/gpu_counters.py
diff --git a/test/trace_processor/graphics/gpu_frequency_metric.out b/test/trace_processor/diff_tests/graphics/gpu_frequency_metric.out
similarity index 100%
rename from test/trace_processor/graphics/gpu_frequency_metric.out
rename to test/trace_processor/diff_tests/graphics/gpu_frequency_metric.out
diff --git a/test/trace_processor/graphics/gpu_frequency_metric.textproto b/test/trace_processor/diff_tests/graphics/gpu_frequency_metric.textproto
similarity index 100%
rename from test/trace_processor/graphics/gpu_frequency_metric.textproto
rename to test/trace_processor/diff_tests/graphics/gpu_frequency_metric.textproto
diff --git a/test/trace_processor/graphics/gpu_log.py b/test/trace_processor/diff_tests/graphics/gpu_log.py
similarity index 100%
rename from test/trace_processor/graphics/gpu_log.py
rename to test/trace_processor/diff_tests/graphics/gpu_log.py
diff --git a/test/trace_processor/graphics/gpu_mem_total.py b/test/trace_processor/diff_tests/graphics/gpu_mem_total.py
similarity index 100%
rename from test/trace_processor/graphics/gpu_mem_total.py
rename to test/trace_processor/diff_tests/graphics/gpu_mem_total.py
diff --git a/test/trace_processor/graphics/gpu_mem_total_after_free.py b/test/trace_processor/diff_tests/graphics/gpu_mem_total_after_free.py
similarity index 100%
rename from test/trace_processor/graphics/gpu_mem_total_after_free.py
rename to test/trace_processor/diff_tests/graphics/gpu_mem_total_after_free.py
diff --git a/test/trace_processor/diff_tests/graphics/gpu_mem_total_test.sql b/test/trace_processor/diff_tests/graphics/gpu_mem_total_test.sql
new file mode 100644
index 0000000..b1b6d89
--- /dev/null
+++ b/test/trace_processor/diff_tests/graphics/gpu_mem_total_test.sql
@@ -0,0 +1,6 @@
+SELECT ct.name, ct.unit, ct.description, c.ts, p.pid, CAST(c.value AS INT) AS value
+FROM counter_track ct
+LEFT JOIN process_counter_track pct USING (id)
+LEFT JOIN process p USING (upid)
+LEFT JOIN counter c ON c.track_id = ct.id
+ORDER BY ts;
diff --git a/test/trace_processor/graphics/gpu_metric.py b/test/trace_processor/diff_tests/graphics/gpu_metric.py
similarity index 100%
rename from test/trace_processor/graphics/gpu_metric.py
rename to test/trace_processor/diff_tests/graphics/gpu_metric.py
diff --git a/test/trace_processor/graphics/gpu_render_stages.out b/test/trace_processor/diff_tests/graphics/gpu_render_stages.out
similarity index 100%
rename from test/trace_processor/graphics/gpu_render_stages.out
rename to test/trace_processor/diff_tests/graphics/gpu_render_stages.out
diff --git a/test/trace_processor/graphics/gpu_render_stages.py b/test/trace_processor/diff_tests/graphics/gpu_render_stages.py
similarity index 100%
rename from test/trace_processor/graphics/gpu_render_stages.py
rename to test/trace_processor/diff_tests/graphics/gpu_render_stages.py
diff --git a/test/trace_processor/graphics/gpu_render_stages_interned_spec.out b/test/trace_processor/diff_tests/graphics/gpu_render_stages_interned_spec.out
similarity index 100%
rename from test/trace_processor/graphics/gpu_render_stages_interned_spec.out
rename to test/trace_processor/diff_tests/graphics/gpu_render_stages_interned_spec.out
diff --git a/test/trace_processor/graphics/gpu_render_stages_interned_spec.textproto b/test/trace_processor/diff_tests/graphics/gpu_render_stages_interned_spec.textproto
similarity index 100%
rename from test/trace_processor/graphics/gpu_render_stages_interned_spec.textproto
rename to test/trace_processor/diff_tests/graphics/gpu_render_stages_interned_spec.textproto
diff --git a/test/trace_processor/diff_tests/graphics/gpu_render_stages_test.sql b/test/trace_processor/diff_tests/graphics/gpu_render_stages_test.sql
new file mode 100644
index 0000000..bac5239
--- /dev/null
+++ b/test/trace_processor/diff_tests/graphics/gpu_render_stages_test.sql
@@ -0,0 +1,24 @@
+--
+-- 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 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_target_name, render_pass, render_pass_name,
+  command_buffer, command_buffer_name, submission_id, hw_queue_id, render_subpasses
+FROM gpu_track
+LEFT JOIN track USING (id)
+JOIN gpu_slice ON gpu_track.id = gpu_slice.track_id
+LEFT JOIN args ON gpu_slice.arg_set_id = args.arg_set_id
+ORDER BY ts;
diff --git a/test/trace_processor/graphics/graphics_frame_events.out b/test/trace_processor/diff_tests/graphics/graphics_frame_events.out
similarity index 100%
rename from test/trace_processor/graphics/graphics_frame_events.out
rename to test/trace_processor/diff_tests/graphics/graphics_frame_events.out
diff --git a/test/trace_processor/graphics/graphics_frame_events.py b/test/trace_processor/diff_tests/graphics/graphics_frame_events.py
similarity index 100%
rename from test/trace_processor/graphics/graphics_frame_events.py
rename to test/trace_processor/diff_tests/graphics/graphics_frame_events.py
diff --git a/test/trace_processor/graphics/surfaceflinger_gpu_invocation.py b/test/trace_processor/diff_tests/graphics/surfaceflinger_gpu_invocation.py
similarity index 100%
rename from test/trace_processor/graphics/surfaceflinger_gpu_invocation.py
rename to test/trace_processor/diff_tests/graphics/surfaceflinger_gpu_invocation.py
diff --git a/test/trace_processor/diff_tests/graphics/tests.py b/test/trace_processor/diff_tests/graphics/tests.py
new file mode 100644
index 0000000..b9b74c8
--- /dev/null
+++ b/test/trace_processor/diff_tests/graphics/tests.py
@@ -0,0 +1,476 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class Graphics(TestSuite):
+  # Contains tests for graphics related events and tables. Graphics frame
+  # trace tests.
+  def test_graphics_frame_events(self):
+    return DiffTestBlueprint(
+        trace=Path('graphics_frame_events.py'),
+        query="""
+        SELECT ts, gpu_track.name AS track_name, dur, frame_slice.name AS slice_name,
+          frame_number, layer_name
+        FROM gpu_track
+        LEFT JOIN frame_slice ON gpu_track.id = frame_slice.track_id
+        WHERE scope = 'graphics_frame_event'
+        ORDER BY ts;
+        """,
+        out=Path('graphics_frame_events.out'))
+
+  # GPU Memory ftrace packets
+  def test_gpu_mem_total(self):
+    return DiffTestBlueprint(
+        trace=Path('gpu_mem_total.py'),
+        query=Path('gpu_mem_total_test.sql'),
+        out=Csv("""
+        "name","unit","description","ts","pid","value"
+        "GPU Memory","7","Total GPU memory used by the entire system",0,"[NULL]",123
+        "GPU Memory","7","Total GPU memory used by this process",0,1,100
+        "GPU Memory","7","Total GPU memory used by the entire system",5,"[NULL]",256
+        "GPU Memory","7","Total GPU memory used by this process",5,1,233
+        "GPU Memory","7","Total GPU memory used by the entire system",10,"[NULL]",123
+        "GPU Memory","7","Total GPU memory used by this process",10,1,0
+        """))
+
+  def test_gpu_mem_total_after_free_gpu_mem_total(self):
+    return DiffTestBlueprint(
+        trace=Path('gpu_mem_total_after_free.py'),
+        query=Path('gpu_mem_total_test.sql'),
+        out=Csv("""
+        "name","unit","description","ts","pid","value"
+        "GPU Memory","7","Total GPU memory used by this process",0,1,100
+        "GPU Memory","7","Total GPU memory used by this process",5,1,233
+        "GPU Memory","7","Total GPU memory used by this process",10,1,50
+        """))
+
+  # Clock sync
+  def test_clock_sync(self):
+    return DiffTestBlueprint(
+        trace=Path('clock_sync.py'),
+        query="""
+        SELECT ts, cast(value AS integer) AS int_value
+        FROM counters
+        WHERE name GLOB 'gpu_counter*';
+        """,
+        out=Csv("""
+        "ts","int_value"
+        1,3
+        102,5
+        1003,7
+        1005,9
+        2006,11
+        2010,12
+        2013,13
+        3007,14
+        3010,15
+        """))
+
+  # Android SurfaceFlinger metrics
+  def test_frame_missed_event_frame_missed(self):
+    return DiffTestBlueprint(
+        trace=Path('frame_missed.py'),
+        query="""
+        SELECT RUN_METRIC('android/android_surfaceflinger.sql');
+
+        SELECT ts, dur
+        FROM android_surfaceflinger_event;
+        """,
+        out=Csv("""
+        "ts","dur"
+        100,1
+        102,1
+        103,1
+        """))
+
+  def test_frame_missed_metrics(self):
+    return DiffTestBlueprint(
+        trace=Path('frame_missed.py'),
+        query=Metric('android_surfaceflinger'),
+        out=TextProto(r"""
+        android_surfaceflinger {
+          missed_frames: 3
+          missed_hwc_frames: 0
+          missed_gpu_frames: 0
+          missed_frame_rate: 0.42857142857142855 # = 3/7
+          gpu_invocations: 0
+        }
+        """))
+
+  def test_surfaceflinger_gpu_invocation(self):
+    return DiffTestBlueprint(
+        trace=Path('surfaceflinger_gpu_invocation.py'),
+        query=Metric('android_surfaceflinger'),
+        out=TextProto(r"""
+        android_surfaceflinger {
+          missed_frames: 0
+          missed_hwc_frames: 0
+          missed_gpu_frames: 0
+          gpu_invocations: 4
+          avg_gpu_waiting_dur_ms: 4
+          total_non_empty_gpu_waiting_dur_ms: 11
+        }
+        """))
+
+  # GPU metrics
+  def test_gpu_metric(self):
+    return DiffTestBlueprint(
+        trace=Path('gpu_metric.py'),
+        query=Metric('android_gpu'),
+        out=TextProto(r"""
+        android_gpu {
+          processes {
+            name: "app_1"
+            mem_max: 8
+            mem_min: 2
+            mem_avg: 3
+          }
+          processes {
+            name: "app_2"
+            mem_max: 10
+            mem_min: 6
+            mem_avg: 8
+          }
+          mem_max: 4
+          mem_min: 1
+          mem_avg: 2
+        }
+        """))
+
+  def test_gpu_frequency_metric(self):
+    return DiffTestBlueprint(
+        trace=Path('gpu_frequency_metric.textproto'),
+        query=Metric('android_gpu'),
+        out=Path('gpu_frequency_metric.out'))
+
+  # Android Jank CUJ metric
+  def test_android_jank_cuj(self):
+    return DiffTestBlueprint(
+        trace=Path('android_jank_cuj.py'),
+        query=Metric('android_jank_cuj'),
+        out=Path('android_jank_cuj.out'))
+
+  def test_android_jank_cuj_query(self):
+    return DiffTestBlueprint(
+        trace=Path('android_jank_cuj.py'),
+        query=Path('android_jank_cuj_query_test.sql'),
+        out=Path('android_jank_cuj_query.out'))
+
+  # Frame Timeline event trace tests
+  def test_expected_frame_timeline_events(self):
+    return DiffTestBlueprint(
+        trace=Path('frame_timeline_events.py'),
+        query=Path('expected_frame_timeline_events_test.sql'),
+        out=Csv("""
+        "ts","dur","pid","display_frame_token","surface_frame_token","layer_name"
+        20,6,666,2,0,"[NULL]"
+        21,15,1000,4,1,"Layer1"
+        40,6,666,4,0,"[NULL]"
+        41,15,1000,6,5,"Layer1"
+        80,6,666,6,0,"[NULL]"
+        90,16,1000,8,7,"Layer1"
+        120,6,666,8,0,"[NULL]"
+        140,6,666,12,0,"[NULL]"
+        150,20,1000,15,14,"Layer1"
+        170,6,666,15,0,"[NULL]"
+        200,6,666,17,0,"[NULL]"
+        220,10,666,18,0,"[NULL]"
+        """))
+
+  def test_actual_frame_timeline_events(self):
+    return DiffTestBlueprint(
+        trace=Path('frame_timeline_events.py'),
+        query=Path('actual_frame_timeline_events_test.sql'),
+        out=Path('actual_frame_timeline_events.out'))
+
+  # Composition layer
+  def test_composition_layer_count(self):
+    return DiffTestBlueprint(
+        trace=Path('composition_layer.py'),
+        query="""
+        SELECT RUN_METRIC('android/android_hwcomposer.sql');
+
+        SELECT AVG(value)
+        FROM total_layers;
+        """,
+        out=Csv("""
+        "AVG(value)"
+        3.000000
+        """))
+
+  # G2D metrics TODO(rsavitski): find a real trace and double-check that the
+  # is realistic. One kernel's source I checked had tgid=0 for all counter
+  # Initial support was added/discussed in b/171296908.
+  def test_g2d_metrics(self):
+    return DiffTestBlueprint(
+        trace=Path('g2d_metrics.textproto'),
+        query=Metric('g2d'),
+        out=Path('g2d_metrics.out'))
+
+  # Composer execution
+  def test_composer_execution(self):
+    return DiffTestBlueprint(
+        trace=Path('composer_execution.py'),
+        query="""
+        SELECT RUN_METRIC('android/composer_execution.sql',
+          'output', 'hwc_execution_spans');
+
+        SELECT
+          validation_type,
+          COUNT(*) AS count,
+          SUM(execution_time_ns) AS total
+        FROM hwc_execution_spans
+        GROUP BY validation_type
+        ORDER BY validation_type;
+        """,
+        out=Csv("""
+        "validation_type","count","total"
+        "separated_validation",1,200
+        "skipped_validation",2,200
+        "unskipped_validation",1,200
+        """))
+
+  # Display metrics
+  def test_display_metrics(self):
+    return DiffTestBlueprint(
+        trace=Path('display_metrics.py'),
+        query=Metric('display_metrics'),
+        out=TextProto(r"""
+        display_metrics {
+          total_duplicate_frames: 0
+          duplicate_frames_logged: 0
+          total_dpu_underrun_count: 0
+          refresh_rate_switches: 5
+          refresh_rate_stats {
+            refresh_rate_fps: 60
+            count: 2
+            total_dur_ms: 2
+            avg_dur_ms: 1
+          }
+          refresh_rate_stats {
+            refresh_rate_fps: 90
+            count: 2
+            total_dur_ms: 2
+            avg_dur_ms: 1
+          }
+          refresh_rate_stats {
+            refresh_rate_fps: 120
+            count: 1
+            total_dur_ms: 2
+            avg_dur_ms: 2
+          }
+          update_power_state {
+            avg_runtime_micro_secs: 4000
+          }
+        }
+        """))
+
+  # DPU vote clock and bandwidth
+  def test_dpu_vote_clock_bw(self):
+    return DiffTestBlueprint(
+        trace=Path('dpu_vote_clock_bw.textproto'),
+        query=Metric('android_hwcomposer'),
+        out=TextProto(r"""
+        android_hwcomposer {
+          skipped_validation_count: 0
+          unskipped_validation_count: 0
+          separated_validation_count: 0
+          unknown_validation_count: 0
+          dpu_vote_metrics {
+            tid: 237
+            avg_dpu_vote_clock: 206250
+            avg_dpu_vote_avg_bw: 210000
+            avg_dpu_vote_peak_bw: 205000
+            avg_dpu_vote_rt_bw: 271000
+          }
+          dpu_vote_metrics {
+            tid: 299
+            avg_dpu_vote_clock: 250000
+          }
+        }
+        """))
+
+  # Video 4 Linux 2 related tests
+  def test_v4l2_vidioc_slice(self):
+    return DiffTestBlueprint(
+        trace=Path('v4l2_vidioc.textproto'),
+        query="""
+        SELECT ts, dur, name
+        FROM slice
+        WHERE category = 'Video 4 Linux 2';
+        """,
+        out=Csv("""
+        "ts","dur","name"
+        593268475912,0,"VIDIOC_QBUF minor=0 seq=0 type=9 index=19"
+        593268603800,0,"VIDIOC_QBUF minor=0 seq=0 type=9 index=20"
+        593528238295,0,"VIDIOC_DQBUF minor=0 seq=0 type=9 index=19"
+        593544028229,0,"VIDIOC_DQBUF minor=0 seq=0 type=9 index=20"
+        """))
+
+  def test_v4l2_vidioc_flow(self):
+    return DiffTestBlueprint(
+        trace=Path('v4l2_vidioc.textproto'),
+        query="""
+        SELECT qbuf.ts, qbuf.dur, qbuf.name, dqbuf.ts, dqbuf.dur, dqbuf.name
+        FROM flow
+        JOIN slice qbuf ON flow.slice_out = qbuf.id
+        JOIN slice dqbuf ON flow.slice_in = dqbuf.id;
+        """,
+        out=Path('v4l2_vidioc_flow.out'))
+
+  def test_virtio_video_slice(self):
+    return DiffTestBlueprint(
+        trace=Path('virtio_video.textproto'),
+        query="""
+        SELECT slice.ts, slice.dur, slice.name, track.name
+        FROM slice
+        JOIN track ON slice.track_id = track.id;
+        """,
+        out=Csv("""
+        "ts","dur","name","name"
+        593125003271,84500592,"Resource #102","virtio_video stream #4 OUTPUT"
+        593125003785,100000,"RESOURCE_QUEUE","virtio_video stream #4 Requests"
+        593125084611,709696,"Resource #62","virtio_video stream #3 OUTPUT"
+        593125084935,100000,"RESOURCE_QUEUE","virtio_video stream #3 Requests"
+        593125794194,100000,"RESOURCE_QUEUE","virtio_video stream #3 Responses"
+        593209502603,100000,"RESOURCE_QUEUE","virtio_video stream #4 Responses"
+        """))
+
+  # virtgpu (drm/virtio) related tests
+  def test_virtio_gpu(self):
+    return DiffTestBlueprint(
+        trace=Path('virtio_gpu.textproto'),
+        query="""
+        SELECT
+          ts,
+          dur,
+          name
+        FROM
+          slice
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "ts","dur","name"
+        1345090723759,1180312,"SUBMIT_3D"
+        1345090746311,1167135,"CTX_DETACH_RESOURCE"
+        """))
+
+  # mali GPU events
+  def test_mali(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          ftrace_events {
+            cpu: 2
+            event {
+              timestamp: 751796307210
+              pid: 2857
+              mali_mali_KCPU_CQS_WAIT_START {
+                info_val1: 1
+                info_val2: 0
+                kctx_tgid: 2201
+                kctx_id: 10
+                id: 0
+              }
+            }
+            event {
+              timestamp: 751800621175
+              pid: 2857
+              mali_mali_KCPU_CQS_WAIT_END {
+                info_val1: 412313493488
+                info_val2: 0
+                kctx_tgid: 2201
+                kctx_id: 10
+                id: 0
+              }
+            }
+            event {
+              timestamp: 751800638997
+              pid: 2857
+              mali_mali_KCPU_CQS_SET {
+                info_val1: 412313493480
+                info_val2: 0
+                kctx_tgid: 2201
+                kctx_id: 10
+                id: 0
+              }
+            }
+          }
+        }
+        """),
+        query="""
+        SELECT ts, dur, name FROM slice WHERE name GLOB "mali_KCPU_CQS*";
+        """,
+        out=Csv("""
+        "ts","dur","name"
+        751796307210,4313965,"mali_KCPU_CQS_WAIT"
+        751800638997,0,"mali_KCPU_CQS_SET"
+        """))
+
+  def test_mali_fence(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          ftrace_events {
+            cpu: 2
+            event {
+              timestamp: 751796307210
+              pid: 2857
+              mali_mali_KCPU_FENCE_WAIT_START {
+                info_val1: 1
+                info_val2: 0
+                kctx_tgid: 2201
+                kctx_id: 10
+                id: 0
+              }
+            }
+            event {
+              timestamp: 751800621175
+              pid: 2857
+              mali_mali_KCPU_FENCE_WAIT_END {
+                info_val1: 412313493488
+                info_val2: 0
+                kctx_tgid: 2201
+                kctx_id: 10
+                id: 0
+              }
+            }
+            event {
+              timestamp: 751800638997
+              pid: 2857
+              mali_mali_KCPU_FENCE_SIGNAL {
+                info_val1: 412313493480
+                info_val2: 0
+                kctx_tgid: 2201
+                kctx_id: 10
+                id: 0
+              }
+            }
+          }
+        }
+        """),
+        query="""
+        SELECT ts, dur, name FROM slice WHERE name GLOB "mali_KCPU_FENCE*";
+        """,
+        out=Csv("""
+        "ts","dur","name"
+        751796307210,4313965,"mali_KCPU_FENCE_WAIT"
+        751800638997,0,"mali_KCPU_FENCE_SIGNAL"
+        """))
diff --git a/test/trace_processor/diff_tests/graphics/tests_drm_related_ftrace_events.py b/test/trace_processor/diff_tests/graphics/tests_drm_related_ftrace_events.py
new file mode 100644
index 0000000..51bed18
--- /dev/null
+++ b/test/trace_processor/diff_tests/graphics/tests_drm_related_ftrace_events.py
@@ -0,0 +1,192 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class GraphicsDrmRelatedFtraceEvents(TestSuite):
+
+  def test_drm_vblank_gpu_track(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          ftrace_events {
+            cpu: 0
+            event {
+              timestamp: 6159770881976
+              pid: 0
+              drm_vblank_event {
+                crtc: 0
+                high_prec: 1
+                seq: 3551
+                time: 6159771267407
+              }
+            }
+          }
+        }
+        packet {
+          ftrace_events {
+            cpu: 4
+            event {
+              timestamp: 6159770993376
+              pid: 144
+              drm_vblank_event_delivered {
+                crtc: 0
+                file: 18446743526216291840
+                seq: 3551
+              }
+            }
+          }
+        }
+        """),
+        query="""
+        SELECT
+          gpu_track.name,
+          ts,
+          dur,
+          slice.name,
+          flat_key,
+          int_value,
+          string_value
+        FROM
+          gpu_track
+        JOIN slice
+          ON slice.track_id = gpu_track.id
+        JOIN args
+          ON slice.arg_set_id = args.arg_set_id
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "name","ts","dur","name","flat_key","int_value","string_value"
+        "vblank-0",6159770881976,0,"signal","vblank seqno",3551,"[NULL]"
+        "vblank-0",6159770993376,0,"deliver","vblank seqno",3551,"[NULL]"
+        """))
+
+  def test_drm_sched_gpu_track(self):
+    return DiffTestBlueprint(
+        trace=Path('drm_sched.textproto'),
+        query="""
+        SELECT
+          gpu_track.name,
+          ts,
+          dur,
+          slice.name,
+          flat_key,
+          int_value,
+          string_value
+        FROM
+          gpu_track
+        JOIN slice
+          ON slice.track_id = gpu_track.id
+        JOIN args
+          ON slice.arg_set_id = args.arg_set_id
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "name","ts","dur","name","flat_key","int_value","string_value"
+        "sched-ring0",9246165349383,4729073,"job","gpu sched job",13481,"[NULL]"
+        "sched-ring0",9246170078456,3941571,"job","gpu sched job",13482,"[NULL]"
+        "sched-ring0",9246174020027,25156,"job","gpu sched job",13483,"[NULL]"
+        "sched-ring0",9246181933273,4726312,"job","gpu sched job",13484,"[NULL]"
+        """))
+
+  def test_drm_sched_thread_track(self):
+    return DiffTestBlueprint(
+        trace=Path('drm_sched.textproto'),
+        query="""
+        SELECT
+          utid,
+          ts,
+          dur,
+          slice.name,
+          flat_key,
+          int_value,
+          string_value
+        FROM
+          thread_track
+        JOIN slice
+          ON slice.track_id = thread_track.id
+        JOIN args
+          ON slice.arg_set_id = args.arg_set_id
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "utid","ts","dur","name","flat_key","int_value","string_value"
+        1,9246165326050,0,"drm_sched_job","gpu sched ring","[NULL]","ring0"
+        1,9246165326050,0,"drm_sched_job","gpu sched job",13481,"[NULL]"
+        3,9246166957616,0,"drm_sched_job","gpu sched ring","[NULL]","ring0"
+        3,9246166957616,0,"drm_sched_job","gpu sched job",13482,"[NULL]"
+        3,9246167272512,0,"drm_sched_job","gpu sched ring","[NULL]","ring0"
+        3,9246167272512,0,"drm_sched_job","gpu sched job",13483,"[NULL]"
+        1,9246181907439,0,"drm_sched_job","gpu sched ring","[NULL]","ring0"
+        1,9246181907439,0,"drm_sched_job","gpu sched job",13484,"[NULL]"
+        """))
+
+  def test_drm_dma_fence_gpu_track(self):
+    return DiffTestBlueprint(
+        trace=Path('drm_dma_fence.textproto'),
+        query="""
+        SELECT
+          gpu_track.name,
+          ts,
+          dur,
+          slice.name,
+          flat_key,
+          int_value,
+          string_value
+        FROM
+          gpu_track
+        JOIN slice
+          ON slice.track_id = gpu_track.id
+        JOIN args
+          ON slice.arg_set_id = args.arg_set_id
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "name","ts","dur","name","flat_key","int_value","string_value"
+        "fence-gpu-ring-0-1",11303602488073,12813,"fence","fence seqno",16665,"[NULL]"
+        "fence-gpu-ring-0-1",11303602500886,4805626,"fence","fence seqno",16665,"[NULL]"
+        "fence-gpu-ring-0-1",11303607306512,3850783,"fence","fence seqno",16666,"[NULL]"
+        "fence-ring0-9",11303702681699,4868387,"fence","fence seqno",5065,"[NULL]"
+        """))
+
+  def test_drm_dma_fence_thread_track(self):
+    return DiffTestBlueprint(
+        trace=Path('drm_dma_fence.textproto'),
+        query="""
+        SELECT
+          utid,
+          ts,
+          dur,
+          slice.name,
+          flat_key,
+          int_value,
+          string_value
+        FROM
+          thread_track
+        JOIN slice
+          ON slice.track_id = thread_track.id
+        JOIN args
+          ON slice.arg_set_id = args.arg_set_id
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "utid","ts","dur","name","flat_key","int_value","string_value"
+        3,11303702851231,4867658,"dma_fence_wait","fence context",9,"[NULL]"
+        3,11303702851231,4867658,"dma_fence_wait","fence seqno",5065,"[NULL]"
+        """))
diff --git a/test/trace_processor/diff_tests/graphics/tests_gpu_trace.py b/test/trace_processor/diff_tests/graphics/tests_gpu_trace.py
new file mode 100644
index 0000000..ead5000
--- /dev/null
+++ b/test/trace_processor/diff_tests/graphics/tests_gpu_trace.py
@@ -0,0 +1,118 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class GraphicsGpuTrace(TestSuite):
+
+  def test_gpu_counters(self):
+    return DiffTestBlueprint(
+        trace=Path('gpu_counters.py'),
+        query="""
+        SELECT "ts", "value", "name", "gpu_id", "description", "unit"
+        FROM counter
+        JOIN gpu_counter_track
+          ON counter.track_id = gpu_counter_track.id
+        ORDER BY "ts";
+        """,
+        out=Csv("""
+        "ts","value","name","gpu_id","description","unit"
+        11,5.000000,"Vertex / Second",0,"Number of vertices per second","25/22"
+        12,7.000000,"Fragment / Second",0,"Number of fragments per second","26/22"
+        14,0.000000,"Triangle Acceleration",0,"Number of triangles per ms-ms","27/21:21"
+        21,10.000000,"Vertex / Second",0,"Number of vertices per second","25/22"
+        22,14.000000,"Fragment / Second",0,"Number of fragments per second","26/22"
+        24,9.000000,"Triangle Acceleration",0,"Number of triangles per ms-ms","27/21:21"
+        31,15.000000,"Vertex / Second",0,"Number of vertices per second","25/22"
+        32,21.000000,"Fragment / Second",0,"Number of fragments per second","26/22"
+        34,7.000000,"Triangle Acceleration",0,"Number of triangles per ms-ms","27/21:21"
+        """))
+
+  def test_gpu_counter_specs(self):
+    return DiffTestBlueprint(
+        trace=Path('gpu_counter_specs.textproto'),
+        query="""
+        SELECT group_id, c.name, c.description, unit
+        FROM gpu_counter_group AS g
+        JOIN gpu_counter_track AS c
+          ON g.track_id = c.id;
+        """,
+        out=Csv("""
+        "group_id","name","description","unit"
+        0,"GPU Frequency","clock speed","/22"
+        3,"Fragments / vertex","Number of fragments per vertex","39/25"
+        2,"Fragments / vertex","Number of fragments per vertex","39/25"
+        3,"Fragment / Second","Number of fragments per second","26/22"
+        4,"Triangle Acceleration","Number of triangles per ms-ms","27/21:21"
+        """))
+
+  def test_gpu_render_stages(self):
+    return DiffTestBlueprint(
+        trace=Path('gpu_render_stages.py'),
+        query=Path('gpu_render_stages_test.sql'),
+        out=Path('gpu_render_stages.out'))
+
+  def test_gpu_render_stages_interned_spec(self):
+    return DiffTestBlueprint(
+        trace=Path('gpu_render_stages_interned_spec.textproto'),
+        query=Path('gpu_render_stages_test.sql'),
+        out=Path('gpu_render_stages_interned_spec.out'))
+
+  def test_vulkan_api_events(self):
+    return DiffTestBlueprint(
+        trace=Path('vulkan_api_events.py'),
+        query="""
+        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)
+        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;
+        """,
+        out=Path('vulkan_api_events.out'))
+
+  def test_gpu_log(self):
+    return DiffTestBlueprint(
+        trace=Path('gpu_log.py'),
+        query="""
+        SELECT scope, track.name AS track_name, ts, dur, gpu_slice.name AS slice_name,
+          key, string_value AS value
+        FROM gpu_track
+        LEFT JOIN track USING (id)
+        LEFT JOIN gpu_slice ON gpu_track.id = gpu_slice.track_id
+        LEFT JOIN args USING (arg_set_id)
+        ORDER BY ts, slice_name, key;
+        """,
+        out=Csv("""
+        "scope","track_name","ts","dur","slice_name","key","value"
+        "gpu_log","GPU Log",1,0,"VERBOSE","message","message0"
+        "gpu_log","GPU Log",1,0,"VERBOSE","tag","tag0"
+        "gpu_log","GPU Log",2,0,"DEBUG","message","message1"
+        "gpu_log","GPU Log",2,0,"DEBUG","tag","tag0"
+        "gpu_log","GPU Log",3,0,"INFO","message","message2"
+        "gpu_log","GPU Log",3,0,"INFO","tag","tag0"
+        "gpu_log","GPU Log",4,0,"ERROR","message","message4"
+        "gpu_log","GPU Log",4,0,"ERROR","tag","tag0"
+        "gpu_log","GPU Log",4,0,"WARNING","message","message3"
+        "gpu_log","GPU Log",4,0,"WARNING","tag","tag0"
+        "gpu_log","GPU Log",5,0,"VERBOSE","message","message5"
+        "gpu_log","GPU Log",5,0,"VERBOSE","tag","tag1"
+        """))
diff --git a/test/trace_processor/diff_tests/graphics/v4l2_vidioc.textproto b/test/trace_processor/diff_tests/graphics/v4l2_vidioc.textproto
new file mode 100644
index 0000000..45c80e8
--- /dev/null
+++ b/test/trace_processor/diff_tests/graphics/v4l2_vidioc.textproto
@@ -0,0 +1,101 @@
+packet {
+  ftrace_events {
+    cpu: 0
+    event {
+      timestamp: 593268475912
+      pid: 2488
+      v4l2_qbuf {
+        bytesused: 0
+        field: 0
+        flags: 16386
+        index: 19
+        minor: 0
+        sequence: 0
+        timecode_flags: 0
+        timecode_frames: 0
+        timecode_hours: 0
+        timecode_minutes: 0
+        timecode_seconds: 0
+        timecode_type: 0
+        timecode_userbits0: 0
+        timecode_userbits1: 0
+        timecode_userbits2: 0
+        timecode_userbits3: 0
+        timestamp: 404000000000
+        type: 9
+      }
+    }
+    event {
+      timestamp: 593268603800
+      pid: 2489
+      v4l2_qbuf {
+        bytesused: 0
+        field: 0
+        flags: 16386
+        index: 20
+        minor: 0
+        sequence: 0
+        timecode_flags: 0
+        timecode_frames: 0
+        timecode_hours: 0
+        timecode_minutes: 0
+        timecode_seconds: 0
+        timecode_type: 0
+        timecode_userbits0: 0
+        timecode_userbits1: 0
+        timecode_userbits2: 0
+        timecode_userbits3: 0
+        timestamp: 405000000000
+        type: 9
+      }
+    }
+    event {
+      timestamp: 593528238295
+      pid: 2489
+      v4l2_dqbuf {
+        bytesused: 0
+        field: 0
+        flags: 16384
+        index: 19
+        minor: 0
+        sequence: 0
+        timecode_flags: 0
+        timecode_frames: 0
+        timecode_hours: 0
+        timecode_minutes: 0
+        timecode_seconds: 0
+        timecode_type: 0
+        timecode_userbits0: 0
+        timecode_userbits1: 0
+        timecode_userbits2: 0
+        timecode_userbits3: 0
+        timestamp: 404000000000
+        type: 9
+      }
+    }
+    event {
+      timestamp: 593544028229
+      pid: 2489
+      v4l2_dqbuf {
+        bytesused: 0
+        field: 0
+        flags: 16384
+        index: 20
+        minor: 0
+        sequence: 0
+        timecode_flags: 0
+        timecode_frames: 0
+        timecode_hours: 0
+        timecode_minutes: 0
+        timecode_seconds: 0
+        timecode_type: 0
+        timecode_userbits0: 0
+        timecode_userbits1: 0
+        timecode_userbits2: 0
+        timecode_userbits3: 0
+        timestamp: 405000000000
+        type: 9
+      }
+    }
+  }
+}
diff --git a/test/trace_processor/diff_tests/graphics/v4l2_vidioc_flow.out b/test/trace_processor/diff_tests/graphics/v4l2_vidioc_flow.out
new file mode 100644
index 0000000..4832df7
--- /dev/null
+++ b/test/trace_processor/diff_tests/graphics/v4l2_vidioc_flow.out
@@ -0,0 +1,3 @@
+"ts","dur","name","ts","dur","name"
+593268475912,0,"VIDIOC_QBUF minor=0 seq=0 type=9 index=19",593528238295,0,"VIDIOC_DQBUF minor=0 seq=0 type=9 index=19"
+593268603800,0,"VIDIOC_QBUF minor=0 seq=0 type=9 index=20",593544028229,0,"VIDIOC_DQBUF minor=0 seq=0 type=9 index=20"
diff --git a/test/trace_processor/diff_tests/graphics/virtio_gpu.textproto b/test/trace_processor/diff_tests/graphics/virtio_gpu.textproto
new file mode 100644
index 0000000..c46be94
--- /dev/null
+++ b/test/trace_processor/diff_tests/graphics/virtio_gpu.textproto
@@ -0,0 +1,76 @@
+packet {
+  ftrace_events {
+    cpu: 0
+    event {
+      timestamp: 1345091904071
+      pid: 4244
+      virtio_gpu_cmd_response {
+        ctx_id: 27
+        dev: 4
+        fence_id: 67980
+        flags: 3
+        name: "control"
+        num_free: 506
+        seqno: 143910
+        type: 4352
+        vq: 0
+      }
+    }
+    event {
+      timestamp: 1345091913446
+      pid: 4244
+      virtio_gpu_cmd_response {
+        ctx_id: 0
+        dev: 4
+        fence_id: 0
+        flags: 0
+        name: "control"
+        num_free: 506
+        seqno: 143911
+        type: 4352
+        vq: 0
+      }
+    }
+  }
+}
+packet {
+  ftrace_events {
+    cpu: 7
+    event {
+      timestamp: 1345090723759
+      pid: 3687
+      virtio_gpu_cmd_queue {
+        ctx_id: 27
+        dev: 4
+        fence_id: 67980
+        flags: 3
+        name: "control"
+        num_free: 503
+        seqno: 143910
+        type: 519
+        vq: 0
+      }
+    }
+  }
+}
+packet {
+  ftrace_events {
+    cpu: 5
+    event {
+      timestamp: 1345090746311
+      pid: 3687
+      virtio_gpu_cmd_queue {
+        ctx_id: 27
+        dev: 4
+        fence_id: 0
+        flags: 0
+        name: "control"
+        num_free: 501
+        seqno: 143911
+        type: 515
+        vq: 0
+      }
+    }
+  }
+}
+
diff --git a/test/trace_processor/diff_tests/graphics/virtio_video.textproto b/test/trace_processor/diff_tests/graphics/virtio_video.textproto
new file mode 100644
index 0000000..0545d40
--- /dev/null
+++ b/test/trace_processor/diff_tests/graphics/virtio_video.textproto
@@ -0,0 +1,93 @@
+packet {
+  ftrace_events {
+    cpu: 0
+    event {
+      timestamp: 593125003271
+      pid: 2509
+      virtio_video_resource_queue {
+        data_size0: 0
+        data_size1: 0
+        data_size2: 0
+        data_size3: 0
+        queue_type: 257
+        resource_id: 102
+        stream_id: 4
+        timestamp: 378000000000
+      }
+    }
+    event {
+      timestamp: 593125003785
+      pid: 2509
+      virtio_video_cmd {
+        stream_id: 4
+        type: 261
+      }
+    }
+    event {
+      timestamp: 593125084611
+      pid: 2509
+      virtio_video_resource_queue {
+        data_size0: 0
+        data_size1: 0
+        data_size2: 0
+        data_size3: 0
+        queue_type: 257
+        resource_id: 62
+        stream_id: 3
+        timestamp: 392000000000
+      }
+    }
+    event {
+      timestamp: 593125084935
+      pid: 2509
+      virtio_video_cmd {
+        stream_id: 3
+        type: 261
+      }
+    }
+    event {
+      timestamp: 593209502603
+      pid: 2516
+      virtio_video_cmd_done {
+        stream_id: 4
+        type: 261
+      }
+    }
+    event {
+      timestamp: 593125794194
+      pid: 2516
+      virtio_video_cmd_done {
+        stream_id: 3
+        type: 261
+      }
+    }
+    event {
+      timestamp: 593125794307
+      pid: 2516
+      virtio_video_resource_queue_done {
+        data_size0: 0
+        data_size1: 0
+        data_size2: 0
+        data_size3: 0
+        queue_type: 257
+        resource_id: 62
+        stream_id: 3
+        timestamp: 416000000000
+      }
+    }
+    event {
+      timestamp: 593209503863
+      pid: 2516
+      virtio_video_resource_queue_done {
+        data_size0: 0
+        data_size1: 0
+        data_size2: 0
+        data_size3: 0
+        queue_type: 257
+        resource_id: 102
+        stream_id: 4
+        timestamp: 402000000000
+      }
+    }
+  }
+}
diff --git a/test/trace_processor/graphics/vulkan_api_events.out b/test/trace_processor/diff_tests/graphics/vulkan_api_events.out
similarity index 100%
rename from test/trace_processor/graphics/vulkan_api_events.out
rename to test/trace_processor/diff_tests/graphics/vulkan_api_events.out
diff --git a/test/trace_processor/graphics/vulkan_api_events.py b/test/trace_processor/diff_tests/graphics/vulkan_api_events.py
similarity index 100%
rename from test/trace_processor/graphics/vulkan_api_events.py
rename to test/trace_processor/diff_tests/graphics/vulkan_api_events.py
diff --git a/test/trace_processor/diff_tests/include_index.py b/test/trace_processor/diff_tests/include_index.py
new file mode 100644
index 0000000..09e084e
--- /dev/null
+++ b/test/trace_processor/diff_tests/include_index.py
@@ -0,0 +1,160 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import os
+import sys
+from typing import List
+
+from python.generators.diff_tests import testing
+
+# A hack to import using `diff_tests.` which prevents the risk name conflicts,
+# i.e importing a module when user has a different package of the same name
+# installed.
+TRACE_PROCESSOR_TEST_DIR = os.path.dirname(
+    os.path.dirname(os.path.abspath(__file__)))
+sys.path.append(TRACE_PROCESSOR_TEST_DIR)
+
+from diff_tests.android.tests import Android
+from diff_tests.android.tests_bugreport import AndroidBugreport
+from diff_tests.android.tests_games import AndroidGames
+from diff_tests.atrace.tests import Atrace
+from diff_tests.atrace.tests_error_handling import AtraceErrorHandling
+from diff_tests.camera.tests import Camera
+from diff_tests.chrome.tests import Chrome
+from diff_tests.chrome.tests_args import ChromeArgs
+from diff_tests.chrome.tests_memory_snapshots import ChromeMemorySnapshots
+from diff_tests.chrome.tests_processes import ChromeProcesses
+from diff_tests.chrome.tests_rail_modes import ChromeRailModes
+from diff_tests.chrome.tests_scroll_jank import ChromeScrollJank
+from diff_tests.chrome.tests_touch_gesture import ChromeTouchGesture
+from diff_tests.cros.tests import Cros
+from diff_tests.dynamic.tests import Dynamic
+from diff_tests.fs.tests import Fs
+from diff_tests.fuchsia.tests import Fuchsia
+from diff_tests.functions.tests import Functions
+from diff_tests.graphics.tests import Graphics
+from diff_tests.graphics.tests_drm_related_ftrace_events import \
+    GraphicsDrmRelatedFtraceEvents
+from diff_tests.graphics.tests_gpu_trace import GraphicsGpuTrace
+from diff_tests.memory.tests import Memory
+from diff_tests.memory.tests_metrics import MemoryMetrics
+from diff_tests.network.tests import Network
+from diff_tests.parsing.tests import Parsing
+from diff_tests.parsing.tests_memory_counters import ParsingMemoryCounters
+from diff_tests.parsing.tests_rss_stats import ParsingRssStats
+from diff_tests.performance.tests import Performance
+from diff_tests.power.tests import Power
+from diff_tests.power.tests_energy_breakdown import PowerEnergyBreakdown
+from diff_tests.power.tests_power_rails import PowerPowerRails
+from diff_tests.power.tests_voltage_and_scaling import PowerVoltageAndScaling
+from diff_tests.process_tracking.tests import ProcessTracking
+from diff_tests.profiling.tests import Profiling
+from diff_tests.profiling.tests_heap_graph import ProfilingHeapGraph
+from diff_tests.profiling.tests_heap_profiling import ProfilingHeapProfiling
+from diff_tests.profiling.tests_llvm_symbolizer import ProfilingLlvmSymbolizer
+from diff_tests.profiling.tests_metrics import ProfilingMetrics
+from diff_tests.scheduler.tests import Scheduler
+from diff_tests.smoke.tests import Smoke
+from diff_tests.smoke.tests_compute_metrics import SmokeComputeMetrics
+from diff_tests.smoke.tests_json import SmokeJson
+from diff_tests.smoke.tests_sched_events import SmokeSchedEvents
+from diff_tests.span_join.tests_left_join import SpanJoinLeftJoin
+from diff_tests.span_join.tests_outer_join import SpanJoinOuterJoin
+from diff_tests.span_join.tests_regression import SpanJoinRegression
+from diff_tests.span_join.tests_smoke import SpanJoinSmoke
+from diff_tests.startup.tests import Startup
+from diff_tests.startup.tests_broadcasts import StartupBroadcasts
+from diff_tests.startup.tests_lock_contention import StartupLockContention
+from diff_tests.startup.tests_metrics import StartupMetrics
+from diff_tests.tables.tests import Tables
+from diff_tests.tables.tests_counters import TablesCounters
+from diff_tests.tables.tests_sched import TablesSched
+from diff_tests.track_event.tests import TrackEvent
+from diff_tests.translation.tests import Translation
+from diff_tests.ufs.tests import Ufs
+
+sys.path.pop()
+
+
+def fetch_all_diff_tests(index_path: str) -> List['testing.TestCase']:
+  return [
+      *Android(index_path, 'android', 'Android').fetch(),
+      *AndroidBugreport(index_path, 'android', 'AndroidBugreport').fetch(),
+      *AndroidGames(index_path, 'android', 'AndroidGames').fetch(),
+      *Atrace(index_path, 'atrace', 'Atrace').fetch(),
+      *AtraceErrorHandling(index_path, 'atrace', 'AtraceErrorHandling').fetch(),
+      *Camera(index_path, 'camera', 'Camera').fetch(),
+      *ChromeScrollJank(index_path, 'chrome', 'ChromeScrollJank').fetch(),
+      *ChromeTouchGesture(index_path, 'chrome', 'ChromeTouchGesture').fetch(),
+      *ChromeMemorySnapshots(index_path, 'chrome',
+                             'ChromeMemorySnapshots').fetch(),
+      *ChromeRailModes(index_path, 'chrome', 'ChromeRailModes').fetch(),
+      *ChromeProcesses(index_path, 'chrome', 'ChromeProcesses').fetch(),
+      *ChromeArgs(index_path, 'chrome', 'ChromeArgs').fetch(),
+      *Chrome(index_path, 'chrome', 'Chrome').fetch(),
+      *Cros(index_path, 'cros', 'Cros').fetch(),
+      *Dynamic(index_path, 'dynamic', 'Dynamic').fetch(),
+      *Fs(index_path, 'fs', 'Fs').fetch(),
+      *Fuchsia(index_path, 'fuchsia', 'Fuchsia').fetch(),
+      *Functions(index_path, 'functions', 'Functions').fetch(),
+      *Graphics(index_path, 'graphics', 'Graphics').fetch(),
+      *GraphicsGpuTrace(index_path, 'graphics', 'GraphicsGpuTrace').fetch(),
+      *GraphicsDrmRelatedFtraceEvents(index_path, 'graphics',
+                                      'GraphicsDrmRelatedFtraceEvents').fetch(),
+      *Ufs(index_path, 'ufs', 'Ufs').fetch(),
+      *Memory(index_path, 'memory', 'Memory').fetch(),
+      *MemoryMetrics(index_path, 'memory', 'MemoryMetrics').fetch(),
+      *Network(index_path, 'network', 'Network').fetch(),
+      *Parsing(index_path, 'parsing', 'Parsing').fetch(),
+      *ParsingRssStats(index_path, 'parsing', 'ParsingRssStats').fetch(),
+      *ParsingMemoryCounters(index_path, 'parsing',
+                             'ParsingMemoryCounters').fetch(),
+      *Performance(index_path, 'performance', 'Performance').fetch(),
+      *Power(index_path, 'power', 'Power').fetch(),
+      *PowerPowerRails(index_path, 'power', 'PowerPowerRails').fetch(),
+      *PowerVoltageAndScaling(index_path, 'power',
+                              'PowerVoltageAndScaling').fetch(),
+      *PowerEnergyBreakdown(index_path, 'power',
+                            'PowerEnergyBreakdown').fetch(),
+      *ProcessTracking(index_path, 'process_tracking',
+                       'ProcessTracking').fetch(),
+      *Profiling(index_path, 'profiling', 'Profiling').fetch(),
+      *ProfilingHeapProfiling(index_path, 'profiling',
+                              'ProfilingHeapProfiling').fetch(),
+      *ProfilingHeapGraph(index_path, 'profiling',
+                          'ProfilingHeapGraph').fetch(),
+      *ProfilingMetrics(index_path, 'profiling', 'ProfilingMetrics').fetch(),
+      *ProfilingLlvmSymbolizer(index_path, 'profiling',
+                               'ProfilingLlvmSymbolizer').fetch(),
+      *Scheduler(index_path, 'scheduler', 'Scheduler').fetch(),
+      *Smoke(index_path, 'smoke', 'Smoke').fetch(),
+      *SmokeJson(index_path, 'smoke', 'SmokeJson').fetch(),
+      *SmokeSchedEvents(index_path, 'smoke', 'SmokeSchedEvents').fetch(),
+      *SmokeComputeMetrics(index_path, 'smoke', 'SmokeComputeMetrics').fetch(),
+      *SpanJoinOuterJoin(index_path, 'span_join', 'SpanJoinOuterJoin').fetch(),
+      *SpanJoinLeftJoin(index_path, 'span_join', 'SpanJoinLeftJoin').fetch(),
+      *SpanJoinSmoke(index_path, 'span_join', 'SpanJoinSmoke').fetch(),
+      *SpanJoinRegression(index_path, 'span_join',
+                          'SpanJoinRegression').fetch(),
+      *Startup(index_path, 'startup', 'Startup').fetch(),
+      *StartupBroadcasts(index_path, 'startup', 'StartupBroadcasts').fetch(),
+      *StartupMetrics(index_path, 'startup', 'StartupMetrics').fetch(),
+      *StartupLockContention(index_path, 'startup',
+                             'StartupLockContention').fetch(),
+      *Tables(index_path, 'tables', 'Tables').fetch(),
+      *TablesCounters(index_path, 'tables', 'TablesCounters').fetch(),
+      *TablesSched(index_path, 'tables', 'TablesSched').fetch(),
+      *TrackEvent(index_path, 'track_event', 'TrackEvent').fetch(),
+      *Translation(index_path, 'translation', 'Translation').fetch(),
+  ]
diff --git a/test/trace_processor/memory/android_ion.py b/test/trace_processor/diff_tests/memory/android_ion.py
similarity index 100%
rename from test/trace_processor/memory/android_ion.py
rename to test/trace_processor/diff_tests/memory/android_ion.py
diff --git a/test/trace_processor/memory/android_lmk_reason.out b/test/trace_processor/diff_tests/memory/android_lmk_reason.out
similarity index 100%
rename from test/trace_processor/memory/android_lmk_reason.out
rename to test/trace_processor/diff_tests/memory/android_lmk_reason.out
diff --git a/test/trace_processor/memory/android_mem_by_priority.out b/test/trace_processor/diff_tests/memory/android_mem_by_priority.out
similarity index 100%
rename from test/trace_processor/memory/android_mem_by_priority.out
rename to test/trace_processor/diff_tests/memory/android_mem_by_priority.out
diff --git a/test/trace_processor/memory/android_mem_by_priority.py b/test/trace_processor/diff_tests/memory/android_mem_by_priority.py
similarity index 100%
rename from test/trace_processor/memory/android_mem_by_priority.py
rename to test/trace_processor/diff_tests/memory/android_mem_by_priority.py
diff --git a/test/trace_processor/memory/android_mem_counters.out b/test/trace_processor/diff_tests/memory/android_mem_counters.out
similarity index 100%
rename from test/trace_processor/memory/android_mem_counters.out
rename to test/trace_processor/diff_tests/memory/android_mem_counters.out
diff --git a/test/trace_processor/memory/android_mem_delta.py b/test/trace_processor/diff_tests/memory/android_mem_delta.py
similarity index 100%
rename from test/trace_processor/memory/android_mem_delta.py
rename to test/trace_processor/diff_tests/memory/android_mem_delta.py
diff --git a/test/trace_processor/memory/android_systrace_lmk.py b/test/trace_processor/diff_tests/memory/android_systrace_lmk.py
similarity index 100%
rename from test/trace_processor/memory/android_systrace_lmk.py
rename to test/trace_processor/diff_tests/memory/android_systrace_lmk.py
diff --git a/test/trace_processor/diff_tests/memory/tests.py b/test/trace_processor/diff_tests/memory/tests.py
new file mode 100644
index 0000000..aca4edf
--- /dev/null
+++ b/test/trace_processor/diff_tests/memory/tests.py
@@ -0,0 +1,323 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class Memory(TestSuite):
+  # Contains test for Android memory metrics. ION metric
+  def test_android_ion(self):
+    return DiffTestBlueprint(
+        trace=Path('android_ion.py'),
+        query=Metric('android_ion'),
+        out=TextProto(r"""
+        android_ion {
+          buffer {
+            name: "adsp"
+            avg_size_bytes: 1000.0
+            min_size_bytes: 1000.0
+            max_size_bytes: 1100.0
+            total_alloc_size_bytes: 1100.0
+          }
+          buffer {
+            name: "system"
+            avg_size_bytes: 1497.4874371859296
+            min_size_bytes: 1000.0
+            max_size_bytes: 2000.0
+            total_alloc_size_bytes: 2000.0
+          }
+        }
+        """))
+
+  def test_android_ion_stat(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          ftrace_events {
+            cpu: 0
+            event {
+              timestamp: 100
+              pid: 1
+              ion_stat {
+                buffer_id: 123
+                len: 1000
+                total_allocated: 2000
+              }
+            }
+          }
+        }
+        packet {
+          ftrace_events {
+            cpu: 0
+            event {
+              timestamp: 200
+              pid: 1
+              ion_stat {
+                buffer_id: 123
+                len: -1000
+                total_allocated: 1000
+              }
+            }
+          }
+        }
+        """),
+        query=Metric('android_ion'),
+        out=TextProto(r"""
+        android_ion {
+          buffer {
+            name: "all"
+            avg_size_bytes: 2000.0
+            min_size_bytes: 1000.0
+            max_size_bytes: 2000.0
+            total_alloc_size_bytes: 1000.0
+          }
+        }
+        """))
+
+  # DMA-BUF heap Metric
+  def test_android_dma_heap_stat(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          ftrace_events {
+            cpu: 0
+            event {
+              timestamp: 100
+              pid: 1
+              dma_heap_stat {
+                inode: 123
+                len: 1024
+                total_allocated: 2048
+              }
+            }
+          }
+        }
+        packet {
+          ftrace_events {
+            cpu: 0
+            event {
+              timestamp: 200
+              pid: 1
+              dma_heap_stat {
+                inode: 123
+                len: -1024
+                total_allocated: 1024
+              }
+            }
+          }
+        }
+        """),
+        query=Metric('android_dma_heap'),
+        out=TextProto(r"""
+        android_dma_heap {
+            avg_size_bytes: 2048.0
+            min_size_bytes: 1024.0
+            max_size_bytes: 2048.0
+            total_alloc_size_bytes: 1024.0
+        }
+        """))
+
+  def test_android_dma_buffer_tracks(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          ftrace_events {
+            cpu: 0
+            event {
+              timestamp: 100
+              pid: 1
+              dma_heap_stat {
+                inode: 123
+                len: 1024
+                total_allocated: 2048
+              }
+            }
+          }
+        }
+        packet {
+          ftrace_events {
+            cpu: 0
+            event {
+              timestamp: 200
+              pid: 1
+              dma_heap_stat {
+                inode: 123
+                len: -1024
+                total_allocated: 1024
+              }
+            }
+          }
+        }
+        """),
+        query="""
+        SELECT track.name, slice.ts, slice.dur, slice.name
+        FROM slice JOIN track ON slice.track_id = track.id
+        WHERE track.name = 'mem.dma_buffer';
+        """,
+        out=Csv("""
+        "name","ts","dur","name"
+        "mem.dma_buffer",100,100,"1 kB"
+        """))
+
+  # fastrpc metric
+  def test_android_fastrpc_dma_stat(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          ftrace_events {
+            cpu: 0
+            event {
+              timestamp: 100
+              pid: 1
+              fastrpc_dma_stat {
+                cid: 1
+                len: 1000
+                total_allocated: 2000
+              }
+            }
+          }
+        }
+        packet {
+          ftrace_events {
+            cpu: 0
+            event {
+              timestamp: 200
+              pid: 1
+              fastrpc_dma_stat {
+                cid: 1
+                len: -1000
+                total_allocated: 1000
+              }
+            }
+          }
+        }
+        """),
+        query=Metric('android_fastrpc'),
+        out=TextProto(r"""
+        android_fastrpc {
+          subsystem {
+            name: "MDSP"
+            avg_size_bytes: 2000.0
+            min_size_bytes: 1000.0
+            max_size_bytes: 2000.0
+            total_alloc_size_bytes: 1000.0
+          }
+        }
+        """))
+
+  # shrink slab
+  def test_shrink_slab(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          ftrace_events {
+            cpu: 7
+            event {
+              timestamp: 36448185787847
+              pid: 156
+              mm_shrink_slab_start {
+                cache_items: 1
+                delta: 0
+                gfp_flags: 3264
+                nr_objects_to_shrink: 0
+                shr: 18446743882920355600
+                shrink: 90
+                total_scan: 0
+                nid: 0
+                priority: 12
+              }
+            }
+          }
+        }
+        packet {
+          ftrace_events {
+            cpu: 7
+            event {
+              timestamp: 36448185788539
+              pid: 156
+              mm_shrink_slab_end {
+                new_scan: 0
+                retval: 0
+                shr: 18446743882920355600
+                shrink: 90
+                total_scan: 0
+                unused_scan: 0
+                nid: 0
+              }
+            }
+          }
+        }
+        """),
+        query="""
+        SELECT ts, dur, name FROM slice WHERE name = 'mm_vmscan_shrink_slab';
+        """,
+        out=Csv("""
+        "ts","dur","name"
+        36448185787847,692,"mm_vmscan_shrink_slab"
+        """))
+
+  # cma alloc
+  def test_cma(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          system_info {
+            utsname {
+              sysname: "Linux"
+              release: "5.10.0"
+            }
+          }
+        }
+        packet {
+          ftrace_events {
+            cpu: 4
+            event {
+              timestamp: 74288080958099
+              pid: 537
+              cma_alloc_start {
+                align: 4
+                count: 6592
+                name: "farawimg"
+              }
+            }
+            event {
+              timestamp: 74288191109751
+              pid: 537
+              cma_alloc_info {
+                align: 4
+                count: 6592
+                err_iso: 0
+                err_mig: 0
+                err_test: 0
+                name: "farawimg"
+                nr_mapped: 832596
+                nr_migrated: 6365
+                nr_reclaimed: 7
+                pfn: 10365824
+              }
+            }
+          }
+        }
+        """),
+        query="""
+        SELECT ts, dur, name FROM slice WHERE name = 'mm_cma_alloc';
+        """,
+        out=Csv("""
+        "ts","dur","name"
+        74288080958099,110151652,"mm_cma_alloc"
+        """))
diff --git a/test/trace_processor/diff_tests/memory/tests_metrics.py b/test/trace_processor/diff_tests/memory/tests_metrics.py
new file mode 100644
index 0000000..8a45185
--- /dev/null
+++ b/test/trace_processor/diff_tests/memory/tests_metrics.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class MemoryMetrics(TestSuite):
+
+  def test_android_mem_counters(self):
+    return DiffTestBlueprint(
+        trace=DataPath('memory_counters.pb'),
+        query=Metric('android_mem'),
+        out=Path('android_mem_counters.out'))
+
+  def test_trace_metadata(self):
+    return DiffTestBlueprint(
+        trace=DataPath('memory_counters.pb'),
+        query=Metric('trace_metadata'),
+        out=Path('trace_metadata.out'))
+
+  def test_android_mem_by_priority(self):
+    return DiffTestBlueprint(
+        trace=Path('android_mem_by_priority.py'),
+        query=Metric('android_mem'),
+        out=Path('android_mem_by_priority.out'))
+
+  def test_android_mem_lmk(self):
+    return DiffTestBlueprint(
+        trace=Path('android_systrace_lmk.py'),
+        query=Metric('android_lmk'),
+        out=TextProto(r"""
+        android_lmk {
+          total_count: 1
+            by_oom_score {
+            oom_score_adj: 900
+            count: 1
+          }
+          oom_victim_count: 0
+        }
+        """))
+
+  def test_android_lmk_oom(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        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
+              }
+            }
+          }
+        }
+        """),
+        query=Metric('android_lmk'),
+        out=TextProto(r"""
+        android_lmk {
+          total_count: 0
+          oom_victim_count: 1
+        }
+        """))
+
+  def test_android_mem_delta(self):
+    return DiffTestBlueprint(
+        trace=Path('android_mem_delta.py'),
+        query=Metric('android_mem'),
+        out=TextProto(r"""
+        android_mem {
+          process_metrics {
+            process_name: "com.my.pkg"
+            total_counters {
+              file_rss {
+                min: 2000.0
+                max: 10000.0
+                avg: 6666.666666666667
+                delta: 7000.0
+              }
+            }
+          }
+        }
+        """))
diff --git a/test/trace_processor/memory/trace_metadata.out b/test/trace_processor/diff_tests/memory/trace_metadata.out
similarity index 100%
rename from test/trace_processor/memory/trace_metadata.out
rename to test/trace_processor/diff_tests/memory/trace_metadata.out
diff --git a/test/trace_processor/network/inet_sock_set_state.textproto b/test/trace_processor/diff_tests/network/inet_sock_set_state.textproto
similarity index 100%
rename from test/trace_processor/network/inet_sock_set_state.textproto
rename to test/trace_processor/diff_tests/network/inet_sock_set_state.textproto
diff --git a/test/trace_processor/network/napi_gro_receive.textproto b/test/trace_processor/diff_tests/network/napi_gro_receive.textproto
similarity index 100%
rename from test/trace_processor/network/napi_gro_receive.textproto
rename to test/trace_processor/diff_tests/network/napi_gro_receive.textproto
diff --git a/test/trace_processor/network/net_dev_xmit.textproto b/test/trace_processor/diff_tests/network/net_dev_xmit.textproto
similarity index 100%
rename from test/trace_processor/network/net_dev_xmit.textproto
rename to test/trace_processor/diff_tests/network/net_dev_xmit.textproto
diff --git a/test/trace_processor/network/netif_receive_skb.textproto b/test/trace_processor/diff_tests/network/netif_receive_skb.textproto
similarity index 100%
rename from test/trace_processor/network/netif_receive_skb.textproto
rename to test/trace_processor/diff_tests/network/netif_receive_skb.textproto
diff --git a/test/trace_processor/network/netperf_metric.out b/test/trace_processor/diff_tests/network/netperf_metric.out
similarity index 100%
rename from test/trace_processor/network/netperf_metric.out
rename to test/trace_processor/diff_tests/network/netperf_metric.out
diff --git a/test/trace_processor/network/netperf_metric.textproto b/test/trace_processor/diff_tests/network/netperf_metric.textproto
similarity index 100%
rename from test/trace_processor/network/netperf_metric.textproto
rename to test/trace_processor/diff_tests/network/netperf_metric.textproto
diff --git a/test/trace_processor/diff_tests/network/tests.py b/test/trace_processor/diff_tests/network/tests.py
new file mode 100644
index 0000000..a8bdd72
--- /dev/null
+++ b/test/trace_processor/diff_tests/network/tests.py
@@ -0,0 +1,266 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class Network(TestSuite):
+  # Network performance
+  def test_netif_receive_skb(self):
+    return DiffTestBlueprint(
+        trace=Path('netif_receive_skb.textproto'),
+        query="""
+        SELECT
+          ts,
+          REPLACE(name, " Received KB", "") AS dev,
+          EXTRACT_ARG(arg_set_id, 'cpu') AS cpu,
+          EXTRACT_ARG(arg_set_id, 'len') AS len
+        FROM
+          counter AS c
+        LEFT JOIN
+          counter_track AS t
+          ON c.track_id = t.id
+        WHERE
+          name GLOB "* Received KB"
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "ts","dev","cpu","len"
+        10000,"rmnet0",0,1000
+        10000,"rmnet0",1,1000
+        10010,"rmnet0",0,1000
+        10011,"rmnet0",1,1000
+        12000,"wlan",4,1300
+        """))
+
+  def test_net_dev_xmit(self):
+    return DiffTestBlueprint(
+        trace=Path('net_dev_xmit.textproto'),
+        query="""
+        SELECT
+          ts,
+          REPLACE(name, " Transmitted KB", "") AS dev,
+          EXTRACT_ARG(arg_set_id, 'cpu') AS cpu,
+          EXTRACT_ARG(arg_set_id, 'len') AS len
+        FROM
+          counter AS c
+        LEFT JOIN
+          counter_track AS t
+          ON c.track_id = t.id
+        WHERE
+          name GLOB "* Transmitted KB"
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "ts","dev","cpu","len"
+        10000,"rmnet0",0,1000
+        10000,"rmnet0",1,1000
+        10010,"rmnet0",0,1000
+        12000,"wlan0",4,1300
+        """))
+
+  def test_netperf_metric(self):
+    return DiffTestBlueprint(
+        trace=Path('netperf_metric.textproto'),
+        query=Metric('android_netperf'),
+        out=Path('netperf_metric.out'))
+
+  def test_inet_sock_set_state(self):
+    return DiffTestBlueprint(
+        trace=Path('inet_sock_set_state.textproto'),
+        query="""
+        SELECT
+          ts,
+          s.name,
+          dur,
+          t.name
+        FROM
+          slice AS s
+        LEFT JOIN track AS t
+          ON s.track_id = t.id
+        WHERE
+          t.name GLOB "TCP stream#*"
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "ts","name","dur","name"
+        10000000,"TCP_SYN_SENT(pid=123)",100000000,"TCP stream#1"
+        110000000,"TCP_ESTABLISHED(sport=56789,dport=5001)",500000000,"TCP stream#1"
+        610000000,"TCP_CLOSE_WAIT",-1,"TCP stream#1"
+        710000000,"TCP_SYN_SENT(pid=567)",10000000,"TCP stream#2"
+        720000000,"TCP_ESTABLISHED(sport=56790,dport=5002)",300000000,"TCP stream#2"
+        1020000000,"TCP_CLOSE_WAIT",-1,"TCP stream#2"
+        """))
+
+  def test_tcp_retransmit_skb(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          ftrace_events {
+            cpu: 1
+            event {
+              timestamp: 110000000
+              pid: 234
+              tcp_retransmit_skb {
+                daddr: 19216801
+                saddr: 127001
+                dport: 5001
+                sport: 56789
+                state: 1
+                skaddr: 77889900
+              }
+            }
+          }
+        }
+        packet {
+          ftrace_events {
+            cpu: 1
+            event {
+              timestamp: 720000000
+              pid: 234
+              tcp_retransmit_skb {
+                daddr: 0
+                saddr: 0
+                dport: 5002
+                sport: 56790
+                state: 2
+                skaddr: 33445566
+              }
+            }
+          }
+        }
+        """),
+        query="""
+        SELECT
+          ts,
+          s.name,
+          dur
+        FROM
+          slice AS s
+        LEFT JOIN track AS t
+          ON s.track_id = t.id
+        WHERE
+          t.name = "TCP Retransmit Skb"
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "ts","name","dur"
+        110000000,"sport=56789,dport=5001",0
+        720000000,"sport=56790,dport=5002",0
+        """))
+
+  def test_napi_gro_receive(self):
+    return DiffTestBlueprint(
+        trace=Path('napi_gro_receive.textproto'),
+        query="""
+        SELECT
+          ts,
+          s.name,
+          dur,
+          cat,
+          t.name,
+          EXTRACT_ARG(arg_set_id, 'ret') AS ret,
+          EXTRACT_ARG(arg_set_id, 'len') AS len
+        FROM
+          slice AS s
+        LEFT JOIN
+          track AS t
+          ON s.track_id = t.id
+        WHERE
+          t.name GLOB "Napi Gro Cpu *"
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "ts","name","dur","cat","name","ret","len"
+        10000,"rmnet0",20,"napi_gro","Napi Gro Cpu 2",2,1000
+        20000,"rmnet0",20,"napi_gro","Napi Gro Cpu 2",1,1000
+        30000,"wlan",20,"napi_gro","Napi Gro Cpu 4",3,500
+        """))
+
+  def test_kfree_skb(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          ftrace_events {
+            cpu: 2
+            event {
+              timestamp: 10000
+              pid: 200
+              kfree_skb {
+                protocol: 2048
+              }
+            }
+          }
+        }
+        packet {
+          ftrace_events {
+            cpu: 2
+            event {
+              timestamp: 10020
+              pid: 300
+              kfree_skb {
+                protocol: 34525
+              }
+            }
+          }
+        }
+        packet {
+          ftrace_events {
+            cpu: 2
+            event {
+              timestamp: 20000
+              pid: 200
+              kfree_skb {
+                protocol: 1536
+              }
+            }
+          }
+        }
+        packet {
+          ftrace_events {
+            cpu: 2
+            event {
+              timestamp: 20020
+              pid: 300
+              kfree_skb {
+                protocol: 2048
+              }
+            }
+          }
+        }
+        """),
+        query="""
+        SELECT
+          ts,
+          value,
+          EXTRACT_ARG(arg_set_id, 'protocol') AS prot
+        FROM
+          counter AS c
+        LEFT JOIN
+          counter_track AS t
+          ON c.track_id = t.id
+        WHERE
+          name GLOB "Kfree Skb IP Prot"
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "ts","value","prot"
+        10000,1.000000,"IP"
+        10020,2.000000,"IPV6"
+        20020,3.000000,"IP"
+        """))
diff --git a/test/trace_processor/parsing/all_atoms_test.sql b/test/trace_processor/diff_tests/parsing/all_atoms_test.sql
similarity index 100%
rename from test/trace_processor/parsing/all_atoms_test.sql
rename to test/trace_processor/diff_tests/parsing/all_atoms_test.sql
diff --git a/test/trace_processor/parsing/android_binder.py b/test/trace_processor/diff_tests/parsing/android_binder.py
similarity index 100%
rename from test/trace_processor/parsing/android_binder.py
rename to test/trace_processor/diff_tests/parsing/android_binder.py
diff --git a/test/trace_processor/diff_tests/parsing/android_log_counts_test.sql b/test/trace_processor/diff_tests/parsing/android_log_counts_test.sql
new file mode 100644
index 0000000..7ba0af4
--- /dev/null
+++ b/test/trace_processor/diff_tests/parsing/android_log_counts_test.sql
@@ -0,0 +1,22 @@
+--
+-- 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 count(*) AS cnt FROM android_logs UNION ALL
+SELECT count(*) AS cnt FROM android_logs WHERE prio = 3 UNION ALL
+SELECT count(*) AS cnt FROM android_logs WHERE prio > 4 UNION ALL
+SELECT count(*) AS cnt FROM android_logs WHERE tag = 'screen_toggled' UNION ALL
+SELECT count(*) AS cnt FROM android_logs WHERE tag GLOB '*_pss' UNION ALL
+SELECT count(*) AS cnt FROM android_logs WHERE msg GLOB '*i2c?write*' OR msg GLOB '*I2C?Write*' UNION ALL
+SELECT count(*) AS cnt FROM android_logs WHERE ts >= 1510113924391 AND ts < 1512610021879;
diff --git a/test/trace_processor/parsing/android_log_msgs.out b/test/trace_processor/diff_tests/parsing/android_log_msgs.out
similarity index 100%
rename from test/trace_processor/parsing/android_log_msgs.out
rename to test/trace_processor/diff_tests/parsing/android_log_msgs.out
diff --git a/test/trace_processor/diff_tests/parsing/android_log_msgs_test.sql b/test/trace_processor/diff_tests/parsing/android_log_msgs_test.sql
new file mode 100644
index 0000000..fde19d7
--- /dev/null
+++ b/test/trace_processor/diff_tests/parsing/android_log_msgs_test.sql
@@ -0,0 +1,34 @@
+--
+-- 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.
+--
+CREATE VIEW v1 AS SELECT tag, count(*) FROM android_logs GROUP BY tag ORDER BY 2 DESC LIMIT 5;
+
+CREATE VIEW v2 AS SELECT tag, count(*) FROM android_logs GROUP BY tag ORDER BY 2 ASC LIMIT 5;
+
+CREATE VIEW v3 AS
+SELECT tag, count(*)
+FROM android_logs
+WHERE msg GLOB '*wakelock*' OR msg GLOB '*Wakelock*' OR msg GLOB '*WakeLock*' OR msg GLOB '*wakeLock*'
+GROUP BY tag;
+
+CREATE VIEW v4 AS SELECT msg, 1 FROM android_logs LIMIT 10;
+
+SELECT * FROM v1 UNION ALL
+SELECT '-----', 0 UNION ALL
+SELECT * FROM v2 UNION ALL
+SELECT '-----', 0 UNION ALL
+SELECT * FROM v3 UNION ALL
+SELECT '-----', 0 UNION ALL
+SELECT * FROM v4;
diff --git a/test/trace_processor/parsing/android_multiuser_switch.textproto b/test/trace_processor/diff_tests/parsing/android_multiuser_switch.textproto
similarity index 100%
rename from test/trace_processor/parsing/android_multiuser_switch.textproto
rename to test/trace_processor/diff_tests/parsing/android_multiuser_switch.textproto
diff --git a/test/trace_processor/parsing/android_package_list.py b/test/trace_processor/diff_tests/parsing/android_package_list.py
similarity index 100%
rename from test/trace_processor/parsing/android_package_list.py
rename to test/trace_processor/diff_tests/parsing/android_package_list.py
diff --git a/test/trace_processor/parsing/android_sched_and_ps_stats.out b/test/trace_processor/diff_tests/parsing/android_sched_and_ps_stats.out
similarity index 100%
rename from test/trace_processor/parsing/android_sched_and_ps_stats.out
rename to test/trace_processor/diff_tests/parsing/android_sched_and_ps_stats.out
diff --git a/test/trace_processor/diff_tests/parsing/args_string_filter_null_test.sql b/test/trace_processor/diff_tests/parsing/args_string_filter_null_test.sql
new file mode 100644
index 0000000..cb4657e
--- /dev/null
+++ b/test/trace_processor/diff_tests/parsing/args_string_filter_null_test.sql
@@ -0,0 +1,48 @@
+--
+-- 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.
+--
+-- noqa: disable=L049
+SELECT string_value
+FROM args
+WHERE string_value = NULL  -- noqa
+UNION
+SELECT string_value
+FROM args
+WHERE string_value != NULL
+UNION
+SELECT string_value
+FROM args
+WHERE string_value < NULL
+UNION
+SELECT string_value
+FROM args
+WHERE string_value <= NULL
+UNION
+SELECT string_value
+FROM args
+WHERE string_value > NULL
+UNION
+SELECT string_value
+FROM args
+WHERE string_value >= NULL
+UNION
+SELECT string_value
+FROM args
+WHERE string_value GLOB NULL
+UNION
+SELECT string_value
+FROM args
+WHERE string_value GLOB NULL;
+-- noqa: enable=L049
diff --git a/test/trace_processor/diff_tests/parsing/b120487929_test.sql b/test/trace_processor/diff_tests/parsing/b120487929_test.sql
new file mode 100644
index 0000000..42e21cf
--- /dev/null
+++ b/test/trace_processor/diff_tests/parsing/b120487929_test.sql
@@ -0,0 +1,71 @@
+--
+-- 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.
+--
+CREATE VIEW freq_view AS
+SELECT
+  ts,
+  lead(ts) OVER (PARTITION BY track_id ORDER BY ts) - ts AS dur,
+  cpu,
+  name AS freq_name,
+  value AS freq_value
+FROM counter
+JOIN cpu_counter_track
+  ON counter.track_id = cpu_counter_track.id
+WHERE name = 'cpufreq';
+
+CREATE VIEW idle_view
+AS SELECT
+  ts,
+  lead(ts) OVER (PARTITION BY track_id ORDER BY ts) - ts AS dur,
+  cpu,
+  name AS idle_name,
+  value AS idle_value
+FROM counter
+JOIN cpu_counter_track
+  ON counter.track_id = cpu_counter_track.id
+WHERE name = 'cpuidle';
+
+CREATE VIRTUAL TABLE freq_idle
+USING span_join(freq_view PARTITIONED cpu, idle_view PARTITIONED cpu);
+
+CREATE VIRTUAL TABLE window_freq_idle USING window;
+
+CREATE VIRTUAL TABLE span_freq_idle
+USING span_join(freq_idle PARTITIONED cpu, window_freq_idle);
+
+UPDATE window_freq_idle
+SET
+  window_start = (SELECT min(ts) FROM sched),
+  window_dur = (SELECT max(ts) - min(ts) FROM sched),
+  quantum = 1000000
+WHERE rowid = 0;
+
+CREATE VIEW counter_view
+AS SELECT
+  ts,
+  dur,
+  quantum_ts,
+  cpu,
+  CASE idle_value
+    WHEN 4294967295 THEN "freq"
+    ELSE "idle"
+  END AS name,
+  CASE idle_value
+    WHEN 4294967295 THEN freq_value
+    ELSE idle_value
+  END AS value
+FROM span_freq_idle;
+
+SELECT cpu, name, value, sum(dur) FROM counter_view GROUP BY cpu, name, value;
diff --git a/test/trace_processor/parsing/cgroup_attach_task_post_s_print_systrace.out b/test/trace_processor/diff_tests/parsing/cgroup_attach_task_post_s_print_systrace.out
similarity index 100%
rename from test/trace_processor/parsing/cgroup_attach_task_post_s_print_systrace.out
rename to test/trace_processor/diff_tests/parsing/cgroup_attach_task_post_s_print_systrace.out
diff --git a/test/trace_processor/parsing/cgroup_attach_task_pre_s_print_systrace.out b/test/trace_processor/diff_tests/parsing/cgroup_attach_task_pre_s_print_systrace.out
similarity index 100%
rename from test/trace_processor/parsing/cgroup_attach_task_pre_s_print_systrace.out
rename to test/trace_processor/diff_tests/parsing/cgroup_attach_task_pre_s_print_systrace.out
diff --git a/test/trace_processor/diff_tests/parsing/chrome_metadata.out b/test/trace_processor/diff_tests/parsing/chrome_metadata.out
new file mode 100644
index 0000000..16b0b8d
--- /dev/null
+++ b/test/trace_processor/diff_tests/parsing/chrome_metadata.out
@@ -0,0 +1,7 @@
+"id","type","name","key_type","int_value","str_value"
+0,"metadata","trace_uuid","single","[NULL]","00000000-0000-0000-707e-42fe4a525c33"
+1,"metadata","cr-background_tracing_metadata","single","[NULL]","CgA="
+2,"metadata","cr-playstore_version_code","single",101,"[NULL]"
+3,"metadata","cr-enabled_categories","single","[NULL]","cat1,cat2,cat3"
+4,"metadata","trace_size_bytes","single",54,"[NULL]"
+5,"metadata","trace_type","single","[NULL]","proto"
diff --git a/test/trace_processor/parsing/cpu_counters_b120487929.out b/test/trace_processor/diff_tests/parsing/cpu_counters_b120487929.out
similarity index 100%
rename from test/trace_processor/parsing/cpu_counters_b120487929.out
rename to test/trace_processor/diff_tests/parsing/cpu_counters_b120487929.out
diff --git a/test/trace_processor/parsing/cpu_freq.out b/test/trace_processor/diff_tests/parsing/cpu_freq.out
similarity index 100%
rename from test/trace_processor/parsing/cpu_freq.out
rename to test/trace_processor/diff_tests/parsing/cpu_freq.out
diff --git a/test/trace_processor/parsing/cpu_info.textproto b/test/trace_processor/diff_tests/parsing/cpu_info.textproto
similarity index 100%
rename from test/trace_processor/parsing/cpu_info.textproto
rename to test/trace_processor/diff_tests/parsing/cpu_info.textproto
diff --git a/test/trace_processor/parsing/flow_events_json_v1.json b/test/trace_processor/diff_tests/parsing/flow_events_json_v1.json
similarity index 100%
rename from test/trace_processor/parsing/flow_events_json_v1.json
rename to test/trace_processor/diff_tests/parsing/flow_events_json_v1.json
diff --git a/test/trace_processor/parsing/flow_events_json_v2.json b/test/trace_processor/diff_tests/parsing/flow_events_json_v2.json
similarity index 100%
rename from test/trace_processor/parsing/flow_events_json_v2.json
rename to test/trace_processor/diff_tests/parsing/flow_events_json_v2.json
diff --git a/test/trace_processor/parsing/ftrace_with_tracing_start.py b/test/trace_processor/diff_tests/parsing/ftrace_with_tracing_start.py
similarity index 100%
rename from test/trace_processor/parsing/ftrace_with_tracing_start.py
rename to test/trace_processor/diff_tests/parsing/ftrace_with_tracing_start.py
diff --git a/test/trace_processor/diff_tests/parsing/funcgraph_trace.textproto b/test/trace_processor/diff_tests/parsing/funcgraph_trace.textproto
new file mode 100644
index 0000000..543d3a7
--- /dev/null
+++ b/test/trace_processor/diff_tests/parsing/funcgraph_trace.textproto
@@ -0,0 +1,57 @@
+packet {
+  ftrace_events {
+    cpu: 0
+    event {
+      timestamp: 679375600673065
+      pid: 385482
+      funcgraph_entry {
+        depth: 0
+        func: 1
+      }
+    }
+    event {
+      timestamp: 679375600673769
+      pid: 385482
+      funcgraph_entry {
+        depth: 1
+        func: 2
+      }
+    }
+    event {
+      timestamp: 679375600675495
+      pid: 385482
+      funcgraph_exit {
+        calltime: 679391369445509
+        depth: 1
+        func: 2
+        overrun: 0
+        rettime: 679391369447205
+      }
+    }
+    event {
+      timestamp: 679375600676862
+      pid: 385482
+      funcgraph_exit {
+        calltime: 679391369444548
+        depth: 0
+        func: 1
+        overrun: 0
+        rettime: 679391369448763
+      }
+    }
+  }
+  sequence_flags: 1
+  interned_data {
+    kernel_symbols {
+      iid: 1
+      str: "__handle_mm_fault"
+    }
+    kernel_symbols {
+      iid: 2
+      str: "alloc_pages_vma"
+    }
+  }
+  trusted_uid: 304336
+  trusted_packet_sequence_id: 2
+  trusted_pid: 385482
+}
diff --git a/test/trace_processor/parsing/kernel_dpu_tmw_counter_thread_counter_and_track.out b/test/trace_processor/diff_tests/parsing/kernel_dpu_tmw_counter_thread_counter_and_track.out
similarity index 100%
rename from test/trace_processor/parsing/kernel_dpu_tmw_counter_thread_counter_and_track.out
rename to test/trace_processor/diff_tests/parsing/kernel_dpu_tmw_counter_thread_counter_and_track.out
diff --git a/test/trace_processor/parsing/kernel_tmw_counter.textproto b/test/trace_processor/diff_tests/parsing/kernel_tmw_counter.textproto
similarity index 100%
rename from test/trace_processor/parsing/kernel_tmw_counter.textproto
rename to test/trace_processor/diff_tests/parsing/kernel_tmw_counter.textproto
diff --git a/test/trace_processor/parsing/kernel_tmw_counter_thread_counter_and_track.out b/test/trace_processor/diff_tests/parsing/kernel_tmw_counter_thread_counter_and_track.out
similarity index 100%
rename from test/trace_processor/parsing/kernel_tmw_counter_thread_counter_and_track.out
rename to test/trace_processor/diff_tests/parsing/kernel_tmw_counter_thread_counter_and_track.out
diff --git a/test/trace_processor/parsing/mm_event.out b/test/trace_processor/diff_tests/parsing/mm_event.out
similarity index 100%
rename from test/trace_processor/parsing/mm_event.out
rename to test/trace_processor/diff_tests/parsing/mm_event.out
diff --git a/test/trace_processor/diff_tests/parsing/oom_query_test.sql b/test/trace_processor/diff_tests/parsing/oom_query_test.sql
new file mode 100644
index 0000000..6e3de63
--- /dev/null
+++ b/test/trace_processor/diff_tests/parsing/oom_query_test.sql
@@ -0,0 +1,105 @@
+--
+-- 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.
+--
+/* Create the file RSS table. */
+CREATE VIEW file_rss AS
+SELECT ts,
+       LEAD(ts, 1, ts) OVER(PARTITION BY track_id ORDER BY ts) - ts AS dur,
+       upid,
+       value AS file
+FROM counter
+JOIN process_counter_track
+  ON counter.track_id = process_counter_track.id
+WHERE process_counter_track.name IN ("mem.rss.file", "rss_stat.mm_filepages");
+
+/* Create the anon RSS table. */
+CREATE VIEW anon_rss AS
+SELECT ts,
+       LEAD(ts, 1, ts) OVER(PARTITION BY track_id ORDER BY ts) - ts AS dur,
+       upid,
+       value AS anon
+FROM counter
+JOIN process_counter_track
+  ON counter.track_id = process_counter_track.id
+WHERE process_counter_track.name IN ("mem.rss.anon", "rss_stat.mm_anonpages");
+
+/* Create the oom adj table. */
+CREATE VIEW oom_adj AS
+SELECT ts,
+       LEAD(ts, 1, ts) OVER(PARTITION BY track_id ORDER BY ts) - ts AS dur,
+       upid,
+       value AS oom_score_adj
+FROM counter
+JOIN process_counter_track
+  ON counter.track_id = process_counter_track.id
+WHERE process_counter_track.name = 'oom_score_adj';
+
+/* Harmonise the three tables above into a single table. */
+CREATE VIRTUAL TABLE anon_file USING span_join(anon_rss PARTITIONED upid, file_rss PARTITIONED upid);
+
+CREATE VIRTUAL TABLE anon_file_oom USING span_join(anon_file PARTITIONED upid, oom_adj PARTITIONED upid);
+
+/* For each span, compute the RSS (for a given upid) for each category of oom_adj */
+CREATE VIEW rss_spans_for_oom_upid AS
+SELECT ts,
+       dur,
+       upid,
+       CASE WHEN oom_score_adj < 0 THEN file + anon ELSE 0 END AS rss_oom_lt_zero,
+       CASE WHEN oom_score_adj <= 0 THEN file + anon ELSE 0 END AS rss_oom_eq_zero,
+       CASE WHEN oom_score_adj < 20 THEN file + anon ELSE 0 END AS rss_fg,
+       CASE WHEN oom_score_adj < 700 THEN file + anon ELSE 0 END AS rss_bg,
+       CASE WHEN oom_score_adj < 900 THEN file + anon ELSE 0 END AS rss_low_bg,
+       file + anon AS rss_cached
+FROM anon_file_oom;
+
+/* Convert the raw RSS values to the change in RSS (for a given upid) over time */
+CREATE VIEW rss_spans_rss_change AS
+SELECT ts,
+       dur,
+       upid,
+       rss_oom_lt_zero - lag(rss_oom_lt_zero, 1, 0) OVER win AS rss_oom_lt_zero_diff,
+       rss_oom_eq_zero - lag(rss_oom_eq_zero, 1, 0) OVER win AS rss_oom_eq_zero_diff,
+       rss_fg - lag(rss_fg, 1, 0) OVER win AS rss_fg_diff,
+       rss_bg - lag(rss_bg, 1, 0) OVER win AS rss_bg_diff,
+       rss_low_bg - lag(rss_low_bg, 1, 0) OVER win AS rss_low_bg_diff,
+       rss_cached - lag(rss_cached, 1, 0) OVER win AS rss_cached_diff
+FROM rss_spans_for_oom_upid
+WINDOW win AS (PARTITION BY upid ORDER BY ts);
+
+/*
+* Compute a rolling sum of anon + file for each category of process state.
+* (note: we no longer consider upid in the windows which means we are now
+* computing a rolling sum for all the processes).
+*/
+CREATE VIEW output AS
+SELECT ts,
+       lead(ts, 1, ts + dur) OVER win - ts AS dur,
+       SUM(rss_oom_lt_zero_diff) OVER win AS rss_oom_lt_zero,
+       SUM(rss_oom_eq_zero_diff) OVER win AS rss_oom_eq_zero,
+       SUM(rss_fg_diff) OVER win AS rss_fg,
+       SUM(rss_bg_diff) OVER win AS rss_bg,
+       SUM(rss_low_bg_diff) OVER win AS rss_low_bg,
+       SUM(rss_cached_diff) OVER win AS rss_cached
+FROM rss_spans_rss_change
+WINDOW win AS (ORDER BY ts)
+ORDER BY ts;
+
+/*
+* Print out the final result (note: dur = 0 is excluded to account for times
+* where multiple processes report a new memory value at the same timestamp)
+*/
+SELECT *
+FROM output
+WHERE dur > 0;
diff --git a/test/trace_processor/parsing/otheruuids.textproto b/test/trace_processor/diff_tests/parsing/otheruuids.textproto
similarity index 100%
rename from test/trace_processor/parsing/otheruuids.textproto
rename to test/trace_processor/diff_tests/parsing/otheruuids.textproto
diff --git a/test/trace_processor/parsing/print_systrace_lmk_userspace.out b/test/trace_processor/diff_tests/parsing/print_systrace_lmk_userspace.out
similarity index 100%
rename from test/trace_processor/parsing/print_systrace_lmk_userspace.out
rename to test/trace_processor/diff_tests/parsing/print_systrace_lmk_userspace.out
diff --git a/test/trace_processor/parsing/print_systrace_unsigned.out b/test/trace_processor/diff_tests/parsing/print_systrace_unsigned.out
similarity index 100%
rename from test/trace_processor/parsing/print_systrace_unsigned.out
rename to test/trace_processor/diff_tests/parsing/print_systrace_unsigned.out
diff --git a/test/trace_processor/parsing/print_systrace_unsigned.py b/test/trace_processor/diff_tests/parsing/print_systrace_unsigned.py
similarity index 100%
rename from test/trace_processor/parsing/print_systrace_unsigned.py
rename to test/trace_processor/diff_tests/parsing/print_systrace_unsigned.py
diff --git a/test/trace_processor/parsing/process_stats_poll_oom_score.out b/test/trace_processor/diff_tests/parsing/process_stats_poll_oom_score.out
similarity index 100%
rename from test/trace_processor/parsing/process_stats_poll_oom_score.out
rename to test/trace_processor/diff_tests/parsing/process_stats_poll_oom_score.out
diff --git a/test/trace_processor/parsing/rss_stat_after_free.py b/test/trace_processor/diff_tests/parsing/rss_stat_after_free.py
similarity index 100%
rename from test/trace_processor/parsing/rss_stat_after_free.py
rename to test/trace_processor/diff_tests/parsing/rss_stat_after_free.py
diff --git a/test/trace_processor/parsing/rss_stat_legacy.py b/test/trace_processor/diff_tests/parsing/rss_stat_legacy.py
similarity index 100%
rename from test/trace_processor/parsing/rss_stat_legacy.py
rename to test/trace_processor/diff_tests/parsing/rss_stat_legacy.py
diff --git a/test/trace_processor/parsing/rss_stat_mm_id.py b/test/trace_processor/diff_tests/parsing/rss_stat_mm_id.py
similarity index 100%
rename from test/trace_processor/parsing/rss_stat_mm_id.py
rename to test/trace_processor/diff_tests/parsing/rss_stat_mm_id.py
diff --git a/test/trace_processor/parsing/rss_stat_mm_id_clone.py b/test/trace_processor/diff_tests/parsing/rss_stat_mm_id_clone.py
similarity index 100%
rename from test/trace_processor/parsing/rss_stat_mm_id_clone.py
rename to test/trace_processor/diff_tests/parsing/rss_stat_mm_id_clone.py
diff --git a/test/trace_processor/parsing/rss_stat_mm_id_reuse.py b/test/trace_processor/diff_tests/parsing/rss_stat_mm_id_reuse.py
similarity index 100%
rename from test/trace_processor/parsing/rss_stat_mm_id_reuse.py
rename to test/trace_processor/diff_tests/parsing/rss_stat_mm_id_reuse.py
diff --git a/test/trace_processor/parsing/sched_blocked_proto.py b/test/trace_processor/diff_tests/parsing/sched_blocked_proto.py
similarity index 100%
rename from test/trace_processor/parsing/sched_blocked_proto.py
rename to test/trace_processor/diff_tests/parsing/sched_blocked_proto.py
diff --git a/test/trace_processor/parsing/sched_blocked_reason_symbolized.textproto b/test/trace_processor/diff_tests/parsing/sched_blocked_reason_symbolized.textproto
similarity index 100%
rename from test/trace_processor/parsing/sched_blocked_reason_symbolized.textproto
rename to test/trace_processor/diff_tests/parsing/sched_blocked_reason_symbolized.textproto
diff --git a/test/trace_processor/parsing/sched_blocked_reason_symbolized_to_systrace.out b/test/trace_processor/diff_tests/parsing/sched_blocked_reason_symbolized_to_systrace.out
similarity index 100%
rename from test/trace_processor/parsing/sched_blocked_reason_symbolized_to_systrace.out
rename to test/trace_processor/diff_tests/parsing/sched_blocked_reason_symbolized_to_systrace.out
diff --git a/test/trace_processor/parsing/sched_blocked_systrace.systrace b/test/trace_processor/diff_tests/parsing/sched_blocked_systrace.systrace
similarity index 100%
rename from test/trace_processor/parsing/sched_blocked_systrace.systrace
rename to test/trace_processor/diff_tests/parsing/sched_blocked_systrace.systrace
diff --git a/test/trace_processor/parsing/sched_slices_sched_switch_compact.out b/test/trace_processor/diff_tests/parsing/sched_slices_sched_switch_compact.out
similarity index 100%
rename from test/trace_processor/parsing/sched_slices_sched_switch_compact.out
rename to test/trace_processor/diff_tests/parsing/sched_slices_sched_switch_compact.out
diff --git a/test/trace_processor/parsing/sched_slices_sched_switch_original.out b/test/trace_processor/diff_tests/parsing/sched_slices_sched_switch_original.out
similarity index 100%
rename from test/trace_processor/parsing/sched_slices_sched_switch_original.out
rename to test/trace_processor/diff_tests/parsing/sched_slices_sched_switch_original.out
diff --git a/test/trace_processor/parsing/sched_waking_instants_compact_sched.out b/test/trace_processor/diff_tests/parsing/sched_waking_instants_compact_sched.out
similarity index 100%
rename from test/trace_processor/parsing/sched_waking_instants_compact_sched.out
rename to test/trace_processor/diff_tests/parsing/sched_waking_instants_compact_sched.out
diff --git a/test/trace_processor/diff_tests/parsing/sched_waking_raw_compact_sched.out b/test/trace_processor/diff_tests/parsing/sched_waking_raw_compact_sched.out
new file mode 100644
index 0000000..14440c0
--- /dev/null
+++ b/test/trace_processor/diff_tests/parsing/sched_waking_raw_compact_sched.out
@@ -0,0 +1,325 @@
+"ts","name","cpu","key","int_value","string_value"
+250978451591891,"sched_waking",0,"comm","[NULL]","rcu_sched"
+250978451591891,"sched_waking",0,"pid",8,"[NULL]"
+250978451591891,"sched_waking",0,"prio",120,"[NULL]"
+250978451591891,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978451609131,"sched_waking",0,"comm","[NULL]","kworker/u16:18"
+250978451609131,"sched_waking",0,"pid",17473,"[NULL]"
+250978451609131,"sched_waking",0,"prio",120,"[NULL]"
+250978451609131,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978596565656,"sched_waking",0,"comm","[NULL]","kworker/2:0"
+250978596565656,"sched_waking",0,"pid",17438,"[NULL]"
+250978596565656,"sched_waking",0,"prio",120,"[NULL]"
+250978596565656,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978600598417,"sched_waking",0,"comm","[NULL]","kworker/2:0"
+250978600598417,"sched_waking",0,"pid",17438,"[NULL]"
+250978600598417,"sched_waking",0,"prio",120,"[NULL]"
+250978600598417,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978600639042,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
+250978600639042,"sched_waking",0,"pid",6,"[NULL]"
+250978600639042,"sched_waking",0,"prio",120,"[NULL]"
+250978600639042,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978604651907,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
+250978604651907,"sched_waking",0,"pid",6,"[NULL]"
+250978604651907,"sched_waking",0,"prio",120,"[NULL]"
+250978604651907,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978604739980,"sched_waking",0,"comm","[NULL]","kworker/2:0"
+250978604739980,"sched_waking",0,"pid",17438,"[NULL]"
+250978604739980,"sched_waking",0,"prio",120,"[NULL]"
+250978604739980,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978608903886,"sched_waking",0,"comm","[NULL]","kworker/2:0"
+250978608903886,"sched_waking",0,"pid",17438,"[NULL]"
+250978608903886,"sched_waking",0,"prio",120,"[NULL]"
+250978608903886,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978608942220,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
+250978608942220,"sched_waking",0,"pid",6,"[NULL]"
+250978608942220,"sched_waking",0,"prio",120,"[NULL]"
+250978608942220,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978612887272,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
+250978612887272,"sched_waking",0,"pid",6,"[NULL]"
+250978612887272,"sched_waking",0,"prio",120,"[NULL]"
+250978612887272,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978612971283,"sched_waking",0,"comm","[NULL]","kworker/2:0"
+250978612971283,"sched_waking",0,"pid",17438,"[NULL]"
+250978612971283,"sched_waking",0,"prio",120,"[NULL]"
+250978612971283,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978616876595,"sched_waking",0,"comm","[NULL]","kworker/2:0"
+250978616876595,"sched_waking",0,"pid",17438,"[NULL]"
+250978616876595,"sched_waking",0,"prio",120,"[NULL]"
+250978616876595,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978616921700,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
+250978616921700,"sched_waking",0,"pid",6,"[NULL]"
+250978616921700,"sched_waking",0,"prio",120,"[NULL]"
+250978616921700,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978617016804,"sched_waking",0,"comm","[NULL]","kworker/u16:18"
+250978617016804,"sched_waking",0,"pid",17473,"[NULL]"
+250978617016804,"sched_waking",0,"prio",120,"[NULL]"
+250978617016804,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978620859669,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
+250978620859669,"sched_waking",0,"pid",6,"[NULL]"
+250978620859669,"sched_waking",0,"prio",120,"[NULL]"
+250978620859669,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978620939148,"sched_waking",0,"comm","[NULL]","kworker/2:0"
+250978620939148,"sched_waking",0,"pid",17438,"[NULL]"
+250978620939148,"sched_waking",0,"prio",120,"[NULL]"
+250978620939148,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978624836805,"sched_waking",0,"comm","[NULL]","kworker/2:0"
+250978624836805,"sched_waking",0,"pid",17438,"[NULL]"
+250978624836805,"sched_waking",0,"prio",120,"[NULL]"
+250978624836805,"sched_waking",0,"target_cpu",2,"[NULL]"
+250978624875398,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
+250978624875398,"sched_waking",0,"pid",6,"[NULL]"
+250978624875398,"sched_waking",0,"prio",120,"[NULL]"
+250978624875398,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978624978003,"sched_waking",0,"comm","[NULL]","kworker/u16:18"
+250978624978003,"sched_waking",0,"pid",17473,"[NULL]"
+250978624978003,"sched_waking",0,"prio",120,"[NULL]"
+250978624978003,"sched_waking",0,"target_cpu",0,"[NULL]"
+250978441664547,"sched_waking",1,"comm","[NULL]","ksoftirqd/1"
+250978441664547,"sched_waking",1,"pid",18,"[NULL]"
+250978441664547,"sched_waking",1,"prio",120,"[NULL]"
+250978441664547,"sched_waking",1,"target_cpu",1,"[NULL]"
+250978441702515,"sched_waking",1,"comm","[NULL]","kworker/u16:18"
+250978441702515,"sched_waking",1,"pid",17473,"[NULL]"
+250978441702515,"sched_waking",1,"prio",120,"[NULL]"
+250978441702515,"sched_waking",1,"target_cpu",2,"[NULL]"
+250978805144375,"sched_waking",1,"comm","[NULL]","traced_probes"
+250978805144375,"sched_waking",1,"pid",17772,"[NULL]"
+250978805144375,"sched_waking",1,"prio",120,"[NULL]"
+250978805144375,"sched_waking",1,"target_cpu",1,"[NULL]"
+250978808058698,"sched_waking",1,"comm","[NULL]","rcuop/0"
+250978808058698,"sched_waking",1,"pid",10,"[NULL]"
+250978808058698,"sched_waking",1,"prio",120,"[NULL]"
+250978808058698,"sched_waking",1,"target_cpu",2,"[NULL]"
+250978884120216,"sched_waking",1,"comm","[NULL]","traced"
+250978884120216,"sched_waking",1,"pid",17771,"[NULL]"
+250978884120216,"sched_waking",1,"prio",120,"[NULL]"
+250978884120216,"sched_waking",1,"target_cpu",1,"[NULL]"
+250978885260685,"sched_waking",1,"comm","[NULL]","rcuop/0"
+250978885260685,"sched_waking",1,"pid",10,"[NULL]"
+250978885260685,"sched_waking",1,"prio",120,"[NULL]"
+250978885260685,"sched_waking",1,"target_cpu",2,"[NULL]"
+250978885786674,"sched_waking",1,"comm","[NULL]","traced_probes"
+250978885786674,"sched_waking",1,"pid",17772,"[NULL]"
+250978885786674,"sched_waking",1,"prio",120,"[NULL]"
+250978885786674,"sched_waking",1,"target_cpu",1,"[NULL]"
+250978444958245,"sched_waking",2,"comm","[NULL]","rcuop/0"
+250978444958245,"sched_waking",2,"pid",10,"[NULL]"
+250978444958245,"sched_waking",2,"prio",120,"[NULL]"
+250978444958245,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978444974443,"sched_waking",2,"comm","[NULL]","rcuop/1"
+250978444974443,"sched_waking",2,"pid",21,"[NULL]"
+250978444974443,"sched_waking",2,"prio",120,"[NULL]"
+250978444974443,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978444984234,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978444984234,"sched_waking",2,"pid",7,"[NULL]"
+250978444984234,"sched_waking",2,"prio",120,"[NULL]"
+250978444984234,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978451590173,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978451590173,"sched_waking",2,"pid",7,"[NULL]"
+250978451590173,"sched_waking",2,"prio",120,"[NULL]"
+250978451590173,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978451646579,"sched_waking",2,"comm","[NULL]","rcuos/0"
+250978451646579,"sched_waking",2,"pid",11,"[NULL]"
+250978451646579,"sched_waking",2,"prio",120,"[NULL]"
+250978451646579,"sched_waking",2,"target_cpu",4,"[NULL]"
+250978451716891,"sched_waking",2,"comm","[NULL]","rcuos/1"
+250978451716891,"sched_waking",2,"pid",22,"[NULL]"
+250978451716891,"sched_waking",2,"prio",120,"[NULL]"
+250978451716891,"sched_waking",2,"target_cpu",1,"[NULL]"
+250978452047048,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978452047048,"sched_waking",2,"pid",17473,"[NULL]"
+250978452047048,"sched_waking",2,"prio",120,"[NULL]"
+250978452047048,"sched_waking",2,"target_cpu",0,"[NULL]"
+250978458337725,"sched_waking",2,"comm","[NULL]","sugov:0"
+250978458337725,"sched_waking",2,"pid",625,"[NULL]"
+250978458337725,"sched_waking",2,"prio",49,"[NULL]"
+250978458337725,"sched_waking",2,"target_cpu",3,"[NULL]"
+250978458362100,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978458362100,"sched_waking",2,"pid",7,"[NULL]"
+250978458362100,"sched_waking",2,"prio",120,"[NULL]"
+250978458362100,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978458398455,"sched_waking",2,"comm","[NULL]","rcuop/0"
+250978458398455,"sched_waking",2,"pid",10,"[NULL]"
+250978458398455,"sched_waking",2,"prio",120,"[NULL]"
+250978458398455,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978458419496,"sched_waking",2,"comm","[NULL]","rcuop/1"
+250978458419496,"sched_waking",2,"pid",21,"[NULL]"
+250978458419496,"sched_waking",2,"prio",120,"[NULL]"
+250978458419496,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978459118038,"sched_waking",2,"comm","[NULL]","sugov:0"
+250978459118038,"sched_waking",2,"pid",625,"[NULL]"
+250978459118038,"sched_waking",2,"prio",49,"[NULL]"
+250978459118038,"sched_waking",2,"target_cpu",3,"[NULL]"
+250978593466697,"sched_waking",2,"comm","[NULL]","kworker/2:0"
+250978593466697,"sched_waking",2,"pid",17438,"[NULL]"
+250978593466697,"sched_waking",2,"prio",120,"[NULL]"
+250978593466697,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978593734510,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978593734510,"sched_waking",2,"pid",17473,"[NULL]"
+250978593734510,"sched_waking",2,"prio",120,"[NULL]"
+250978593734510,"sched_waking",2,"target_cpu",3,"[NULL]"
+250978595521125,"sched_waking",2,"comm","[NULL]","kworker/2:3"
+250978595521125,"sched_waking",2,"pid",17754,"[NULL]"
+250978595521125,"sched_waking",2,"prio",120,"[NULL]"
+250978595521125,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978595696645,"sched_waking",2,"comm","[NULL]","kworker/2:0"
+250978595696645,"sched_waking",2,"pid",17438,"[NULL]"
+250978595696645,"sched_waking",2,"prio",120,"[NULL]"
+250978595696645,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978596068468,"sched_waking",2,"comm","[NULL]","kworker/2:3"
+250978596068468,"sched_waking",2,"pid",17754,"[NULL]"
+250978596068468,"sched_waking",2,"prio",120,"[NULL]"
+250978596068468,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978605185448,"sched_waking",2,"comm","[NULL]","ksoftirqd/2"
+250978605185448,"sched_waking",2,"pid",26,"[NULL]"
+250978605185448,"sched_waking",2,"prio",120,"[NULL]"
+250978605185448,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978605260240,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978605260240,"sched_waking",2,"pid",17473,"[NULL]"
+250978605260240,"sched_waking",2,"prio",120,"[NULL]"
+250978605260240,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978628278107,"sched_waking",2,"comm","[NULL]","logd.klogd"
+250978628278107,"sched_waking",2,"pid",651,"[NULL]"
+250978628278107,"sched_waking",2,"prio",130,"[NULL]"
+250978628278107,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978629289722,"sched_waking",2,"comm","[NULL]","rcuop/2"
+250978629289722,"sched_waking",2,"pid",29,"[NULL]"
+250978629289722,"sched_waking",2,"prio",120,"[NULL]"
+250978629289722,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978629405763,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978629405763,"sched_waking",2,"pid",7,"[NULL]"
+250978629405763,"sched_waking",2,"prio",120,"[NULL]"
+250978629405763,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978635024462,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978635024462,"sched_waking",2,"pid",7,"[NULL]"
+250978635024462,"sched_waking",2,"prio",120,"[NULL]"
+250978635024462,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978635060191,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978635060191,"sched_waking",2,"pid",17473,"[NULL]"
+250978635060191,"sched_waking",2,"prio",120,"[NULL]"
+250978635060191,"sched_waking",2,"target_cpu",0,"[NULL]"
+250978635219514,"sched_waking",2,"comm","[NULL]","rcuop/2"
+250978635219514,"sched_waking",2,"pid",29,"[NULL]"
+250978635219514,"sched_waking",2,"prio",120,"[NULL]"
+250978635219514,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978683441081,"sched_waking",2,"comm","[NULL]","kworker/2:0"
+250978683441081,"sched_waking",2,"pid",17438,"[NULL]"
+250978683441081,"sched_waking",2,"prio",120,"[NULL]"
+250978683441081,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978683732331,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978683732331,"sched_waking",2,"pid",17473,"[NULL]"
+250978683732331,"sched_waking",2,"prio",120,"[NULL]"
+250978683732331,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978685694259,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978685694259,"sched_waking",2,"pid",17473,"[NULL]"
+250978685694259,"sched_waking",2,"prio",120,"[NULL]"
+250978685694259,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978740127441,"sched_waking",2,"comm","[NULL]","kworker/2:0"
+250978740127441,"sched_waking",2,"pid",17438,"[NULL]"
+250978740127441,"sched_waking",2,"prio",120,"[NULL]"
+250978740127441,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978740425410,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978740425410,"sched_waking",2,"pid",17473,"[NULL]"
+250978740425410,"sched_waking",2,"prio",120,"[NULL]"
+250978740425410,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978743210358,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978743210358,"sched_waking",2,"pid",17473,"[NULL]"
+250978743210358,"sched_waking",2,"prio",120,"[NULL]"
+250978743210358,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978744945567,"sched_waking",2,"comm","[NULL]","logd.klogd"
+250978744945567,"sched_waking",2,"pid",651,"[NULL]"
+250978744945567,"sched_waking",2,"prio",130,"[NULL]"
+250978744945567,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978746028431,"sched_waking",2,"comm","[NULL]","rcuop/2"
+250978746028431,"sched_waking",2,"pid",29,"[NULL]"
+250978746028431,"sched_waking",2,"prio",120,"[NULL]"
+250978746028431,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978746173171,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978746173171,"sched_waking",2,"pid",7,"[NULL]"
+250978746173171,"sched_waking",2,"prio",120,"[NULL]"
+250978746173171,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978751622494,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978751622494,"sched_waking",2,"pid",7,"[NULL]"
+250978751622494,"sched_waking",2,"prio",120,"[NULL]"
+250978751622494,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978751661348,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978751661348,"sched_waking",2,"pid",17473,"[NULL]"
+250978751661348,"sched_waking",2,"prio",120,"[NULL]"
+250978751661348,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978751792859,"sched_waking",2,"comm","[NULL]","rcuop/2"
+250978751792859,"sched_waking",2,"pid",29,"[NULL]"
+250978751792859,"sched_waking",2,"prio",120,"[NULL]"
+250978751792859,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978751971817,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978751971817,"sched_waking",2,"pid",7,"[NULL]"
+250978751971817,"sched_waking",2,"prio",120,"[NULL]"
+250978751971817,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978758784578,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978758784578,"sched_waking",2,"pid",7,"[NULL]"
+250978758784578,"sched_waking",2,"prio",120,"[NULL]"
+250978758784578,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978758873745,"sched_waking",2,"comm","[NULL]","rcuop/2"
+250978758873745,"sched_waking",2,"pid",29,"[NULL]"
+250978758873745,"sched_waking",2,"prio",120,"[NULL]"
+250978758873745,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978808519271,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978808519271,"sched_waking",2,"pid",7,"[NULL]"
+250978808519271,"sched_waking",2,"prio",120,"[NULL]"
+250978808519271,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978815488490,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978815488490,"sched_waking",2,"pid",7,"[NULL]"
+250978815488490,"sched_waking",2,"prio",120,"[NULL]"
+250978815488490,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978815535678,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978815535678,"sched_waking",2,"pid",17473,"[NULL]"
+250978815535678,"sched_waking",2,"prio",120,"[NULL]"
+250978815535678,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978815675782,"sched_waking",2,"comm","[NULL]","rcuop/0"
+250978815675782,"sched_waking",2,"pid",10,"[NULL]"
+250978815675782,"sched_waking",2,"prio",120,"[NULL]"
+250978815675782,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978815738022,"sched_waking",2,"comm","[NULL]","rcuop/1"
+250978815738022,"sched_waking",2,"pid",21,"[NULL]"
+250978815738022,"sched_waking",2,"prio",120,"[NULL]"
+250978815738022,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978860203182,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978860203182,"sched_waking",2,"pid",17473,"[NULL]"
+250978860203182,"sched_waking",2,"prio",120,"[NULL]"
+250978860203182,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978880151205,"sched_waking",2,"comm","[NULL]","kworker/2:0"
+250978880151205,"sched_waking",2,"pid",17438,"[NULL]"
+250978880151205,"sched_waking",2,"prio",120,"[NULL]"
+250978880151205,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978880432143,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
+250978880432143,"sched_waking",2,"pid",17473,"[NULL]"
+250978880432143,"sched_waking",2,"prio",120,"[NULL]"
+250978880432143,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978885784018,"sched_waking",2,"comm","[NULL]","rcu_preempt"
+250978885784018,"sched_waking",2,"pid",7,"[NULL]"
+250978885784018,"sched_waking",2,"prio",120,"[NULL]"
+250978885784018,"sched_waking",2,"target_cpu",2,"[NULL]"
+250978465172309,"sched_waking",3,"comm","[NULL]","ksoftirqd/3"
+250978465172309,"sched_waking",3,"pid",34,"[NULL]"
+250978465172309,"sched_waking",3,"prio",120,"[NULL]"
+250978465172309,"sched_waking",3,"target_cpu",3,"[NULL]"
+250978465266789,"sched_waking",3,"comm","[NULL]","kworker/u16:18"
+250978465266789,"sched_waking",3,"pid",17473,"[NULL]"
+250978465266789,"sched_waking",3,"prio",120,"[NULL]"
+250978465266789,"sched_waking",3,"target_cpu",0,"[NULL]"
+250978439491994,"sched_waking",4,"comm","[NULL]","rcu_sched"
+250978439491994,"sched_waking",4,"pid",8,"[NULL]"
+250978439491994,"sched_waking",4,"prio",120,"[NULL]"
+250978439491994,"sched_waking",4,"target_cpu",1,"[NULL]"
+250978445783141,"sched_waking",5,"comm","[NULL]","rcu_sched"
+250978445783141,"sched_waking",5,"pid",8,"[NULL]"
+250978445783141,"sched_waking",5,"prio",120,"[NULL]"
+250978445783141,"sched_waking",5,"target_cpu",5,"[NULL]"
+250978806359896,"sched_waking",5,"comm","[NULL]","kworker/5:1"
+250978806359896,"sched_waking",5,"pid",17667,"[NULL]"
+250978806359896,"sched_waking",5,"prio",120,"[NULL]"
+250978806359896,"sched_waking",5,"target_cpu",5,"[NULL]"
+250978806428646,"sched_waking",5,"comm","[NULL]","kworker/u16:18"
+250978806428646,"sched_waking",5,"pid",17473,"[NULL]"
+250978806428646,"sched_waking",5,"prio",120,"[NULL]"
+250978806428646,"sched_waking",5,"target_cpu",2,"[NULL]"
diff --git a/test/trace_processor/diff_tests/parsing/sched_waking_raw_test.sql b/test/trace_processor/diff_tests/parsing/sched_waking_raw_test.sql
new file mode 100644
index 0000000..7fe9c9d
--- /dev/null
+++ b/test/trace_processor/diff_tests/parsing/sched_waking_raw_test.sql
@@ -0,0 +1,16 @@
+--
+-- 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, cpu, key, int_value, string_value FROM raw JOIN args ON raw.arg_set_id = args.arg_set_id WHERE name = "sched_waking" ORDER BY cpu ASC, ts ASC;
diff --git a/test/trace_processor/parsing/statsd_atoms_all_atoms.out b/test/trace_processor/diff_tests/parsing/statsd_atoms_all_atoms.out
similarity index 100%
rename from test/trace_processor/parsing/statsd_atoms_all_atoms.out
rename to test/trace_processor/diff_tests/parsing/statsd_atoms_all_atoms.out
diff --git a/test/trace_processor/parsing/synth_oom.py b/test/trace_processor/diff_tests/parsing/synth_oom.py
similarity index 100%
rename from test/trace_processor/parsing/synth_oom.py
rename to test/trace_processor/diff_tests/parsing/synth_oom.py
diff --git a/test/trace_processor/parsing/synth_oom_oom_query.out b/test/trace_processor/diff_tests/parsing/synth_oom_oom_query.out
similarity index 100%
rename from test/trace_processor/parsing/synth_oom_oom_query.out
rename to test/trace_processor/diff_tests/parsing/synth_oom_oom_query.out
diff --git a/test/trace_processor/parsing/syscall.py b/test/trace_processor/diff_tests/parsing/syscall.py
similarity index 100%
rename from test/trace_processor/parsing/syscall.py
rename to test/trace_processor/diff_tests/parsing/syscall.py
diff --git a/test/trace_processor/diff_tests/parsing/systrace_html.out b/test/trace_processor/diff_tests/parsing/systrace_html.out
new file mode 100644
index 0000000..9b736bd
--- /dev/null
+++ b/test/trace_processor/diff_tests/parsing/systrace_html.out
@@ -0,0 +1,3700 @@
+"ts","cpu","dur","ts_end","utid","end_state","priority","upid","name","tid"
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824711826997000,1,21000,6824711827018000,18,"S",120,18,"rcuos/1",21
+6824711827018000,1,27430000,6824711854448000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824711827085000,2,1808000,6824711828893000,0,"R",120,0,"swapper",0
+6824711827100000,6,4000,6824711827104000,702,"S",120,702,"kworker/u16:7",19422
+6824711827104000,6,9000,6824711827113000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824711827144000,6,1680000,6824711828824000,0,"R",120,0,"swapper",0
+6824711827183000,4,899199000,6824712726382000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824711828867000,6,19000,6824711828886000,669,"S",120,669,"kworker/6:1",14833
+6824711828886000,6,64000,6824711828950000,0,"R",120,0,"swapper",0
+6824711828893000,2,28000,6824711828921000,2487,"S",120,739,"UsbFfs-worker",20308
+6824711828921000,2,72000,6824711828993000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824711829019000,2,208000,6824711829227000,0,"R",120,0,"swapper",0
+6824711829100000,6,34000,6824711829134000,630,"S",100,630,"kworker/u17:1",1134
+6824711829134000,6,38000,6824711829172000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824711829437000,6,7000,6824711829444000,630,"S",100,630,"kworker/u17:1",1134
+6824711829444000,6,452000,6824711829896000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824711829896000,6,16000,6824711829912000,669,"S",120,669,"kworker/6:1",14833
+6824711829912000,6,3511000,6824711833423000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824711834507000,5,6564000,6824711841071000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824711841098000,6,3082000,6824711844180000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824711844275000,6,572000,6824711844847000,0,"R",120,0,"swapper",0
+6824711844547000,3,172000,6824711844719000,77,"S",120,77,"smem_native_rpm",87
+6824711844719000,3,2190000,6824711846909000,0,"R",120,0,"swapper",0
+6824711844847000,6,13000,6824711844860000,702,"S",120,702,"kworker/u16:7",19422
+6824711844860000,6,1757000,6824711846617000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824711846815000,6,7290000,6824711854105000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824711847350000,2,8611000,6824711855961000,0,"R",120,0,"swapper",0
+6824711850532000,3,50000,6824711850582000,29,"S",120,29,"ksoftirqd/3",33
+6824711850582000,3,24018000,6824711874600000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824711854169000,0,120000,6824711854289000,743,"S",120,743,"kworker/0:5",20371
+6824711854289000,0,462000,6824711854751000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824711855078000,0,471000,6824711855549000,0,"R",120,0,"swapper",0
+6824711855178000,1,179000,6824711855357000,771,"S",97,493,"DispSync",676
+6824711855357000,1,545000,6824711855902000,0,"R",120,0,"swapper",0
+6824711855549000,0,272000,6824711855821000,773,"S",97,493,"app",678
+6824711855821000,0,1794000,6824711857615000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824711857615000,0,250000,6824711857865000,770,"S",120,493,"Binder:640_2",675
+6824711857673000,1,347000,6824711858020000,0,"R",120,0,"swapper",0
+6824711857865000,0,426000,6824711858291000,0,"R",120,0,"swapper",0
+6824711858020000,1,151000,6824711858171000,773,"S",97,493,"app",678
+6824711858171000,1,29595000,6824711887766000,0,"R",120,0,"swapper",0
+6824711858291000,0,79000,6824711858370000,771,"S",97,493,"DispSync",676
+6824711858370000,0,1693000,6824711860063000,0,"R",120,0,"swapper",0
+6824711860063000,0,48000,6824711860111000,3,"S",120,3,"ksoftirqd/0",3
+6824711860111000,0,5836000,6824711865947000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824711861384000,2,457000,6824711861841000,45,"S",120,45,"rcuop/5",52
+6824711861841000,2,27907000,6824711889748000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824711867021000,0,47000,6824711867068000,3,"S",120,3,"ksoftirqd/0",3
+6824711867068000,0,702000,6824711867770000,0,"R",120,0,"swapper",0
+6824711867770000,0,70000,6824711867840000,5,"S",120,5,"rcu_preempt",7
+6824711867840000,0,4623000,6824711872463000,0,"R",120,0,"swapper",0
+6824711872463000,0,52000,6824711872515000,3,"S",120,3,"ksoftirqd/0",3
+6824711872515000,0,868000,6824711873383000,0,"R",120,0,"swapper",0
+6824711873308000,6,68000,6824711873376000,702,"D",120,702,"kworker/u16:7",19422
+6824711873376000,6,665000,6824711874041000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824711874041000,6,477000,6824711874518000,702,"D",120,702,"kworker/u16:7",19422
+6824711874518000,6,672000,6824711875190000,0,"R",120,0,"swapper",0
+6824711874600000,3,138000,6824711874738000,77,"S",120,77,"smem_native_rpm",87
+6824711874738000,3,920000,6824711875658000,0,"R",120,0,"swapper",0
+6824711875190000,6,207000,6824711875397000,702,"D",120,702,"kworker/u16:7",19422
+6824711875397000,6,667000,6824711876064000,0,"R",120,0,"swapper",0
+6824711875658000,3,86000,6824711875744000,77,"S",120,77,"smem_native_rpm",87
+6824711875744000,3,729000,6824711876473000,0,"R",120,0,"swapper",0
+6824711876064000,6,202000,6824711876266000,702,"D",120,702,"kworker/u16:7",19422
+6824711876266000,6,110000,6824711876376000,0,"R",120,0,"swapper",0
+6824711876376000,6,96000,6824711876472000,702,"D",120,702,"kworker/u16:7",19422
+6824711876472000,6,704000,6824711877176000,0,"R",120,0,"swapper",0
+6824711876473000,3,71000,6824711876544000,77,"S",120,77,"smem_native_rpm",87
+6824711876544000,3,783000,6824711877327000,0,"R",120,0,"swapper",0
+6824711877176000,6,125000,6824711877301000,702,"D",120,702,"kworker/u16:7",19422
+6824711877301000,6,678000,6824711877979000,0,"R",120,0,"swapper",0
+6824711877327000,3,143000,6824711877470000,77,"S",120,77,"smem_native_rpm",87
+6824711877470000,3,716000,6824711878186000,0,"R",120,0,"swapper",0
+6824711877979000,6,198000,6824711878177000,702,"D",120,702,"kworker/u16:7",19422
+6824711878177000,6,472000,6824711878649000,0,"R",120,0,"swapper",0
+6824711878186000,3,193000,6824711878379000,77,"S",120,77,"smem_native_rpm",87
+6824711878379000,3,469000,6824711878848000,0,"R",120,0,"swapper",0
+6824711878649000,6,182000,6824711878831000,702,"D",120,702,"kworker/u16:7",19422
+6824711878831000,6,446000,6824711879277000,0,"R",120,0,"swapper",0
+6824711878848000,3,140000,6824711878988000,77,"S",120,77,"smem_native_rpm",87
+6824711878988000,3,666000,6824711879654000,0,"R",120,0,"swapper",0
+6824711879277000,6,161000,6824711879438000,702,"D",120,702,"kworker/u16:7",19422
+6824711879438000,6,112000,6824711879550000,0,"R",120,0,"swapper",0
+6824711879550000,6,82000,6824711879632000,702,"D",120,702,"kworker/u16:7",19422
+6824711879632000,6,169000,6824711879801000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824711879993000,6,231000,6824711880224000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824711880298000,3,94000,6824711880392000,0,"R",120,0,"swapper",0
+6824711880367000,6,186000,6824711880553000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824711880611000,6,5024000,6824711885635000,0,"R",120,0,"swapper",0
+6824711885635000,6,44000,6824711885679000,50,"S",120,50,"ksoftirqd/6",57
+6824711885679000,6,8649000,6824711894328000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824711887462000,0,118000,6824711887580000,743,"S",120,743,"kworker/0:5",20371
+6824711887580000,0,455000,6824711888035000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824711888231000,1,85000,6824711888316000,771,"S",97,493,"DispSync",676
+6824711888316000,1,979000,6824711889295000,0,"R",120,0,"swapper",0
+6824711888362000,0,604000,6824711888966000,0,"R",120,0,"swapper",0
+6824711888966000,0,178000,6824711889144000,771,"S",97,493,"DispSync",676
+6824711889144000,0,532000,6824711889676000,0,"R",120,0,"swapper",0
+6824711889295000,1,275000,6824711889570000,773,"S",97,493,"app",678
+6824711889570000,1,1823000,6824711891393000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824711891393000,1,280000,6824711891673000,770,"S",120,493,"Binder:640_2",675
+6824711891429000,0,393000,6824711891822000,0,"R",120,0,"swapper",0
+6824711891673000,1,472000,6824711892145000,0,"R",120,0,"swapper",0
+6824711891822000,0,178000,6824711892000000,773,"S",97,493,"app",678
+6824711892000000,0,1451000,6824711893451000,0,"R",120,0,"swapper",0
+6824711892145000,1,95000,6824711892240000,771,"S",97,493,"DispSync",676
+6824711892240000,1,12180000,6824711904420000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824711894328000,6,300000,6824711894628000,702,"D",120,702,"kworker/u16:7",19422
+6824711894628000,6,2368000,6824711896996000,0,"R",120,0,"swapper",0
+6824711894870000,3,77000,6824711894947000,77,"S",120,77,"smem_native_rpm",87
+6824711894947000,3,4289000,6824711899236000,0,"R",120,0,"swapper",0
+6824711896996000,6,306000,6824711897302000,702,"D",120,702,"kworker/u16:7",19422
+6824711897302000,6,48000,6824711897350000,0,"R",120,0,"swapper",0
+6824711897350000,6,43000,6824711897393000,50,"S",120,50,"ksoftirqd/6",57
+6824711897393000,6,3585000,6824711900978000,0,"R",120,0,"swapper",0
+6824711899236000,3,257000,6824711899493000,77,"S",120,77,"smem_native_rpm",87
+6824711899493000,3,4694000,6824711904187000,0,"R",120,0,"swapper",0
+6824711900978000,6,122000,6824711901100000,702,"S",120,702,"kworker/u16:7",19422
+6824711901100000,6,50000,6824711901150000,0,"R",120,0,"swapper",0
+6824711901150000,6,40000,6824711901190000,50,"S",120,50,"ksoftirqd/6",57
+6824711901190000,6,826872000,6824712728062000,0,"R",120,0,"swapper",0
+6824711904187000,3,212000,6824711904399000,77,"S",120,77,"smem_native_rpm",87
+6824711904399000,3,55334000,6824711959733000,0,"R",120,0,"swapper",0
+6824711904420000,1,72000,6824711904492000,15,"S",120,15,"ksoftirqd/1",17
+6824711904492000,1,14769000,6824711919261000,0,"R",120,0,"swapper",0
+6824711919261000,1,145000,6824711919406000,15,"S",120,15,"ksoftirqd/1",17
+6824711919406000,1,3219000,6824711922625000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824711922383000,0,57000,6824711922440000,3,"S",120,3,"ksoftirqd/0",3
+6824711922440000,0,585000,6824711923025000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824711923264000,0,1073000,6824711924337000,0,"R",120,0,"swapper",0
+6824711923686000,1,409000,6824711924095000,773,"S",97,493,"app",678
+6824711924095000,1,2591000,6824711926686000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824711926686000,1,341000,6824711927027000,770,"S",120,493,"Binder:640_2",675
+6824711926753000,0,601000,6824711927354000,0,"R",120,0,"swapper",0
+6824711927027000,1,795000,6824711927822000,0,"R",120,0,"swapper",0
+6824711927354000,0,186000,6824711927540000,773,"S",97,493,"app",678
+6824711927540000,0,30747000,6824711958287000,0,"R",120,0,"swapper",0
+6824711927822000,1,86000,6824711927908000,771,"S",97,493,"DispSync",676
+6824711927908000,1,30404000,6824711958312000,0,"R",120,0,"swapper",0
+6824711935090000,5,59000,6824711935149000,113,"S",120,113,"kswapd0",150
+6824711935149000,5,791709000,6824712726858000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824711960206000,2,86000,6824711960292000,771,"S",97,493,"DispSync",676
+6824711960292000,2,31867000,6824711992159000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824711963017000,0,225000,6824711963242000,77,"S",120,77,"smem_native_rpm",87
+6824711963242000,0,720000,6824711963962000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824711963962000,0,93000,6824711964055000,771,"S",97,493,"DispSync",676
+6824711964055000,0,26981000,6824711991036000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824711991963000,1,86000,6824711992049000,15,"S",120,15,"ksoftirqd/1",17
+6824711992049000,1,1109000,6824711993158000,0,"R",120,0,"swapper",0
+6824711992159000,2,539000,6824711992698000,773,"S",97,493,"app",678
+6824711992203000,0,811000,6824711993014000,0,"R",120,0,"swapper",0
+6824711992457000,3,561000,6824711993018000,777,"S",120,493,"HwBinder:640_1",721
+6824711992698000,2,31632000,6824712024330000,0,"R",120,0,"swapper",0
+6824711993014000,0,3047000,6824711996061000,644,"S",120,644,"ndroid.systemui",1664
+6824711993018000,3,29607000,6824712022625000,0,"R",120,0,"swapper",0
+6824711993158000,1,88000,6824711993246000,771,"S",97,493,"DispSync",676
+6824711993246000,1,2846000,6824711996092000,0,"R",120,0,"swapper",0
+6824711996061000,0,723000,6824711996784000,0,"R",120,0,"swapper",0
+6824711996092000,1,399000,6824711996491000,770,"S",120,493,"Binder:640_2",675
+6824711996491000,1,795000,6824711997286000,0,"R",120,0,"swapper",0
+6824711996784000,0,206000,6824711996990000,773,"S",97,493,"app",678
+6824711996990000,0,72000,6824711997062000,0,"R",120,0,"swapper",0
+6824711997062000,0,65000,6824711997127000,3,"S",120,3,"ksoftirqd/0",3
+6824711997127000,0,25255000,6824712022382000,0,"R",120,0,"swapper",0
+6824711997286000,1,89000,6824711997375000,771,"S",97,493,"DispSync",676
+6824711997375000,1,26567000,6824712023942000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712024330000,2,324000,6824712024654000,771,"S",97,493,"DispSync",676
+6824712024581000,1,1190000,6824712025771000,0,"R",120,0,"swapper",0
+6824712024654000,2,1208000,6824712025862000,0,"R",120,0,"swapper",0
+6824712024951000,0,531000,6824712025482000,773,"S",97,493,"app",678
+6824712025482000,0,3497000,6824712028979000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712028934000,1,717000,6824712029651000,0,"R",120,0,"swapper",0
+6824712028979000,0,393000,6824712029372000,770,"S",120,493,"Binder:640_2",675
+6824712029372000,0,798000,6824712030170000,0,"R",120,0,"swapper",0
+6824712029651000,1,204000,6824712029855000,773,"S",97,493,"app",678
+6824712029855000,1,29454000,6824712059309000,0,"R",120,0,"swapper",0
+6824712030170000,0,89000,6824712030259000,771,"S",97,493,"DispSync",676
+6824712030259000,0,70000,6824712030329000,0,"R",120,0,"swapper",0
+6824712030329000,0,70000,6824712030399000,3,"S",120,3,"ksoftirqd/0",3
+6824712030399000,0,28593000,6824712058992000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712060406000,3,546000,6824712060952000,777,"S",120,493,"HwBinder:640_1",721
+6824712060637000,2,31702000,6824712092339000,0,"R",120,0,"swapper",0
+6824712060952000,3,31540000,6824712092492000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712064440000,1,710000,6824712065150000,0,"R",120,0,"swapper",0
+6824712064472000,0,400000,6824712064872000,770,"S",120,493,"Binder:640_2",675
+6824712064872000,0,780000,6824712065652000,0,"R",120,0,"swapper",0
+6824712065150000,1,201000,6824712065351000,773,"S",97,493,"app",678
+6824712065351000,1,10437000,6824712075788000,0,"R",120,0,"swapper",0
+6824712065652000,0,89000,6824712065741000,771,"S",97,493,"DispSync",676
+6824712065741000,0,24857000,6824712090598000,0,"R",120,0,"swapper",0
+6824712075788000,1,292000,6824712076080000,1919,"S",120,667,"Executor-7",14762
+6824712076080000,1,88000,6824712076168000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712092146000,1,1144000,6824712093290000,0,"R",120,0,"swapper",0
+6824712092233000,0,79000,6824712092312000,3,"S",120,3,"ksoftirqd/0",3
+6824712092312000,0,820000,6824712093132000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712092979000,3,33505000,6824712126484000,0,"R",120,0,"swapper",0
+6824712093132000,0,90000,6824712093222000,771,"S",97,493,"DispSync",676
+6824712093222000,0,3125000,6824712096347000,0,"R",120,0,"swapper",0
+6824712093290000,1,3028000,6824712096318000,644,"S",120,644,"ndroid.systemui",1664
+6824712096318000,1,443000,6824712096761000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712096969000,1,28547000,6824712125516000,0,"R",120,0,"swapper",0
+6824712097293000,2,94000,6824712097387000,771,"S",97,493,"DispSync",676
+6824712097387000,2,27295000,6824712124682000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712126484000,3,712000,6824712127196000,777,"S",120,493,"HwBinder:640_1",721
+6824712127196000,3,32061000,6824712159257000,0,"R",120,0,"swapper",0
+6824712127359000,0,84000,6824712127443000,771,"S",97,493,"DispSync",676
+6824712127443000,0,1769000,6824712129212000,0,"R",120,0,"swapper",0
+6824712129212000,0,299000,6824712129511000,770,"S",120,493,"Binder:640_2",675
+6824712129255000,2,29843000,6824712159098000,0,"R",120,0,"swapper",0
+6824712129511000,0,530000,6824712130041000,0,"R",120,0,"swapper",0
+6824712129680000,1,177000,6824712129857000,773,"S",97,493,"app",678
+6824712129857000,1,28493000,6824712158350000,0,"R",120,0,"swapper",0
+6824712130041000,0,88000,6824712130129000,771,"S",97,493,"DispSync",676
+6824712130129000,0,64000,6824712130193000,0,"R",120,0,"swapper",0
+6824712130193000,0,56000,6824712130249000,3,"S",120,3,"ksoftirqd/0",3
+6824712130249000,0,496000,6824712130745000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712158889000,1,87000,6824712158976000,15,"S",120,15,"ksoftirqd/1",17
+6824712158976000,1,981000,6824712159957000,0,"R",120,0,"swapper",0
+6824712159015000,0,478000,6824712159493000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712159816000,3,33271000,6824712193087000,0,"R",120,0,"swapper",0
+6824712159957000,1,87000,6824712160044000,771,"S",97,493,"DispSync",676
+6824712160044000,1,3767000,6824712163811000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712163811000,1,405000,6824712164216000,770,"S",120,493,"Binder:640_2",675
+6824712164216000,1,792000,6824712165008000,0,"R",120,0,"swapper",0
+6824712164501000,0,203000,6824712164704000,773,"S",97,493,"app",678
+6824712164704000,0,26926000,6824712191630000,0,"R",120,0,"swapper",0
+6824712165008000,1,88000,6824712165096000,771,"S",97,493,"DispSync",676
+6824712165096000,1,26545000,6824712191641000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712193513000,0,1329000,6824712194842000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712194663000,1,2149000,6824712196812000,0,"R",120,0,"swapper",0
+6824712194842000,0,89000,6824712194931000,771,"S",97,493,"DispSync",676
+6824712194931000,0,1217000,6824712196148000,0,"R",120,0,"swapper",0
+6824712196116000,2,30414000,6824712226530000,0,"R",120,0,"swapper",0
+6824712196148000,0,385000,6824712196533000,770,"S",120,493,"Binder:640_2",675
+6824712196533000,0,781000,6824712197314000,0,"R",120,0,"swapper",0
+6824712196812000,1,200000,6824712197012000,773,"S",97,493,"app",678
+6824712197012000,1,28707000,6824712225719000,0,"R",120,0,"swapper",0
+6824712197314000,0,88000,6824712197402000,771,"S",97,493,"DispSync",676
+6824712197402000,0,28073000,6824712225475000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712226321000,1,87000,6824712226408000,15,"S",120,15,"ksoftirqd/1",17
+6824712226408000,1,1183000,6824712227591000,0,"R",120,0,"swapper",0
+6824712226530000,2,669000,6824712227199000,773,"S",97,493,"app",678
+6824712226595000,0,894000,6824712227489000,0,"R",120,0,"swapper",0
+6824712226848000,3,546000,6824712227394000,777,"S",120,493,"HwBinder:640_1",721
+6824712227199000,2,32486000,6824712259685000,0,"R",120,0,"swapper",0
+6824712227394000,3,101387000,6824712328781000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712230650000,0,717000,6824712231367000,0,"R",120,0,"swapper",0
+6824712230693000,1,394000,6824712231087000,770,"S",120,493,"Binder:640_2",675
+6824712231087000,1,782000,6824712231869000,0,"R",120,0,"swapper",0
+6824712231367000,0,200000,6824712231567000,773,"S",97,493,"app",678
+6824712231567000,0,13149000,6824712244716000,0,"R",120,0,"swapper",0
+6824712231869000,1,87000,6824712231956000,771,"S",97,493,"DispSync",676
+6824712231956000,1,12891000,6824712244847000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712245040000,1,14256000,6824712259296000,0,"R",120,0,"swapper",0
+6824712245127000,0,258000,6824712245385000,3,"S",120,3,"ksoftirqd/0",3
+6824712245385000,0,12652000,6824712258037000,0,"R",120,0,"swapper",0
+6824712247262000,7,203000,6824712247465000,711,"S",120,711,"kworker/u16:2",19725
+6824712247465000,7,71000,6824712247536000,0,"R",120,0,"swapper",0
+6824712247536000,7,62000,6824712247598000,57,"S",120,57,"ksoftirqd/7",65
+6824712247598000,7,14402000,6824712262000000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712259478000,0,86000,6824712259564000,3,"S",120,3,"ksoftirqd/0",3
+6824712259564000,0,904000,6824712260468000,0,"R",120,0,"swapper",0
+6824712259685000,2,648000,6824712260333000,771,"S",97,493,"DispSync",676
+6824712259922000,1,1420000,6824712261342000,0,"R",120,0,"swapper",0
+6824712260333000,2,128000,6824712260461000,694,"S",120,694,"kworker/2:0",18823
+6824712260461000,2,979000,6824712261440000,0,"R",120,0,"swapper",0
+6824712260468000,0,523000,6824712260991000,773,"S",97,493,"app",678
+6824712260991000,0,3489000,6824712264480000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712262000000,7,773000,6824712262773000,711,"S",120,711,"kworker/u16:2",19725
+6824712262773000,7,467225000,6824712729998000,0,"R",120,0,"swapper",0
+6824712264416000,1,745000,6824712265161000,0,"R",120,0,"swapper",0
+6824712264480000,0,399000,6824712264879000,770,"S",120,493,"Binder:640_2",675
+6824712264879000,0,791000,6824712265670000,0,"R",120,0,"swapper",0
+6824712265161000,1,209000,6824712265370000,773,"S",97,493,"app",678
+6824712265370000,1,26002000,6824712291372000,0,"R",120,0,"swapper",0
+6824712265670000,0,96000,6824712265766000,771,"S",97,493,"DispSync",676
+6824712265766000,0,19023000,6824712284789000,0,"R",120,0,"swapper",0
+6824712284789000,0,487000,6824712285276000,1694,"D",100,660,"thermal-engine",2490
+6824712285276000,0,89000,6824712285365000,0,"R",120,0,"swapper",0
+6824712285365000,0,85000,6824712285450000,3,"S",120,3,"ksoftirqd/0",3
+6824712285450000,0,2460000,6824712287910000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712288868000,0,79000,6824712288947000,3,"S",120,3,"ksoftirqd/0",3
+6824712288947000,0,1078000,6824712290025000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712291181000,0,48000,6824712291229000,3,"S",120,3,"ksoftirqd/0",3
+6824712291229000,0,733000,6824712291962000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712292278000,0,1237000,6824712293515000,0,"R",120,0,"swapper",0
+6824712292682000,2,555000,6824712293237000,773,"S",97,493,"app",678
+6824712293237000,2,4208000,6824712297445000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712296584000,0,315000,6824712296899000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712297146000,1,30662000,6824712327808000,0,"R",120,0,"swapper",0
+6824712297445000,2,91000,6824712297536000,771,"S",97,493,"DispSync",676
+6824712297536000,2,29403000,6824712326939000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712328559000,0,938000,6824712329497000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712329497000,0,87000,6824712329584000,771,"S",97,493,"DispSync",676
+6824712329584000,0,2242000,6824712331826000,0,"R",120,0,"swapper",0
+6824712331793000,2,29260000,6824712361053000,0,"R",120,0,"swapper",0
+6824712331826000,0,394000,6824712332220000,770,"S",120,493,"Binder:640_2",675
+6824712332220000,0,790000,6824712333010000,0,"R",120,0,"swapper",0
+6824712332509000,1,202000,6824712332711000,773,"S",97,493,"app",678
+6824712332711000,1,27609000,6824712360320000,0,"R",120,0,"swapper",0
+6824712333010000,0,85000,6824712333095000,771,"S",97,493,"DispSync",676
+6824712333095000,0,26074000,6824712359169000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712361444000,1,3028000,6824712364472000,644,"S",120,644,"ndroid.systemui",1664
+6824712361543000,2,32596000,6824712394139000,0,"R",120,0,"swapper",0
+6824712361634000,3,102000,6824712361736000,771,"S",97,493,"DispSync",676
+6824712361736000,3,33366000,6824712395102000,0,"R",120,0,"swapper",0
+6824712362118000,0,2398000,6824712364516000,0,"R",120,0,"swapper",0
+6824712364472000,1,724000,6824712365196000,0,"R",120,0,"swapper",0
+6824712364516000,0,394000,6824712364910000,770,"S",120,493,"Binder:640_2",675
+6824712364910000,0,797000,6824712365707000,0,"R",120,0,"swapper",0
+6824712365196000,1,209000,6824712365405000,773,"S",97,493,"app",678
+6824712365405000,1,27749000,6824712393154000,0,"R",120,0,"swapper",0
+6824712365707000,0,88000,6824712365795000,771,"S",97,493,"DispSync",676
+6824712365795000,0,27098000,6824712392893000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712394139000,2,561000,6824712394700000,773,"S",97,493,"app",678
+6824712394204000,0,745000,6824712394949000,0,"R",120,0,"swapper",0
+6824712394700000,2,32976000,6824712427676000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712395292000,1,2827000,6824712398119000,0,"R",120,0,"swapper",0
+6824712398082000,0,723000,6824712398805000,0,"R",120,0,"swapper",0
+6824712398119000,1,404000,6824712398523000,770,"S",120,493,"Binder:640_2",675
+6824712398523000,1,782000,6824712399305000,0,"R",120,0,"swapper",0
+6824712398805000,0,206000,6824712399011000,773,"S",97,493,"app",678
+6824712399011000,0,27138000,6824712426149000,0,"R",120,0,"swapper",0
+6824712399305000,1,88000,6824712399393000,771,"S",97,493,"DispSync",676
+6824712399393000,1,27547000,6824712426940000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712428005000,1,3964000,6824712431969000,0,"R",120,0,"swapper",0
+6824712428161000,2,33130000,6824712461291000,0,"R",120,0,"swapper",0
+6824712428251000,3,103000,6824712428354000,771,"S",97,493,"DispSync",676
+6824712428354000,3,33090000,6824712461444000,0,"R",120,0,"swapper",0
+6824712428804000,0,3132000,6824712431936000,644,"S",120,644,"ndroid.systemui",1664
+6824712431936000,0,704000,6824712432640000,0,"R",120,0,"swapper",0
+6824712431969000,1,397000,6824712432366000,770,"S",120,493,"Binder:640_2",675
+6824712432366000,1,792000,6824712433158000,0,"R",120,0,"swapper",0
+6824712432640000,0,216000,6824712432856000,773,"S",97,493,"app",678
+6824712432856000,0,26763000,6824712459619000,0,"R",120,0,"swapper",0
+6824712433158000,1,88000,6824712433246000,771,"S",97,493,"DispSync",676
+6824712433246000,1,27279000,6824712460525000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712465308000,1,737000,6824712466045000,0,"R",120,0,"swapper",0
+6824712465361000,0,404000,6824712465765000,770,"S",120,493,"Binder:640_2",675
+6824712465765000,0,787000,6824712466552000,0,"R",120,0,"swapper",0
+6824712466045000,1,204000,6824712466249000,773,"S",97,493,"app",678
+6824712466249000,1,27754000,6824712494003000,0,"R",120,0,"swapper",0
+6824712466552000,0,174000,6824712466726000,771,"S",97,493,"DispSync",676
+6824712466726000,0,26386000,6824712493112000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712495145000,1,3951000,6824712499096000,0,"R",120,0,"swapper",0
+6824712495270000,2,33176000,6824712528446000,0,"R",120,0,"swapper",0
+6824712495377000,3,105000,6824712495482000,771,"S",97,493,"DispSync",676
+6824712495482000,3,33062000,6824712528544000,0,"R",120,0,"swapper",0
+6824712495913000,0,3157000,6824712499070000,644,"S",120,644,"ndroid.systemui",1664
+6824712499070000,0,712000,6824712499782000,0,"R",120,0,"swapper",0
+6824712499096000,1,408000,6824712499504000,770,"S",120,493,"Binder:640_2",675
+6824712499504000,1,790000,6824712500294000,0,"R",120,0,"swapper",0
+6824712499782000,0,306000,6824712500088000,773,"S",97,493,"app",678
+6824712500088000,0,26796000,6824712526884000,0,"R",120,0,"swapper",0
+6824712500294000,1,92000,6824712500386000,771,"S",97,493,"DispSync",676
+6824712500386000,1,27266000,6824712527652000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712528168000,0,70000,6824712528238000,702,"S",120,702,"kworker/u16:7",19422
+6824712528238000,0,994000,6824712529232000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712528985000,2,78000,6824712529063000,773,"S",97,493,"app",678
+6824712529063000,2,32595000,6824712561658000,0,"R",120,0,"swapper",0
+6824712529164000,3,33240000,6824712562404000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712532591000,1,395000,6824712532986000,770,"S",120,493,"Binder:640_2",675
+6824712532986000,1,786000,6824712533772000,0,"R",120,0,"swapper",0
+6824712533274000,0,198000,6824712533472000,773,"S",97,493,"app",678
+6824712533472000,0,70000,6824712533542000,0,"R",120,0,"swapper",0
+6824712533542000,0,67000,6824712533609000,3,"S",120,3,"ksoftirqd/0",3
+6824712533609000,0,26476000,6824712560085000,0,"R",120,0,"swapper",0
+6824712533772000,1,89000,6824712533861000,771,"S",97,493,"DispSync",676
+6824712533861000,1,27413000,6824712561274000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712561987000,2,1400000,6824712563387000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712562980000,3,66067000,6824712629047000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712563537000,2,61000,6824712563598000,22,"S",120,22,"ksoftirqd/2",25
+6824712563598000,2,28176000,6824712591774000,0,"R",120,0,"swapper",0
+6824712566295000,1,912000,6824712567207000,0,"R",120,0,"swapper",0
+6824712566346000,0,580000,6824712566926000,770,"S",120,493,"Binder:640_2",675
+6824712566926000,0,798000,6824712567724000,0,"R",120,0,"swapper",0
+6824712567207000,1,209000,6824712567416000,773,"S",97,493,"app",678
+6824712567416000,1,25855000,6824712593271000,0,"R",120,0,"swapper",0
+6824712567724000,0,90000,6824712567814000,771,"S",97,493,"DispSync",676
+6824712567814000,0,23951000,6824712591765000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712592918000,0,855000,6824712593773000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712594086000,0,1123000,6824712595209000,0,"R",120,0,"swapper",0
+6824712594094000,1,73000,6824712594167000,15,"S",120,15,"ksoftirqd/1",17
+6824712594167000,1,1145000,6824712595312000,0,"R",120,0,"swapper",0
+6824712594391000,2,543000,6824712594934000,773,"S",97,493,"app",678
+6824712594934000,2,33189000,6824712628123000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712598402000,0,638000,6824712599040000,0,"R",120,0,"swapper",0
+6824712598442000,1,395000,6824712598837000,770,"S",120,493,"Binder:640_2",675
+6824712598837000,1,566000,6824712599403000,0,"R",120,0,"swapper",0
+6824712599040000,0,182000,6824712599222000,773,"S",97,493,"app",678
+6824712599222000,0,27826000,6824712627048000,0,"R",120,0,"swapper",0
+6824712599403000,1,87000,6824712599490000,771,"S",97,493,"DispSync",676
+6824712599490000,1,27821000,6824712627311000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712628610000,2,29797000,6824712658407000,0,"R",120,0,"swapper",0
+6824712628645000,1,4458000,6824712633103000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712632386000,3,20618000,6824712653004000,0,"R",120,0,"swapper",0
+6824712632420000,0,405000,6824712632825000,770,"S",120,493,"Binder:640_2",675
+6824712632825000,0,777000,6824712633602000,0,"R",120,0,"swapper",0
+6824712633103000,1,300000,6824712633403000,773,"S",97,493,"app",678
+6824712633403000,1,24713000,6824712658116000,0,"R",120,0,"swapper",0
+6824712633602000,0,89000,6824712633691000,771,"S",97,493,"DispSync",676
+6824712633691000,0,8761000,6824712642452000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712653004000,3,59000,6824712653063000,469,"S",100,469,"kworker/3:1H",578
+6824712653063000,3,70763000,6824712723826000,0,"R",120,0,"swapper",0
+6824712653362000,0,46000,6824712653408000,458,"S",100,458,"kworker/0:1H",558
+6824712653408000,0,4244000,6824712657652000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712658290000,0,53000,6824712658343000,3,"S",120,3,"ksoftirqd/0",3
+6824712658343000,0,1697000,6824712660040000,0,"R",120,0,"swapper",0
+6824712658407000,2,117000,6824712658524000,771,"S",97,493,"DispSync",676
+6824712658524000,2,2025000,6824712660549000,0,"R",120,0,"swapper",0
+6824712658563000,1,1537000,6824712660100000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712660528000,0,434000,6824712660962000,0,"R",120,0,"swapper",0
+6824712660549000,2,334000,6824712660883000,773,"S",97,493,"app",678
+6824712660883000,2,33798000,6824712694681000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712662806000,1,304000,6824712663110000,770,"S",120,493,"Binder:640_2",675
+6824712662890000,0,402000,6824712663292000,0,"R",120,0,"swapper",0
+6824712663110000,1,497000,6824712663607000,0,"R",120,0,"swapper",0
+6824712663292000,0,190000,6824712663482000,773,"S",97,493,"app",678
+6824712663482000,0,57000,6824712663539000,0,"R",120,0,"swapper",0
+6824712663539000,0,53000,6824712663592000,3,"S",120,3,"ksoftirqd/0",3
+6824712663592000,0,2553000,6824712666145000,0,"R",120,0,"swapper",0
+6824712663607000,1,90000,6824712663697000,771,"S",97,493,"DispSync",676
+6824712663697000,1,25560000,6824712689257000,0,"R",120,0,"swapper",0
+6824712666145000,0,109000,6824712666254000,77,"S",120,77,"smem_native_rpm",87
+6824712666254000,0,23413000,6824712689667000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712689667000,0,99000,6824712689766000,702,"S",120,702,"kworker/u16:7",19422
+6824712689766000,0,1565000,6824712691331000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712692447000,1,1171000,6824712693618000,0,"R",120,0,"swapper",0
+6824712692474000,0,113000,6824712692587000,771,"S",97,493,"DispSync",676
+6824712692587000,0,1292000,6824712693879000,0,"R",120,0,"swapper",0
+6824712693618000,1,43000,6824712693661000,15,"S",120,15,"ksoftirqd/1",17
+6824712693661000,1,535000,6824712694196000,0,"R",120,0,"swapper",0
+6824712693879000,0,175000,6824712694054000,771,"S",97,493,"DispSync",676
+6824712694054000,0,541000,6824712694595000,0,"R",120,0,"swapper",0
+6824712694196000,1,322000,6824712694518000,773,"S",97,493,"app",678
+6824712694518000,1,1868000,6824712696386000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712696386000,1,443000,6824712696829000,770,"S",120,493,"Binder:640_2",675
+6824712696470000,0,141000,6824712696611000,0,"R",120,0,"swapper",0
+6824712696611000,0,78000,6824712696689000,773,"S",97,493,"app",678
+6824712696689000,0,44000,6824712696733000,0,"R",120,0,"swapper",0
+6824712696733000,0,164000,6824712696897000,773,"S",97,493,"app",678
+6824712696829000,1,10308000,6824712707137000,0,"R",120,0,"swapper",0
+6824712696897000,0,6225000,6824712703122000,0,"R",120,0,"swapper",0
+6824712697037000,2,91000,6824712697128000,771,"S",97,493,"DispSync",676
+6824712697128000,2,25635000,6824712722763000,0,"R",120,0,"swapper",0
+6824712703122000,0,62000,6824712703184000,702,"D",120,702,"kworker/u16:7",19422
+6824712703184000,0,133000,6824712703317000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712707137000,1,57000,6824712707194000,15,"S",120,15,"ksoftirqd/1",17
+6824712707194000,1,15412000,6824712722606000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712722606000,1,109000,6824712722715000,13,"S",0,13,"watchdog/1",15
+6824712722715000,1,79000,6824712722794000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712722858000,1,2789000,6824712725647000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712723898000,3,34414000,6824712758312000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712726130000,1,1587000,6824712727717000,0,"R",120,0,"swapper",0
+6824712726382000,4,67000,6824712726449000,34,"S",0,34,"watchdog/4",39
+6824712726449000,4,657913000,6824713384362000,0,"R",120,0,"swapper",0
+6824712726858000,5,46000,6824712726904000,41,"S",0,41,"watchdog/5",47
+6824712726904000,5,48000,6824712726952000,0,"R",120,0,"swapper",0
+6824712726952000,5,38000,6824712726990000,43,"S",120,43,"ksoftirqd/5",49
+6824712726990000,5,678798000,6824713405788000,0,"R",120,0,"swapper",0
+6824712727327000,0,178000,6824712727505000,771,"S",97,493,"DispSync",676
+6824712727505000,0,702000,6824712728207000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712728110000,6,110547000,6824712838657000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712729998000,7,46000,6824712730044000,55,"S",0,55,"watchdog/7",63
+6824712730044000,7,136266000,6824712866310000,0,"R",120,0,"swapper",0
+6824712730330000,1,314000,6824712730644000,770,"S",120,493,"Binder:640_2",675
+6824712730357000,0,484000,6824712730841000,0,"R",120,0,"swapper",0
+6824712730644000,1,567000,6824712731211000,0,"R",120,0,"swapper",0
+6824712730841000,0,182000,6824712731023000,773,"S",97,493,"app",678
+6824712731023000,0,2119000,6824712733142000,0,"R",120,0,"swapper",0
+6824712731211000,1,95000,6824712731306000,771,"S",97,493,"DispSync",676
+6824712731306000,1,27902000,6824712759208000,0,"R",120,0,"swapper",0
+6824712733142000,0,190000,6824712733332000,77,"S",120,77,"smem_native_rpm",87
+6824712733332000,0,25059000,6824712758391000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712758487000,0,574000,6824712759061000,786,"S",111,494,"SDM_EventThread",685
+6824712759061000,0,699000,6824712759760000,0,"R",120,0,"swapper",0
+6824712759208000,1,489000,6824712759697000,777,"S",120,493,"HwBinder:640_1",721
+6824712759697000,1,1610000,6824712761307000,0,"R",120,0,"swapper",0
+6824712759760000,0,131000,6824712759891000,771,"S",97,493,"DispSync",676
+6824712759891000,0,227000,6824712760118000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712760912000,0,182000,6824712761094000,771,"S",97,493,"DispSync",676
+6824712761094000,0,708000,6824712761802000,0,"R",120,0,"swapper",0
+6824712761307000,1,363000,6824712761670000,773,"S",97,493,"app",678
+6824712761670000,1,2291000,6824712763961000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712763961000,1,312000,6824712764273000,770,"S",120,493,"Binder:640_2",675
+6824712763992000,0,457000,6824712764449000,0,"R",120,0,"swapper",0
+6824712764273000,1,539000,6824712764812000,0,"R",120,0,"swapper",0
+6824712764449000,0,182000,6824712764631000,773,"S",97,493,"app",678
+6824712764631000,0,30567000,6824712795198000,0,"R",120,0,"swapper",0
+6824712764812000,1,95000,6824712764907000,771,"S",97,493,"DispSync",676
+6824712764907000,1,30302000,6824712795209000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712796932000,1,4294000,6824712801226000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712797779000,0,87000,6824712797866000,771,"S",97,493,"DispSync",676
+6824712797866000,0,2676000,6824712800542000,0,"R",120,0,"swapper",0
+6824712800521000,3,61041000,6824712861562000,0,"R",120,0,"swapper",0
+6824712800542000,0,397000,6824712800939000,770,"S",120,493,"Binder:640_2",675
+6824712800939000,0,790000,6824712801729000,0,"R",120,0,"swapper",0
+6824712801226000,1,203000,6824712801429000,773,"S",97,493,"app",678
+6824712801429000,1,28175000,6824712829604000,0,"R",120,0,"swapper",0
+6824712801729000,0,88000,6824712801817000,771,"S",97,493,"DispSync",676
+6824712801817000,0,26133000,6824712827950000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712830184000,2,302000,6824712830486000,771,"S",97,493,"DispSync",676
+6824712830375000,1,1386000,6824712831761000,0,"R",120,0,"swapper",0
+6824712830486000,2,1407000,6824712831893000,0,"R",120,0,"swapper",0
+6824712830947000,0,531000,6824712831478000,773,"S",97,493,"app",678
+6824712831478000,0,3444000,6824712834922000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712834889000,1,707000,6824712835596000,0,"R",120,0,"swapper",0
+6824712834922000,0,398000,6824712835320000,770,"S",120,493,"Binder:640_2",675
+6824712835320000,0,775000,6824712836095000,0,"R",120,0,"swapper",0
+6824712835596000,1,201000,6824712835797000,773,"S",97,493,"app",678
+6824712835797000,1,24848000,6824712860645000,0,"R",120,0,"swapper",0
+6824712836095000,0,93000,6824712836188000,771,"S",97,493,"DispSync",676
+6824712836188000,0,23047000,6824712859235000,0,"R",120,0,"swapper",0
+6824712838657000,6,193000,6824712838850000,630,"S",100,630,"kworker/u17:1",1134
+6824712838850000,6,70000,6824712838920000,0,"R",120,0,"swapper",0
+6824712838920000,6,49000,6824712838969000,50,"S",120,50,"ksoftirqd/6",57
+6824712838969000,6,3751000,6824712842720000,0,"R",120,0,"swapper",0
+6824712841394000,2,165000,6824712841559000,22,"S",120,22,"ksoftirqd/2",25
+6824712841559000,2,3497000,6824712845056000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712845056000,2,686000,6824712845742000,2487,"S",120,739,"UsbFfs-worker",20308
+6824712845742000,2,3405000,6824712849147000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712861354000,1,1038000,6824712862392000,0,"R",120,0,"swapper",0
+6824712861562000,3,169000,6824712861731000,771,"S",97,493,"DispSync",676
+6824712861731000,3,1521000,6824712863252000,0,"R",120,0,"swapper",0
+6824712861812000,0,223000,6824712862035000,771,"S",97,493,"DispSync",676
+6824712862035000,0,716000,6824712862751000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712866837000,1,771000,6824712867608000,0,"R",120,0,"swapper",0
+6824712867041000,0,285000,6824712867326000,773,"S",97,493,"app",678
+6824712867326000,0,9974000,6824712877300000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712873102000,7,72000,6824712873174000,145,"S",120,145,"hwrng",215
+6824712873174000,7,5793000,6824712878967000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712890529000,1,61000,6824712890590000,693,"S",120,693,"kworker/1:1",18800
+6824712890590000,1,2884000,6824712893474000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712891573000,0,40000,6824712891613000,5,"S",120,5,"rcu_preempt",7
+6824712891613000,0,803000,6824712892416000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712893474000,1,566000,6824712894040000,777,"S",120,493,"HwBinder:640_1",721
+6824712894040000,1,1068000,6824712895108000,0,"R",120,0,"swapper",0
+6824712894171000,0,111000,6824712894282000,771,"S",97,493,"DispSync",676
+6824712894282000,0,714000,6824712894996000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712895437000,0,1223000,6824712896660000,644,"R",120,644,"ndroid.systemui",1664
+6824712895518000,1,2511000,6824712898029000,0,"R",120,0,"swapper",0
+6824712895858000,3,55000,6824712895913000,771,"S",97,493,"DispSync",676
+6824712895913000,3,20866000,6824712916779000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712898399000,1,53000,6824712898452000,771,"S",97,493,"DispSync",676
+6824712898452000,1,28312000,6824712926764000,0,"R",120,0,"swapper",0
+6824712898460000,0,1923000,6824712900383000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712901640000,7,72000,6824712901712000,145,"S",120,145,"hwrng",215
+6824712901712000,7,80781000,6824712982493000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712917196000,0,38000,6824712917234000,6,"S",120,6,"rcu_sched",8
+6824712917234000,0,3105000,6824712920339000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712923300000,0,66000,6824712923366000,6,"S",120,6,"rcu_sched",8
+6824712923366000,0,2472000,6824712925838000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712927513000,0,89000,6824712927602000,771,"S",97,493,"DispSync",676
+6824712927602000,0,969000,6824712928571000,0,"R",120,0,"swapper",0
+6824712928571000,0,92000,6824712928663000,771,"S",97,493,"DispSync",676
+6824712928663000,0,909000,6824712929572000,0,"R",120,0,"swapper",0
+6824712928975000,1,295000,6824712929270000,773,"S",97,493,"app",678
+6824712929270000,1,2298000,6824712931568000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712931425000,0,319000,6824712931744000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712931846000,0,29956000,6824712961802000,0,"R",120,0,"swapper",0
+6824712932123000,2,33000,6824712932156000,771,"S",97,493,"DispSync",676
+6824712932156000,2,1529000,6824712933685000,0,"R",120,0,"swapper",0
+6824712933309000,1,55000,6824712933364000,5,"S",120,5,"rcu_preempt",7
+6824712933364000,1,935000,6824712934299000,0,"R",120,0,"swapper",0
+6824712933685000,2,248000,6824712933933000,24,"S",120,24,"rcuop/2",28
+6824712933933000,2,27866000,6824712961799000,0,"R",120,0,"swapper",0
+6824712934299000,1,19000,6824712934318000,5,"S",120,5,"rcu_preempt",7
+6824712934318000,1,7968000,6824712942286000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712963915000,1,403000,6824712964318000,644,"R",120,644,"ndroid.systemui",1664
+6824712964069000,2,11256000,6824712975325000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712965264000,0,98000,6824712965362000,771,"S",97,493,"DispSync",676
+6824712965362000,0,9149000,6824712974511000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824712975325000,2,324000,6824712975649000,773,"S",97,493,"app",678
+6824712975649000,2,11143000,6824712986792000,0,"R",120,0,"swapper",0
+6824712975930000,0,113000,6824712976043000,771,"S",97,493,"DispSync",676
+6824712976043000,0,5748000,6824712981791000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712994664000,0,752000,6824712995416000,0,"R",120,0,"swapper",0
+6824712994894000,3,230000,6824712995124000,771,"S",97,493,"DispSync",676
+6824712995124000,3,1089000,6824712996213000,0,"R",120,0,"swapper",0
+6824712995416000,0,388000,6824712995804000,773,"S",97,493,"app",678
+6824712995804000,0,2854000,6824712998658000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824712999071000,0,62000,6824712999133000,771,"S",97,493,"DispSync",676
+6824712999133000,0,22793000,6824713021926000,0,"R",120,0,"swapper",0
+6824712999139000,2,28073000,6824713027212000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713027467000,3,179000,6824713027646000,771,"S",97,493,"DispSync",676
+6824713027646000,3,1029000,6824713028675000,0,"R",120,0,"swapper",0
+6824713027659000,2,901000,6824713028560000,0,"R",120,0,"swapper",0
+6824713027954000,1,314000,6824713028268000,773,"S",97,493,"app",678
+6824713028268000,1,2352000,6824713030620000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713030620000,1,274000,6824713030894000,770,"S",120,493,"Binder:640_2",675
+6824713030894000,1,696000,6824713031590000,0,"R",120,0,"swapper",0
+6824713031179000,2,137000,6824713031316000,773,"S",97,493,"app",678
+6824713031316000,2,29313000,6824713060629000,0,"R",120,0,"swapper",0
+6824713031590000,1,43000,6824713031633000,771,"S",97,493,"DispSync",676
+6824713031633000,1,2153000,6824713033786000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713060921000,1,103000,6824713061024000,771,"S",97,493,"DispSync",676
+6824713061024000,1,915000,6824713061939000,0,"R",120,0,"swapper",0
+6824713061060000,2,1313000,6824713062373000,0,"R",120,0,"swapper",0
+6824713061939000,1,85000,6824713062024000,771,"S",97,493,"DispSync",676
+6824713062024000,1,1073000,6824713063097000,0,"R",120,0,"swapper",0
+6824713062373000,2,326000,6824713062699000,773,"S",97,493,"app",678
+6824713062699000,2,2701000,6824713065400000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713064767000,3,15375000,6824713080142000,0,"R",120,0,"swapper",0
+6824713064913000,1,237000,6824713065150000,770,"S",120,493,"Binder:640_2",675
+6824713065150000,1,321000,6824713065471000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713065510000,2,1194000,6824713066704000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713066750000,1,18000,6824713066768000,5,"S",120,5,"rcu_preempt",7
+6824713066757000,2,4703000,6824713071460000,0,"R",120,0,"swapper",0
+6824713066768000,1,4421000,6824713071189000,0,"R",120,0,"swapper",0
+6824713071189000,1,109000,6824713071298000,2728,"S",120,739,"shell",20461
+6824713071298000,1,679000,6824713071977000,0,"R",120,0,"swapper",0
+6824713071460000,2,412000,6824713071872000,739,"S",120,739,"adbd",20305
+6824713071872000,2,322000,6824713072194000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713072239000,2,259000,6824713072498000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713072583000,1,374000,6824713072957000,0,"R",120,0,"swapper",0
+6824713072640000,2,21000,6824713072661000,630,"S",100,630,"kworker/u17:1",1134
+6824713072661000,2,249000,6824713072910000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713072990000,2,99000,6824713073089000,739,"S",120,739,"adbd",20305
+6824713073028000,1,235000,6824713073263000,0,"R",120,0,"swapper",0
+6824713073089000,2,196000,6824713073285000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713073402000,2,3701000,6824713077103000,0,"R",120,0,"swapper",0
+6824713073558000,1,15000,6824713073573000,5,"S",120,5,"rcu_preempt",7
+6824713073573000,1,3170000,6824713076743000,0,"R",120,0,"swapper",0
+6824713076743000,1,148000,6824713076891000,702,"D",120,702,"kworker/u16:7",19422
+6824713076891000,1,199000,6824713077090000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713077122000,2,2542000,6824713079664000,0,"R",120,0,"swapper",0
+6824713079437000,1,81000,6824713079518000,2728,"S",120,739,"shell",20461
+6824713079518000,1,417000,6824713079935000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713080018000,1,518000,6824713080536000,0,"R",120,0,"swapper",0
+6824713080142000,3,24000,6824713080166000,5,"S",120,5,"rcu_preempt",7
+6824713080166000,3,419000,6824713080585000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713080536000,1,38000,6824713080574000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713080574000,1,488000,6824713081062000,0,"R",120,0,"swapper",0
+6824713080585000,3,26000,6824713080611000,630,"S",100,630,"kworker/u17:1",1134
+6824713080611000,3,6419000,6824713087030000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713081062000,1,72000,6824713081134000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713081134000,1,6852000,6824713087986000,0,"R",120,0,"swapper",0
+6824713081251000,2,106000,6824713081357000,739,"S",120,739,"adbd",20305
+6824713081357000,2,6049000,6824713087406000,0,"R",120,0,"swapper",0
+6824713087030000,3,62000,6824713087092000,5,"S",120,5,"rcu_preempt",7
+6824713087092000,3,964000,6824713088056000,0,"R",120,0,"swapper",0
+6824713087406000,2,284000,6824713087690000,8,"S",120,8,"rcuop/0",10
+6824713087690000,2,783000,6824713088473000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713088114000,3,404000,6824713088518000,739,"S",120,739,"adbd",20305
+6824713088125000,1,464000,6824713088589000,0,"R",120,0,"swapper",0
+6824713088473000,2,53000,6824713088526000,630,"S",100,630,"kworker/u17:1",1134
+6824713088518000,3,43000,6824713088561000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713088600000,2,1021000,6824713089621000,0,"R",120,0,"swapper",0
+6824713088626000,1,590000,6824713089216000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713088820000,3,17000,6824713088837000,630,"S",100,630,"kworker/u17:1",1134
+6824713088837000,3,135000,6824713088972000,0,"R",120,0,"swapper",0
+6824713088972000,3,44000,6824713089016000,630,"S",100,630,"kworker/u17:1",1134
+6824713089016000,3,47000,6824713089063000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713089216000,1,154000,6824713089370000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713089370000,1,4023000,6824713093393000,0,"R",120,0,"swapper",0
+6824713089621000,2,127000,6824713089748000,739,"S",120,739,"adbd",20305
+6824713089748000,2,4425000,6824713094173000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713093973000,1,507000,6824713094480000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713094549000,2,126000,6824713094675000,777,"S",120,493,"HwBinder:640_1",721
+6824713094579000,1,9000,6824713094588000,0,"R",120,0,"swapper",0
+6824713094588000,1,74000,6824713094662000,771,"S",97,493,"DispSync",676
+6824713094662000,1,934000,6824713095596000,0,"R",120,0,"swapper",0
+6824713094675000,2,1004000,6824713095679000,0,"R",120,0,"swapper",0
+6824713095007000,3,267000,6824713095274000,773,"S",97,493,"app",678
+6824713095274000,3,5029000,6824713100303000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713097848000,2,321000,6824713098169000,770,"S",120,493,"Binder:640_2",675
+6824713098169000,2,724000,6824713098893000,0,"R",120,0,"swapper",0
+6824713098433000,1,171000,6824713098604000,773,"S",97,493,"app",678
+6824713098604000,1,39000,6824713098643000,0,"R",120,0,"swapper",0
+6824713098643000,1,113000,6824713098756000,482,"S",49,482,"sugov:0",605
+6824713098756000,1,488000,6824713099244000,0,"R",120,0,"swapper",0
+6824713098893000,2,25000,6824713098918000,771,"S",97,493,"DispSync",676
+6824713098918000,2,480000,6824713099398000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713099478000,1,65000,6824713099543000,1919,"S",120,667,"Executor-7",14762
+6824713099543000,1,1075000,6824713100618000,0,"R",120,0,"swapper",0
+6824713100303000,3,67000,6824713100370000,630,"S",100,630,"kworker/u17:1",1134
+6824713100370000,3,9000,6824713100379000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713100974000,2,632000,6824713101606000,0,"R",120,0,"swapper",0
+6824713101233000,1,787000,6824713102020000,0,"R",120,0,"swapper",0
+6824713101255000,3,47000,6824713101302000,630,"S",100,630,"kworker/u17:1",1134
+6824713101302000,3,605000,6824713101907000,0,"R",120,0,"swapper",0
+6824713101606000,2,31000,6824713101637000,5,"S",120,5,"rcu_preempt",7
+6824713101637000,2,471000,6824713102108000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713102108000,2,177000,6824713102285000,739,"S",120,739,"adbd",20305
+6824713102197000,1,5230000,6824713107427000,0,"R",120,0,"swapper",0
+6824713102285000,2,4714000,6824713106999000,0,"R",120,0,"swapper",0
+6824713106999000,2,46000,6824713107045000,5,"S",120,5,"rcu_preempt",7
+6824713107045000,2,1111000,6824713108156000,0,"R",120,0,"swapper",0
+6824713107427000,1,382000,6824713107809000,8,"S",120,8,"rcuop/0",10
+6824713107809000,1,1784000,6824713109593000,0,"R",120,0,"swapper",0
+6824713108156000,2,24000,6824713108180000,5,"S",120,5,"rcu_preempt",7
+6824713108180000,2,1871000,6824713110051000,0,"R",120,0,"swapper",0
+6824713109593000,1,155000,6824713109748000,2728,"S",120,739,"shell",20461
+6824713109748000,1,1604000,6824713111352000,0,"R",120,0,"swapper",0
+6824713110051000,2,506000,6824713110557000,739,"S",120,739,"adbd",20305
+6824713110557000,2,728000,6824713111285000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713111285000,2,35000,6824713111320000,630,"S",100,630,"kworker/u17:1",1134
+6824713111320000,2,474000,6824713111794000,0,"R",120,0,"swapper",0
+6824713111352000,1,75000,6824713111427000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713111427000,1,882000,6824713112309000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713112395000,2,142000,6824713112537000,739,"S",120,739,"adbd",20305
+6824713112477000,1,4894000,6824713117371000,0,"R",120,0,"swapper",0
+6824713112537000,2,766000,6824713113303000,0,"R",120,0,"swapper",0
+6824713113303000,2,30000,6824713113333000,5,"S",120,5,"rcu_preempt",7
+6824713113333000,2,3671000,6824713117004000,0,"R",120,0,"swapper",0
+6824713117004000,2,42000,6824713117046000,5,"S",120,5,"rcu_preempt",7
+6824713117046000,2,851000,6824713117897000,0,"R",120,0,"swapper",0
+6824713117371000,1,213000,6824713117584000,8,"S",120,8,"rcuop/0",10
+6824713117584000,1,3602000,6824713121186000,0,"R",120,0,"swapper",0
+6824713117897000,2,22000,6824713117919000,5,"S",120,5,"rcu_preempt",7
+6824713117919000,2,3713000,6824713121632000,0,"R",120,0,"swapper",0
+6824713121186000,1,154000,6824713121340000,2728,"S",120,739,"shell",20461
+6824713121340000,1,1525000,6824713122865000,0,"R",120,0,"swapper",0
+6824713121632000,2,519000,6824713122151000,739,"S",120,739,"adbd",20305
+6824713122151000,2,341000,6824713122492000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713122523000,2,120000,6824713122643000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713122741000,2,16000,6824713122757000,630,"S",100,630,"kworker/u17:1",1134
+6824713122757000,2,136000,6824713122893000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713122960000,1,168000,6824713123128000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713123175000,2,190000,6824713123365000,739,"S",120,739,"adbd",20305
+6824713123280000,1,3559000,6824713126839000,0,"R",120,0,"swapper",0
+6824713123365000,2,3548000,6824713126913000,0,"R",120,0,"swapper",0
+6824713123633000,3,30000,6824713123663000,5,"S",120,5,"rcu_preempt",7
+6824713123663000,3,3867000,6824713127530000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713127530000,3,44000,6824713127574000,77,"S",120,77,"smem_native_rpm",87
+6824713127574000,3,559000,6824713128133000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713128133000,3,26000,6824713128159000,771,"S",97,493,"DispSync",676
+6824713128159000,3,8000,6824713128167000,0,"R",120,0,"swapper",0
+6824713128167000,3,13000,6824713128180000,771,"S",97,493,"DispSync",676
+6824713128180000,3,1804000,6824713129984000,0,"R",120,0,"swapper",0
+6824713128186000,1,2334000,6824713130520000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713130188000,0,29833000,6824713160021000,2722,"R",120,757,"ps",20463
+6824713130421000,2,758000,6824713131179000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713131406000,1,828000,6824713132234000,0,"R",120,0,"swapper",0
+6824713131641000,3,30000,6824713131671000,771,"S",97,493,"DispSync",676
+6824713131671000,3,95000,6824713131766000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713131949000,3,1543000,6824713133492000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713133142000,2,65000,6824713133207000,694,"S",120,694,"kworker/2:0",18823
+6824713133207000,2,70000,6824713133277000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713133545000,3,9733000,6824713143278000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713133683000,1,142000,6824713133825000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713133706000,2,37000,6824713133743000,0,"R",120,0,"swapper",0
+6824713133743000,2,140000,6824713133883000,739,"S",120,739,"adbd",20305
+6824713133825000,1,6827000,6824713140652000,0,"R",120,0,"swapper",0
+6824713133883000,2,3063000,6824713136946000,0,"R",120,0,"swapper",0
+6824713136946000,2,41000,6824713136987000,5,"S",120,5,"rcu_preempt",7
+6824713136987000,2,3274000,6824713140261000,0,"R",120,0,"swapper",0
+6824713140261000,2,37000,6824713140298000,5,"S",120,5,"rcu_preempt",7
+6824713140298000,2,1112000,6824713141410000,0,"R",120,0,"swapper",0
+6824713140652000,1,423000,6824713141075000,8,"S",120,8,"rcuop/0",10
+6824713141075000,1,1022000,6824713142097000,0,"R",120,0,"swapper",0
+6824713141410000,2,18000,6824713141428000,5,"S",120,5,"rcu_preempt",7
+6824713141428000,2,1111000,6824713142539000,0,"R",120,0,"swapper",0
+6824713142097000,1,152000,6824713142249000,2728,"S",120,739,"shell",20461
+6824713142249000,1,2185000,6824713144434000,0,"R",120,0,"swapper",0
+6824713142539000,2,518000,6824713143057000,739,"S",120,739,"adbd",20305
+6824713143057000,2,1528000,6824713144585000,0,"R",120,0,"swapper",0
+6824713143278000,3,57000,6824713143335000,670,"S",100,670,"kworker/u17:2",14944
+6824713143335000,3,606000,6824713143941000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713144434000,1,56000,6824713144490000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713144490000,1,687000,6824713145177000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713144646000,2,26000,6824713144672000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713144908000,3,9292000,6824713154200000,0,"R",120,0,"swapper",0
+6824713145177000,1,151000,6824713145328000,739,"S",120,739,"adbd",20305
+6824713145328000,1,1971000,6824713147299000,0,"R",120,0,"swapper",0
+6824713146916000,2,42000,6824713146958000,5,"S",120,5,"rcu_preempt",7
+6824713146958000,2,950000,6824713147908000,0,"R",120,0,"swapper",0
+6824713147299000,1,265000,6824713147564000,8,"S",120,8,"rcuop/0",10
+6824713147564000,1,6404000,6824713153968000,0,"R",120,0,"swapper",0
+6824713147908000,2,20000,6824713147928000,5,"S",120,5,"rcu_preempt",7
+6824713147928000,2,5718000,6824713153646000,0,"R",120,0,"swapper",0
+6824713153646000,2,49000,6824713153695000,5,"S",120,5,"rcu_preempt",7
+6824713153695000,2,724000,6824713154419000,0,"R",120,0,"swapper",0
+6824713153968000,1,154000,6824713154122000,2728,"S",120,739,"shell",20461
+6824713154122000,1,591000,6824713154713000,0,"R",120,0,"swapper",0
+6824713154200000,3,176000,6824713154376000,8,"S",120,8,"rcuop/0",10
+6824713154376000,3,873000,6824713155249000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713154983000,2,683000,6824713155666000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713155750000,3,177000,6824713155927000,0,"R",120,0,"swapper",0
+6824713155817000,1,92000,6824713155909000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713155943000,3,6823000,6824713162766000,0,"R",120,0,"swapper",0
+6824713155946000,1,615000,6824713156561000,0,"R",120,0,"swapper",0
+6824713156108000,2,34000,6824713156142000,630,"S",100,630,"kworker/u17:1",1134
+6824713156142000,2,27000,6824713156169000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713156751000,2,4509000,6824713161260000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713161260000,2,459000,6824713161719000,777,"S",120,493,"HwBinder:640_1",721
+6824713161719000,2,304000,6824713162023000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713162426000,1,1725000,6824713164151000,644,"S",120,644,"ndroid.systemui",1664
+6824713162470000,2,1183000,6824713163653000,0,"R",120,0,"swapper",0
+6824713162766000,3,32000,6824713162798000,771,"S",97,493,"DispSync",676
+6824713162798000,3,917000,6824713163715000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713164151000,1,689000,6824713164840000,0,"R",120,0,"swapper",0
+6824713164352000,3,251000,6824713164603000,770,"S",120,493,"Binder:640_2",675
+6824713164404000,2,820000,6824713165224000,0,"R",120,0,"swapper",0
+6824713164603000,3,1967000,6824713166570000,0,"R",120,0,"swapper",0
+6824713164840000,1,120000,6824713164960000,773,"S",97,493,"app",678
+6824713164960000,1,408000,6824713165368000,0,"R",120,0,"swapper",0
+6824713165224000,2,31000,6824713165255000,771,"S",97,493,"DispSync",676
+6824713165255000,2,581000,6824713165836000,0,"R",120,0,"swapper",0
+6824713165368000,1,186000,6824713165554000,2728,"S",120,739,"shell",20461
+6824713165554000,1,1573000,6824713167127000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713167269000,1,51000,6824713167320000,630,"S",100,630,"kworker/u17:1",1134
+6824713167320000,1,35000,6824713167355000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713167572000,1,42000,6824713167614000,0,"R",120,0,"swapper",0
+6824713167614000,1,71000,6824713167685000,630,"S",100,630,"kworker/u17:1",1134
+6824713167660000,3,456000,6824713168116000,0,"R",120,0,"swapper",0
+6824713167685000,1,45000,6824713167730000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713168188000,2,118000,6824713168306000,739,"S",120,739,"adbd",20305
+6824713168261000,3,1720000,6824713169981000,0,"R",120,0,"swapper",0
+6824713168306000,2,8737000,6824713177043000,0,"R",120,0,"swapper",0
+6824713169981000,3,63000,6824713170044000,5,"S",120,5,"rcu_preempt",7
+6824713170044000,3,912000,6824713170956000,0,"R",120,0,"swapper",0
+6824713170393000,1,203000,6824713170596000,8,"S",120,8,"rcuop/0",10
+6824713170596000,1,7765000,6824713178361000,0,"R",120,0,"swapper",0
+6824713170956000,3,31000,6824713170987000,5,"S",120,5,"rcu_preempt",7
+6824713170987000,3,6024000,6824713177011000,0,"R",120,0,"swapper",0
+6824713177011000,3,31000,6824713177042000,5,"S",120,5,"rcu_preempt",7
+6824713177042000,3,653000,6824713177695000,0,"R",120,0,"swapper",0
+6824713177043000,2,352000,6824713177395000,702,"D",120,702,"kworker/u16:7",19422
+6824713177395000,2,498000,6824713177893000,0,"R",120,0,"swapper",0
+6824713177695000,3,144000,6824713177839000,77,"S",120,77,"smem_native_rpm",87
+6824713177839000,3,5798000,6824713183637000,0,"R",120,0,"swapper",0
+6824713177893000,2,35000,6824713177928000,702,"S",120,702,"kworker/u16:7",19422
+6824713177928000,2,920000,6824713178848000,0,"R",120,0,"swapper",0
+6824713178361000,1,204000,6824713178565000,2728,"S",120,739,"shell",20461
+6824713178565000,1,1277000,6824713179842000,0,"R",120,0,"swapper",0
+6824713178848000,2,801000,6824713179649000,739,"S",120,739,"adbd",20305
+6824713179649000,2,622000,6824713180271000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713180271000,2,74000,6824713180345000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713180345000,2,438000,6824713180783000,0,"R",120,0,"swapper",0
+6824713180570000,1,46000,6824713180616000,630,"S",100,630,"kworker/u17:1",1134
+6824713180616000,1,116000,6824713180732000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713180825000,1,17000,6824713180842000,630,"S",100,630,"kworker/u17:1",1134
+6824713180842000,1,170000,6824713181012000,0,"R",120,0,"swapper",0
+6824713180854000,2,1030000,6824713181884000,0,"R",120,0,"swapper",0
+6824713181012000,1,44000,6824713181056000,630,"S",100,630,"kworker/u17:1",1134
+6824713181056000,1,406000,6824713181462000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713182138000,1,1920000,6824713184058000,0,"R",120,0,"swapper",0
+6824713183637000,3,58000,6824713183695000,5,"S",120,5,"rcu_preempt",7
+6824713183695000,3,1423000,6824713185118000,0,"R",120,0,"swapper",0
+6824713184058000,1,700000,6824713184758000,8,"S",120,8,"rcuop/0",10
+6824713184758000,1,3775000,6824713188533000,0,"R",120,0,"swapper",0
+6824713185118000,3,22000,6824713185140000,5,"S",120,5,"rcu_preempt",7
+6824713185140000,3,5295000,6824713190435000,0,"R",120,0,"swapper",0
+6824713188533000,1,169000,6824713188702000,2728,"S",120,739,"shell",20461
+6824713188702000,1,1106000,6824713189808000,0,"R",120,0,"swapper",0
+6824713189029000,2,560000,6824713189589000,739,"S",120,739,"adbd",20305
+6824713189589000,2,668000,6824713190257000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713190257000,2,67000,6824713190324000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713190324000,2,156000,6824713190480000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713190567000,1,41000,6824713190608000,630,"S",100,630,"kworker/u17:1",1134
+6824713190608000,1,445000,6824713191053000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713191204000,1,40000,6824713191244000,630,"S",100,630,"kworker/u17:1",1134
+6824713191212000,2,58000,6824713191270000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713191418000,3,4124000,6824713195542000,0,"R",120,0,"swapper",0
+6824713191519000,1,2312000,6824713193831000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713195229000,1,889000,6824713196118000,0,"R",120,0,"swapper",0
+6824713195542000,3,272000,6824713195814000,773,"S",97,493,"app",678
+6824713195814000,3,811000,6824713196625000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713196797000,0,30115000,6824713226912000,2722,"R+",120,757,"ps",20463
+6824713198160000,1,730000,6824713198890000,0,"R",120,0,"swapper",0
+6824713198323000,3,309000,6824713198632000,770,"S",120,493,"Binder:640_2",675
+6824713198632000,3,5040000,6824713203672000,0,"R",120,0,"swapper",0
+6824713198890000,1,161000,6824713199051000,773,"S",97,493,"app",678
+6824713199051000,1,29000,6824713199080000,0,"R",120,0,"swapper",0
+6824713199080000,1,118000,6824713199198000,482,"S",49,482,"sugov:0",605
+6824713199198000,1,5504000,6824713204702000,0,"R",120,0,"swapper",0
+6824713199334000,2,29000,6824713199363000,771,"S",97,493,"DispSync",676
+6824713199363000,2,4705000,6824713204068000,0,"R",120,0,"swapper",0
+6824713203672000,3,74000,6824713203746000,5,"S",120,5,"rcu_preempt",7
+6824713203746000,3,1079000,6824713204825000,0,"R",120,0,"swapper",0
+6824713204068000,2,436000,6824713204504000,8,"S",120,8,"rcuop/0",10
+6824713204504000,2,743000,6824713205247000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713204917000,1,1067000,6824713205984000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713206279000,1,1131000,6824713207410000,0,"R",120,0,"swapper",0
+6824713206507000,3,43000,6824713206550000,630,"S",100,630,"kworker/u17:1",1134
+6824713206550000,3,436000,6824713206986000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713207410000,1,204000,6824713207614000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713207614000,1,16701000,6824713224315000,0,"R",120,0,"swapper",0
+6824713207853000,2,179000,6824713208032000,739,"S",120,739,"adbd",20305
+6824713208032000,2,2366000,6824713210398000,0,"R",120,0,"swapper",0
+6824713209971000,3,55000,6824713210026000,5,"S",120,5,"rcu_preempt",7
+6824713210026000,3,1318000,6824713211344000,0,"R",120,0,"swapper",0
+6824713210398000,2,635000,6824713211033000,8,"S",120,8,"rcuop/0",10
+6824713211033000,2,6391000,6824713217424000,0,"R",120,0,"swapper",0
+6824713211344000,3,26000,6824713211370000,5,"S",120,5,"rcu_preempt",7
+6824713211370000,3,5656000,6824713217026000,0,"R",120,0,"swapper",0
+6824713217026000,3,51000,6824713217077000,5,"S",120,5,"rcu_preempt",7
+6824713217077000,3,770000,6824713217847000,0,"R",120,0,"swapper",0
+6824713217424000,2,75000,6824713217499000,8,"S",120,8,"rcuop/0",10
+6824713217499000,2,6882000,6824713224381000,0,"R",120,0,"swapper",0
+6824713217847000,3,19000,6824713217866000,5,"S",120,5,"rcu_preempt",7
+6824713217866000,3,6087000,6824713223953000,0,"R",120,0,"swapper",0
+6824713223953000,3,87000,6824713224040000,5,"S",120,5,"rcu_preempt",7
+6824713224040000,3,879000,6824713224919000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713224639000,2,1469000,6824713226108000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713225851000,3,755000,6824713226606000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713226606000,3,144000,6824713226750000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713226750000,3,199000,6824713226949000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713227035000,3,61000,6824713227096000,630,"S",100,630,"kworker/u17:1",1134
+6824713227065000,2,78000,6824713227143000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713227289000,1,566000,6824713227855000,786,"S",111,494,"SDM_EventThread",685
+6824713227305000,2,440000,6824713227745000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713227855000,1,548000,6824713228403000,0,"R",120,0,"swapper",0
+6824713228188000,2,681000,6824713228869000,0,"R",120,0,"swapper",0
+6824713228403000,1,135000,6824713228538000,771,"S",97,493,"DispSync",676
+6824713228538000,1,565000,6824713229103000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713229467000,3,23000,6824713229490000,771,"S",97,493,"DispSync",676
+6824713229490000,3,1513000,6824713231003000,0,"R",120,0,"swapper",0
+6824713230310000,2,45000,6824713230355000,5,"S",120,5,"rcu_preempt",7
+6824713230355000,2,1542000,6824713231897000,0,"R",120,0,"swapper",0
+6824713230856000,1,642000,6824713231498000,0,"R",120,0,"swapper",0
+6824713231003000,3,258000,6824713231261000,770,"S",120,493,"Binder:640_2",675
+6824713231261000,3,5466000,6824713236727000,0,"R",120,0,"swapper",0
+6824713231498000,1,138000,6824713231636000,773,"S",97,493,"app",678
+6824713231636000,1,6415000,6824713238051000,0,"R",120,0,"swapper",0
+6824713231897000,2,28000,6824713231925000,771,"S",97,493,"DispSync",676
+6824713231925000,2,4745000,6824713236670000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713236986000,2,17000,6824713237003000,5,"S",120,5,"rcu_preempt",7
+6824713236994000,3,1550000,6824713238544000,0,"R",120,0,"swapper",0
+6824713237003000,2,3473000,6824713240476000,0,"R",120,0,"swapper",0
+6824713238051000,1,210000,6824713238261000,2728,"S",120,739,"shell",20461
+6824713238261000,1,7678000,6824713245939000,0,"R",120,0,"swapper",0
+6824713238544000,3,824000,6824713239368000,739,"S",120,739,"adbd",20305
+6824713239368000,3,631000,6824713239999000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713240476000,2,102000,6824713240578000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713240578000,2,560000,6824713241138000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713241197000,3,40000,6824713241237000,630,"S",100,630,"kworker/u17:1",1134
+6824713241211000,2,447000,6824713241658000,0,"R",120,0,"swapper",0
+6824713241237000,3,43000,6824713241280000,686,"S",120,686,"kworker/3:1",17791
+6824713241280000,3,763000,6824713242043000,0,"R",120,0,"swapper",0
+6824713241658000,2,183000,6824713241841000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713241841000,2,1454000,6824713243295000,0,"R",120,0,"swapper",0
+6824713242043000,3,215000,6824713242258000,739,"S",120,739,"adbd",20305
+6824713242258000,3,5163000,6824713247421000,0,"R",120,0,"swapper",0
+6824713243295000,2,43000,6824713243338000,5,"S",120,5,"rcu_preempt",7
+6824713243338000,2,3652000,6824713246990000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713246990000,2,51000,6824713247041000,5,"S",120,5,"rcu_preempt",7
+6824713247041000,2,1235000,6824713248276000,0,"R",120,0,"swapper",0
+6824713247421000,3,487000,6824713247908000,8,"S",120,8,"rcuop/0",10
+6824713247908000,3,4863000,6824713252771000,0,"R",120,0,"swapper",0
+6824713248276000,2,25000,6824713248301000,5,"S",120,5,"rcu_preempt",7
+6824713248301000,2,4882000,6824713253183000,0,"R",120,0,"swapper",0
+6824713250362000,6,413000,6824713250775000,2728,"S",120,739,"shell",20461
+6824713250775000,6,72000,6824713250847000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713251509000,0,58000,6824713251567000,52,"S",120,52,"rcuop/6",60
+6824713251567000,0,7123000,6824713258690000,0,"R",120,0,"swapper",0
+6824713252050000,7,3110000,6824713255160000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713254121000,3,94000,6824713254215000,630,"S",100,630,"kworker/u17:1",1134
+6824713254215000,3,534000,6824713254749000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713255075000,3,5496000,6824713260571000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713258690000,0,164000,6824713258854000,739,"S",120,739,"adbd",20305
+6824713258854000,0,1935000,6824713260789000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713261084000,3,994000,6824713262078000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713261539000,1,1177000,6824713262716000,2729,"R+",120,758,"ps",20464
+6824713261690000,2,748000,6824713262438000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713262397000,3,11856000,6824713274253000,0,"R",120,0,"swapper",0
+6824713262438000,2,46000,6824713262484000,771,"S",97,493,"DispSync",676
+6824713262484000,2,2645000,6824713265129000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713265888000,0,23236000,6824713289124000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713271412000,7,52000,6824713271464000,6,"S",120,6,"rcu_sched",8
+6824713271464000,7,1149000,6824713272613000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713287161000,2,80000,6824713287241000,5,"S",120,5,"rcu_preempt",7
+6824713287241000,2,805000,6824713288046000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713289124000,0,238000,6824713289362000,1694,"D",100,660,"thermal-engine",2490
+6824713289362000,0,803000,6824713290165000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713293609000,2,45000,6824713293654000,5,"S",120,5,"rcu_preempt",7
+6824713293654000,2,1390000,6824713295044000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713295127000,0,113000,6824713295240000,777,"S",120,493,"HwBinder:640_1",721
+6824713295230000,2,1013000,6824713296243000,0,"R",120,0,"swapper",0
+6824713295240000,0,922000,6824713296162000,0,"R",120,0,"swapper",0
+6824713295553000,3,326000,6824713295879000,773,"S",97,493,"app",678
+6824713295879000,3,4297000,6824713300176000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713298120000,0,672000,6824713298792000,0,"R",120,0,"swapper",0
+6824713298257000,2,260000,6824713298517000,770,"S",120,493,"Binder:640_2",675
+6824713298517000,2,708000,6824713299225000,0,"R",120,0,"swapper",0
+6824713298792000,0,153000,6824713298945000,773,"S",97,493,"app",678
+6824713298945000,0,25615000,6824713324560000,0,"R",120,0,"swapper",0
+6824713299225000,2,37000,6824713299262000,771,"S",97,493,"DispSync",676
+6824713299262000,2,1065000,6824713300327000,0,"R",120,0,"swapper",0
+6824713300176000,3,48000,6824713300224000,8,"S",120,8,"rcuop/0",10
+6824713300224000,3,7418000,6824713307642000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713307718000,1,9664000,6824713317382000,2729,"R+",120,758,"ps",20464
+6824713307726000,2,5944000,6824713313670000,0,"R",120,0,"swapper",0
+6824713313670000,2,31000,6824713313701000,5,"S",120,5,"rcu_preempt",7
+6824713313701000,2,3234000,6824713316935000,0,"R",120,0,"swapper",0
+6824713316935000,2,46000,6824713316981000,5,"S",120,5,"rcu_preempt",7
+6824713316981000,2,773000,6824713317754000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713324048000,3,59000,6824713324107000,8,"S",120,8,"rcuop/0",10
+6824713324107000,3,3572000,6824713327679000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713324769000,0,2944000,6824713327713000,0,"R",120,0,"swapper",0
+6824713326940000,2,357000,6824713327297000,702,"D",120,702,"kworker/u16:7",19422
+6824713327297000,2,154000,6824713327451000,0,"R",120,0,"swapper",0
+6824713327451000,2,31000,6824713327482000,702,"S",120,702,"kworker/u16:7",19422
+6824713327482000,2,276000,6824713327758000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713327758000,2,602000,6824713328360000,786,"S",111,494,"SDM_EventThread",685
+6824713327805000,0,570000,6824713328375000,0,"R",120,0,"swapper",0
+6824713327807000,1,48000,6824713327855000,14,"S",0,14,"migration/1",16
+6824713327855000,1,1280000,6824713329135000,0,"R",120,0,"swapper",0
+6824713328360000,2,884000,6824713329244000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713329337000,3,722000,6824713330059000,0,"R",120,0,"swapper",0
+6824713329488000,2,470000,6824713329958000,0,"R",120,0,"swapper",0
+6824713329623000,6,113000,6824713329736000,0,"R",120,0,"swapper",0
+6824713329642000,0,49000,6824713329691000,11,"S",0,11,"migration/0",13
+6824713329691000,0,2040000,6824713331731000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713331658000,2,60000,6824713331718000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713332435000,2,4516000,6824713336951000,0,"R",120,0,"swapper",0
+6824713332613000,0,24000,6824713332637000,771,"S",97,493,"DispSync",676
+6824713332637000,0,5094000,6824713337731000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713337341000,3,53000,6824713337394000,8,"S",120,8,"rcuop/0",10
+6824713337394000,3,14884000,6824713352278000,0,"R",120,0,"swapper",0
+6824713337731000,0,167000,6824713337898000,17,"S",120,17,"rcuop/1",20
+6824713337898000,0,14895000,6824713352793000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713361801000,0,57000,6824713361858000,670,"S",100,670,"kworker/u17:2",14944
+6824713361858000,0,46000,6824713361904000,0,"R",120,0,"swapper",0
+6824713361904000,0,39000,6824713361943000,670,"S",100,670,"kworker/u17:2",14944
+6824713361943000,0,43000,6824713361986000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713364450000,3,65000,6824713364515000,771,"S",97,493,"DispSync",676
+6824713364515000,3,13500000,6824713378015000,0,"R",120,0,"swapper",0
+6824713366417000,1,380000,6824713366797000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713366940000,1,28138000,6824713395078000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713377045000,2,741000,6824713377786000,702,"S",120,702,"kworker/u16:7",19422
+6824713377786000,2,2753000,6824713380539000,0,"R",120,0,"swapper",0
+6824713378015000,3,111000,6824713378126000,77,"S",120,77,"smem_native_rpm",87
+6824713378126000,3,5664000,6824713383790000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713383864000,2,90000,6824713383954000,0,"R",120,0,"swapper",0
+6824713383954000,2,184000,6824713384138000,2487,"R+",120,739,"UsbFfs-worker",20308
+6824713383993000,0,6047000,6824713390040000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713390627000,2,119000,6824713390746000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713390746000,2,516000,6824713391262000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713391262000,2,94000,6824713391356000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713391356000,2,737000,6824713392093000,0,"R",120,0,"swapper",0
+6824713391491000,0,76000,6824713391567000,670,"S",100,670,"kworker/u17:2",14944
+6824713391567000,0,314000,6824713391881000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713393791000,0,72000,6824713393863000,670,"S",100,670,"kworker/u17:2",14944
+6824713393833000,2,136000,6824713393969000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713394095000,0,67000,6824713394162000,670,"S",100,670,"kworker/u17:2",14944
+6824713394136000,2,347000,6824713394483000,0,"R",120,0,"swapper",0
+6824713394162000,0,179000,6824713394341000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713394670000,0,199000,6824713394869000,771,"S",97,493,"DispSync",676
+6824713394869000,0,625000,6824713395494000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713395484000,3,8157000,6824713403641000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713397312000,0,423000,6824713397735000,0,"R",120,0,"swapper",0
+6824713397319000,1,268000,6824713397587000,770,"S",120,493,"Binder:640_2",675
+6824713397587000,1,483000,6824713398070000,0,"R",120,0,"swapper",0
+6824713397735000,0,162000,6824713397897000,773,"S",97,493,"app",678
+6824713397897000,0,2323000,6824713400220000,0,"R",120,0,"swapper",0
+6824713398070000,1,74000,6824713398144000,771,"S",97,493,"DispSync",676
+6824713398144000,1,3426000,6824713401570000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713402068000,0,1411000,6824713403479000,0,"R",120,0,"swapper",0
+6824713402488000,7,51000,6824713402539000,2728,"S",120,739,"shell",20461
+6824713402539000,7,4230000,6824713406769000,0,"R",120,0,"swapper",0
+6824713402761000,4,165000,6824713402926000,739,"S",120,739,"adbd",20305
+6824713402926000,4,350000,6824713403276000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713403737000,3,885000,6824713404622000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713404681000,3,582000,6824713405263000,0,"R",120,0,"swapper",0
+6824713404689000,1,62000,6824713404751000,5,"S",120,5,"rcu_preempt",7
+6824713404751000,1,4130000,6824713408881000,0,"R",120,0,"swapper",0
+6824713405022000,2,120000,6824713405142000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713405142000,2,330000,6824713405472000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713405717000,2,2451000,6824713408168000,0,"R",120,0,"swapper",0
+6824713405788000,5,62000,6824713405850000,739,"S",120,739,"adbd",20305
+6824713405850000,5,1152000,6824713407002000,0,"R",120,0,"swapper",0
+6824713406769000,7,10000,6824713406779000,38,"S",120,38,"rcuop/4",44
+6824713406779000,7,26544000,6824713433323000,0,"R",120,0,"swapper",0
+6824713407002000,5,29000,6824713407031000,2728,"S",120,739,"shell",20461
+6824713407031000,5,7986000,6824713415017000,0,"R",120,0,"swapper",0
+6824713407229000,6,96000,6824713407325000,739,"S",120,739,"adbd",20305
+6824713407325000,6,1876000,6824713409201000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713408168000,2,97000,6824713408265000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713408265000,2,326000,6824713408591000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713408708000,0,44000,6824713408752000,670,"S",100,670,"kworker/u17:2",14944
+6824713408752000,0,7418000,6824713416170000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713409142000,1,888000,6824713410030000,0,"R",120,0,"swapper",0
+6824713409201000,6,60000,6824713409261000,739,"S",120,739,"adbd",20305
+6824713409261000,6,6003000,6824713415264000,0,"R",120,0,"swapper",0
+6824713410030000,1,92000,6824713410122000,5,"S",120,5,"rcu_preempt",7
+6824713410122000,1,6146000,6824713416268000,0,"R",120,0,"swapper",0
+6824713410365000,2,1409000,6824713411774000,52,"S",120,52,"rcuop/6",60
+6824713411774000,2,3953000,6824713415727000,0,"R",120,0,"swapper",0
+6824713415017000,5,41000,6824713415058000,2728,"S",120,739,"shell",20461
+6824713415058000,5,871000,6824713415929000,0,"R",120,0,"swapper",0
+6824713415264000,6,114000,6824713415378000,739,"S",120,739,"adbd",20305
+6824713415378000,6,2157000,6824713417535000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713416080000,2,579000,6824713416659000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713416403000,3,2160000,6824713418563000,0,"R",120,0,"swapper",0
+6824713416403000,1,272000,6824713416675000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713416743000,2,74000,6824713416817000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713417253000,1,234000,6824713417487000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713417487000,1,1008000,6824713418495000,0,"R",120,0,"swapper",0
+6824713417535000,6,118000,6824713417653000,739,"S",120,739,"adbd",20305
+6824713417653000,6,1707000,6824713419360000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713418804000,2,62000,6824713418866000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713419057000,1,251000,6824713419308000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713419308000,1,1797000,6824713421105000,0,"R",120,0,"swapper",0
+6824713419360000,6,50000,6824713419410000,739,"S",120,739,"adbd",20305
+6824713419410000,6,798000,6824713420208000,0,"R",120,0,"swapper",0
+6824713419965000,5,29000,6824713419994000,2728,"S",120,739,"shell",20461
+6824713419994000,5,3214000,6824713423208000,0,"R",120,0,"swapper",0
+6824713420208000,6,90000,6824713420298000,739,"S",120,739,"adbd",20305
+6824713420298000,6,1655000,6824713421953000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713421234000,1,424000,6824713421658000,0,"R",120,0,"swapper",0
+6824713421345000,0,93000,6824713421438000,670,"S",100,670,"kworker/u17:2",14944
+6824713421438000,0,49000,6824713421487000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713421882000,1,1476000,6824713423358000,0,"R",120,0,"swapper",0
+6824713421953000,6,52000,6824713422005000,739,"S",120,739,"adbd",20305
+6824713422005000,6,1245000,6824713423250000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713423331000,6,90000,6824713423421000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713423511000,6,2065000,6824713425576000,0,"R",120,0,"swapper",0
+6824713423553000,1,47000,6824713423600000,5,"S",120,5,"rcu_preempt",7
+6824713423600000,1,800000,6824713424400000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713424400000,1,98000,6824713424498000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713424498000,1,773000,6824713425271000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713425531000,1,3451000,6824713428982000,0,"R",120,0,"swapper",0
+6824713425576000,6,50000,6824713425626000,739,"S",120,739,"adbd",20305
+6824713425626000,6,2459000,6824713428085000,0,"R",120,0,"swapper",0
+6824713426874000,2,103000,6824713426977000,702,"S",120,702,"kworker/u16:7",19422
+6824713426977000,2,1675000,6824713428652000,0,"R",120,0,"swapper",0
+6824713427836000,5,29000,6824713427865000,2728,"S",120,739,"shell",20461
+6824713427865000,5,2265000,6824713430130000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713428242000,0,898000,6824713429140000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713429140000,0,196000,6824713429336000,771,"S",97,493,"DispSync",676
+6824713429146000,3,734000,6824713429880000,0,"R",120,0,"swapper",0
+6824713429304000,1,93000,6824713429397000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713429336000,0,735000,6824713430071000,0,"R",120,0,"swapper",0
+6824713429397000,1,470000,6824713429867000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713430006000,2,61000,6824713430067000,5,"S",120,5,"rcu_preempt",7
+6824713430060000,1,492000,6824713430552000,0,"R",120,0,"swapper",0
+6824713430067000,2,113000,6824713430180000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713430180000,2,112000,6824713430292000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713430226000,3,1706000,6824713431932000,0,"R",120,0,"swapper",0
+6824713430292000,2,469000,6824713430761000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713430983000,2,607000,6824713431590000,0,"R",120,0,"swapper",0
+6824713431054000,6,118000,6824713431172000,739,"S",120,739,"adbd",20305
+6824713431172000,6,1852000,6824713433024000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713431854000,2,46000,6824713431900000,773,"S",97,493,"app",678
+6824713431861000,0,492000,6824713432353000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713432059000,2,655000,6824713432714000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713432353000,0,196000,6824713432549000,773,"S",97,493,"app",678
+6824713432549000,0,28791000,6824713461340000,0,"R",120,0,"swapper",0
+6824713432581000,1,110000,6824713432691000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713432691000,1,29017000,6824713461708000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713432880000,2,3801000,6824713436681000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713433323000,7,68000,6824713433391000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713433391000,7,2508000,6824713435899000,0,"R",120,0,"swapper",0
+6824713433547000,5,47000,6824713433594000,739,"S",120,739,"adbd",20305
+6824713433594000,5,1241000,6824713434835000,0,"R",120,0,"swapper",0
+6824713434835000,5,30000,6824713434865000,2728,"S",120,739,"shell",20461
+6824713434865000,5,2740000,6824713437605000,0,"R",120,0,"swapper",0
+6824713435062000,6,101000,6824713435163000,739,"S",120,739,"adbd",20305
+6824713435163000,6,471000,6824713435634000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713435899000,7,15000,6824713435914000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713435914000,7,346000,6824713436260000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713436260000,7,20000,6824713436280000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713436280000,7,302000,6824713436582000,0,"R",120,0,"swapper",0
+6824713436309000,6,29000,6824713436338000,630,"S",100,630,"kworker/u17:1",1134
+6824713436338000,6,35000,6824713436373000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713436653000,6,83000,6824713436736000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713436952000,6,678000,6824713437630000,0,"R",120,0,"swapper",0
+6824713436992000,2,48000,6824713437040000,5,"S",120,5,"rcu_preempt",7
+6824713437040000,2,6321000,6824713443361000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713437712000,6,15000,6824713437727000,0,"R",120,0,"swapper",0
+6824713437727000,6,14000,6824713437741000,630,"S",100,630,"kworker/u17:1",1134
+6824713437741000,6,40000,6824713437781000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713437848000,6,25000,6824713437873000,630,"S",100,630,"kworker/u17:1",1134
+6824713437873000,6,136000,6824713438009000,0,"R",120,0,"swapper",0
+6824713437982000,7,13000,6824713437995000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713437995000,7,43000,6824713438038000,0,"R",120,0,"swapper",0
+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,6,283000,6824713438321000,0,"R",120,0,"swapper",0
+6824713438038000,7,7000,6824713438045000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713438045000,7,470000,6824713438515000,0,"R",120,0,"swapper",0
+6824713438321000,6,20000,6824713438341000,630,"S",100,630,"kworker/u17:1",1134
+6824713438341000,6,34000,6824713438375000,0,"R",120,0,"swapper",0
+6824713438375000,6,29000,6824713438404000,630,"S",100,630,"kworker/u17:1",1134
+6824713438404000,6,71000,6824713438475000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713438528000,6,43000,6824713438571000,739,"S",120,739,"adbd",20305
+6824713438549000,7,3192000,6824713441741000,0,"R",120,0,"swapper",0
+6824713438571000,6,2294000,6824713440865000,0,"R",120,0,"swapper",0
+6824713440637000,5,30000,6824713440667000,2728,"S",120,739,"shell",20461
+6824713440667000,5,2936000,6824713443603000,0,"R",120,0,"swapper",0
+6824713440865000,6,84000,6824713440949000,739,"S",120,739,"adbd",20305
+6824713440949000,6,533000,6824713441482000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713441741000,7,12000,6824713441753000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713441753000,7,349000,6824713442102000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713442102000,7,15000,6824713442117000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713442117000,7,369000,6824713442486000,0,"R",120,0,"swapper",0
+6824713442148000,6,16000,6824713442164000,630,"S",100,630,"kworker/u17:1",1134
+6824713442164000,6,35000,6824713442199000,0,"R",120,0,"swapper",0
+6824713442199000,6,26000,6824713442225000,630,"S",100,630,"kworker/u17:1",1134
+6824713442225000,6,51000,6824713442276000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713442549000,6,1704000,6824713444253000,0,"R",120,0,"swapper",0
+6824713443361000,2,92000,6824713443453000,5,"S",120,5,"rcu_preempt",7
+6824713443453000,2,362000,6824713443815000,0,"R",120,0,"swapper",0
+6824713443603000,5,161000,6824713443764000,38,"S",120,38,"rcuop/4",44
+6824713443764000,5,254000,6824713444018000,0,"R",120,0,"swapper",0
+6824713443815000,2,49000,6824713443864000,5,"S",120,5,"rcu_preempt",7
+6824713443864000,2,6152000,6824713450016000,0,"R",120,0,"swapper",0
+6824713444018000,5,29000,6824713444047000,2728,"S",120,739,"shell",20461
+6824713444047000,5,6341000,6824713450388000,0,"R",120,0,"swapper",0
+6824713444253000,6,82000,6824713444335000,739,"S",120,739,"adbd",20305
+6824713444335000,6,323000,6824713444658000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713444885000,7,14000,6824713444899000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713444899000,7,439000,6824713445338000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713445395000,6,2679000,6824713448074000,0,"R",120,0,"swapper",0
+6824713447845000,7,24000,6824713447869000,2728,"S",120,739,"shell",20461
+6824713447869000,7,782000,6824713448651000,0,"R",120,0,"swapper",0
+6824713448074000,6,85000,6824713448159000,739,"S",120,739,"adbd",20305
+6824713448159000,6,279000,6824713448438000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713448651000,7,12000,6824713448663000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713448663000,7,456000,6824713449119000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713449119000,7,15000,6824713449134000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713449134000,7,353000,6824713449487000,0,"R",120,0,"swapper",0
+6824713449168000,6,27000,6824713449195000,630,"S",100,630,"kworker/u17:1",1134
+6824713449195000,6,48000,6824713449243000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713449487000,7,38000,6824713449525000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713449525000,7,2642000,6824713452167000,0,"R",120,0,"swapper",0
+6824713449676000,6,51000,6824713449727000,739,"S",120,739,"adbd",20305
+6824713449727000,6,349000,6824713450076000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713450163000,6,2199000,6824713452362000,0,"R",120,0,"swapper",0
+6824713450388000,5,13000,6824713450401000,5,"S",120,5,"rcu_preempt",7
+6824713450401000,5,6481000,6824713456882000,0,"R",120,0,"swapper",0
+6824713452167000,7,26000,6824713452193000,2728,"S",120,739,"shell",20461
+6824713452193000,7,989000,6824713453182000,0,"R",120,0,"swapper",0
+6824713452362000,6,87000,6824713452449000,739,"S",120,739,"adbd",20305
+6824713452449000,6,485000,6824713452934000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713453182000,7,12000,6824713453194000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713453194000,7,648000,6824713453842000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713453842000,7,15000,6824713453857000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713453857000,7,551000,6824713454408000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713454471000,6,1697000,6824713456168000,0,"R",120,0,"swapper",0
+6824713455943000,7,21000,6824713455964000,2728,"S",120,739,"shell",20461
+6824713455964000,7,1116000,6824713457080000,0,"R",120,0,"swapper",0
+6824713456168000,6,83000,6824713456251000,739,"S",120,739,"adbd",20305
+6824713456251000,6,535000,6824713456786000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713456882000,5,15000,6824713456897000,5,"S",120,5,"rcu_preempt",7
+6824713456897000,5,644000,6824713457541000,0,"R",120,0,"swapper",0
+6824713457080000,7,12000,6824713457092000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713457092000,7,408000,6824713457500000,0,"R",120,0,"swapper",0
+6824713457244000,6,70000,6824713457314000,38,"S",120,38,"rcuop/4",44
+6824713457314000,6,585000,6824713457899000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713457556000,7,13000,6824713457569000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713457569000,7,270000,6824713457839000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713457945000,6,5819000,6824713463764000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713460298000,5,97000,6824713460395000,739,"S",120,739,"adbd",20305
+6824713460395000,5,1447000,6824713461842000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713461440000,2,373000,6824713461813000,786,"S",111,494,"SDM_EventThread",685
+6824713461449000,0,460000,6824713461909000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713461708000,1,421000,6824713462129000,777,"S",120,493,"HwBinder:640_1",721
+6824713461813000,2,364000,6824713462177000,0,"R",120,0,"swapper",0
+6824713461842000,5,49000,6824713461891000,739,"S",120,739,"adbd",20305
+6824713461891000,5,1341000,6824713463232000,0,"R",120,0,"swapper",0
+6824713461909000,0,68000,6824713461977000,771,"S",97,493,"DispSync",676
+6824713461977000,0,38000,6824713462015000,0,"R",120,0,"swapper",0
+6824713462015000,0,162000,6824713462177000,771,"S",97,493,"DispSync",676
+6824713462129000,1,262000,6824713462391000,0,"R",120,0,"swapper",0
+6824713462177000,0,142000,6824713462319000,0,"R",120,0,"swapper",0
+6824713462177000,2,255000,6824713462432000,773,"S",97,493,"app",678
+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,0,"swapper",0
+6824713462466000,1,2550000,6824713465016000,0,"R",120,0,"swapper",0
+6824713463232000,5,9000,6824713463241000,5,"S",120,5,"rcu_preempt",7
+6824713463241000,5,334000,6824713463575000,0,"R",120,0,"swapper",0
+6824713463575000,5,14000,6824713463589000,5,"S",120,5,"rcu_preempt",7
+6824713463589000,5,243000,6824713463832000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713463837000,5,1034000,6824713464871000,0,"R",120,0,"swapper",0
+6824713464109000,0,488000,6824713464597000,0,"R",120,0,"swapper",0
+6824713464139000,2,5608000,6824713469747000,0,"R",120,0,"swapper",0
+6824713464149000,3,282000,6824713464431000,770,"S",120,493,"Binder:640_2",675
+6824713464431000,3,3478000,6824713467909000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713464769000,0,2190000,6824713466959000,0,"R",120,0,"swapper",0
+6824713464871000,5,85000,6824713464956000,739,"S",120,739,"adbd",20305
+6824713464956000,5,693000,6824713465649000,0,"R",120,0,"swapper",0
+6824713465016000,1,91000,6824713465107000,771,"S",97,493,"DispSync",676
+6824713465107000,1,1435000,6824713466542000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713465553000,7,29000,6824713465582000,630,"S",100,630,"kworker/u17:1",1134
+6824713465582000,7,689000,6824713466271000,0,"R",120,0,"swapper",0
+6824713465649000,5,20000,6824713465669000,2728,"S",120,739,"shell",20461
+6824713465669000,5,418000,6824713466087000,0,"R",120,0,"swapper",0
+6824713466071000,6,16000,6824713466087000,39,"S",120,39,"rcuos/4",45
+6824713466080000,4,812000,6824713466892000,0,"R",120,0,"swapper",0
+6824713466087000,6,4893000,6824713470980000,0,"R",120,0,"swapper",0
+6824713466087000,5,8000,6824713466095000,6,"S",120,6,"rcu_sched",8
+6824713466095000,5,2929000,6824713469024000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713467020000,0,1748000,6824713468768000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713467909000,3,104000,6824713468013000,77,"S",120,77,"smem_native_rpm",87
+6824713468013000,3,1027000,6824713469040000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713468839000,0,3202000,6824713472041000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713469089000,4,153000,6824713469242000,0,"R",120,0,"swapper",0
+6824713469111000,3,1771000,6824713470882000,0,"R",120,0,"swapper",0
+6824713469242000,4,14000,6824713469256000,483,"S",49,483,"sugov:4",606
+6824713469256000,4,2174000,6824713471430000,0,"R",120,0,"swapper",0
+6824713469747000,2,68000,6824713469815000,9,"S",120,9,"rcuos/0",11
+6824713469815000,2,281000,6824713470096000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713470096000,2,3770000,6824713473866000,2720,"x",120,755,"sh",20460
+6824713470126000,1,2368000,6824713472494000,0,"R",120,0,"swapper",0
+6824713470702000,5,43000,6824713470745000,5,"S",120,5,"rcu_preempt",7
+6824713470745000,5,381000,6824713471126000,0,"R",120,0,"swapper",0
+6824713470882000,3,62000,6824713470944000,24,"S",120,24,"rcuop/2",28
+6824713470944000,3,15328000,6824713486272000,0,"R",120,0,"swapper",0
+6824713470980000,6,109000,6824713471089000,38,"S",120,38,"rcuop/4",44
+6824713471089000,6,2971000,6824713474060000,0,"R",120,0,"swapper",0
+6824713471126000,5,89000,6824713471215000,2728,"S",120,739,"shell",20461
+6824713471215000,5,687000,6824713471902000,0,"R",120,0,"swapper",0
+6824713471430000,4,246000,6824713471676000,739,"S",120,739,"adbd",20305
+6824713471676000,4,2529000,6824713474205000,0,"R",120,0,"swapper",0
+6824713471902000,5,68000,6824713471970000,2728,"S",120,739,"shell",20461
+6824713471970000,5,263000,6824713472233000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713472291000,0,508000,6824713472799000,0,"R",120,0,"swapper",0
+6824713472494000,1,96000,6824713472590000,670,"S",100,670,"kworker/u17:2",14944
+6824713472590000,1,47000,6824713472637000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713472842000,1,2946000,6824713475788000,0,"R",120,0,"swapper",0
+6824713472860000,0,44000,6824713472904000,0,"R",120,0,"swapper",0
+6824713472904000,0,75000,6824713472979000,670,"S",100,670,"kworker/u17:2",14944
+6824713472979000,0,453000,6824713473432000,0,"R",120,0,"swapper",0
+6824713473109000,5,8000,6824713473117000,25,"S",120,25,"rcuos/2",29
+6824713473117000,5,861000,6824713473978000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713473866000,2,283000,6824713474149000,0,"R",120,0,"swapper",0
+6824713473978000,5,75000,6824713474053000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713474053000,5,975000,6824713475028000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713474205000,4,149000,6824713474354000,739,"S",120,739,"adbd",20305
+6824713474304000,6,2488000,6824713476792000,0,"R",120,0,"swapper",0
+6824713474354000,4,1066000,6824713475420000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713474875000,2,189000,6824713475064000,0,"R",120,0,"swapper",0
+6824713475028000,5,13000,6824713475041000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713475041000,5,348000,6824713475389000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713475437000,5,75000,6824713475512000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713475568000,0,10190000,6824713485758000,0,"R",120,0,"swapper",0
+6824713475569000,2,10187000,6824713485756000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713475877000,1,621000,6824713476498000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713476852000,1,9339000,6824713486191000,0,"R",120,0,"swapper",0
+6824713477028000,5,42000,6824713477070000,2487,"S",120,739,"UsbFfs-worker",20308
+6824713477070000,5,7334000,6824713484404000,0,"R",120,0,"swapper",0
+6824713477142000,7,19000,6824713477161000,39,"S",120,39,"rcuos/4",45
+6824713477161000,7,61280000,6824713538441000,0,"R",120,0,"swapper",0
+6824713477264000,4,40000,6824713477304000,739,"S",120,739,"adbd",20305
+6824713477304000,4,31231000,6824713508535000,0,"R",120,0,"swapper",0
+6824713484404000,5,44000,6824713484448000,5,"S",120,5,"rcu_preempt",7
+6824713484448000,5,365000,6824713484813000,0,"R",120,0,"swapper",0
+6824713484499000,6,23000,6824713484522000,6,"S",120,6,"rcu_sched",8
+6824713484522000,6,6802000,6824713491324000,0,"R",120,0,"swapper",0
+6824713484813000,5,36000,6824713484849000,25,"S",120,25,"rcuos/2",29
+6824713484849000,5,6226000,6824713491075000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713485915000,0,9244000,6824713495159000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713486366000,3,6074000,6824713492440000,0,"R",120,0,"swapper",0
+6824713491075000,5,58000,6824713491133000,5,"S",120,5,"rcu_preempt",7
+6824713491133000,5,6463000,6824713497596000,0,"R",120,0,"swapper",0
+6824713491324000,6,54000,6824713491378000,38,"S",120,38,"rcuop/4",44
+6824713491378000,6,47434000,6824713538812000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713492656000,2,2845000,6824713495501000,0,"R",120,0,"swapper",0
+6824713495159000,0,143000,6824713495302000,743,"S",120,743,"kworker/0:5",20371
+6824713495302000,0,61000,6824713495363000,0,"R",120,0,"swapper",0
+6824713495363000,0,62000,6824713495425000,3,"S",120,3,"ksoftirqd/0",3
+6824713495425000,0,586000,6824713496011000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713496275000,0,505000,6824713496780000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713496881000,0,83000,6824713496964000,0,"R",120,0,"swapper",0
+6824713496964000,0,65000,6824713497029000,771,"S",97,493,"DispSync",676
+6824713496992000,2,10168000,6824713507160000,0,"R",120,0,"swapper",0
+6824713497029000,0,2730000,6824713499759000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713499282000,3,328000,6824713499610000,770,"S",120,493,"Binder:640_2",675
+6824713499302000,1,572000,6824713499874000,0,"R",120,0,"swapper",0
+6824713499610000,3,7250000,6824713506860000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713500028000,1,7524000,6824713507552000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713507151000,0,1456000,6824713508607000,0,"R",120,0,"swapper",0
+6824713507160000,2,99000,6824713507259000,22,"S",120,22,"ksoftirqd/2",25
+6824713507259000,2,10658000,6824713517917000,0,"R",120,0,"swapper",0
+6824713507552000,1,436000,6824713507988000,17,"S",120,17,"rcuop/1",20
+6824713507988000,1,16424000,6824713524412000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713508785000,4,35595000,6824713544380000,0,"R",120,0,"swapper",0
+6824713512798000,0,237000,6824713513035000,5,"S",120,5,"rcu_preempt",7
+6824713513035000,0,4264000,6824713517299000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713518137000,2,83000,6824713518220000,45,"S",120,45,"rcuop/5",52
+6824713518220000,2,10550000,6824713528770000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713524412000,1,87000,6824713524499000,17,"S",120,17,"rcuop/1",20
+6824713524499000,1,4727000,6824713529226000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713530415000,2,1002000,6824713531417000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713534377000,1,510000,6824713534887000,0,"R",120,0,"swapper",0
+6824713534532000,0,188000,6824713534720000,773,"S",97,493,"app",678
+6824713534720000,0,2286000,6824713537006000,0,"R",120,0,"swapper",0
+6824713534887000,1,85000,6824713534972000,771,"S",97,493,"DispSync",676
+6824713534972000,1,29038000,6824713564010000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713538441000,7,113000,6824713538554000,39,"S",120,39,"rcuos/4",45
+6824713538554000,7,5437000,6824713543991000,0,"R",120,0,"swapper",0
+6824713538812000,6,55000,6824713538867000,6,"S",120,6,"rcu_sched",8
+6824713538867000,6,4782000,6824713543649000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713543991000,7,134000,6824713544125000,39,"S",120,39,"rcuos/4",45
+6824713544125000,7,6499000,6824713550624000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713544507000,6,5784000,6824713550291000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713550291000,6,80000,6824713550371000,6,"S",120,6,"rcu_sched",8
+6824713550371000,6,24532000,6824713574903000,0,"R",120,0,"swapper",0
+6824713550624000,7,88000,6824713550712000,39,"S",120,39,"rcuos/4",45
+6824713550712000,7,23645000,6824713574357000,0,"R",120,0,"swapper",0
+6824713550971000,4,48000,6824713551019000,46,"S",120,46,"rcuos/5",53
+6824713551019000,4,14066000,6824713565085000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713564595000,3,3222000,6824713567817000,644,"S",120,644,"ndroid.systemui",1664
+6824713564620000,0,1978000,6824713566598000,0,"R",120,0,"swapper",0
+6824713564760000,2,90000,6824713564850000,771,"S",97,493,"DispSync",676
+6824713564850000,2,3091000,6824713567941000,0,"R",120,0,"swapper",0
+6824713565085000,4,514000,6824713565599000,943,"D",100,563,"FileWatcherThre",908
+6824713565599000,4,22373000,6824713587972000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713567081000,1,530000,6824713567611000,943,"S",100,563,"FileWatcherThre",908
+6824713567611000,1,1518000,6824713569129000,0,"R",120,0,"swapper",0
+6824713567817000,3,13943000,6824713581760000,0,"R",120,0,"swapper",0
+6824713567836000,0,68000,6824713567904000,8,"S",120,8,"rcuop/0",10
+6824713567904000,0,700000,6824713568604000,0,"R",120,0,"swapper",0
+6824713567941000,2,390000,6824713568331000,770,"S",120,493,"Binder:640_2",675
+6824713568331000,2,4718000,6824713573049000,0,"R",120,0,"swapper",0
+6824713568604000,0,218000,6824713568822000,773,"S",97,493,"app",678
+6824713568822000,0,11756000,6824713580578000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713573049000,2,113000,6824713573162000,145,"S",120,145,"hwrng",215
+6824713573162000,2,2555000,6824713575717000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713574903000,6,48000,6824713574951000,6,"S",120,6,"rcu_sched",8
+6824713574951000,6,5495000,6824713580446000,0,"R",120,0,"swapper",0
+6824713575717000,2,111000,6824713575828000,145,"S",120,145,"hwrng",215
+6824713575828000,2,22002000,6824713597830000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713581961000,1,15601000,6824713597562000,0,"R",120,0,"swapper",0
+6824713582275000,0,89000,6824713582364000,702,"S",120,702,"kworker/u16:7",19422
+6824713582364000,0,14442000,6824713596806000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713587449000,7,83000,6824713587532000,39,"S",120,39,"rcuos/4",45
+6824713587532000,7,6610000,6824713594142000,0,"R",120,0,"swapper",0
+6824713587972000,4,51000,6824713588023000,46,"S",120,46,"rcuos/5",53
+6824713588023000,4,6549000,6824713594572000,0,"R",120,0,"swapper",0
+6824713588081000,6,24000,6824713588105000,6,"S",120,6,"rcu_sched",8
+6824713588105000,6,5555000,6824713593660000,0,"R",120,0,"swapper",0
+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,-1,6824713593701999,0,"[NULL]",120,0,"swapper",0
+6824713594142000,7,54000,6824713594196000,39,"S",120,39,"rcuos/4",45
+6824713594196000,7,-1,6824713594195999,0,"[NULL]",120,0,"swapper",0
+6824713594572000,4,31000,6824713594603000,46,"S",120,46,"rcuos/5",53
+6824713594603000,4,-1,6824713594602999,0,"[NULL]",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713598635000,2,932000,6824713599567000,0,"R",120,0,"swapper",0
+6824713598845000,1,556000,6824713599401000,777,"S",120,493,"HwBinder:640_1",721
+6824713599401000,1,2173000,6824713601574000,0,"R",120,0,"swapper",0
+6824713599567000,2,90000,6824713599657000,771,"S",97,493,"DispSync",676
+6824713599657000,2,23565000,6824713623222000,0,"R",120,0,"swapper",0
+6824713601561000,0,678000,6824713602239000,0,"R",120,0,"swapper",0
+6824713601574000,1,395000,6824713601969000,770,"S",120,493,"Binder:640_2",675
+6824713601969000,1,395000,6824713602364000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+6824713602448000,0,26982000,6824713629430000,0,"R",120,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+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,0,"swapper",0
+6824713631536000,3,-1,6824713631535999,0,"[NULL]",120,0,"swapper",0
+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,0,"swapper",0
+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,-1,6824713633755999,2738,"[NULL]",120,764,"atrace",20467
+6824713633887000,0,-1,6824713633886999,0,"[NULL]",120,0,"swapper",0
+6824713634629000,1,131000,6824713634760000,773,"S",97,493,"app",678
+6824713634760000,1,-1,6824713634759999,0,"[NULL]",120,0,"swapper",0
+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,-1,6824713634972999,0,"[NULL]",120,0,"swapper",0
diff --git a/test/trace_processor/diff_tests/parsing/tests.py b/test/trace_processor/diff_tests/parsing/tests.py
new file mode 100644
index 0000000..1352521
--- /dev/null
+++ b/test/trace_processor/diff_tests/parsing/tests.py
@@ -0,0 +1,1128 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class Parsing(TestSuite):
+  # Contains tests for parsing events which are applicable to more than one
+  # "area". Generally, events here are of high importance (e.g. sched_switch
+  # tested here is and is used by every embedder of trace processor)  Note:
+  # generally *not* advisable to add tests here. Check the guidance provided
+  # http://perfetto/dev/docs/analysis/trace-processor#diff-tests for choosing
+  # folder to add a new test to. TODO(lalitm): some tests here should be moved
+  # of here and into the area folders; they are only here because they predate
+  # modularisation of diff tests. Sched
+  def test_ts_desc_filter_android_sched_and_ps(self):
+    return DiffTestBlueprint(
+        trace=DataPath('android_sched_and_ps.pb'),
+        query="""
+        SELECT ts
+        FROM sched
+        JOIN thread USING(utid)
+        WHERE tid = 23850
+        ORDER BY ts DESC
+        LIMIT 10;
+        """,
+        out=Csv("""
+        "ts"
+        81492536383477
+        81491101817952
+        81491101296858
+        81491101029618
+        81491099541806
+        81491099514618
+        81491099495504
+        81491099477014
+        81491098894566
+        81491096076181
+        """))
+
+  # Sched reason
+  def test_android_sched_and_ps_end_reason_eq(self):
+    return DiffTestBlueprint(
+        trace=DataPath('android_sched_and_ps.pb'),
+        query="""
+        SELECT end_state, count(*)
+        FROM sched
+        WHERE end_state = 'D'
+        GROUP BY end_state;
+        """,
+        out=Csv("""
+        "end_state","count(*)"
+        "D",10503
+        """))
+
+  def test_android_sched_and_ps_end_reason_neq(self):
+    return DiffTestBlueprint(
+        trace=DataPath('android_sched_and_ps.pb'),
+        query="""
+        SELECT end_state, count(*)
+        FROM sched
+        WHERE end_state != 'D'
+        GROUP BY end_state;
+        """,
+        out=Csv("""
+        "end_state","count(*)"
+        "DK",30
+        "R",91189
+        "R+",9428
+        "S",110560
+        "x",82
+        """))
+
+  # CPU Frequency
+  def test_cpu_counters_b120487929(self):
+    return DiffTestBlueprint(
+        trace=DataPath('cpu_counters.pb'),
+        query=Path('b120487929_test.sql'),
+        out=Path('cpu_counters_b120487929.out'))
+
+  # Test the filtering of ftrace events before tracing_start.
+  def test_ftrace_with_tracing_start_list_sched_slice_spans(self):
+    return DiffTestBlueprint(
+        trace=Path('ftrace_with_tracing_start.py'),
+        query="""
+        SELECT ts, dur, tid
+        FROM sched
+        JOIN thread USING(utid)
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "ts","dur","tid"
+        100,10,1
+        110,-1,2
+        """))
+
+  # Scheduling slices from sched_switch events. There are two tests, one for
+  # typical encoding of sched_switch events, and one for the same trace
+  # in the compact format. The output should be identical apart from the
+  # having one slice fewer for each cpu (the first compact sched_switch event
+  # start a slice). Six slices in this case.
+  def test_sched_slices_sched_switch_original(self):
+    return DiffTestBlueprint(
+        trace=DataPath('sched_switch_original.pb'),
+        query="""
+        SELECT ts, cpu, dur, ts_end, end_state, priority, tid, name
+        FROM sched JOIN thread ON sched.utid = thread.utid
+        ORDER BY cpu, sched.ts ASC;
+        """,
+        out=Path('sched_slices_sched_switch_original.out'))
+
+  def test_sched_slices_sched_switch_compact(self):
+    return DiffTestBlueprint(
+        trace=DataPath('sched_switch_compact.pb'),
+        query="""
+        SELECT ts, cpu, dur, ts_end, end_state, priority, tid, name
+        FROM sched JOIN thread ON sched.utid = thread.utid
+        ORDER BY cpu, sched.ts ASC;
+        """,
+        out=Path('sched_slices_sched_switch_compact.out'))
+
+  # Decoding of sched_waking events from a trace with compact scheduling
+  # Verifies the contents of raw & instants tables.
+  def test_sched_waking_raw_compact_sched(self):
+    return DiffTestBlueprint(
+        trace=DataPath('compact_sched.pb'),
+        query=Path('sched_waking_raw_test.sql'),
+        out=Path('sched_waking_raw_compact_sched.out'))
+
+  def test_sched_waking_instants_compact_sched(self):
+    return DiffTestBlueprint(
+        trace=DataPath('compact_sched.pb'),
+        query="""
+        SELECT ts, thread.name, thread.tid
+        FROM thread_state
+        JOIN thread USING (utid)
+        WHERE state = 'R'
+        ORDER BY ts;
+        """,
+        out=Path('sched_waking_instants_compact_sched.out'))
+
+  # Mm Event
+  def test_mm_event(self):
+    return DiffTestBlueprint(
+        trace=DataPath('mm_event.pb'),
+        query="""
+        SELECT ts, name, value
+        FROM counter
+        JOIN counter_track
+          ON counter.track_id = counter_track.id
+        WHERE name GLOB 'mem.mm.*'
+        ORDER BY ts
+        LIMIT 40;
+        """,
+        out=Path('mm_event.out'))
+
+  # Check the systrace conversion code in the raw table. Print events
+  def test_print_systrace_lmk_userspace(self):
+    return DiffTestBlueprint(
+        trace=DataPath('lmk_userspace.pb'),
+        query="""
+        SELECT to_ftrace(id)
+        FROM raw;
+        """,
+        out=Path('print_systrace_lmk_userspace.out'))
+
+  def test_kernel_tmw_counter_process_counter_and_track(self):
+    return DiffTestBlueprint(
+        trace=Path('kernel_tmw_counter.textproto'),
+        query="""
+        SELECT ts, pct.name, value, pid
+        FROM counter c
+        JOIN process_counter_track pct ON c.track_id = pct.id
+        JOIN process USING (upid)
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "ts","name","value","pid"
+        795572805481,"g2d_frame_hw#15",0.000000,237
+        795572870504,"g2d_frame_sw#15",0.000000,237
+        795620516581,"g2d_frame_sw#15",1.000000,237
+        795620943421,"g2d_frame_hw#15",1.000000,237
+        795623633810,"g2d_frame_hw#15",0.000000,237
+        795623633810,"g2d_frame_hw#15",0.000000,237
+        795623739848,"g2d_frame_sw#15",0.000000,237
+        """))
+
+  def test_kernel_dpu_tmw_counter_process_counter_and_track(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          ftrace_events {
+            cpu: 2
+            event {
+              timestamp: 795572805481
+              pid: 237
+              dpu_tracing_mark_write {
+                pid: 237
+                name: "dpu_vote_clock"
+                type: 67
+                value: 123
+              }
+            }
+            event {
+              timestamp: 795572870504
+              pid: 515
+              dpu_tracing_mark_write {
+                pid: 237
+                name: "dpu_vote_clock"
+                type: 67
+                value: 100
+              }
+            }
+            event {
+              timestamp: 795620516581
+              pid: 237
+              dpu_tracing_mark_write {
+                pid: 237
+                name: "dpu_vote_clock"
+                type: 67
+                value: 125
+              }
+            }
+            event {
+              timestamp: 795620943421
+              pid: 515
+              dpu_tracing_mark_write {
+                pid: 237
+                name: "dpu_vote_clock"
+                type: 67
+                value: 100
+              }
+            }
+          }
+          trusted_uid: 9999
+          trusted_packet_sequence_id: 3
+        }
+        """),
+        query="""
+        SELECT ts, pct.name, value, pid
+        FROM counter c
+        JOIN process_counter_track pct ON c.track_id = pct.id
+        JOIN process USING (upid)
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "ts","name","value","pid"
+        795572805481,"dpu_vote_clock",123.000000,237
+        795572870504,"dpu_vote_clock",100.000000,237
+        795620516581,"dpu_vote_clock",125.000000,237
+        795620943421,"dpu_vote_clock",100.000000,237
+        """))
+
+  # Unsigned integers
+  def test_print_systrace_unsigned(self):
+    return DiffTestBlueprint(
+        trace=Path('print_systrace_unsigned.py'),
+        query="""
+        SELECT to_ftrace(id)
+        FROM raw;
+        """,
+        out=Path('print_systrace_unsigned.out'))
+
+  # cgroup_attach_task systrace conversion.
+  def test_cgroup_attach_task_pre_s_print_systrace(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+            ftrace_events {
+              cpu: 3
+              event {
+                timestamp: 74289018336
+                pid: 1
+                cgroup_attach_task {
+                  dst_root: 1
+                  dst_id: 2
+                  pid: 3
+                  comm: "foo"
+                  cname: "bar"
+                }
+              }
+            }
+          }
+        """),
+        query="""
+        SELECT to_ftrace(id)
+        FROM raw;
+        """,
+        out=Path('cgroup_attach_task_pre_s_print_systrace.out'))
+
+  def test_cgroup_attach_task_post_s_print_systrace(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+            ftrace_events {
+              cpu: 3
+              event {
+                timestamp: 74289018336
+                pid: 1
+                cgroup_attach_task {
+                  dst_root: 1
+                  dst_id: 2
+                  pid: 3
+                  comm: "foo"
+                  dst_level: 4
+                  dst_path: "bar"
+                }
+              }
+            }
+          }
+        """),
+        query="""
+        SELECT to_ftrace(id)
+        FROM raw;
+        """,
+        out=Path('cgroup_attach_task_post_s_print_systrace.out'))
+
+  # Parsing systrace files
+  def test_systrace_html(self):
+    return DiffTestBlueprint(
+        trace=DataPath('systrace.html'),
+        query="""
+        SELECT ts, cpu, dur, ts_end, utid, end_state, priority, upid, name, tid
+        FROM sched
+        JOIN thread USING(utid)
+        ORDER BY ts;
+        """,
+        out=Path('systrace_html.out'))
+
+  def test_sched_smoke_trailing_empty(self):
+    return DiffTestBlueprint(
+        trace=DataPath('trailing_empty.systrace'),
+        query="""
+        SELECT COUNT(1)
+        FROM sched;
+        """,
+        out=Csv("""
+        "COUNT(1)"
+        2
+        """))
+
+  # LMK handling
+  def test_lmk_userspace_lmk(self):
+    return DiffTestBlueprint(
+        trace=DataPath('lmk_userspace.pb'),
+        query="""
+        SELECT ts, process.pid
+        FROM instant
+        JOIN process_track ON instant.track_id = process_track.id
+        JOIN process USING (upid);
+        """,
+        out=Csv("""
+        "ts","pid"
+        732246100696424,17924
+        732246180149452,21090
+        732246388596557,21120
+        732246415955101,21151
+        """))
+
+  def test_oom_kill(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        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
+              }
+            }
+          }
+        }
+        """),
+        query="""
+        SELECT ts, instant.name, process.pid, process.name
+        FROM instant
+        JOIN thread_track ON instant.track_id = thread_track.id
+        JOIN thread USING (utid)
+        JOIN process USING (upid);
+        """,
+        out=Csv("""
+        "ts","name","pid","name"
+        1234,"mem.oom_kill",1000,"com.google.android.gm"
+        """))
+
+  # Logcat
+  def test_android_log_counts(self):
+    return DiffTestBlueprint(
+        trace=DataPath('android_log.pb'),
+        query=Path('android_log_counts_test.sql'),
+        out=Csv("""
+        "cnt"
+        2249
+        431
+        264
+        2
+        4
+        31
+        246
+        """))
+
+  def test_android_log_msgs(self):
+    return DiffTestBlueprint(
+        trace=DataPath('android_log.pb'),
+        query=Path('android_log_msgs_test.sql'),
+        out=Path('android_log_msgs.out'))
+
+  def test_android_log_ring_buffer_mode(self):
+    return DiffTestBlueprint(
+        trace=DataPath('android_log_ring_buffer_mode.pb'),
+        query="""
+        SELECT count(*) FROM android_logs;
+        """,
+        out=Csv("""
+        "count(*)"
+        26
+        """))
+
+  # Oom Score
+  def test_synth_oom_oom_query(self):
+    return DiffTestBlueprint(
+        trace=Path('synth_oom.py'),
+        query=Path('oom_query_test.sql'),
+        out=Path('synth_oom_oom_query.out'))
+
+  def test_process_stats_poll_oom_score(self):
+    return DiffTestBlueprint(
+        trace=DataPath('process_stats_poll.pb'),
+        query="""
+        SELECT ts, name, value, upid
+        FROM counter c
+        JOIN process_counter_track t
+          ON c.track_id = t.id
+        WHERE name = "oom_score_adj"
+        ORDER BY ts
+        LIMIT 20;
+        """,
+        out=Path('process_stats_poll_oom_score.out'))
+
+  # Stats
+  def test_android_sched_and_ps_stats(self):
+    return DiffTestBlueprint(
+        trace=DataPath('android_sched_and_ps.pb'),
+        query="""
+        SELECT name, idx, severity, source, value
+        FROM stats WHERE name GLOB 'ftrace_cpu_*' OR name GLOB 'traced_buf_*';
+        """,
+        out=Path('android_sched_and_ps_stats.out'))
+
+  # Syscalls
+  def test_sys_syscall(self):
+    return DiffTestBlueprint(
+        trace=Path('syscall.py'),
+        query="""
+        SELECT ts, dur, name
+        FROM slices
+        LIMIT 10;
+        """,
+        out=Csv("""
+        "ts","dur","name"
+        100,6,"sys_io_setup"
+        105,5,"sys_io_destroy"
+        """))
+
+  # thread_slice tables.
+  def test_thread_time_in_thread_slice(self):
+    return DiffTestBlueprint(
+        trace=Path('flow_events_json_v2.json'),
+        query="""
+        SELECT
+          name, thread_ts, thread_dur
+        FROM slice;
+        """,
+        out=Csv("""
+        "name","thread_ts","thread_dur"
+        "SenderB",1000,5000
+        "Blergh","[NULL]","[NULL]"
+        "SenderA",3005000,7000
+        "OtherSlice",3204000,100000
+        "SomeSlice",3335000,340000
+        "SomeOtherSlice",3335000,996000
+        "SomeOtherSliceInstant","[NULL]","[NULL]"
+        """))
+
+  # Initial display state
+  def test_initial_display_state(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet: {
+          timestamp: 1
+          initial_display_state: {
+            display_state: 2
+            brightness: 0.5
+          }
+        }
+        packet {
+          ftrace_events {
+            cpu: 0
+            event {
+              timestamp: 1000
+              pid: 1234
+              print {
+                buf: "C|5678|ScreenState|0\n"
+              }
+            }
+          }
+        }
+        """),
+        query="""
+        SELECT t.name,
+               c.ts,
+               c.value
+        FROM counter_track t
+        JOIN counter c ON t.id = c.track_id
+        WHERE t.name = 'ScreenState';
+        """,
+        out=Csv("""
+        "name","ts","value"
+        "ScreenState",1,2.000000
+        "ScreenState",1000,0.000000
+        """))
+
+  # Config & metadata
+  def test_config_metadata(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          clock_snapshot {
+            clocks {
+              clock_id: 6
+              timestamp: 101000002
+            }
+            clocks {
+              clock_id: 128
+              timestamp: 2
+            }
+          }
+          timestamp: 101000002
+        }
+        packet {
+          trace_config {
+            trace_uuid_msb: 1314564453825188563
+            trace_uuid_lsb: -6605018796207623390
+          }
+        }
+        packet {
+          system_info {
+            android_build_fingerprint: "the fingerprint"
+          }
+        }
+        """),
+        query="""
+        SELECT name, str_value FROM metadata WHERE str_value IS NOT NULL ORDER BY name;
+        """,
+        out=Csv("""
+        "name","str_value"
+        "android_build_fingerprint","the fingerprint"
+        "trace_config_pbtxt","trace_uuid_msb: 1314564453825188563
+        trace_uuid_lsb: -6605018796207623390"
+        "trace_type","proto"
+        "trace_uuid","123e4567-e89b-12d3-a456-426655443322"
+        """))
+
+  def test_triggers_packets_trigger_packet_trace(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        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
+        }
+        """),
+        query=Path('triggers_packets_test.sql'),
+        out=Csv("""
+        "ts","name","string_value","int_value"
+        101000002,"test1","producer1",3
+        101000004,"test2","producer2",4
+        """))
+
+  def test_chrome_metadata(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          clock_snapshot {
+            clocks {
+              clock_id: 6
+              timestamp: 101000002
+            }
+          }
+          trusted_packet_sequence_id: 1
+          timestamp: 101000002
+        }
+        packet {
+          trusted_packet_sequence_id: 1
+          timestamp: 101000002
+          chrome_metadata {
+            background_tracing_metadata {
+              triggered_rule {}
+            }
+            chrome_version_code: 101
+            enabled_categories: "cat1,cat2,cat3"
+          }
+        }
+        """),
+        query="""
+        SELECT * FROM metadata;
+        """,
+        out=Path('chrome_metadata.out'))
+
+  # CPU info
+  def test_cpu(self):
+    return DiffTestBlueprint(
+        trace=Path('cpu_info.textproto'),
+        query="""
+        SELECT
+          id,
+          cluster_id,
+          processor
+        FROM cpu;
+        """,
+        out=Csv("""
+        "id","cluster_id","processor"
+        0,0,"AArch64 Processor rev 13 (aarch64)"
+        1,0,"AArch64 Processor rev 13 (aarch64)"
+        2,0,"AArch64 Processor rev 13 (aarch64)"
+        3,0,"AArch64 Processor rev 13 (aarch64)"
+        4,0,"AArch64 Processor rev 13 (aarch64)"
+        5,0,"AArch64 Processor rev 13 (aarch64)"
+        6,1,"AArch64 Processor rev 13 (aarch64)"
+        7,1,"AArch64 Processor rev 13 (aarch64)"
+        """))
+
+  def test_cpu_freq(self):
+    return DiffTestBlueprint(
+        trace=Path('cpu_info.textproto'),
+        query="""
+        SELECT
+          freq,
+          GROUP_CONCAT(cpu_id) AS cpus
+        FROM cpu_freq
+        GROUP BY freq
+        ORDER BY freq;
+        """,
+        out=Path('cpu_freq.out'))
+
+  # Trace size
+  def test_android_sched_and_ps_trace_size(self):
+    return DiffTestBlueprint(
+        trace=DataPath('android_sched_and_ps.pb'),
+        query="""
+        SELECT int_value FROM metadata WHERE name = 'trace_size_bytes';
+        """,
+        out=Csv("""
+        "int_value"
+        18761615
+        """))
+
+  # Package list handling
+  def test_android_package_list(self):
+    return DiffTestBlueprint(
+        trace=Path('android_package_list.py'),
+        query=Metric('android_package_list'),
+        out=TextProto(r"""
+        android_package_list {
+          packages {
+            package_name: "com.my.pkg"
+            uid: 123
+            version_code: 456000
+          }
+        }
+        """))
+
+  # Ensures process -> package matching works as expected.
+  def test_process_metadata_matching(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          process_tree {
+            processes {
+              pid: 1
+              ppid: 0
+              cmdline: "init"
+              uid: 0
+            }
+            processes {
+              pid: 2
+              ppid: 1
+              cmdline: "system_server"
+              uid: 1000
+            }
+            processes {
+              pid: 3
+              ppid: 1
+              cmdline: "com.google.android.gms"
+              uid: 10100
+            }
+            processes {
+              pid: 4
+              ppid: 1
+              cmdline: "com.google.android.gms.persistent"
+              uid: 10100
+            }
+            processes {
+              pid: 5
+              ppid: 1
+              cmdline: "com.google.android.gms"
+              uid: 1010100
+            }
+          }
+        }
+        packet {
+          packages_list {
+            packages {
+              name: "com.google.android.gms"
+              uid: 10100
+              version_code: 1234
+            }
+            packages {
+              name: "com.google.android.gsf"
+              uid: 10100
+              version_code: 1
+            }
+          }
+        }
+        """),
+        query="""
+        CREATE TABLE TEST_TMP AS
+        SELECT RUN_METRIC('android/process_metadata.sql');
+
+        DROP TABLE TEST_TMP;
+
+        SELECT upid, process_name, uid, shared_uid, package_name, version_code
+        FROM process_metadata_table
+        WHERE upid != 0;
+        """,
+        out=Csv("""
+        "upid","process_name","uid","shared_uid","package_name","version_code"
+        1,"init",0,"[NULL]","[NULL]","[NULL]"
+        2,"system_server",1000,"[NULL]","[NULL]","[NULL]"
+        3,"com.google.android.gms",10100,1,"com.google.android.gms",1234
+        4,"com.google.android.gms.persistent",10100,1,"com.google.android.gms",1234
+        5,"com.google.android.gms",10100,1,"com.google.android.gms",1234
+        """))
+
+  # Flow events importing from json
+  def test_flow_events_json_v1(self):
+    return DiffTestBlueprint(
+        trace=Path('flow_events_json_v1.json'),
+        query="""
+        SELECT t1.name AS slice_out, t2.name AS slice_in FROM flow t
+        JOIN slice t1 ON t.slice_out = t1.slice_id
+        JOIN slice t2 ON t.slice_in = t2.slice_id;
+        """,
+        out=Csv("""
+        "slice_out","slice_in"
+        "SenderB","Blergh"
+        "SenderA","OtherSlice"
+        "OtherSlice","SomeSlice"
+        """))
+
+  def test_flow_events_json_v2(self):
+    return DiffTestBlueprint(
+        trace=Path('flow_events_json_v2.json'),
+        query="""
+        SELECT t1.name AS slice_out, t2.name AS slice_in FROM flow t
+        JOIN slice t1 ON t.slice_out = t1.slice_id
+        JOIN slice t2 ON t.slice_in = t2.slice_id;
+        """,
+        out=Csv("""
+        "slice_out","slice_in"
+        "SenderB","Blergh"
+        "SenderA","OtherSlice"
+        "OtherSlice","SomeSlice"
+        "OtherSlice","SomeOtherSlice"
+        """))
+
+  # Importing displayTimeUnit
+  def test_display_time_unit_slices(self):
+    return DiffTestBlueprint(
+        trace=Json(r"""
+        {"displayTimeUnit":"ns","traceEvents":[
+          {
+            "name": "process_name",
+            "pid": 1,
+            "ph": "M",
+            "args": {
+              "name": "api-service-65fc94b8c7-68w9w"
+            }
+          },
+          {
+            "name": "add_graph",
+            "pid": 1,
+            "tid": 1,
+            "ph": "B",
+            "ts": 1597071955492308000
+          },
+          {
+            "name": "add_graph",
+            "pid": 1,
+            "tid": 1,
+            "ph": "E",
+            "ts": 1597071955703771000
+          }
+        ]
+        }
+        """),
+        query="""
+        SELECT ts, dur, name FROM slice ORDER BY ts DESC;
+        """,
+        out=Csv("""
+        "ts","dur","name"
+        -7794778920422990592,211463000000,"add_graph"
+        """))
+
+  # Parsing sched_blocked_reason
+  def test_sched_blocked_proto_sched_blocked_reason(self):
+    return DiffTestBlueprint(
+        trace=Path('sched_blocked_proto.py'),
+        query="""
+        SELECT ts, tid, io_wait
+        FROM thread_state
+        JOIN thread USING (utid)
+        WHERE state = 'D'
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "ts","tid","io_wait"
+        100,1,0
+        110,2,1
+        """))
+
+  def test_sched_blocked_systrace_sched_blocked_reason(self):
+    return DiffTestBlueprint(
+        trace=Path('sched_blocked_systrace.systrace'),
+        query="""
+        SELECT ts, tid, io_wait
+        FROM thread_state
+        JOIN thread USING (utid)
+        WHERE state = 'D'
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "ts","tid","io_wait"
+        20258854000,269,0
+        21123838000,2172,1
+        """))
+
+  # Kernel symbolization
+  def test_sched_blocked_reason_symbolized_sched_blocked_reason_function(self):
+    return DiffTestBlueprint(
+        trace=Path('sched_blocked_reason_symbolized.textproto'),
+        query="""
+        SELECT
+          ts,
+          thread.tid AS pid,
+          blocked_function AS func
+        FROM thread_state
+        JOIN thread USING (utid)
+        WHERE state = 'D'
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "ts","pid","func"
+        999000,105,"some_fn"
+        999000,102,"filemap_fault"
+        1000000,100,"filemap_fault"
+        1001000,101,"[NULL]"
+        1002000,103,"[NULL]"
+        1003000,100,"some_other_fn"
+        1005000,104,"filemap_fault"
+        """))
+
+  def test_sched_blocked_reason_symbolized_to_systrace(self):
+    return DiffTestBlueprint(
+        trace=Path('sched_blocked_reason_symbolized.textproto'),
+        query="""
+        SELECT to_ftrace(id) AS line
+        FROM raw;
+        """,
+        out=Path('sched_blocked_reason_symbolized_to_systrace.out'))
+
+  # Floating point numbers
+  def test_decimal_timestamp_slices(self):
+    return DiffTestBlueprint(
+        trace=Json(r"""
+        {
+          "traceEvents": [{
+            "pid": 1234,
+            "tid": 1234,
+            "ts": 5.1,
+            "dur": 500.1,
+            "name": "name.exec",
+            "ph": "XXX",
+            "cat": "aaa"
+          }]
+        }
+        """),
+        query="""
+        SELECT ts, dur, name FROM slice ORDER BY ts DESC;
+        """,
+        out=Csv("""
+        "ts","dur","name"
+        5100,500100,"name.exec"
+        """))
+
+  # JSON instants and counters
+  def test_counters_json_counters(self):
+    return DiffTestBlueprint(
+        trace=Json(r"""
+
+        [
+            {"pid": "1000", "name": "ctr", "ph": "C", "ts":  0, "args": {"cats":  0}},
+            {"pid": "1000", "name": "ctr", "ph": "C", "ts": 10, "args": {"cats": 10}},
+            {"pid": "1000", "name": "ctr", "ph": "C", "ts": 20, "args": {"cats":  0}}
+        ]
+        """),
+        query="""
+        SELECT
+          process_counter_track.name,
+          counter.ts,
+          counter.value
+        FROM counter
+        JOIN process_counter_track ON (counter.track_id = process_counter_track.id);
+        """,
+        out=Csv("""
+        "name","ts","value"
+        "ctr cats",0,0.000000
+        "ctr cats",10000,10.000000
+        "ctr cats",20000,0.000000
+        """))
+
+  def test_instants_json_instants(self):
+    return DiffTestBlueprint(
+        trace=DataPath('instants.json'),
+        query="""
+        SELECT
+          slice.ts,
+          slice.name AS slice_name,
+          thread.tid,
+          process.pid
+        FROM slice
+        JOIN track ON (slice.track_id = track.id)
+        LEFT JOIN thread_track ON (slice.track_id = thread_track.id)
+        LEFT JOIN thread ON (thread_track.utid = thread.utid)
+        LEFT JOIN process_track ON (slice.track_id = process_track.id)
+        LEFT JOIN process ON (process_track.upid = process.upid)
+        WHERE dur = 0;
+        """,
+        out=Csv("""
+        "ts","slice_name","tid","pid"
+        1234523300,"Thread",2347,"[NULL]"
+        1235523300,"Global","[NULL]","[NULL]"
+        1236523300,"Process","[NULL]",2320
+        1237523300,"Nonei",6790,"[NULL]"
+        1238523300,"NoneI",6790,"[NULL]"
+        1239523300,"NoneR",6790,"[NULL]"
+        """))
+
+  # Trace quality metric
+  def test_very_long_sched_android_trace_quality(self):
+    return DiffTestBlueprint(
+        trace=Path('very_long_sched.py'),
+        query=Metric('android_trace_quality'),
+        out=TextProto(r"""
+        android_trace_quality {
+          failures {
+            name: "sched_slice_too_long"
+          }
+        }
+        """))
+
+  # Regression test for b/193721088 (infra prepending " done\n" to atrace)
+  def test_sched_smoke_trailing_empty_2(self):
+    return DiffTestBlueprint(
+        trace=DataPath('atrace_b_193721088.atr'),
+        query="""
+        SELECT COUNT(1)
+        FROM sched;
+        """,
+        out=Csv("""
+        "COUNT(1)"
+        2
+        """))
+
+  # Multiuser
+  def test_android_multiuser_switch(self):
+    return DiffTestBlueprint(
+        trace=Path('android_multiuser_switch.textproto'),
+        query=Metric('android_multiuser'),
+        out=TextProto(r"""
+        android_multiuser: {
+          user_switch: {
+            duration_ms: 4900
+          }
+        }
+        """))
+
+  # Output of atrace -z.
+  def test_atrace_compressed_sched_count(self):
+    return DiffTestBlueprint(
+        trace=DataPath('atrace_compressed.ctrace'),
+        query="""
+        SELECT COUNT(1)
+        FROM sched;
+        """,
+        out=Csv("""
+        "COUNT(1)"
+        1120
+        """))
+
+  # Output of adb shell "atrace -t 1 sched" > out.txt". It has extra garbage
+  # from stderr before the TRACE: marker. See b/208691037.
+  def test_atrace_uncompressed_sched_count(self):
+    return DiffTestBlueprint(
+        trace=DataPath('atrace_uncompressed_b_208691037'),
+        query="""
+        SELECT COUNT(1)
+        FROM sched;
+        """,
+        out=Csv("""
+        "COUNT(1)"
+        9
+        """))
+
+  def test_otheruuids_android_other_traces(self):
+    return DiffTestBlueprint(
+        trace=Path('otheruuids.textproto'),
+        query=Metric('android_other_traces'),
+        out=TextProto(r"""
+        android_other_traces {
+          finalized_traces_uuid: "75e4c6d0-d8f6-4f82-fa4b-9e09c5512288"
+          finalized_traces_uuid: "ad836701-3113-3fb1-be4f-f7731e23fbbf"
+          finalized_traces_uuid: "0de1a010-efa1-a081-2345-969b1186a6ab"
+        }
+        """))
+
+  # Per-process Binder transaction metrics
+  def test_android_binder(self):
+    return DiffTestBlueprint(
+        trace=Path('android_binder.py'),
+        query=Metric('android_binder'),
+        out=TextProto(r"""
+        android_binder {
+          process_breakdown {
+            process_name: "test_process_a"
+            pid: 1
+            slice_name: "binder transaction"
+            count: 2
+          }
+          process_breakdown {
+            process_name: "test_process_b"
+            pid: 2
+            slice_name: "binder reply"
+            count: 1
+          }
+          process_breakdown {
+            process_name: "test_process_c"
+            pid: 3
+            slice_name: "binder reply"
+            count: 1
+          }
+        }
+        """))
+
+  # Statsd Atoms
+  def test_statsd_atoms_all_atoms(self):
+    return DiffTestBlueprint(
+        trace=DataPath('statsd_atoms.pb'),
+        query=Path('all_atoms_test.sql'),
+        out=Path('statsd_atoms_all_atoms.out'))
+
+  # Kernel function tracing.
+  def test_funcgraph_trace_funcgraph(self):
+    return DiffTestBlueprint(
+        trace=Path('funcgraph_trace.textproto'),
+        query="""
+        SELECT ts, dur, tid, s.name, depth
+        FROM slices s
+        JOIN thread_track tt ON (s.track_id = tt.id)
+        JOIN thread USING (utid)
+        WHERE tid = 385482;
+        """,
+        out=Csv("""
+        "ts","dur","tid","name","depth"
+        679375600673065,3797,385482,"__handle_mm_fault",0
+        679375600673769,1726,385482,"alloc_pages_vma",1
+        """))
diff --git a/test/trace_processor/diff_tests/parsing/tests_memory_counters.py b/test/trace_processor/diff_tests/parsing/tests_memory_counters.py
new file mode 100644
index 0000000..733ab4c
--- /dev/null
+++ b/test/trace_processor/diff_tests/parsing/tests_memory_counters.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class ParsingMemoryCounters(TestSuite):
+
+  def test_memory_counters_args_string_filter_null(self):
+    return DiffTestBlueprint(
+        trace=DataPath('memory_counters.pb'),
+        query=Path('args_string_filter_null_test.sql'),
+        out=Csv("""
+        "string_value"
+        """))
+
+  def test_memory_counters_args_string_is_null(self):
+    return DiffTestBlueprint(
+        trace=DataPath('memory_counters.pb'),
+        query="""
+        SELECT string_value
+        FROM args
+        WHERE string_value IS NULL
+        LIMIT 10;
+        """,
+        out=Csv("""
+        "string_value"
+        "[NULL]"
+        "[NULL]"
+        "[NULL]"
+        "[NULL]"
+        "[NULL]"
+        "[NULL]"
+        "[NULL]"
+        "[NULL]"
+        "[NULL]"
+        "[NULL]"
+        """))
+
+  def test_memory_counters_args_string_is_not_null(self):
+    return DiffTestBlueprint(
+        trace=DataPath('memory_counters.pb'),
+        query="""
+        SELECT string_value
+        FROM args
+        WHERE string_value IS NOT NULL
+        LIMIT 10;
+        """,
+        out=Csv("""
+        "string_value"
+        "traced_probes"
+        "rcuos/0"
+        "rcuos/0"
+        "rcu_sched"
+        "rcu_sched"
+        "atrace"
+        "atrace"
+        "traced_probes"
+        "swapper/1"
+        "rcu_preempt"
+        """))
+
+  def test_memory_counters_b120605557(self):
+    return DiffTestBlueprint(
+        trace=DataPath('memory_counters.pb'),
+        query="""
+        SELECT count(*)
+        FROM counter
+        JOIN counter_track ON counter_track.id = counter.track_id;
+        """,
+        out=Csv("""
+        "count(*)"
+        98688
+        """))
+
+  def test_global_memory_counter_memory_counters(self):
+    return DiffTestBlueprint(
+        trace=DataPath('memory_counters.pb'),
+        query="""
+        SELECT ts, value, name
+        FROM counter
+        JOIN counter_track ON counter.track_id = counter_track.id
+        WHERE name = 'MemAvailable' AND counter_track.type = 'counter_track'
+        LIMIT 10;
+        """,
+        out=Csv("""
+        "ts","value","name"
+        22240334823167,2696392704.000000,"MemAvailable"
+        22240356169836,2696392704.000000,"MemAvailable"
+        22240468594483,2696392704.000000,"MemAvailable"
+        22240566948190,2696392704.000000,"MemAvailable"
+        22240667383304,2696392704.000000,"MemAvailable"
+        22240766505085,2696392704.000000,"MemAvailable"
+        22240866794106,2696392704.000000,"MemAvailable"
+        22240968271928,2696392704.000000,"MemAvailable"
+        22241065777407,2696392704.000000,"MemAvailable"
+        22241165839708,2696392704.000000,"MemAvailable"
+        """))
+
+  def test_ion_stat(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          ftrace_events {
+            cpu: 4
+            event {
+              timestamp: 1234
+              pid: 4321
+              ion_stat {
+                buffer_id: 101010
+                len: 100
+                total_allocated: 200
+              }
+            }
+          }
+        }
+        """),
+        query="""
+        SELECT t.name, c.ts, c.value
+        FROM counter c
+        JOIN track t ON c.track_id = t.id
+        WHERE t.name GLOB 'mem.ion*';
+        """,
+        out=Csv("""
+        "name","ts","value"
+        "mem.ion",1234,200.000000
+        "mem.ion_change",1234,100.000000
+        """))
diff --git a/test/trace_processor/diff_tests/parsing/tests_rss_stats.py b/test/trace_processor/diff_tests/parsing/tests_rss_stats.py
new file mode 100644
index 0000000..add0709
--- /dev/null
+++ b/test/trace_processor/diff_tests/parsing/tests_rss_stats.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class ParsingRssStats(TestSuite):
+
+  def test_rss_stat_mm_id(self):
+    return DiffTestBlueprint(
+        trace=Path('rss_stat_mm_id.py'),
+        query="""
+        SELECT c.ts, t.name, p.pid, p.name, c.value
+        FROM counter c
+        JOIN process_counter_track t ON c.track_id = t.id
+        JOIN process p USING (upid)
+        ORDER BY ts, pid;
+        """,
+        out=Csv("""
+        "ts","name","pid","name","value"
+        90,"mem.rss.file",3,"kthreadd_child",9.000000
+        99,"mem.rss.file",3,"kthreadd_child",10.000000
+        100,"mem.rss.file",10,"process",1000.000000
+        101,"mem.rss.file",10,"process",900.000000
+        """))
+
+  def test_rss_stat_mm_id_clone(self):
+    return DiffTestBlueprint(
+        trace=Path('rss_stat_mm_id_clone.py'),
+        query="""
+        SELECT c.ts, t.name, p.pid, p.name, c.value
+        FROM counter c
+        JOIN process_counter_track t ON c.track_id = t.id
+        JOIN process p USING (upid)
+        ORDER BY ts, pid;
+        """,
+        out=Csv("""
+        "ts","name","pid","name","value"
+        100,"mem.rss.file",3,"kernel_thread",10.000000
+        100,"mem.rss.file",10,"parent_process",100.000000
+        102,"mem.rss.file",4,"kernel_thread2",20.000000
+        102,"mem.rss.file",11,"child_process",90.000000
+        104,"mem.rss.file",11,"child_process",10.000000
+        105,"mem.rss.file",10,"parent_process",95.000000
+        107,"mem.rss.file",10,"parent_process",105.000000
+        108,"mem.rss.file",10,"parent_process",110.000000
+        """))
+
+  def test_rss_stat_mm_id_reuse(self):
+    return DiffTestBlueprint(
+        trace=Path('rss_stat_mm_id_reuse.py'),
+        query="""
+        SELECT c.ts, t.name, p.pid, p.name, c.value
+        FROM counter c
+        JOIN process_counter_track t ON c.track_id = t.id
+        JOIN process p USING (upid)
+        ORDER BY ts, pid;
+        """,
+        out=Csv("""
+        "ts","name","pid","name","value"
+        100,"mem.rss.file",10,"parent_process",100.000000
+        103,"mem.rss.file",10,"new_process",10.000000
+        """))
+
+  def test_rss_stat_legacy(self):
+    return DiffTestBlueprint(
+        trace=Path('rss_stat_legacy.py'),
+        query="""
+        SELECT c.ts, t.name, p.pid, p.name, c.value
+        FROM counter c
+        JOIN process_counter_track t ON c.track_id = t.id
+        JOIN process p USING (upid)
+        ORDER BY ts, pid;
+        """,
+        out=Csv("""
+        "ts","name","pid","name","value"
+        90,"mem.rss.file",3,"kthreadd_child",9.000000
+        91,"mem.rss.file",3,"kthreadd_child",900.000000
+        99,"mem.rss.file",10,"process",10.000000
+        100,"mem.rss.file",10,"process",1000.000000
+        101,"mem.rss.file",3,"kthreadd_child",900.000000
+        """))
+
+  def test_rss_stat_after_free(self):
+    return DiffTestBlueprint(
+        trace=Path('rss_stat_after_free.py'),
+        query="""
+        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;
+        """,
+        out=Csv("""
+        "pid","last_rss","process_end"
+        10,100,101
+        11,90,"[NULL]"
+        """))
diff --git a/test/trace_processor/diff_tests/parsing/thread_counter_and_track_test.sql b/test/trace_processor/diff_tests/parsing/thread_counter_and_track_test.sql
new file mode 100644
index 0000000..197c704
--- /dev/null
+++ b/test/trace_processor/diff_tests/parsing/thread_counter_and_track_test.sql
@@ -0,0 +1,5 @@
+SELECT ts, t.name, value, tid
+FROM counter c
+JOIN thread_counter_track t ON c.track_id = t.id
+JOIN thread USING (utid)
+ORDER BY ts;
diff --git a/test/trace_processor/parsing/thread_time_in_state.out b/test/trace_processor/diff_tests/parsing/thread_time_in_state.out
similarity index 100%
rename from test/trace_processor/parsing/thread_time_in_state.out
rename to test/trace_processor/diff_tests/parsing/thread_time_in_state.out
diff --git a/test/trace_processor/parsing/thread_time_in_state_event.out b/test/trace_processor/diff_tests/parsing/thread_time_in_state_event.out
similarity index 100%
rename from test/trace_processor/parsing/thread_time_in_state_event.out
rename to test/trace_processor/diff_tests/parsing/thread_time_in_state_event.out
diff --git a/test/trace_processor/parsing/thread_time_in_state_event.py b/test/trace_processor/diff_tests/parsing/thread_time_in_state_event.py
similarity index 100%
rename from test/trace_processor/parsing/thread_time_in_state_event.py
rename to test/trace_processor/diff_tests/parsing/thread_time_in_state_event.py
diff --git a/test/trace_processor/diff_tests/parsing/triggers_packets_test.sql b/test/trace_processor/diff_tests/parsing/triggers_packets_test.sql
new file mode 100644
index 0000000..a67f265
--- /dev/null
+++ b/test/trace_processor/diff_tests/parsing/triggers_packets_test.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/parsing/very_long_sched.py b/test/trace_processor/diff_tests/parsing/very_long_sched.py
similarity index 100%
rename from test/trace_processor/parsing/very_long_sched.py
rename to test/trace_processor/diff_tests/parsing/very_long_sched.py
diff --git a/test/trace_processor/performance/cpu_frequency_limits.textproto b/test/trace_processor/diff_tests/performance/cpu_frequency_limits.textproto
similarity index 100%
rename from test/trace_processor/performance/cpu_frequency_limits.textproto
rename to test/trace_processor/diff_tests/performance/cpu_frequency_limits.textproto
diff --git a/test/trace_processor/performance/frame_timeline_metric.out b/test/trace_processor/diff_tests/performance/frame_timeline_metric.out
similarity index 100%
rename from test/trace_processor/performance/frame_timeline_metric.out
rename to test/trace_processor/diff_tests/performance/frame_timeline_metric.out
diff --git a/test/trace_processor/performance/frame_timeline_metric.py b/test/trace_processor/diff_tests/performance/frame_timeline_metric.py
similarity index 100%
rename from test/trace_processor/performance/frame_timeline_metric.py
rename to test/trace_processor/diff_tests/performance/frame_timeline_metric.py
diff --git a/test/trace_processor/performance/irq_runtime_metric.out b/test/trace_processor/diff_tests/performance/irq_runtime_metric.out
similarity index 100%
rename from test/trace_processor/performance/irq_runtime_metric.out
rename to test/trace_processor/diff_tests/performance/irq_runtime_metric.out
diff --git a/test/trace_processor/performance/irq_runtime_metric.textproto b/test/trace_processor/diff_tests/performance/irq_runtime_metric.textproto
similarity index 100%
rename from test/trace_processor/performance/irq_runtime_metric.textproto
rename to test/trace_processor/diff_tests/performance/irq_runtime_metric.textproto
diff --git a/test/trace_processor/diff_tests/performance/tests.py b/test/trace_processor/diff_tests/performance/tests.py
new file mode 100644
index 0000000..7358373
--- /dev/null
+++ b/test/trace_processor/diff_tests/performance/tests.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class Performance(TestSuite):
+  # IRQ max runtime and count over 1ms
+  def test_irq_runtime_metric(self):
+    return DiffTestBlueprint(
+        trace=Path('irq_runtime_metric.textproto'),
+        query=Metric('android_irq_runtime'),
+        out=Path('irq_runtime_metric.out'))
+
+  # CPU frequency maximum & minimum limits change
+  def test_cpu_frequency_limits(self):
+    return DiffTestBlueprint(
+        trace=Path('cpu_frequency_limits.textproto'),
+        query="""
+        SELECT
+          ts,
+          value,
+          REPLACE(name, " Freq Limit", "") AS cpu
+        FROM
+          counter AS c
+        LEFT JOIN
+          counter_track AS t
+          ON c.track_id = t.id
+        WHERE
+          name GLOB "* Freq Limit"
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "ts","value","cpu"
+        90000000,2800000.000000,"Cpu 6 Max"
+        90000000,500000.000000,"Cpu 6 Min"
+        100000000,1700000.000000,"Cpu 6 Max"
+        100000000,500000.000000,"Cpu 6 Min"
+        110000000,2800000.000000,"Cpu 6 Max"
+        110000000,1400000.000000,"Cpu 6 Min"
+        120000000,1500000.000000,"Cpu 6 Max"
+        120000000,500000.000000,"Cpu 6 Min"
+        120000000,1400000.000000,"Cpu 4 Max"
+        120000000,600000.000000,"Cpu 4 Min"
+        130000000,2200000.000000,"Cpu 4 Max"
+        130000000,800000.000000,"Cpu 4 Min"
+        """))
+
+  # frame_timeline_metric collects App_Deadline_Missed metrics
+  def test_frame_timeline_metric(self):
+    return DiffTestBlueprint(
+        trace=Path('frame_timeline_metric.py'),
+        query=Metric('android_frame_timeline_metric'),
+        out=Path('frame_timeline_metric.out'))
diff --git a/test/trace_processor/diff_tests/power/cpu_counters_p_state_test.out b/test/trace_processor/diff_tests/power/cpu_counters_p_state_test.out
new file mode 100644
index 0000000..aa15808
--- /dev/null
+++ b/test/trace_processor/diff_tests/power/cpu_counters_p_state_test.out
@@ -0,0 +1,17 @@
+
+"cpu","freq_khz","idle_value","dur_ns"
+0,1171200,-1,2709377
+0,1171200,0,38125
+0,1900800,-1,2051510
+0,1900800,0,411875
+1,1171200,-1,2776199
+1,1900800,-1,1735834
+1,1900800,0,400260
+2,1900800,0,0
+4,1574400,-1,11198
+4,1651200,-1,3274426
+5,1651200,-1,3231145
+6,1651200,-1,2107082
+7,1574400,0,173021
+7,1651200,-1,2191459
+7,1651200,0,30104
diff --git a/test/trace_processor/power/dvfs_metric.out b/test/trace_processor/diff_tests/power/dvfs_metric.out
similarity index 100%
rename from test/trace_processor/power/dvfs_metric.out
rename to test/trace_processor/diff_tests/power/dvfs_metric.out
diff --git a/test/trace_processor/power/dvfs_metric.textproto b/test/trace_processor/diff_tests/power/dvfs_metric.textproto
similarity index 100%
rename from test/trace_processor/power/dvfs_metric.textproto
rename to test/trace_processor/diff_tests/power/dvfs_metric.textproto
diff --git a/test/trace_processor/diff_tests/power/energy_breakdown.textproto b/test/trace_processor/diff_tests/power/energy_breakdown.textproto
new file mode 100644
index 0000000..ba2e47e
--- /dev/null
+++ b/test/trace_processor/diff_tests/power/energy_breakdown.textproto
@@ -0,0 +1,61 @@
+packet {
+  android_energy_estimation_breakdown {
+    energy_consumer_descriptor {
+      energy_consumers {
+        energy_consumer_id: 0
+        ordinal: 0
+        type: "CPU_CLUSTER"
+        name: "CPUCL0"
+      }
+      energy_consumers {
+        energy_consumer_id: 1
+        ordinal: 1
+        type: "CPU_CLUSTER"
+        name: "CPUCL1"
+      }
+      energy_consumers {
+        energy_consumer_id: 2
+        ordinal: 2
+        type: "CPU_CLUSTER"
+        name: "CPUCL2"
+      }
+      energy_consumers {
+        energy_consumer_id: 3
+        ordinal: 0
+        type: "OTHER"
+        name: "GPU"
+      }
+      energy_consumers {
+        energy_consumer_id: 4
+        ordinal: 0
+        type: "MOBILE_RADIO"
+        name: "MODEM"
+      }
+      energy_consumers {
+        energy_consumer_id: 5
+        ordinal: 0
+        type: "GNSS"
+        name: "GPS"
+      }
+      energy_consumers {
+        energy_consumer_id: 6
+        ordinal: 0
+        type: "DISPLAY"
+        name: "display"
+      }
+    }
+  }
+  trusted_uid: 9999
+  trusted_packet_sequence_id: 9
+  trusted_pid: 1003
+}
+packet {
+  timestamp: 1030255882785
+  android_energy_estimation_breakdown {
+    energy_consumer_id: 0
+    energy_uws: 98567522
+  }
+  trusted_uid: 9999
+  trusted_packet_sequence_id: 9
+  trusted_pid: 1003
+}
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/power/energy_breakdown_uid.textproto b/test/trace_processor/diff_tests/power/energy_breakdown_uid.textproto
new file mode 100644
index 0000000..b25e671
--- /dev/null
+++ b/test/trace_processor/diff_tests/power/energy_breakdown_uid.textproto
@@ -0,0 +1,73 @@
+packet {
+  android_energy_estimation_breakdown {
+    energy_consumer_descriptor {
+      energy_consumers {
+        energy_consumer_id: 0
+        ordinal: 0
+        type: "CPU_CLUSTER"
+        name: "CPUCL0"
+      }
+      energy_consumers {
+        energy_consumer_id: 1
+        ordinal: 1
+        type: "CPU_CLUSTER"
+        name: "CPUCL1"
+      }
+      energy_consumers {
+        energy_consumer_id: 2
+        ordinal: 2
+        type: "CPU_CLUSTER"
+        name: "CPUCL2"
+      }
+      energy_consumers {
+        energy_consumer_id: 3
+        ordinal: 0
+        type: "OTHER"
+        name: "GPU"
+      }
+      energy_consumers {
+        energy_consumer_id: 4
+        ordinal: 0
+        type: "MOBILE_RADIO"
+        name: "MODEM"
+      }
+      energy_consumers {
+        energy_consumer_id: 5
+        ordinal: 0
+        type: "GNSS"
+        name: "GPS"
+      }
+      energy_consumers {
+        energy_consumer_id: 6
+        ordinal: 0
+        type: "DISPLAY"
+        name: "display"
+      }
+    }
+  }
+  trusted_uid: 9999
+  trusted_packet_sequence_id: 9
+  trusted_pid: 1003
+}
+packet {
+  timestamp: 1026753926322
+  android_energy_estimation_breakdown {
+    energy_consumer_id: 3
+    energy_uws: 19617115
+    per_uid_breakdown {
+      uid: 10234
+      energy_uws: 3004536
+    }
+    per_uid_breakdown {
+      uid: 10190
+      energy_uws: 0
+    }
+    per_uid_breakdown {
+      uid: 10235
+      energy_uws: 4002274
+    }
+  }
+  trusted_uid: 9999
+  trusted_packet_sequence_id: 9
+  trusted_pid: 1003
+}
\ No newline at end of file
diff --git a/test/trace_processor/power/power_rails.textproto b/test/trace_processor/diff_tests/power/power_rails.textproto
similarity index 100%
rename from test/trace_processor/power/power_rails.textproto
rename to test/trace_processor/diff_tests/power/power_rails.textproto
diff --git a/test/trace_processor/power/power_rails_custom_clock.textproto b/test/trace_processor/diff_tests/power/power_rails_custom_clock.textproto
similarity index 100%
rename from test/trace_processor/power/power_rails_custom_clock.textproto
rename to test/trace_processor/diff_tests/power/power_rails_custom_clock.textproto
diff --git a/test/trace_processor/diff_tests/power/suspend_period.textproto b/test/trace_processor/diff_tests/power/suspend_period.textproto
new file mode 100644
index 0000000..9110e2d
--- /dev/null
+++ b/test/trace_processor/diff_tests/power/suspend_period.textproto
@@ -0,0 +1,84 @@
+packet {
+  ftrace_events {
+    cpu: 0
+    event {
+      timestamp: 20000
+      pid: 0
+      suspend_resume {
+        action: "syscore_suspend"
+        val: 0
+        start: 1
+      }
+    }
+  }
+}
+packet {
+  ftrace_events {
+    cpu: 0
+    event {
+      timestamp: 29999
+      pid: 0
+      suspend_resume {
+        action: "syscore_suspend"
+        val: 0
+        start: 0
+      }
+    }
+  }
+}
+packet {
+  ftrace_events {
+    cpu: 0
+    event {
+      timestamp: 30000
+      pid: 0
+      suspend_resume {
+        action: "syscore_resume"
+        val: 0
+        start: 1
+      }
+    }
+  }
+}
+packet {
+  ftrace_events {
+    cpu: 0
+    event {
+      timestamp: 40000
+      pid: 0
+      suspend_resume {
+        action: "syscore_resume"
+        val: 0
+        start: 0
+      }
+    }
+  }
+}
+packet {
+  ftrace_events {
+    cpu: 4
+    event {
+      timestamp: 50000
+      pid: 0
+      suspend_resume {
+        action: "timekeeping_freeze"
+        val: 4
+        start: 1
+      }
+    }
+  }
+}
+packet {
+  ftrace_events {
+    cpu: 1
+    event {
+      timestamp: 60000
+      pid: 0
+      suspend_resume {
+        action: "timekeeping_freeze"
+        val: 1
+        start: 0
+      }
+    }
+  }
+}
diff --git a/test/trace_processor/diff_tests/power/suspend_resume.textproto b/test/trace_processor/diff_tests/power/suspend_resume.textproto
new file mode 100644
index 0000000..5ad3a96
--- /dev/null
+++ b/test/trace_processor/diff_tests/power/suspend_resume.textproto
@@ -0,0 +1,84 @@
+packet {
+  ftrace_events {
+    cpu: 0
+    event {
+      timestamp: 10000
+      pid: 0
+      suspend_resume {
+        action: "suspend_enter"
+        val: 3
+        start: 1
+      }
+    }
+  }
+}
+packet {
+  ftrace_events {
+    cpu: 0
+    event {
+      timestamp: 20000
+      pid: 0
+      suspend_resume {
+        action: "suspend_enter"
+        val: 3
+        start: 0
+      }
+    }
+  }
+}
+packet {
+  ftrace_events {
+    cpu: 0
+    event {
+      timestamp: 30000
+      pid: 0
+      suspend_resume {
+        action: "CPU"
+        val: 0
+        start: 1
+      }
+    }
+  }
+}
+packet {
+  ftrace_events {
+    cpu: 0
+    event {
+      timestamp: 40000
+      pid: 0
+      suspend_resume {
+        action: "CPU"
+        val: 0
+        start: 0
+      }
+    }
+  }
+}
+packet {
+  ftrace_events {
+    cpu: 4
+    event {
+      timestamp: 50000
+      pid: 0
+      suspend_resume {
+        action: "timekeeping_freeze"
+        val: 4
+        start: 1
+      }
+    }
+  }
+}
+packet {
+  ftrace_events {
+    cpu: 1
+    event {
+      timestamp: 60000
+      pid: 0
+      suspend_resume {
+        action: "timekeeping_freeze"
+        val: 1
+        start: 0
+      }
+    }
+  }
+}
diff --git a/test/trace_processor/diff_tests/power/tests.py b/test/trace_processor/diff_tests/power/tests.py
new file mode 100644
index 0000000..792b1b9
--- /dev/null
+++ b/test/trace_processor/diff_tests/power/tests.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class Power(TestSuite):
+  # Power states
+  def test_cpu_counters_p_state(self):
+    return DiffTestBlueprint(
+        trace=DataPath('cpu_counters.pb'),
+        query="""
+        SELECT RUN_METRIC("android/p_state.sql");
+
+        SELECT * FROM P_STATE_OVER_INTERVAL(2579596465618, 2579606465618);
+        """,
+        out=Path('cpu_counters_p_state_test.out'))
+
+  # CPU power ups
+  def test_cpu_powerups(self):
+    return DiffTestBlueprint(
+        trace=DataPath('cpu_powerups_1.pb'),
+        query="""
+        SELECT IMPORT("chrome.cpu_powerups");
+        SELECT * FROM chrome_cpu_power_first_toplevel_slice_after_powerup;
+        """,
+        out=Csv("""
+        "slice_id","previous_power_state"
+        424,2
+        703,2
+        708,2
+        """))
diff --git a/test/trace_processor/diff_tests/power/tests_energy_breakdown.py b/test/trace_processor/diff_tests/power/tests_energy_breakdown.py
new file mode 100644
index 0000000..f62ba2c
--- /dev/null
+++ b/test/trace_processor/diff_tests/power/tests_energy_breakdown.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class PowerEnergyBreakdown(TestSuite):
+  # Energy Estimation Breakdown
+  def test_energy_breakdown_table(self):
+    return DiffTestBlueprint(
+        trace=Path('energy_breakdown.textproto'),
+        query="""
+        SELECT consumer_id, name, consumer_type, ordinal
+        FROM energy_counter_track;
+        """,
+        out=Csv("""
+        "consumer_id","name","consumer_type","ordinal"
+        0,"CPUCL0","CPU_CLUSTER",0
+        """))
+
+  def test_energy_breakdown_event(self):
+    return DiffTestBlueprint(
+        trace=Path('energy_breakdown.textproto'),
+        query="""
+        SELECT ts, value
+        FROM counter
+        JOIN energy_counter_track ON counter.track_id = energy_counter_track.id
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "ts","value"
+        1030255882785,98567522.000000
+        """))
+
+  def test_energy_breakdown_uid_table(self):
+    return DiffTestBlueprint(
+        trace=Path('energy_breakdown_uid.textproto'),
+        query="""
+        SELECT uid, name
+        FROM uid_counter_track;
+        """,
+        out=Csv("""
+        "uid","name"
+        10234,"GPU"
+        10190,"GPU"
+        10235,"GPU"
+        """))
+
+  def test_energy_breakdown_uid_event(self):
+    return DiffTestBlueprint(
+        trace=Path('energy_breakdown_uid.textproto'),
+        query="""
+        SELECT ts, value
+        FROM counter
+        JOIN uid_counter_track ON counter.track_id = uid_counter_track.id
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "ts","value"
+        1026753926322,3004536.000000
+        1026753926322,0.000000
+        1026753926322,4002274.000000
+        """))
+
+  def test_energy_per_uid_table(self):
+    return DiffTestBlueprint(
+        trace=Path('energy_breakdown_uid.textproto'),
+        query="""
+        SELECT consumer_id, uid
+        FROM energy_per_uid_counter_track;
+        """,
+        out=Csv("""
+        "consumer_id","uid"
+        3,10234
+        3,10190
+        3,10235
+        """))
diff --git a/test/trace_processor/diff_tests/power/tests_power_rails.py b/test/trace_processor/diff_tests/power/tests_power_rails.py
new file mode 100644
index 0000000..869604c
--- /dev/null
+++ b/test/trace_processor/diff_tests/power/tests_power_rails.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class PowerPowerRails(TestSuite):
+
+  def test_power_rails_power_rails(self):
+    return DiffTestBlueprint(
+        trace=DataPath('power_rails.pb'),
+        query="""
+        SELECT name, AVG(value), COUNT(*)
+        FROM counters
+        WHERE name GLOB "power.*"
+        GROUP BY name
+        LIMIT 20;
+        """,
+        out=Csv("""
+        "name","AVG(value)","COUNT(*)"
+        "power.PPVAR_VPH_PWR_ABH_uws",7390700.360656,61
+        "power.PPVAR_VPH_PWR_OLED_uws",202362991.655738,61
+        """))
+
+  def test_power_rails_event_power_rails_custom_clock(self):
+    return DiffTestBlueprint(
+        trace=Path('power_rails_custom_clock.textproto'),
+        query="""
+        SELECT ts, value
+        FROM counters
+        WHERE name GLOB "power.*"
+        LIMIT 20;
+        """,
+        out=Csv("""
+        "ts","value"
+        104000000,333.000000
+        106000000,666.000000
+        106000000,999.000000
+        109000000,0.000000
+        """))
+
+  def test_power_rails_timestamp_sort(self):
+    return DiffTestBlueprint(
+        trace=Path('power_rails.textproto'),
+        query="""
+        SELECT ts, value, t.name AS name
+        FROM counter c JOIN counter_track t ON t.id = c.track_id
+        ORDER BY ts
+        LIMIT 20;
+        """,
+        out=Csv("""
+        "ts","value","name"
+        3000000,333.000000,"power.test_rail_uws"
+        3000000,0.000000,"power.test_rail_uws"
+        3000004,1000.000000,"Testing"
+        3000005,999.000000,"power.test_rail2_uws"
+        5000000,666.000000,"power.test_rail_uws"
+        """))
+
+  def test_power_rails_well_known_power_rails(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          power_rails {
+            rail_descriptor {
+              index: 4
+              rail_name: "S3M_VDD_CPUCL1"
+              subsys_name: "cpu"
+              sampling_rate: 1023
+            }
+          }
+        }
+        packet {
+          timestamp: 3000003
+          power_rails {
+            energy_data {
+              index: 4
+              timestamp_ms: 3
+              energy: 333
+            }
+          }
+        }
+        packet {
+          timestamp: 3000005
+          power_rails {
+            rail_descriptor {
+              index: 3
+              rail_name: "S2S_VDD_G3D"
+              subsys_name: "gpu"
+              sampling_rate: 1022
+            }
+            energy_data {
+              index: 4
+              timestamp_ms: 5
+              energy: 666
+            }
+            energy_data {
+              index: 3
+              energy: 999
+            }
+            energy_data {
+              index: 4
+              timestamp_ms: 3
+              energy: 0
+            }
+          }
+        }
+        """),
+        query="""
+        SELECT name, AVG(value), COUNT(*)
+        FROM counters
+        WHERE name GLOB "power.*"
+        GROUP BY name
+        LIMIT 20;
+        """,
+        out=Csv("""
+        "name","AVG(value)","COUNT(*)"
+        "power.rails.cpu.mid",333.000000,3
+        "power.rails.gpu",999.000000,1
+        """))
diff --git a/test/trace_processor/diff_tests/power/tests_voltage_and_scaling.py b/test/trace_processor/diff_tests/power/tests_voltage_and_scaling.py
new file mode 100644
index 0000000..a5c418a
--- /dev/null
+++ b/test/trace_processor/diff_tests/power/tests_voltage_and_scaling.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class PowerVoltageAndScaling(TestSuite):
+
+  def test_dvfs_metric(self):
+    return DiffTestBlueprint(
+        trace=Path('dvfs_metric.textproto'),
+        query=Metric('android_dvfs'),
+        out=Path('dvfs_metric.out'))
+
+  def test_wakesource_wakesource(self):
+    return DiffTestBlueprint(
+        trace=Path('wakesource.textproto'),
+        query="""
+        SELECT ts, dur, slice.name
+        FROM slice
+        JOIN track ON slice.track_id = track.id
+        WHERE track.name GLOB 'Wakelock*'
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "ts","dur","name"
+        34298714043271,7872467,"Wakelock(s2mpw02-power-keys)"
+        34298721846504,42732654,"Wakelock(event0)"
+        34298721915739,16,"Wakelock(s2mpw02-power-keys)"
+        34298764569658,14538,"Wakelock(eventpoll)"
+        """))
+
+  def test_suspend_resume(self):
+    return DiffTestBlueprint(
+        trace=Path('suspend_resume.textproto'),
+        query="""
+        SELECT
+          s.ts,
+          s.dur,
+          s.name AS action
+        FROM
+          slice AS s
+        JOIN
+          track AS t
+          ON s.track_id = t.id
+        WHERE
+          t.name = 'Suspend/Resume Latency'
+        ORDER BY s.ts;
+        """,
+        out=Csv("""
+        "ts","dur","action"
+        10000,10000,"suspend_enter(3)"
+        30000,10000,"CPU(0)"
+        50000,10000,"timekeeping_freeze(0)"
+        """))
+
+  def test_suspend_period(self):
+    return DiffTestBlueprint(
+        trace=Path('suspend_period.textproto'),
+        query=Metric('android_batt'),
+        out=TextProto(r"""
+        android_batt {
+          battery_aggregates {
+            sleep_ns: 20000
+          }
+          suspend_period {
+            timestamp_ns: 30000
+            duration_ns: 10000
+          }
+          suspend_period {
+            timestamp_ns: 50000
+            duration_ns: 10000
+          }
+        }
+        """))
diff --git a/test/trace_processor/power/wakesource.textproto b/test/trace_processor/diff_tests/power/wakesource.textproto
similarity index 100%
rename from test/trace_processor/power/wakesource.textproto
rename to test/trace_processor/diff_tests/power/wakesource.textproto
diff --git a/test/trace_processor/process_tracking/process_parent_pid_tracking_1.py b/test/trace_processor/diff_tests/process_tracking/process_parent_pid_tracking_1.py
similarity index 100%
rename from test/trace_processor/process_tracking/process_parent_pid_tracking_1.py
rename to test/trace_processor/diff_tests/process_tracking/process_parent_pid_tracking_1.py
diff --git a/test/trace_processor/process_tracking/process_parent_pid_tracking_2.py b/test/trace_processor/diff_tests/process_tracking/process_parent_pid_tracking_2.py
similarity index 100%
rename from test/trace_processor/process_tracking/process_parent_pid_tracking_2.py
rename to test/trace_processor/diff_tests/process_tracking/process_parent_pid_tracking_2.py
diff --git a/test/trace_processor/process_tracking/process_tracking_exec.py b/test/trace_processor/diff_tests/process_tracking/process_tracking_exec.py
similarity index 100%
rename from test/trace_processor/process_tracking/process_tracking_exec.py
rename to test/trace_processor/diff_tests/process_tracking/process_tracking_exec.py
diff --git a/test/trace_processor/process_tracking/process_tracking_short_lived_1.py b/test/trace_processor/diff_tests/process_tracking/process_tracking_short_lived_1.py
similarity index 100%
rename from test/trace_processor/process_tracking/process_tracking_short_lived_1.py
rename to test/trace_processor/diff_tests/process_tracking/process_tracking_short_lived_1.py
diff --git a/test/trace_processor/process_tracking/process_tracking_short_lived_2.py b/test/trace_processor/diff_tests/process_tracking/process_tracking_short_lived_2.py
similarity index 100%
rename from test/trace_processor/process_tracking/process_tracking_short_lived_2.py
rename to test/trace_processor/diff_tests/process_tracking/process_tracking_short_lived_2.py
diff --git a/test/trace_processor/process_tracking/reused_thread_print.py b/test/trace_processor/diff_tests/process_tracking/reused_thread_print.py
similarity index 100%
rename from test/trace_processor/process_tracking/reused_thread_print.py
rename to test/trace_processor/diff_tests/process_tracking/reused_thread_print.py
diff --git a/test/trace_processor/process_tracking/synth_process_tracking.py b/test/trace_processor/diff_tests/process_tracking/synth_process_tracking.py
similarity index 100%
rename from test/trace_processor/process_tracking/synth_process_tracking.py
rename to test/trace_processor/diff_tests/process_tracking/synth_process_tracking.py
diff --git a/test/trace_processor/diff_tests/process_tracking/tests.py b/test/trace_processor/diff_tests/process_tracking/tests.py
new file mode 100644
index 0000000..f1413bd
--- /dev/null
+++ b/test/trace_processor/diff_tests/process_tracking/tests.py
@@ -0,0 +1,228 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class ProcessTracking(TestSuite):
+  # Tests for the core process and thread tracking logic. Smoke tests
+  def test_process_tracking(self):
+    return DiffTestBlueprint(
+        trace=Path('synth_process_tracking.py'),
+        query="""
+        SELECT tid, pid, process.name AS pname, thread.name AS tname
+        FROM thread
+        LEFT JOIN process USING(upid)
+        WHERE tid > 0
+        ORDER BY tid;
+        """,
+        out=Csv("""
+        "tid","pid","pname","tname"
+        10,10,"process1","p1-t0"
+        11,"[NULL]","[NULL]","p1-t1"
+        12,10,"process1","p1-t2"
+        20,20,"process_2","p2-t0"
+        21,20,"process_2","p2-t1"
+        22,20,"process_2","p2-t2"
+        30,30,"process_3","p3-t0"
+        31,30,"process_3","p3-t1"
+        31,40,"process_4","p4-t1"
+        32,30,"process_3","p3-t2"
+        33,30,"process_3","p3-t3"
+        34,30,"process_3","p3-t4"
+        40,40,"process_4","p4-t0"
+        """))
+
+  # Short lived threads/processes
+  def test_process_tracking_process_tracking_short_lived_1(self):
+    return DiffTestBlueprint(
+        trace=Path('process_tracking_short_lived_1.py'),
+        query="""
+        SELECT tid, pid, process.name AS pname, thread.name AS tname
+        FROM thread
+        LEFT JOIN process USING(upid)
+        WHERE tid > 0
+        ORDER BY tid;
+        """,
+        out=Csv("""
+        "tid","pid","pname","tname"
+        10,10,"parent","parent"
+        11,11,"child","child"
+        """))
+
+  def test_process_tracking_process_tracking_short_lived_2(self):
+    return DiffTestBlueprint(
+        trace=Path('process_tracking_short_lived_2.py'),
+        query="""
+        SELECT tid, pid, process.name AS pname, thread.name AS tname
+        FROM thread
+        LEFT JOIN process USING(upid)
+        WHERE tid > 0
+        ORDER BY tid;
+        """,
+        out=Csv("""
+        "tid","pid","pname","tname"
+        10,10,"parent","parent"
+        11,11,"true_name","true_name"
+        """))
+
+  # Process uid handling
+  def test_process_tracking_uid(self):
+    return DiffTestBlueprint(
+        trace=Path('synth_process_tracking.py'),
+        query="""
+        SELECT pid, uid
+        FROM process
+        ORDER BY pid;
+        """,
+        out=Csv("""
+        "pid","uid"
+        0,"[NULL]"
+        10,1001
+        20,1002
+        30,"[NULL]"
+        40,"[NULL]"
+        """))
+
+  # Tracking across execs
+  def test_process_tracking_process_tracking_exec(self):
+    return DiffTestBlueprint(
+        trace=Path('process_tracking_exec.py'),
+        query="""
+        SELECT tid, pid, process.name AS pname, thread.name AS tname
+        FROM thread
+        LEFT JOIN process USING(upid)
+        WHERE tid > 0
+        ORDER BY tid;
+        """,
+        out=Csv("""
+        "tid","pid","pname","tname"
+        10,10,"parent","parent"
+        11,11,"true_process_name","true_name"
+        """))
+
+  # Tracking parent threads
+  def test_process_parent_pid_process_parent_pid_tracking_1(self):
+    return DiffTestBlueprint(
+        trace=Path('process_parent_pid_tracking_1.py'),
+        query="""
+        SELECT
+          child.pid AS child_pid,
+          parent.pid AS parent_pid
+        FROM process AS child
+        JOIN process AS parent
+          ON child.parent_upid = parent.upid
+        ORDER BY child_pid;
+        """,
+        out=Csv("""
+        "child_pid","parent_pid"
+        10,0
+        20,10
+        """))
+
+  def test_process_parent_pid_process_parent_pid_tracking_2(self):
+    return DiffTestBlueprint(
+        trace=Path('process_parent_pid_tracking_2.py'),
+        query="""
+        SELECT
+          child.pid AS child_pid,
+          parent.pid AS parent_pid
+        FROM process AS child
+        JOIN process AS parent
+          ON child.parent_upid = parent.upid
+        ORDER BY child_pid;
+        """,
+        out=Csv("""
+        "child_pid","parent_pid"
+        10,0
+        20,10
+        """))
+
+  # Tracking thread reuse
+  def test_process_tracking_reused_thread_print(self):
+    return DiffTestBlueprint(
+        trace=Path('reused_thread_print.py'),
+        query="""
+        SELECT tid, pid, process.name AS pname, thread.name AS tname
+        FROM thread
+        LEFT JOIN process USING(upid)
+        WHERE tid > 0
+        ORDER BY tid;
+        """,
+        out=Csv("""
+        "tid","pid","pname","tname"
+        10,10,"parent","[NULL]"
+        11,11,"short_lived","[NULL]"
+        11,10,"parent","true_name"
+        """))
+
+  # TODO(lalitm): move this out of this folder.
+  def test_slice_with_pid_sde_tracing_mark_write(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        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
+              }
+            }
+          }
+        }
+        """),
+        query="""
+        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);
+        """,
+        out=Csv("""
+        "name","dur","tid","pid"
+        "test_event",1,403,403
+        """))
+
+  # Check that a <...> thread name doesn't overwrite a useful thread name
+  def test_unknown_thread_name_tracking(self):
+    return DiffTestBlueprint(
+        trace=Path('unknown_thread_name.systrace'),
+        query="""
+        SELECT tid, pid, process.name AS pname, thread.name AS tname
+        FROM thread
+        LEFT JOIN process USING(upid)
+        WHERE tid > 0
+        ORDER BY tid;
+        """,
+        out=Csv("""
+        "tid","pid","pname","tname"
+        19999,"[NULL]","[NULL]","real_name"
+        """))
diff --git a/test/trace_processor/diff_tests/process_tracking/unknown_thread_name.systrace b/test/trace_processor/diff_tests/process_tracking/unknown_thread_name.systrace
new file mode 100644
index 0000000..85391a7
--- /dev/null
+++ b/test/trace_processor/diff_tests/process_tracking/unknown_thread_name.systrace
@@ -0,0 +1,2 @@
+          <idle>-0     [001] d..2  1234.000001: sched_switch: prev_comm=swapper/1 prev_pid=0 prev_prio=120 prev_state=R ==> next_comm=real_name next_pid=19999 next_prio=120
+           <...>-19999 [001] d..3  1234.000002: sched_wake_idle_without_ipi: cpu=2
diff --git a/test/trace_processor/profiling/callstack_sampling_flamegraph.out b/test/trace_processor/diff_tests/profiling/callstack_sampling_flamegraph.out
similarity index 100%
rename from test/trace_processor/profiling/callstack_sampling_flamegraph.out
rename to test/trace_processor/diff_tests/profiling/callstack_sampling_flamegraph.out
diff --git a/test/trace_processor/profiling/heap_graph.textproto b/test/trace_processor/diff_tests/profiling/heap_graph.textproto
similarity index 100%
rename from test/trace_processor/profiling/heap_graph.textproto
rename to test/trace_processor/diff_tests/profiling/heap_graph.textproto
diff --git a/test/trace_processor/profiling/heap_graph_baseapk.textproto b/test/trace_processor/diff_tests/profiling/heap_graph_baseapk.textproto
similarity index 100%
rename from test/trace_processor/profiling/heap_graph_baseapk.textproto
rename to test/trace_processor/diff_tests/profiling/heap_graph_baseapk.textproto
diff --git a/test/trace_processor/profiling/heap_graph_branching.textproto b/test/trace_processor/diff_tests/profiling/heap_graph_branching.textproto
similarity index 100%
rename from test/trace_processor/profiling/heap_graph_branching.textproto
rename to test/trace_processor/diff_tests/profiling/heap_graph_branching.textproto
diff --git a/test/trace_processor/profiling/heap_graph_closest_proc.textproto b/test/trace_processor/diff_tests/profiling/heap_graph_closest_proc.textproto
similarity index 100%
rename from test/trace_processor/profiling/heap_graph_closest_proc.textproto
rename to test/trace_processor/diff_tests/profiling/heap_graph_closest_proc.textproto
diff --git a/test/trace_processor/profiling/heap_graph_deobfuscate_pkg.textproto b/test/trace_processor/diff_tests/profiling/heap_graph_deobfuscate_pkg.textproto
similarity index 100%
rename from test/trace_processor/profiling/heap_graph_deobfuscate_pkg.textproto
rename to test/trace_processor/diff_tests/profiling/heap_graph_deobfuscate_pkg.textproto
diff --git a/test/trace_processor/profiling/heap_graph_duplicate_flamegraph.out b/test/trace_processor/diff_tests/profiling/heap_graph_duplicate_flamegraph.out
similarity index 100%
rename from test/trace_processor/profiling/heap_graph_duplicate_flamegraph.out
rename to test/trace_processor/diff_tests/profiling/heap_graph_duplicate_flamegraph.out
diff --git a/test/trace_processor/profiling/heap_graph_flamegraph.out b/test/trace_processor/diff_tests/profiling/heap_graph_flamegraph.out
similarity index 100%
rename from test/trace_processor/profiling/heap_graph_flamegraph.out
rename to test/trace_processor/diff_tests/profiling/heap_graph_flamegraph.out
diff --git a/test/trace_processor/profiling/heap_graph_flamegraph_focused.out b/test/trace_processor/diff_tests/profiling/heap_graph_flamegraph_focused.out
similarity index 100%
rename from test/trace_processor/profiling/heap_graph_flamegraph_focused.out
rename to test/trace_processor/diff_tests/profiling/heap_graph_flamegraph_focused.out
diff --git a/test/trace_processor/profiling/heap_graph_flamegraph_system-server-heap-graph.out b/test/trace_processor/diff_tests/profiling/heap_graph_flamegraph_system-server-heap-graph.out
similarity index 100%
rename from test/trace_processor/profiling/heap_graph_flamegraph_system-server-heap-graph.out
rename to test/trace_processor/diff_tests/profiling/heap_graph_flamegraph_system-server-heap-graph.out
diff --git a/test/trace_processor/profiling/heap_graph_huge_size.textproto b/test/trace_processor/diff_tests/profiling/heap_graph_huge_size.textproto
similarity index 100%
rename from test/trace_processor/profiling/heap_graph_huge_size.textproto
rename to test/trace_processor/diff_tests/profiling/heap_graph_huge_size.textproto
diff --git a/test/trace_processor/profiling/heap_graph_interleaved.textproto b/test/trace_processor/diff_tests/profiling/heap_graph_interleaved.textproto
similarity index 100%
rename from test/trace_processor/profiling/heap_graph_interleaved.textproto
rename to test/trace_processor/diff_tests/profiling/heap_graph_interleaved.textproto
diff --git a/test/trace_processor/profiling/heap_graph_interleaved_object.out b/test/trace_processor/diff_tests/profiling/heap_graph_interleaved_object.out
similarity index 100%
rename from test/trace_processor/profiling/heap_graph_interleaved_object.out
rename to test/trace_processor/diff_tests/profiling/heap_graph_interleaved_object.out
diff --git a/test/trace_processor/profiling/heap_graph_interleaved_reference.out b/test/trace_processor/diff_tests/profiling/heap_graph_interleaved_reference.out
similarity index 100%
rename from test/trace_processor/profiling/heap_graph_interleaved_reference.out
rename to test/trace_processor/diff_tests/profiling/heap_graph_interleaved_reference.out
diff --git a/test/trace_processor/profiling/heap_graph_legacy.textproto b/test/trace_processor/diff_tests/profiling/heap_graph_legacy.textproto
similarity index 100%
rename from test/trace_processor/profiling/heap_graph_legacy.textproto
rename to test/trace_processor/diff_tests/profiling/heap_graph_legacy.textproto
diff --git a/test/trace_processor/profiling/heap_graph_native_size.textproto b/test/trace_processor/diff_tests/profiling/heap_graph_native_size.textproto
similarity index 100%
rename from test/trace_processor/profiling/heap_graph_native_size.textproto
rename to test/trace_processor/diff_tests/profiling/heap_graph_native_size.textproto
diff --git a/test/trace_processor/profiling/heap_graph_object.out b/test/trace_processor/diff_tests/profiling/heap_graph_object.out
similarity index 100%
rename from test/trace_processor/profiling/heap_graph_object.out
rename to test/trace_processor/diff_tests/profiling/heap_graph_object.out
diff --git a/test/trace_processor/profiling/heap_graph_reference.out b/test/trace_processor/diff_tests/profiling/heap_graph_reference.out
similarity index 100%
rename from test/trace_processor/profiling/heap_graph_reference.out
rename to test/trace_processor/diff_tests/profiling/heap_graph_reference.out
diff --git a/test/trace_processor/profiling/heap_graph_superclass.textproto b/test/trace_processor/diff_tests/profiling/heap_graph_superclass.textproto
similarity index 100%
rename from test/trace_processor/profiling/heap_graph_superclass.textproto
rename to test/trace_processor/diff_tests/profiling/heap_graph_superclass.textproto
diff --git a/test/trace_processor/profiling/heap_graph_two_locations.out b/test/trace_processor/diff_tests/profiling/heap_graph_two_locations.out
similarity index 100%
rename from test/trace_processor/profiling/heap_graph_two_locations.out
rename to test/trace_processor/diff_tests/profiling/heap_graph_two_locations.out
diff --git a/test/trace_processor/profiling/heap_graph_two_locations.textproto b/test/trace_processor/diff_tests/profiling/heap_graph_two_locations.textproto
similarity index 100%
rename from test/trace_processor/profiling/heap_graph_two_locations.textproto
rename to test/trace_processor/diff_tests/profiling/heap_graph_two_locations.textproto
diff --git a/test/trace_processor/profiling/heap_profile.textproto b/test/trace_processor/diff_tests/profiling/heap_profile.textproto
similarity index 100%
rename from test/trace_processor/profiling/heap_profile.textproto
rename to test/trace_processor/diff_tests/profiling/heap_profile.textproto
diff --git a/test/trace_processor/profiling/heap_profile_data_local_tmp.textproto b/test/trace_processor/diff_tests/profiling/heap_profile_data_local_tmp.textproto
similarity index 100%
rename from test/trace_processor/profiling/heap_profile_data_local_tmp.textproto
rename to test/trace_processor/diff_tests/profiling/heap_profile_data_local_tmp.textproto
diff --git a/test/trace_processor/profiling/heap_profile_deobfuscate.textproto b/test/trace_processor/diff_tests/profiling/heap_profile_deobfuscate.textproto
similarity index 100%
rename from test/trace_processor/profiling/heap_profile_deobfuscate.textproto
rename to test/trace_processor/diff_tests/profiling/heap_profile_deobfuscate.textproto
diff --git a/test/trace_processor/profiling/heap_profile_deobfuscate_memfd.textproto b/test/trace_processor/diff_tests/profiling/heap_profile_deobfuscate_memfd.textproto
similarity index 100%
rename from test/trace_processor/profiling/heap_profile_deobfuscate_memfd.textproto
rename to test/trace_processor/diff_tests/profiling/heap_profile_deobfuscate_memfd.textproto
diff --git a/test/trace_processor/profiling/heap_profile_deobfuscate_test.sql b/test/trace_processor/diff_tests/profiling/heap_profile_deobfuscate_test.sql
similarity index 100%
rename from test/trace_processor/profiling/heap_profile_deobfuscate_test.sql
rename to test/trace_processor/diff_tests/profiling/heap_profile_deobfuscate_test.sql
diff --git a/test/trace_processor/profiling/heap_profile_dump_max.textproto b/test/trace_processor/diff_tests/profiling/heap_profile_dump_max.textproto
similarity index 100%
rename from test/trace_processor/profiling/heap_profile_dump_max.textproto
rename to test/trace_processor/diff_tests/profiling/heap_profile_dump_max.textproto
diff --git a/test/trace_processor/profiling/heap_profile_dump_max_legacy.textproto b/test/trace_processor/diff_tests/profiling/heap_profile_dump_max_legacy.textproto
similarity index 100%
rename from test/trace_processor/profiling/heap_profile_dump_max_legacy.textproto
rename to test/trace_processor/diff_tests/profiling/heap_profile_dump_max_legacy.textproto
diff --git a/test/trace_processor/profiling/heap_profile_flamegraph_system-server-native-profile.out b/test/trace_processor/diff_tests/profiling/heap_profile_flamegraph_system-server-native-profile.out
similarity index 100%
rename from test/trace_processor/profiling/heap_profile_flamegraph_system-server-native-profile.out
rename to test/trace_processor/diff_tests/profiling/heap_profile_flamegraph_system-server-native-profile.out
diff --git a/test/trace_processor/profiling/heap_profile_jit.textproto b/test/trace_processor/diff_tests/profiling/heap_profile_jit.textproto
similarity index 100%
rename from test/trace_processor/profiling/heap_profile_jit.textproto
rename to test/trace_processor/diff_tests/profiling/heap_profile_jit.textproto
diff --git a/test/trace_processor/profiling/heap_profile_no_symbols.textproto b/test/trace_processor/diff_tests/profiling/heap_profile_no_symbols.textproto
similarity index 100%
rename from test/trace_processor/profiling/heap_profile_no_symbols.textproto
rename to test/trace_processor/diff_tests/profiling/heap_profile_no_symbols.textproto
diff --git a/test/trace_processor/profiling/heap_profile_tracker_new_stack.textproto b/test/trace_processor/diff_tests/profiling/heap_profile_tracker_new_stack.textproto
similarity index 100%
rename from test/trace_processor/profiling/heap_profile_tracker_new_stack.textproto
rename to test/trace_processor/diff_tests/profiling/heap_profile_tracker_new_stack.textproto
diff --git a/test/trace_processor/profiling/heap_profile_tracker_twoheaps.textproto b/test/trace_processor/diff_tests/profiling/heap_profile_tracker_twoheaps.textproto
similarity index 100%
rename from test/trace_processor/profiling/heap_profile_tracker_twoheaps.textproto
rename to test/trace_processor/diff_tests/profiling/heap_profile_tracker_twoheaps.textproto
diff --git a/test/trace_processor/profiling/heap_stats_closest_proc.out b/test/trace_processor/diff_tests/profiling/heap_stats_closest_proc.out
similarity index 100%
rename from test/trace_processor/profiling/heap_stats_closest_proc.out
rename to test/trace_processor/diff_tests/profiling/heap_stats_closest_proc.out
diff --git a/test/trace_processor/profiling/java_heap_histogram.out b/test/trace_processor/diff_tests/profiling/java_heap_histogram.out
similarity index 100%
rename from test/trace_processor/profiling/java_heap_histogram.out
rename to test/trace_processor/diff_tests/profiling/java_heap_histogram.out
diff --git a/test/trace_processor/profiling/perf_sample_rvc.out b/test/trace_processor/diff_tests/profiling/perf_sample_rvc.out
similarity index 100%
rename from test/trace_processor/profiling/perf_sample_rvc.out
rename to test/trace_processor/diff_tests/profiling/perf_sample_rvc.out
diff --git a/test/trace_processor/profiling/perf_sample_sc.out b/test/trace_processor/diff_tests/profiling/perf_sample_sc.out
similarity index 100%
rename from test/trace_processor/profiling/perf_sample_sc.out
rename to test/trace_processor/diff_tests/profiling/perf_sample_sc.out
diff --git a/test/trace_processor/diff_tests/profiling/perf_sample_switch_interp.textproto b/test/trace_processor/diff_tests/profiling/perf_sample_switch_interp.textproto
new file mode 100644
index 0000000..a8ddcbf
--- /dev/null
+++ b/test/trace_processor/diff_tests/profiling/perf_sample_switch_interp.textproto
@@ -0,0 +1,127 @@
+packet {
+  interned_data {
+    build_ids {
+      iid: 0
+      str: ""
+    }
+    mapping_paths {
+      iid: 0
+      str: ""
+    }
+    function_names {
+      iid: 0
+      str: ""
+    }
+  }
+  sequence_flags: 1
+  trusted_uid: 9999
+  trusted_packet_sequence_id: 2
+  trusted_pid: 24388
+}
+packet {
+  timestamp: 214681394021835
+  interned_data {
+    mappings {
+      iid: 1
+      path_string_ids: 1
+      path_string_ids: 2
+      path_string_ids: 3
+      path_string_ids: 4
+      build_id: 1
+    }
+    build_ids {
+      iid: 1
+      str: ""
+    }
+    mapping_paths {
+      iid: 1
+      str: "apex"
+    }
+    mapping_paths {
+      iid: 2
+      str: "com.android.art"
+    }
+    mapping_paths {
+      iid: 3
+      str: "lib64"
+    }
+    mapping_paths {
+      iid: 4
+      str: "libart.so"
+    }
+    mappings {
+      iid: 2
+      path_string_ids: 6
+      build_id: 1
+    }
+    mapping_paths {
+      iid: 6
+      str: "example.vdex"
+    }
+    frames {
+      iid: 1
+      function_name_id: 1
+      mapping_id: 2
+    }
+    function_names {
+      iid: 1
+      str: "com.example.managed.frame"
+    }
+    frames {
+      iid: 2
+      function_name_id: 2
+      mapping_id: 1
+    }
+    function_names {
+      iid: 2
+      str: "ExecuteSwitchImplAsm"
+    }
+    frames {
+      iid: 3
+      function_name_id: 3
+      mapping_id: 1
+    }
+    function_names {
+      iid: 3
+      str: "_ZN3art11interpreter20ExecuteSwitchImplCppILb0EEEvPNS0_17SwitchImplContextE"
+    }
+    frames {
+      iid: 4
+      function_name_id: 4
+      mapping_id: 1
+    }
+    function_names {
+      iid: 4
+      str: "_ZN3art11interpreter6DoCallILb1EEEbPNS_9ArtMethodEPNS_6ThreadERNS_11ShadowFrameEPKNS_11InstructionEtbPNS_6JValueE"
+    }
+    frames {
+      iid: 5
+      function_name_id: 5
+      mapping_id: 1
+    }
+    function_names {
+      iid: 5
+      str: "_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc"
+    }
+    callstacks {
+      iid: 1
+      frame_ids: 1
+      frame_ids: 2
+      frame_ids: 3
+      frame_ids: 4
+      frame_ids: 5
+    }
+  }
+  perf_sample {
+    cpu: 0
+    pid: 1000
+    tid: 1000
+    cpu_mode: MODE_USER
+    timebase_count: 42
+    callstack_iid: 1
+  }
+  trusted_uid: 9999
+  trusted_packet_sequence_id: 2
+  trusted_pid: 24388
+}
+
diff --git a/test/trace_processor/profiling/simpleperf_event.out b/test/trace_processor/diff_tests/profiling/simpleperf_event.out
similarity index 100%
rename from test/trace_processor/profiling/simpleperf_event.out
rename to test/trace_processor/diff_tests/profiling/simpleperf_event.out
diff --git a/test/trace_processor/profiling/simpleperf_event.py b/test/trace_processor/diff_tests/profiling/simpleperf_event.py
similarity index 100%
rename from test/trace_processor/profiling/simpleperf_event.py
rename to test/trace_processor/diff_tests/profiling/simpleperf_event.py
diff --git a/test/trace_processor/diff_tests/profiling/stack_profile_symbols.out b/test/trace_processor/diff_tests/profiling/stack_profile_symbols.out
new file mode 100644
index 0000000..5b33e69
--- /dev/null
+++ b/test/trace_processor/diff_tests/profiling/stack_profile_symbols.out
@@ -0,0 +1,4 @@
+"name","source_file","line_number"
+"_start","??",0
+"(anonymous namespace)::OtherFn(unsigned int, unsigned long)","/builds/master/experiment/external/perfetto/out/linux_clang_release/../../src/profiling/memory/heapprofd_standalone_client_example.cc",24
+"main","/builds/master/experiment/external/perfetto/out/linux_clang_release/../../src/profiling/memory/heapprofd_standalone_client_example.cc",32
diff --git a/test/trace_processor/diff_tests/profiling/tests.py b/test/trace_processor/diff_tests/profiling/tests.py
new file mode 100644
index 0000000..e56fbff
--- /dev/null
+++ b/test/trace_processor/diff_tests/profiling/tests.py
@@ -0,0 +1,358 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class Profiling(TestSuite):
+  # Perf profiling  tests.
+  def test_profiler_smaps(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        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
+            }
+          }
+        }
+        """),
+        query="""
+        SELECT id, type, upid, ts, path, size_kb, private_dirty_kb, swap_kb
+        FROM profiler_smaps;
+        """,
+        out=Csv("""
+        "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
+        """))
+
+  def test_profiler_smaps_metric(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        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
+            }
+          }
+        }
+        """),
+        query=Metric('profiler_smaps'),
+        out=TextProto(r"""
+        profiler_smaps {
+          instance {
+            process {
+              name: "system_server"
+              uid: 1000
+            }
+            mappings {
+              path: "[anon: libc_malloc]"
+              size_kb: 30
+              private_dirty_kb: 10
+              swap_kb: 10
+            }
+            mappings {
+              path: "/system/lib64/libc.so"
+              size_kb: 20
+              private_dirty_kb: 4
+              swap_kb: 4
+            }
+          }
+        }
+        """))
+
+  # Regression test for b/222297079: when cumulative size in a flamegraph
+  # a signed 32-bit integer.
+  def test_heap_graph_flamegraph_matches_objects(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_graph_huge_size.textproto'),
+        query="""
+        SELECT
+          obj.upid AS upid,
+          obj.graph_sample_ts AS ts,
+          SUM(obj.self_size + obj.native_size) AS total_objects_size,
+          (
+            SELECT SUM(cumulative_size)
+            FROM experimental_flamegraph
+            WHERE experimental_flamegraph.upid = obj.upid
+              AND experimental_flamegraph.ts = obj.graph_sample_ts
+              AND profile_type = 'graph'
+              AND depth = 0 -- only the roots
+          ) AS total_flamegraph_size
+        FROM
+          heap_graph_object AS obj
+        WHERE
+          obj.reachable != 0
+        GROUP BY obj.upid, obj.graph_sample_ts;
+        """,
+        out=Csv("""
+        "upid","ts","total_objects_size","total_flamegraph_size"
+        1,10,3000000036,3000000036
+        """))
+
+  # TODO(b/153552977): Stop supporting legacy heap graphs. These never made it
+  # a public release, so we should eventually stop supporting workarounds for
+  def test_heap_graph_flamegraph(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_graph_legacy.textproto'),
+        query="""
+        SELECT
+          id,
+          depth,
+          name,
+          map_name,
+          count,
+          cumulative_count,
+          size,
+          cumulative_size,
+          parent_id
+        FROM experimental_flamegraph
+        WHERE upid = (SELECT max(upid) FROM heap_graph_object)
+          AND profile_type = 'graph'
+          AND ts = (SELECT max(graph_sample_ts) FROM heap_graph_object)
+        LIMIT 10;
+        """,
+        out=Path('heap_graph_flamegraph.out'))
+
+  def test_stack_profile_tracker_empty_callstack(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        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
+              }
+            }
+          }
+        }
+        """),
+        query="""
+        SELECT count(1) AS count FROM heap_profile_allocation;
+        """,
+        out=Csv("""
+        "count"
+        0
+        """))
+
+  # perf_sample table (traced_perf) with android R and S trace inputs.
+  def test_perf_sample_rvc(self):
+    return DiffTestBlueprint(
+        trace=DataPath('perf_sample.pb'),
+        query="""
+        SELECT ps.ts, ps.cpu, ps.cpu_mode, ps.unwind_error, ps.perf_session_id,
+               pct.name AS cntr_name, pct.is_timebase,
+               thread.tid,
+               spf.name
+        FROM experimental_annotated_callstack eac
+        JOIN perf_sample ps
+          ON (eac.start_id = ps.callsite_id)
+        JOIN perf_counter_track pct
+          USING(perf_session_id, cpu)
+        JOIN thread
+          USING(utid)
+        JOIN stack_profile_frame spf
+          ON (eac.frame_id = spf.id)
+        ORDER BY ps.ts ASC, eac.depth ASC;
+        """,
+        out=Path('perf_sample_rvc.out'))
+
+  def test_perf_sample_sc(self):
+    return DiffTestBlueprint(
+        trace=DataPath('perf_sample_sc.pb'),
+        query="""
+        SELECT ps.ts, ps.cpu, ps.cpu_mode, ps.unwind_error, ps.perf_session_id,
+               pct.name AS cntr_name, pct.is_timebase,
+               thread.tid,
+               spf.name
+        FROM experimental_annotated_callstack eac
+        JOIN perf_sample ps
+          ON (eac.start_id = ps.callsite_id)
+        JOIN perf_counter_track pct
+          USING(perf_session_id, cpu)
+        JOIN thread
+          USING(utid)
+        JOIN stack_profile_frame spf
+          ON (eac.frame_id = spf.id)
+        ORDER BY ps.ts ASC, eac.depth ASC;
+        """,
+        out=Path('perf_sample_sc.out'))
+
+  def test_annotations(self):
+    return DiffTestBlueprint(
+        trace=DataPath('perf_sample_annotations.pftrace'),
+        query="""
+        select
+          eac.depth, eac.annotation, spm.name as map_name,
+          ifnull(demangle(spf.name), spf.name) as frame_name
+        from experimental_annotated_callstack eac
+          join stack_profile_frame spf on (eac.frame_id = spf.id)
+          join stack_profile_mapping spm on (spf.mapping = spm.id)
+        where eac.start_id = (
+          select spc.id as callsite_id
+          from stack_profile_callsite spc
+          join stack_profile_frame spf on (spc.frame_id = spf.id)
+          where spf.name = "_ZN3art28ResolveFieldWithAccessChecksEPNS_6ThreadEPNS_11ClassLinkerEtPNS_9ArtMethodEbbm")
+        order by depth asc;
+        """,
+        out=Csv("""
+        "depth","annotation","map_name","frame_name"
+        0,"[NULL]","/apex/com.android.runtime/lib64/bionic/libc.so","__libc_init"
+        1,"[NULL]","/system/bin/app_process64","main"
+        2,"[NULL]","/system/lib64/libandroid_runtime.so","android::AndroidRuntime::start(char const*, android::Vector<android::String8> const&, bool)"
+        3,"[NULL]","/system/lib64/libandroid_runtime.so","_JNIEnv::CallStaticVoidMethod(_jclass*, _jmethodID*, ...)"
+        4,"[NULL]","/apex/com.android.art/lib64/libart.so","art::JNI<true>::CallStaticVoidMethodV(_JNIEnv*, _jclass*, _jmethodID*, std::__va_list)"
+        5,"[NULL]","/apex/com.android.art/lib64/libart.so","art::JValue art::InvokeWithVarArgs<_jmethodID*>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jmethodID*, std::__va_list)"
+        6,"[NULL]","/apex/com.android.art/lib64/libart.so","art_quick_invoke_static_stub"
+        7,"aot","/system/framework/arm64/boot-framework.oat","com.android.internal.os.ZygoteInit.main"
+        8,"aot","/system/framework/arm64/boot-framework.oat","com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run"
+        9,"common-frame","/system/framework/arm64/boot.oat","art_jni_trampoline"
+        10,"[NULL]","/apex/com.android.art/lib64/libart.so","art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobjectArray*) (.__uniq.165753521025965369065708152063621506277)"
+        11,"common-frame","/apex/com.android.art/lib64/libart.so","_jobject* art::InvokeMethod<(art::PointerSize)8>(art::ScopedObjectAccessAlreadyRunnable const&, _jobject*, _jobject*, _jobject*, unsigned long)"
+        12,"common-frame","/apex/com.android.art/lib64/libart.so","art_quick_invoke_static_stub"
+        13,"aot","/system/framework/arm64/boot-framework.oat","android.app.ActivityThread.main"
+        14,"aot","/system/framework/arm64/boot-framework.oat","android.os.Looper.loop"
+        15,"aot","/system/framework/arm64/boot-framework.oat","android.os.Looper.loopOnce"
+        16,"aot","/system/framework/arm64/boot-framework.oat","android.os.Handler.dispatchMessage"
+        17,"aot","/system/framework/arm64/boot-framework.oat","android.view.Choreographer$FrameDisplayEventReceiver.run"
+        18,"aot","/system/framework/arm64/boot-framework.oat","android.view.Choreographer.doFrame"
+        19,"aot","/system/framework/arm64/boot-framework.oat","android.view.Choreographer.doCallbacks"
+        20,"aot","/system/framework/arm64/boot-framework.oat","android.view.ViewRootImpl$TraversalRunnable.run"
+        21,"aot","/system/framework/arm64/boot-framework.oat","android.view.ViewRootImpl.doTraversal"
+        22,"aot","/system/framework/arm64/boot-framework.oat","android.view.ViewRootImpl.performTraversals"
+        23,"interp","/system/framework/framework.jar","android.view.ViewRootImpl.notifyDrawStarted"
+        24,"common-frame-interp","/apex/com.android.art/lib64/libart.so","nterp_op_iget_object_slow_path"
+        25,"common-frame-interp","/apex/com.android.art/lib64/libart.so","nterp_get_instance_field_offset"
+        26,"common-frame-interp","/apex/com.android.art/lib64/libart.so","NterpGetInstanceFieldOffset"
+        27,"common-frame","/apex/com.android.art/lib64/libart.so","art::ResolveFieldWithAccessChecks(art::Thread*, art::ClassLinker*, unsigned short, art::ArtMethod*, bool, bool, unsigned long)"
+        """))
+
+  def test_annotations_switch_interpreter(self):
+    return DiffTestBlueprint(
+        trace=Path('perf_sample_switch_interp.textproto'),
+        query="""
+        select
+          eac.depth, eac.annotation, spm.name as map_name,
+          ifnull(demangle(spf.name), spf.name) as frame_name
+        from experimental_annotated_callstack eac
+          join stack_profile_frame spf on (eac.frame_id = spf.id)
+          join stack_profile_mapping spm on (spf.mapping = spm.id)
+        where eac.start_id = (select callsite_id from perf_sample)
+        order by depth asc;
+        """,
+        out=Csv("""
+        "depth","annotation","map_name","frame_name"
+        0,"interp","/example.vdex","com.example.managed.frame"
+        1,"common-frame-interp","/apex/com.android.art/lib64/libart.so","ExecuteSwitchImplAsm"
+        2,"common-frame-interp","/apex/com.android.art/lib64/libart.so","void art::interpreter::ExecuteSwitchImplCpp<false>(art::interpreter::SwitchImplContext*)"
+        3,"common-frame-interp","/apex/com.android.art/lib64/libart.so","bool art::interpreter::DoCall<true>(art::ArtMethod*, art::Thread*, art::ShadowFrame&, art::Instruction const*, unsigned short, bool, art::JValue*)"
+        4,"common-frame","/apex/com.android.art/lib64/libart.so","art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)"
+        """))
diff --git a/test/trace_processor/diff_tests/profiling/tests_heap_graph.py b/test/trace_processor/diff_tests/profiling/tests_heap_graph.py
new file mode 100644
index 0000000..d222ccd
--- /dev/null
+++ b/test/trace_processor/diff_tests/profiling/tests_heap_graph.py
@@ -0,0 +1,390 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class ProfilingHeapGraph(TestSuite):
+
+  def test_heap_graph_flamegraph(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_graph_baseapk.textproto'),
+        query="""
+        SELECT
+          id,
+          depth,
+          name,
+          map_name,
+          count,
+          cumulative_count,
+          size,
+          cumulative_size,
+          parent_id
+        FROM experimental_flamegraph
+        WHERE upid = (SELECT max(upid) FROM heap_graph_object)
+          AND profile_type = 'graph'
+          AND ts = (SELECT max(graph_sample_ts) FROM heap_graph_object)
+        LIMIT 10;
+        """,
+        out=Path('heap_graph_flamegraph.out'))
+
+  def test_heap_graph_object(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_graph_baseapk.textproto'),
+        query="""
+        SELECT o.id,
+               o.type,
+               o.upid,
+               o.graph_sample_ts,
+               o.self_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;
+        """,
+        out=Path('heap_graph_object.out'))
+
+  def test_heap_graph_reference(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_graph_baseapk.textproto'),
+        query="""
+        SELECT * FROM heap_graph_reference;
+        """,
+        out=Path('heap_graph_reference.out'))
+
+  def test_heap_graph_object_2(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_graph_deobfuscate_pkg.textproto'),
+        query="""
+        SELECT o.id,
+               o.type,
+               o.upid,
+               o.graph_sample_ts,
+               o.self_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;
+        """,
+        out=Path('heap_graph_object.out'))
+
+  def test_heap_graph_duplicate_flamegraph(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          process_tree {
+            processes {
+              pid: 2
+              ppid: 1
+              cmdline: "system_server"
+              uid: 1000
+            }
+          }
+        }
+        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
+            types {
+              id: 1
+              class_name: "FactoryProducerDelegateImplActor"
+              location_id: 1
+            }
+            roots {
+              root_type: ROOT_JAVA_FRAME
+              object_ids: 0x01
+              object_ids: 0x01
+            }
+            objects {
+              id: 0x01
+              type_id: 1
+              self_size: 64
+            }
+            continued: false
+            index: 0
+          }
+        }
+        """),
+        query="""
+        SELECT
+          id,
+          depth,
+          name,
+          map_name,
+          count,
+          cumulative_count,
+          size,
+          cumulative_size,
+          parent_id
+        FROM experimental_flamegraph
+        WHERE upid = (SELECT max(upid) FROM heap_graph_object)
+          AND profile_type = 'graph'
+          AND ts = (SELECT max(graph_sample_ts) FROM heap_graph_object)
+        LIMIT 10;
+        """,
+        out=Path('heap_graph_duplicate_flamegraph.out'))
+
+  def test_heap_graph_flamegraph_2(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_graph.textproto'),
+        query="""
+        SELECT
+          id,
+          depth,
+          name,
+          map_name,
+          count,
+          cumulative_count,
+          size,
+          cumulative_size,
+          parent_id
+        FROM experimental_flamegraph
+        WHERE upid = (SELECT max(upid) FROM heap_graph_object)
+          AND profile_type = 'graph'
+          AND ts = (SELECT max(graph_sample_ts) FROM heap_graph_object)
+        LIMIT 10;
+        """,
+        out=Path('heap_graph_flamegraph.out'))
+
+  def test_heap_graph_object_3(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_graph.textproto'),
+        query="""
+        SELECT o.id,
+               o.type,
+               o.upid,
+               o.graph_sample_ts,
+               o.self_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;
+        """,
+        out=Path('heap_graph_object.out'))
+
+  def test_heap_graph_reference_2(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_graph.textproto'),
+        query="""
+        SELECT * FROM heap_graph_reference;
+        """,
+        out=Path('heap_graph_reference.out'))
+
+  def test_heap_graph_two_locations(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_graph_two_locations.textproto'),
+        query="""
+        SELECT o.id,
+               o.type,
+               o.upid,
+               o.graph_sample_ts,
+               o.self_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;
+        """,
+        out=Path('heap_graph_two_locations.out'))
+
+  def test_heap_graph_object_4(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_graph_legacy.textproto'),
+        query="""
+        SELECT o.id,
+               o.type,
+               o.upid,
+               o.graph_sample_ts,
+               o.self_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;
+        """,
+        out=Path('heap_graph_object.out'))
+
+  def test_heap_graph_reference_3(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_graph_legacy.textproto'),
+        query="""
+        SELECT * FROM heap_graph_reference;
+        """,
+        out=Path('heap_graph_reference.out'))
+
+  def test_heap_graph_interleaved_object(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_graph_interleaved.textproto'),
+        query="""
+        SELECT o.id,
+               o.type,
+               o.upid,
+               o.graph_sample_ts,
+               o.self_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;
+        """,
+        out=Path('heap_graph_interleaved_object.out'))
+
+  def test_heap_graph_interleaved_reference(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_graph_interleaved.textproto'),
+        query="""
+        SELECT * FROM heap_graph_reference;
+        """,
+        out=Path('heap_graph_interleaved_reference.out'))
+
+  def test_heap_graph_flamegraph_system_server_heap_graph(self):
+    return DiffTestBlueprint(
+        trace=DataPath('system-server-heap-graph-new.pftrace'),
+        query="""
+        SELECT
+          id,
+          depth,
+          name,
+          map_name,
+          count,
+          cumulative_count,
+          size,
+          cumulative_size,
+          parent_id
+        FROM experimental_flamegraph
+        WHERE upid = (SELECT max(upid) FROM heap_graph_object)
+          AND profile_type = 'graph'
+          AND ts = (SELECT max(graph_sample_ts) FROM heap_graph_object)
+        LIMIT 10;
+        """,
+        out=Path('heap_graph_flamegraph_system-server-heap-graph.out'))
+
+  def test_heap_profile_flamegraph_system_server_native_profile(self):
+    return DiffTestBlueprint(
+        trace=DataPath('system-server-native-profile'),
+        query="""
+        SELECT * FROM experimental_flamegraph
+        WHERE ts = 605908369259172
+          AND upid = 1
+          AND profile_type = 'native'
+        LIMIT 10;
+        """,
+        out=Path('heap_profile_flamegraph_system-server-native-profile.out'))
+
+  def test_heap_profile_tracker_new_stack(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_profile_tracker_new_stack.textproto'),
+        query="""
+        SELECT * FROM heap_profile_allocation;
+        """,
+        out=Csv("""
+        "id","type","ts","upid","heap_name","callsite_id","count","size"
+        0,"heap_profile_allocation",0,0,"malloc",0,1,1
+        1,"heap_profile_allocation",0,0,"malloc",0,-1,-1
+        2,"heap_profile_allocation",1,0,"malloc",0,1,1
+        3,"heap_profile_allocation",1,0,"malloc",0,-1,-1
+        """))
+
+  def test_heap_profile_tracker_twoheaps(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_profile_tracker_twoheaps.textproto'),
+        query="""
+        SELECT * FROM heap_profile_allocation;
+        """,
+        out=Csv("""
+        "id","type","ts","upid","heap_name","callsite_id","count","size"
+        0,"heap_profile_allocation",0,0,"malloc",0,1,1
+        1,"heap_profile_allocation",0,0,"malloc",0,-1,-1
+        2,"heap_profile_allocation",0,0,"custom",0,1,1
+        3,"heap_profile_allocation",0,0,"custom",0,-1,-1
+        """))
+
+  def test_heap_graph_flamegraph_focused(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_graph_branching.textproto'),
+        query="""
+        SELECT
+          id,
+          depth,
+          name,
+          count,
+          cumulative_count,
+          size,
+          cumulative_size,
+          parent_id
+        FROM experimental_flamegraph
+        WHERE upid = (SELECT max(upid) FROM heap_graph_object)
+          AND profile_type = 'graph'
+          AND ts = (SELECT max(graph_sample_ts) FROM heap_graph_object)
+          AND focus_str = 'left'
+        LIMIT 10;
+        """,
+        out=Path('heap_graph_flamegraph_focused.out'))
+
+  def test_heap_graph_superclass(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_graph_superclass.textproto'),
+        query="""
+        SELECT c.id, c.superclass_id, c.name, s.name AS superclass_name, c.location
+        FROM heap_graph_class c LEFT JOIN heap_graph_class s ON c.superclass_id = s.id;
+        """,
+        out=Csv("""
+        "id","superclass_id","name","superclass_name","location"
+        0,"[NULL]","java.lang.Class<java.lang.Object>","[NULL]","l1"
+        1,"[NULL]","java.lang.Class<MySuperClass>","[NULL]","l1"
+        2,"[NULL]","java.lang.Class<MyChildClass>","[NULL]","l2"
+        3,"[NULL]","java.lang.Object","[NULL]","l1"
+        4,3,"MySuperClass","java.lang.Object","l1"
+        5,4,"MyChildClass","MySuperClass","l2"
+        """))
+
+  def test_heap_graph_native_size(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_graph_native_size.textproto'),
+        query="""
+        SELECT c.name AS type_name,
+               o.native_size
+        FROM heap_graph_object o JOIN heap_graph_class c ON o.type_id = c.id
+        WHERE o.root_type = "ROOT_JAVA_FRAME";
+        """,
+        out=Csv("""
+        "type_name","native_size"
+        "android.graphics.Bitmap",123456
+        "android.os.BinderProxy",0
+        """))
diff --git a/test/trace_processor/diff_tests/profiling/tests_heap_profiling.py b/test/trace_processor/diff_tests/profiling/tests_heap_profiling.py
new file mode 100644
index 0000000..483d045
--- /dev/null
+++ b/test/trace_processor/diff_tests/profiling/tests_heap_profiling.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class ProfilingHeapProfiling(TestSuite):
+
+  def test_heap_profile_jit(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_profile_jit.textproto'),
+        query="""
+        SELECT name, mapping, rel_pc FROM stack_profile_frame ORDER BY name;
+        """,
+        out=Csv("""
+        "name","mapping","rel_pc"
+        "java_frame_1",0,4096
+        "java_frame_2",0,4096
+        """))
+
+  def test_heap_profile_deobfuscate(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_profile_deobfuscate.textproto'),
+        query=Path('heap_profile_deobfuscate_test.sql'),
+        out=Csv("""
+        "deobfuscated_name","mapping","rel_pc"
+        "Bar.function1",0,4096
+        """))
+
+  def test_heap_profile_deobfuscate_2(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_profile_deobfuscate_memfd.textproto'),
+        query=Path('heap_profile_deobfuscate_test.sql'),
+        out=Csv("""
+        "deobfuscated_name","mapping","rel_pc"
+        "Bar.function1",0,4096
+        """))
+
+  def test_heap_profile_dump_max_legacy(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_profile_dump_max_legacy.textproto'),
+        query="""
+        SELECT * FROM heap_profile_allocation;
+        """,
+        out=Csv("""
+        "id","type","ts","upid","heap_name","callsite_id","count","size"
+        0,"heap_profile_allocation",-10,2,"malloc",2,0,1000
+        1,"heap_profile_allocation",-10,2,"malloc",3,0,90
+        """))
+
+  def test_heap_profile_dump_max(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_profile_dump_max.textproto'),
+        query="""
+        SELECT * FROM heap_profile_allocation;
+        """,
+        out=Csv("""
+        "id","type","ts","upid","heap_name","callsite_id","count","size"
+        0,"heap_profile_allocation",-10,2,"malloc",2,6,1000
+        1,"heap_profile_allocation",-10,2,"malloc",3,1,90
+        """))
diff --git a/test/trace_processor/diff_tests/profiling/tests_llvm_symbolizer.py b/test/trace_processor/diff_tests/profiling/tests_llvm_symbolizer.py
new file mode 100644
index 0000000..0733eb5
--- /dev/null
+++ b/test/trace_processor/diff_tests/profiling/tests_llvm_symbolizer.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class ProfilingLlvmSymbolizer(TestSuite):
+  # this uses llvm-symbolizer to test the offline symbolization built into
+  def test_stack_profile_symbols(self):
+    return DiffTestBlueprint(
+        trace=DataPath('heapprofd_standalone_client_example-trace'),
+        query="""
+        SELECT name, source_file, line_number FROM stack_profile_symbol;
+        """,
+        out=Path('stack_profile_symbols.out'))
+
+  def test_callstack_sampling_flamegraph(self):
+    return DiffTestBlueprint(
+        trace=DataPath('callstack_sampling.pftrace'),
+        query="""
+        SELECT ef.*
+        FROM experimental_flamegraph ef
+        JOIN process USING (upid)
+        WHERE pid = 1728
+          AND profile_type = 'perf'
+          AND ts <= 7689491063351
+        LIMIT 10;
+        """,
+        out=Path('callstack_sampling_flamegraph.out'))
+
+  def test_callstack_sampling_flamegraph_multi_process(self):
+    return DiffTestBlueprint(
+        trace=DataPath('callstack_sampling.pftrace'),
+        query="""
+        SELECT count(*) AS count, 'BothProcesses' AS description
+        FROM experimental_flamegraph
+        WHERE
+          upid_group = (
+            SELECT group_concat(DISTINCT upid)
+            FROM perf_sample JOIN thread t USING (utid) JOIN process p USING (upid)
+          )
+          AND profile_type = 'perf'
+          AND ts <= 7689491063351
+          AND size > 0
+        UNION ALL
+        SELECT count(*) AS count, 'FirstProcess' AS description
+        FROM experimental_flamegraph
+        JOIN process USING (upid)
+        WHERE pid = 1728
+          AND profile_type = 'perf'
+          AND ts <= 7689491063351
+          AND size > 0
+        UNION ALL
+        SELECT count(*) AS count, 'SecondProcess' AS description
+        FROM experimental_flamegraph
+        JOIN process USING (upid)
+        WHERE pid = 703
+          AND profile_type = 'perf'
+          AND ts <= 7689491063351
+          AND size > 0;
+        """,
+        out=Csv("""
+        "count","description"
+        658,"BothProcesses"
+        483,"FirstProcess"
+        175,"SecondProcess"
+        """))
+
+  def test_no_build_id(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_profile_data_local_tmp.textproto'),
+        query="""
+        SELECT value FROM stats WHERE name = 'symbolization_tmp_build_id_not_found';
+        """,
+        out=Csv("""
+        "value"
+        1
+        """))
diff --git a/test/trace_processor/diff_tests/profiling/tests_metrics.py b/test/trace_processor/diff_tests/profiling/tests_metrics.py
new file mode 100644
index 0000000..cb210cf
--- /dev/null
+++ b/test/trace_processor/diff_tests/profiling/tests_metrics.py
@@ -0,0 +1,109 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class ProfilingMetrics(TestSuite):
+
+  def test_unsymbolized_frames(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_profile_no_symbols.textproto'),
+        query=Metric('unsymbolized_frames'),
+        out=TextProto(r"""
+        unsymbolized_frames {
+          frames {
+            module: "/liblib.so"
+            build_id: "6275696c642d6964"
+            address: 4096
+            google_lookup_id: "6275696c642d6964"
+          }
+          frames {
+            module: "/liblib.so"
+            build_id: "6275696c642d6964"
+            address: 8192
+            google_lookup_id: "6275696c642d6964"
+          }
+          frames {
+            module: "/libmonochrome_64.so"
+            build_id: "7f0715c286f8b16c10e4ad349cda3b9b56c7a773"
+            address: 4096
+            google_lookup_id: "c215077ff8866cb110e4ad349cda3b9b0"
+          }
+          frames {
+            module: "/libmonochrome_64.so"
+            build_id: "7f0715c286f8b16c10e4ad349cda3b9b56c7a773"
+            address: 8192
+            google_lookup_id: "c215077ff8866cb110e4ad349cda3b9b0"
+          }
+        }
+        """))
+
+  def test_simpleperf_event(self):
+    return DiffTestBlueprint(
+        trace=Path('simpleperf_event.py'),
+        query=Metric('android_simpleperf'),
+        out=Path('simpleperf_event.out'))
+
+  def test_java_heap_stats(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_graph.textproto'),
+        query=Metric('java_heap_stats'),
+        out=TextProto(r"""
+        java_heap_stats {
+          instance_stats {
+            upid: 2
+            process {
+              name: "system_server"
+              uid: 1000
+            }
+            samples {
+              ts: 10
+              heap_size: 1760
+              heap_native_size: 0
+              reachable_heap_size: 352
+              reachable_heap_native_size: 0
+              obj_count: 6
+              reachable_obj_count: 3
+              anon_rss_and_swap_size: 4096000
+              roots {
+                root_type: "ROOT_JAVA_FRAME"
+                type_name: "DeobfuscatedA[]"
+                obj_count: 1
+              }
+              roots {
+                root_type: "ROOT_JAVA_FRAME"
+                type_name: "FactoryProducerDelegateImplActor"
+                obj_count: 1
+              }
+            }
+          }
+        }
+        """))
+
+  def test_heap_stats_closest_proc(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_graph_closest_proc.textproto'),
+        query=Metric('java_heap_stats'),
+        out=Path('heap_stats_closest_proc.out'))
+
+  def test_java_heap_histogram(self):
+    return DiffTestBlueprint(
+        trace=Path('heap_graph.textproto'),
+        query=Metric('java_heap_histogram'),
+        out=Path('java_heap_histogram.out'))
diff --git a/test/trace_processor/scheduler/sched_cpu_util_cfs.textproto b/test/trace_processor/diff_tests/scheduler/sched_cpu_util_cfs.textproto
similarity index 100%
rename from test/trace_processor/scheduler/sched_cpu_util_cfs.textproto
rename to test/trace_processor/diff_tests/scheduler/sched_cpu_util_cfs.textproto
diff --git a/test/trace_processor/diff_tests/scheduler/sched_cpu_util_cfs_test.sql b/test/trace_processor/diff_tests/scheduler/sched_cpu_util_cfs_test.sql
new file mode 100644
index 0000000..52e35bd
--- /dev/null
+++ b/test/trace_processor/diff_tests/scheduler/sched_cpu_util_cfs_test.sql
@@ -0,0 +1,12 @@
+SELECT
+  t.name,
+  c.ts,
+  c.value
+FROM
+  counter AS c
+LEFT JOIN
+  counter_track AS t
+  ON c.track_id = t.id
+WHERE
+  name GLOB "Cpu ? Cap" OR name GLOB "Cpu ? Util" OR name GLOB "Cpu ? Nr Running"
+ORDER BY ts;
diff --git a/test/trace_processor/diff_tests/scheduler/tests.py b/test/trace_processor/diff_tests/scheduler/tests.py
new file mode 100644
index 0000000..b935c6a
--- /dev/null
+++ b/test/trace_processor/diff_tests/scheduler/tests.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class Scheduler(TestSuite):
+  # Scheduler
+  def test_sched_cpu_util_cfs(self):
+    return DiffTestBlueprint(
+        trace=Path('sched_cpu_util_cfs.textproto'),
+        query=Path('sched_cpu_util_cfs_test.sql'),
+        out=Csv("""
+        "name","ts","value"
+        "Cpu 6 Util",10000,1.000000
+        "Cpu 6 Cap",10000,1004.000000
+        "Cpu 6 Nr Running",10000,0.000000
+        "Cpu 7 Util",11000,1.000000
+        "Cpu 7 Cap",11000,1007.000000
+        "Cpu 7 Nr Running",11000,0.000000
+        "Cpu 4 Util",12000,43.000000
+        "Cpu 4 Cap",12000,760.000000
+        "Cpu 4 Nr Running",12000,0.000000
+        "Cpu 5 Util",13000,125.000000
+        "Cpu 5 Cap",13000,757.000000
+        "Cpu 5 Nr Running",13000,1.000000
+        """))
diff --git a/test/trace_processor/smoke/proxy_power.out b/test/trace_processor/diff_tests/smoke/proxy_power.out
similarity index 100%
rename from test/trace_processor/smoke/proxy_power.out
rename to test/trace_processor/diff_tests/smoke/proxy_power.out
diff --git a/test/trace_processor/diff_tests/smoke/tests.py b/test/trace_processor/diff_tests/smoke/tests.py
new file mode 100644
index 0000000..be0836f
--- /dev/null
+++ b/test/trace_processor/diff_tests/smoke/tests.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class Smoke(TestSuite):
+  # Contains smoke tests which test the most fundamentally important features
+  # trace processor  Note: new tests here should only be added by the Perfetto
+  # Compresesed traces
+  def test_compressed_smoke(self):
+    return DiffTestBlueprint(
+        trace=DataPath('compressed.pb'),
+        query="""
+        SELECT
+          ts,
+          cpu,
+          dur,
+          end_state,
+          priority,
+          tid
+        FROM sched
+        JOIN thread USING(utid)
+        ORDER BY ts
+        LIMIT 10;
+        """,
+        out=Csv("""
+        "ts","cpu","dur","end_state","priority","tid"
+        170601497673450,2,53646,"DK",120,6790
+        170601497691210,7,22917,"R",120,0
+        170601497714127,7,29167,"D",120,6732
+        170601497727096,2,55156,"S",120,62
+        170601497743294,7,862656,"R",120,0
+        170601497766106,3,13594,"S",120,8
+        170601497779700,3,31094,"D",120,6790
+        170601497782252,2,875313,"R",120,0
+        170601497810794,3,824635,"R",120,0
+        170601498605950,7,158333,"D",120,6732
+        """))
diff --git a/test/trace_processor/diff_tests/smoke/tests_compute_metrics.py b/test/trace_processor/diff_tests/smoke/tests_compute_metrics.py
new file mode 100644
index 0000000..e6a3cd5
--- /dev/null
+++ b/test/trace_processor/diff_tests/smoke/tests_compute_metrics.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class SmokeComputeMetrics(TestSuite):
+  # Contains smoke tests which test the most fundamentally important features
+  # trace processor  Note: new tests here should only be added by the Perfetto
+  # Compute CPU time metric testing several core tables.
+  def test_thread_cpu_time_example_android_trace_30s(self):
+    return DiffTestBlueprint(
+        trace=DataPath('example_android_trace_30s.pb'),
+        query="""
+        SELECT
+          tid,
+          pid,
+          thread.name AS threadName,
+          process.name AS processName,
+          total_dur AS totalDur
+        FROM
+          thread
+        LEFT JOIN process USING(upid)
+        LEFT JOIN
+          (SELECT upid, sum(dur) AS total_dur
+            FROM sched JOIN thread USING(utid)
+            WHERE dur != -1
+            GROUP BY upid
+          ) USING(upid)
+        WHERE utid != 0
+        ORDER BY total_dur DESC, pid, tid;
+        """,
+        out=Path('thread_cpu_time_example_android_trace_30s.out'))
+
+  # Compute power proxy metric
+  def test_proxy_power(self):
+    return DiffTestBlueprint(
+        trace=DataPath('cpu_counters.pb'),
+        query="""
+        SELECT RUN_METRIC('android/android_proxy_power.sql');
+
+        DROP VIEW device;
+
+        CREATE TABLE device (name STRING);
+
+        INSERT INTO device VALUES ('walleye');
+
+        SELECT
+          tid,
+          SUM(dur * COALESCE(power_ma, 0) / 1e9) AS power_mas
+        FROM power_per_thread
+        JOIN thread USING (utid)
+        GROUP BY utid
+        ORDER BY power_mas DESC
+        LIMIT 10;
+        """,
+        out=Path('proxy_power.out'))
diff --git a/test/trace_processor/diff_tests/smoke/tests_json.py b/test/trace_processor/diff_tests/smoke/tests_json.py
new file mode 100644
index 0000000..7d5593e
--- /dev/null
+++ b/test/trace_processor/diff_tests/smoke/tests_json.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class SmokeJson(TestSuite):
+  # Contains smoke tests which test the most fundamentally important features
+  # trace processor  Note: new tests here should only be added by the Perfetto
+  # JSON trace parsing
+  def test_sfgate_smoke(self):
+    return DiffTestBlueprint(
+        trace=DataPath('sfgate.json'),
+        query="""
+        SELECT
+          ts,
+          cpu,
+          dur,
+          end_state,
+          priority,
+          tid
+        FROM sched
+        JOIN thread USING(utid)
+        ORDER BY ts
+        LIMIT 10;
+        """,
+        out=Csv("""
+        "ts","cpu","dur","end_state","priority","tid"
+        """))
+
+  def test_sfgate_smoke_slices(self):
+    return DiffTestBlueprint(
+        trace=DataPath('sfgate.json'),
+        query="""
+        SELECT track.type AS type, depth, count(*) AS count
+        FROM slice
+        JOIN track ON slice.track_id = track.id
+        GROUP BY track.type, depth
+        ORDER BY track.type, depth;
+        """,
+        out=Csv("""
+        "type","depth","count"
+        "thread_track",0,16888
+        "thread_track",1,19447
+        "thread_track",2,5816
+        "thread_track",3,829
+        "thread_track",4,191
+        "thread_track",5,94
+        "thread_track",6,57
+        "thread_track",7,19
+        "thread_track",8,14
+        "thread_track",9,2
+        """))
diff --git a/test/trace_processor/diff_tests/smoke/tests_sched_events.py b/test/trace_processor/diff_tests/smoke/tests_sched_events.py
new file mode 100644
index 0000000..86e39fd
--- /dev/null
+++ b/test/trace_processor/diff_tests/smoke/tests_sched_events.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class SmokeSchedEvents(TestSuite):
+  # Contains smoke tests which test the most fundamentally important features
+  # trace processor  Note: new tests here should only be added by the Perfetto
+  # Sched events
+  def test_android_sched_and_ps_smoke(self):
+    return DiffTestBlueprint(
+        trace=DataPath('android_sched_and_ps.pb'),
+        query="""
+        SELECT
+          ts,
+          cpu,
+          dur,
+          end_state,
+          priority,
+          tid
+        FROM sched
+        JOIN thread USING(utid)
+        ORDER BY ts
+        LIMIT 10;
+        """,
+        out=Csv("""
+        "ts","cpu","dur","end_state","priority","tid"
+        81473010031230,2,78021,"S",120,26204
+        81473010109251,2,12500,"R",120,0
+        81473010121751,2,58021,"S",120,26205
+        81473010179772,2,24114,"R",120,0
+        81473010203886,2,30834,"S",120,26206
+        81473010234720,2,43802,"R",120,0
+        81473010278522,2,29948,"S",120,26207
+        81473010308470,2,44322,"R",120,0
+        81473010341386,1,158854,"S",116,23912
+        81473010352792,2,32917,"S",120,26208
+        """))
+
+  # Sched events from sythetic trace
+  def test_synth_1_smoke(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query="""
+        SELECT
+          ts,
+          cpu,
+          dur,
+          end_state,
+          priority,
+          tid
+        FROM sched
+        JOIN thread USING(utid)
+        ORDER BY ts
+        LIMIT 10;
+        """,
+        out=Csv("""
+        "ts","cpu","dur","end_state","priority","tid"
+        1,0,99,"R",0,3
+        50,1,70,"R",0,1
+        100,0,15,"R",0,2
+        115,0,-1,"[NULL]",0,3
+        120,1,50,"R",0,2
+        170,1,80,"R",0,0
+        250,1,140,"R",0,2
+        390,1,-1,"[NULL]",0,4
+        """))
diff --git a/test/trace_processor/smoke/thread_cpu_time_example_android_trace_30s.out b/test/trace_processor/diff_tests/smoke/thread_cpu_time_example_android_trace_30s.out
similarity index 100%
rename from test/trace_processor/smoke/thread_cpu_time_example_android_trace_30s.out
rename to test/trace_processor/diff_tests/smoke/thread_cpu_time_example_android_trace_30s.out
diff --git a/test/trace_processor/span_join/android_sched_and_ps_slice_span_join_b118665515.out b/test/trace_processor/diff_tests/span_join/android_sched_and_ps_slice_span_join_b118665515.out
similarity index 100%
rename from test/trace_processor/span_join/android_sched_and_ps_slice_span_join_b118665515.out
rename to test/trace_processor/diff_tests/span_join/android_sched_and_ps_slice_span_join_b118665515.out
diff --git a/test/trace_processor/diff_tests/span_join/slice_span_join_b118665515_test.sql b/test/trace_processor/diff_tests/span_join/slice_span_join_b118665515_test.sql
new file mode 100644
index 0000000..aaf9c6d
--- /dev/null
+++ b/test/trace_processor/diff_tests/span_join/slice_span_join_b118665515_test.sql
@@ -0,0 +1,22 @@
+--
+-- 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.
+--
+CREATE VIRTUAL TABLE window_8 USING window;
+
+CREATE VIRTUAL TABLE span_8 USING span_join(sched PARTITIONED cpu, window_8);
+
+UPDATE window_8 SET window_start = 81473010031230, window_dur = 19684693341, quantum = 10000000 WHERE rowid = 0;
+
+SELECT quantum_ts AS bucket, sum(dur) / cast(10000000 AS float) AS utilization FROM span_8 WHERE cpu = 7 AND utid != 0 GROUP BY quantum_ts;
diff --git a/test/trace_processor/diff_tests/span_join/span_join_unordered_cols_reverse_test.sql b/test/trace_processor/diff_tests/span_join/span_join_unordered_cols_reverse_test.sql
new file mode 100644
index 0000000..2c67277
--- /dev/null
+++ b/test/trace_processor/diff_tests/span_join/span_join_unordered_cols_reverse_test.sql
@@ -0,0 +1,51 @@
+--
+-- 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.
+--
+CREATE TABLE t1(
+  a1 STRING,
+  a2 BIGINT,
+  dur BIGINT,
+  a3 BIGINT,
+  ts BIGINT PRIMARY KEY
+) WITHOUT ROWID;
+
+INSERT INTO t1(a1, a2, dur, a3, ts)
+VALUES
+("A", 1, 10, 100, 0),
+("B", 2, 90, 101, 10),
+("C", 3, 1, 102, 100);
+
+CREATE TABLE t2(
+  b1 STRING,
+  ts BIGINT,
+  b2 BIGINT,
+  part BIGINT,
+  dur BIGINT,
+  b3 BIGINT,
+  PRIMARY KEY (part, ts)
+) WITHOUT ROWID;
+
+INSERT INTO t2(b1, ts, b2, part, dur, b3)
+VALUES
+("A", 10, 10, 0, 90, 100),
+("B", 100, 90, 0, 10, 200),
+("C", 110, 1, 0, 5, 300),
+("A", 5, 10, 1, 45, 100),
+("B", 50, 90, 1, 40, 200),
+("C", 90, 1, 1, 100, 300);
+
+CREATE VIRTUAL TABLE sp USING span_join(t2 PARTITIONED part, t1);
+
+SELECT ts, dur, part, b1, b2, b3, a1, a2, a3 FROM sp;
diff --git a/test/trace_processor/diff_tests/span_join/span_join_unordered_cols_test.sql b/test/trace_processor/diff_tests/span_join/span_join_unordered_cols_test.sql
new file mode 100644
index 0000000..ac7d35f
--- /dev/null
+++ b/test/trace_processor/diff_tests/span_join/span_join_unordered_cols_test.sql
@@ -0,0 +1,51 @@
+--
+-- 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.
+--
+CREATE TABLE t1(
+  a1 STRING,
+  a2 BIGINT,
+  dur BIGINT,
+  a3 BIGINT,
+  ts BIGINT PRIMARY KEY
+) WITHOUT ROWID;
+
+INSERT INTO t1(a1, a2, dur, a3, ts)
+VALUES
+("A", 1, 10, 100, 0),
+("B", 2, 90, 101, 10),
+("C", 3, 1, 102, 100);
+
+CREATE TABLE t2(
+  b1 STRING,
+  ts BIGINT,
+  b2 BIGINT,
+  part BIGINT,
+  dur BIGINT,
+  b3 BIGINT,
+  PRIMARY KEY (part, ts)
+) WITHOUT ROWID;
+
+INSERT INTO t2(b1, ts, b2, part, dur, b3)
+VALUES
+("A", 10, 10, 0, 90, 100),
+("B", 100, 90, 0, 10, 200),
+("C", 110, 1, 0, 5, 300),
+("A", 5, 10, 1, 45, 100),
+("B", 50, 90, 1, 40, 200),
+("C", 90, 1, 1, 100, 300);
+
+CREATE VIRTUAL TABLE sp USING span_join(t1, t2 PARTITIONED part);
+
+SELECT ts, dur, part, b1, b2, b3, a1, a2, a3 FROM sp;
diff --git a/test/trace_processor/diff_tests/span_join/span_join_zero_negative_dur_test.sql b/test/trace_processor/diff_tests/span_join/span_join_zero_negative_dur_test.sql
new file mode 100644
index 0000000..2e25ec9
--- /dev/null
+++ b/test/trace_processor/diff_tests/span_join/span_join_zero_negative_dur_test.sql
@@ -0,0 +1,44 @@
+--
+-- 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.
+--
+CREATE TABLE t1(
+  ts BIGINT,
+  dur BIGINT,
+  part BIGINT,
+  PRIMARY KEY (part, ts, dur)
+) WITHOUT ROWID;
+
+INSERT INTO t1(ts, dur, part)
+VALUES
+(1, 0, 0),
+(5, -1, 0),
+(2, 0, 1);
+
+CREATE TABLE t2(
+  ts BIGINT,
+  dur BIGINT,
+  part BIGINT,
+  PRIMARY KEY (part, ts, dur)
+) WITHOUT ROWID;
+
+INSERT INTO t2(ts, dur, part)
+VALUES
+(1, 2, 0),
+(5, 0, 0),
+(1, 1, 1);
+
+CREATE VIRTUAL TABLE sp USING span_outer_join(t1 PARTITIONED part, t2 PARTITIONED part);
+
+SELECT ts, dur, part FROM sp;
diff --git a/test/trace_processor/span_join/span_left_join.out b/test/trace_processor/diff_tests/span_join/span_left_join.out
similarity index 100%
rename from test/trace_processor/span_join/span_left_join.out
rename to test/trace_processor/diff_tests/span_join/span_left_join.out
diff --git a/test/trace_processor/span_join/span_left_join_left_partitioned.out b/test/trace_processor/diff_tests/span_join/span_left_join_left_partitioned.out
similarity index 100%
rename from test/trace_processor/span_join/span_left_join_left_partitioned.out
rename to test/trace_processor/diff_tests/span_join/span_left_join_left_partitioned.out
diff --git a/test/trace_processor/diff_tests/span_join/span_left_join_left_partitioned_test.sql b/test/trace_processor/diff_tests/span_join/span_left_join_left_partitioned_test.sql
new file mode 100644
index 0000000..7297d03
--- /dev/null
+++ b/test/trace_processor/diff_tests/span_join/span_left_join_left_partitioned_test.sql
@@ -0,0 +1,49 @@
+--
+-- 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.
+--
+CREATE TABLE t1(
+  ts BIGINT,
+  dur BIGINT,
+  part BIGINT,
+  a BIGINT,
+  PRIMARY KEY (part, ts)
+) WITHOUT ROWID;
+
+CREATE TABLE t2(
+  ts BIGINT,
+  dur BIGINT,
+  b BIGINT,
+  PRIMARY KEY (ts)
+) WITHOUT ROWID;
+
+-- Then insert some rows into t1 in part 1, 3, 4 and 5.
+INSERT INTO t1(ts, dur, part, a)
+VALUES
+(100, 400, 1, 111),
+(500, 150, 1, 111),
+(500, 50, 3, 222),
+(500, 100, 4, 333),
+(0, 1000, 5, 444);
+
+-- Then insert some rows into t2.
+INSERT INTO t2(ts, dur, b)
+VALUES
+(50, 200, 111),
+(300, 100, 222),
+(400, 250, 333);
+
+CREATE VIRTUAL TABLE sp USING span_left_join(t1 PARTITIONED part, t2);
+
+SELECT * FROM sp;
diff --git a/test/trace_processor/span_join/span_left_join_left_unpartitioned.out b/test/trace_processor/diff_tests/span_join/span_left_join_left_unpartitioned.out
similarity index 100%
rename from test/trace_processor/span_join/span_left_join_left_unpartitioned.out
rename to test/trace_processor/diff_tests/span_join/span_left_join_left_unpartitioned.out
diff --git a/test/trace_processor/diff_tests/span_join/span_left_join_left_unpartitioned_test.sql b/test/trace_processor/diff_tests/span_join/span_left_join_left_unpartitioned_test.sql
new file mode 100644
index 0000000..8aa1385
--- /dev/null
+++ b/test/trace_processor/diff_tests/span_join/span_left_join_left_unpartitioned_test.sql
@@ -0,0 +1,75 @@
+--
+-- 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.
+--
+CREATE TABLE t1(
+  ts BIGINT,
+  dur BIGINT,
+  a BIGINT,
+  PRIMARY KEY (ts)
+) WITHOUT ROWID;
+
+CREATE TABLE t2(
+  ts BIGINT,
+  dur BIGINT,
+  b BIGINT,
+  part BIGINT,
+  PRIMARY KEY (part, ts)
+) WITHOUT ROWID;
+
+-- Insert some rows into t2 which are in part 0 and 1 but before t1's rows.
+INSERT INTO t2(ts, dur, part, b)
+VALUES
+(0, 25, 0, 111),
+(50, 50, 0, 222),
+(0, 40, 1, 333);
+
+-- Then insert some rows into t1 in part 1, 3, 4 and 5.
+INSERT INTO t1(ts, dur, a)
+VALUES
+(100, 400, 111),
+(500, 50, 222),
+(600, 100, 333),
+(900, 100, 444);
+
+-- Insert a row into t2 which should be split up by t1's first row.
+INSERT INTO t2(ts, dur, part, b) VALUES (50, 200, 1, 444);
+
+-- Insert a row into t2 should should be completely covered by t1's first row.
+INSERT INTO t2(ts, dur, part, b) VALUES (300, 100, 1, 555);
+
+-- Insert a row into t2 which should span between t1's first and second rows.
+INSERT INTO t2(ts, dur, part, b) VALUES (400, 250, 1, 666);
+
+-- Insert a row into t2 in partition 2.
+INSERT INTO t2(ts, dur, part, b) VALUES (100, 1000, 2, 777);
+
+-- Insert a row into t2 before t1's first row in partition 4.
+INSERT INTO t2(ts, dur, part, b) VALUES (50, 50, 4, 888);
+
+-- Insert a row into t2 which perfectly matches the second row in partition 4.
+INSERT INTO t2(ts, dur, part, b) VALUES (500, 50, 4, 999);
+
+-- Insert a row into t2 which intersects the first row of partition 5.
+INSERT INTO t2(ts, dur, part, b) VALUES (50, 75, 5, 1111);
+
+-- Insert a row into t2 which intersects the third row of partition 5.
+INSERT INTO t2(ts, dur, part, b) VALUES (525, 75, 5, 2222);
+
+-- Insert a row into t2 which misses everything in partition 6.
+INSERT INTO t2(ts, dur, part, b) VALUES (0, 100, 6, 2222);
+
+CREATE VIRTUAL TABLE sp USING span_left_join(t1, t2 PARTITIONED part);
+
+SELECT * FROM sp;
diff --git a/test/trace_processor/diff_tests/span_join/span_left_join_test.sql b/test/trace_processor/diff_tests/span_join/span_left_join_test.sql
new file mode 100644
index 0000000..f8d9119
--- /dev/null
+++ b/test/trace_processor/diff_tests/span_join/span_left_join_test.sql
@@ -0,0 +1,78 @@
+--
+-- 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.
+--
+CREATE TABLE t1(
+  ts BIGINT,
+  dur BIGINT,
+  part BIGINT,
+  a BIGINT,
+  PRIMARY KEY (part, ts)
+) WITHOUT ROWID;
+
+CREATE TABLE t2(
+  ts BIGINT,
+  dur BIGINT,
+  part BIGINT,
+  b BIGINT,
+  PRIMARY KEY (part, ts)
+) WITHOUT ROWID;
+
+-- Insert some rows into t2 which are in part 0 and 1 but before t1's rows.
+INSERT INTO t2(ts, dur, part, b)
+VALUES
+(0, 100, 0, 111),
+(100, 200, 0, 222),
+(0, 50, 1, 333);
+
+-- Then insert some rows into t1 in part 1, 3, 4 and 5.
+INSERT INTO t1(ts, dur, part, a)
+VALUES
+(100, 400, 1, 111),
+(500, 50, 1, 222),
+(600, 100, 1, 333),
+(500, 100, 3, 444),
+(100, 100, 4, 555),
+(200, 50, 4, 666),
+(250, 50, 4, 777),
+(100, 100, 5, 888);
+
+-- Insert a row into t2 which should be split up by t1's first row.
+INSERT INTO t2(ts, dur, part, b) VALUES (50, 200, 1, 444);
+
+-- Insert a row into t2 should should be completely covered by t1's first row.
+INSERT INTO t2(ts, dur, part, b) VALUES (300, 100, 1, 555);
+
+-- Insert a row into t2 which should span between t1's first and second rows.
+INSERT INTO t2(ts, dur, part, b) VALUES (400, 250, 1, 666);
+
+-- Insert a row into t2 in partition 2.
+INSERT INTO t2(ts, dur, part, b) VALUES (100, 1000, 2, 777);
+
+-- Insert a row into t2 before t1's first row in partition 4.
+INSERT INTO t2(ts, dur, part, b) VALUES (50, 50, 4, 888);
+
+-- Insert a row into t2 which perfectly matches the second row in partition 4.
+INSERT INTO t2(ts, dur, part, b) VALUES (200, 50, 4, 999);
+
+-- Insert a row into t2 which intersects the first row of partition 5.
+INSERT INTO t2(ts, dur, part, b) VALUES (125, 50, 5, 1111);
+
+-- Insert a row into t2 which intersects the first row of partition 5.
+INSERT INTO t2(ts, dur, part, b) VALUES (190, 20, 5, 2222);
+
+CREATE VIRTUAL TABLE sp USING span_left_join(t1 PARTITIONED part,
+                                             t2 PARTITIONED part);
+
+SELECT * FROM sp;
diff --git a/test/trace_processor/span_join/span_left_join_unpartitioned.out b/test/trace_processor/diff_tests/span_join/span_left_join_unpartitioned.out
similarity index 100%
rename from test/trace_processor/span_join/span_left_join_unpartitioned.out
rename to test/trace_processor/diff_tests/span_join/span_left_join_unpartitioned.out
diff --git a/test/trace_processor/diff_tests/span_join/span_left_join_unpartitioned_test.sql b/test/trace_processor/diff_tests/span_join/span_left_join_unpartitioned_test.sql
new file mode 100644
index 0000000..4a24a3c
--- /dev/null
+++ b/test/trace_processor/diff_tests/span_join/span_left_join_unpartitioned_test.sql
@@ -0,0 +1,49 @@
+--
+-- 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.
+--
+CREATE TABLE t1(
+  ts BIGINT,
+  dur BIGINT,
+  a BIGINT,
+  PRIMARY KEY (ts)
+) WITHOUT ROWID;
+
+CREATE TABLE t2(
+  ts BIGINT,
+  dur BIGINT,
+  b BIGINT,
+  PRIMARY KEY (ts)
+) WITHOUT ROWID;
+
+-- Then insert some rows into t1 in part 1, 3, 4 and 5.
+INSERT INTO t1(ts, dur, a)
+VALUES
+(100, 400, 111),
+(500, 50, 222),
+(600, 100, 333),
+(900, 100, 444);
+
+-- Insert a row into t2 which should be split up by t1's first row.
+INSERT INTO t2(ts, dur, b) VALUES (50, 200, 111);
+
+-- Insert a row into t2 should should be completely covered by t1's first row.
+INSERT INTO t2(ts, dur, b) VALUES (300, 100, 222);
+
+-- Insert a row into t2 which should span between t1's first and second rows.
+INSERT INTO t2(ts, dur, b) VALUES (400, 250, 333);
+
+CREATE VIRTUAL TABLE sp USING span_left_join(t1, t2);
+
+SELECT * FROM sp;
diff --git a/test/trace_processor/span_join/span_outer_join.out b/test/trace_processor/diff_tests/span_join/span_outer_join.out
similarity index 100%
rename from test/trace_processor/span_join/span_outer_join.out
rename to test/trace_processor/diff_tests/span_join/span_outer_join.out
diff --git a/test/trace_processor/span_join/span_outer_join_mixed.out b/test/trace_processor/diff_tests/span_join/span_outer_join_mixed.out
similarity index 100%
rename from test/trace_processor/span_join/span_outer_join_mixed.out
rename to test/trace_processor/diff_tests/span_join/span_outer_join_mixed.out
diff --git a/test/trace_processor/diff_tests/span_join/span_outer_join_mixed_test.sql b/test/trace_processor/diff_tests/span_join/span_outer_join_mixed_test.sql
new file mode 100644
index 0000000..4e9f44d
--- /dev/null
+++ b/test/trace_processor/diff_tests/span_join/span_outer_join_mixed_test.sql
@@ -0,0 +1,49 @@
+--
+-- Copyright 2021 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 TABLE t1(
+  ts BIGINT,
+  dur BIGINT,
+  part BIGINT,
+  a BIGINT,
+  PRIMARY KEY (part, ts)
+) WITHOUT ROWID;
+
+CREATE TABLE t2(
+  ts BIGINT,
+  dur BIGINT,
+  b BIGINT,
+  PRIMARY KEY (ts)
+) WITHOUT ROWID;
+
+-- Add some rows to t1.
+INSERT INTO t1(ts, dur, part, a)
+VALUES
+(100, 400, 1, 10),
+(500, 100, 1, 11),
+(500, 50, 2, 12),
+(600, 100, 3, 13);
+
+-- Add some rows to t2.
+INSERT INTO t2(ts, dur, b)
+VALUES
+(50, 100, 14),
+(550, 50, 15),
+(600, 50, 16),
+(900, 500, 17);
+
+CREATE VIRTUAL TABLE sp USING span_outer_join(t1 PARTITIONED part, t2);
+
+SELECT * FROM sp;
diff --git a/test/trace_processor/diff_tests/span_join/span_outer_join_test.sql b/test/trace_processor/diff_tests/span_join/span_outer_join_test.sql
new file mode 100644
index 0000000..46ff1cc
--- /dev/null
+++ b/test/trace_processor/diff_tests/span_join/span_outer_join_test.sql
@@ -0,0 +1,78 @@
+--
+-- 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.
+--
+CREATE TABLE t1(
+  ts BIGINT,
+  dur BIGINT,
+  part BIGINT,
+  a BIGINT,
+  PRIMARY KEY (part, ts)
+) WITHOUT ROWID;
+
+CREATE TABLE t2(
+  ts BIGINT,
+  dur BIGINT,
+  part BIGINT,
+  b BIGINT,
+  PRIMARY KEY (part, ts)
+) WITHOUT ROWID;
+
+-- Insert some rows into t2 which are in part 0 and 1 but before t1's rows.
+INSERT INTO t2(ts, dur, part, b)
+VALUES
+(0, 100, 0, 111),
+(100, 200, 0, 222),
+(0, 50, 1, 333);
+
+-- Then insert some rows into t1 in part 1, 3, 4 and 5.
+INSERT INTO t1(ts, dur, part, a)
+VALUES
+(100, 400, 1, 111),
+(500, 50, 1, 222),
+(600, 100, 1, 333),
+(500, 100, 3, 444),
+(100, 100, 4, 555),
+(200, 50, 4, 666),
+(250, 50, 4, 777),
+(100, 100, 5, 888);
+
+-- Insert a row into t2 which should be split up by t1's first row.
+INSERT INTO t2(ts, dur, part, b) VALUES (50, 200, 1, 444);
+
+-- Insert a row into t2 should should be completely covered by t1's first row.
+INSERT INTO t2(ts, dur, part, b) VALUES (300, 100, 1, 555);
+
+-- Insert a row into t2 which should span between t1's first and second rows.
+INSERT INTO t2(ts, dur, part, b) VALUES (400, 250, 1, 666);
+
+-- Insert a row into t2 in partition 2.
+INSERT INTO t2(ts, dur, part, b) VALUES (100, 1000, 2, 777);
+
+-- Insert a row into t2 before t1's first row in partition 4.
+INSERT INTO t2(ts, dur, part, b) VALUES (50, 50, 4, 888);
+
+-- Insert a row into t2 which perfectly matches the second row in partition 4.
+INSERT INTO t2(ts, dur, part, b) VALUES (200, 50, 4, 999);
+
+-- Insert a row into t2 which intersects the first row of partition 5.
+INSERT INTO t2(ts, dur, part, b) VALUES (125, 50, 5, 1111);
+
+-- Insert a row into t2 which intersects the first row of partition 5.
+INSERT INTO t2(ts, dur, part, b) VALUES (190, 20, 5, 2222);
+
+CREATE VIRTUAL TABLE sp USING span_outer_join(t1 PARTITIONED part,
+                                              t2 PARTITIONED part);
+
+SELECT * FROM sp;
diff --git a/test/trace_processor/span_join/span_outer_join_unpartitioned.out b/test/trace_processor/diff_tests/span_join/span_outer_join_unpartitioned.out
similarity index 100%
rename from test/trace_processor/span_join/span_outer_join_unpartitioned.out
rename to test/trace_processor/diff_tests/span_join/span_outer_join_unpartitioned.out
diff --git a/test/trace_processor/diff_tests/span_join/span_outer_join_unpartitioned_test.sql b/test/trace_processor/diff_tests/span_join/span_outer_join_unpartitioned_test.sql
new file mode 100644
index 0000000..71928d2
--- /dev/null
+++ b/test/trace_processor/diff_tests/span_join/span_outer_join_unpartitioned_test.sql
@@ -0,0 +1,47 @@
+--
+-- Copyright 2021 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 TABLE t1(
+  ts BIGINT,
+  dur BIGINT,
+  a BIGINT,
+  PRIMARY KEY (ts)
+) WITHOUT ROWID;
+
+CREATE TABLE t2(
+  ts BIGINT,
+  dur BIGINT,
+  b BIGINT,
+  PRIMARY KEY (ts)
+) WITHOUT ROWID;
+
+-- Add some rows to t1.
+INSERT INTO t1(ts, dur, a)
+VALUES
+(100, 400, 1),
+(500, 50, 2),
+(600, 100, 3);
+
+-- Add some rows to t2.
+INSERT INTO t2(ts, dur, b)
+VALUES
+(50, 100, 4),
+(550, 50, 5),
+(600, 50, 6),
+(900, 500, 7);
+
+CREATE VIRTUAL TABLE sp USING span_outer_join(t1, t2);
+
+SELECT * FROM sp;
diff --git a/test/trace_processor/diff_tests/span_join/tests.py b/test/trace_processor/diff_tests/span_join/tests.py
new file mode 100644
index 0000000..e675aff
--- /dev/null
+++ b/test/trace_processor/diff_tests/span_join/tests.py
@@ -0,0 +1,411 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import DiffTestModule
+
+
+class DiffTestModule_SpanJoin(DiffTestModule):
+
+  def test_span_outer_join(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query=Path('span_outer_join_test.sql'),
+        out=Path('span_outer_join.out'))
+
+  def test_span_outer_join_empty(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query="""
+CREATE TABLE t1(
+  ts BIGINT,
+  dur BIGINT,
+  part BIGINT,
+  PRIMARY KEY (part, ts)
+) WITHOUT ROWID;
+
+CREATE TABLE t2(
+  ts BIGINT,
+  dur BIGINT,
+  part BIGINT,
+  PRIMARY KEY (part, ts)
+) WITHOUT ROWID;
+
+INSERT INTO t1(ts, dur, part)
+VALUES (500, 100, 10);
+
+
+CREATE VIRTUAL TABLE sp USING span_outer_join(t1 PARTITIONED part,
+                                              t2 PARTITIONED part);
+
+SELECT * FROM sp;
+""",
+        out=Csv("""
+"ts","dur","part"
+500,100,10
+"""))
+
+  def test_span_outer_join_unpartitioned_empty(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query="""
+CREATE TABLE t1(
+  ts BIGINT,
+  dur BIGINT,
+  PRIMARY KEY (ts)
+) WITHOUT ROWID;
+
+CREATE TABLE t2(
+  ts BIGINT,
+  dur BIGINT,
+  PRIMARY KEY (ts)
+) WITHOUT ROWID;
+
+
+CREATE VIRTUAL TABLE sp USING span_outer_join(t1, t2);
+
+SELECT * FROM sp;
+""",
+        out=Csv("""
+"ts","dur"
+"""))
+
+  def test_span_outer_join_unpartitioned_left_empty(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query="""
+CREATE TABLE t1(
+  ts BIGINT,
+  dur BIGINT,
+  PRIMARY KEY (ts)
+) WITHOUT ROWID;
+
+CREATE TABLE t2(
+  ts BIGINT,
+  dur BIGINT,
+  PRIMARY KEY (ts)
+) WITHOUT ROWID;
+
+INSERT INTO t2(ts, dur)
+VALUES
+(100, 400),
+(500, 50),
+(600, 100);
+
+CREATE VIRTUAL TABLE sp USING span_outer_join(t1, t2);
+
+SELECT * FROM sp;
+""",
+        out=Csv("""
+"ts","dur"
+100,400
+500,50
+600,100
+"""))
+
+  def test_span_outer_join_unpartitioned_right_empty(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query="""
+CREATE TABLE t1(
+  ts BIGINT,
+  dur BIGINT,
+  PRIMARY KEY (ts)
+) WITHOUT ROWID;
+
+CREATE TABLE t2(
+  ts BIGINT,
+  dur BIGINT,
+  PRIMARY KEY (ts)
+) WITHOUT ROWID;
+
+INSERT INTO t1(ts, dur)
+VALUES
+(100, 400),
+(500, 50),
+(600, 100);
+
+CREATE VIRTUAL TABLE sp USING span_outer_join(t1, t2);
+
+SELECT * FROM sp;
+""",
+        out=Csv("""
+"ts","dur"
+100,400
+500,50
+600,100
+"""))
+
+  def test_span_outer_join_mixed(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query=Path('span_outer_join_mixed_test.sql'),
+        out=Path('span_outer_join_mixed.out'))
+
+  def test_span_outer_join_mixed_empty(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query="""
+CREATE TABLE t1(
+  ts BIGINT,
+  dur BIGINT,
+  part BIGINT,
+  PRIMARY KEY (part, ts)
+) WITHOUT ROWID;
+
+CREATE TABLE t2(
+  ts BIGINT,
+  dur BIGINT,
+  PRIMARY KEY (ts)
+) WITHOUT ROWID;
+
+
+CREATE VIRTUAL TABLE sp USING span_outer_join(t1 PARTITIONED part, t2);
+
+SELECT * FROM sp;
+""",
+        out=Csv("""
+"ts","dur","part"
+"""))
+
+  def test_span_outer_join_mixed_left_empty(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query="""
+CREATE TABLE t1(
+  ts BIGINT,
+  dur BIGINT,
+  part BIGINT,
+  PRIMARY KEY (part, ts)
+) WITHOUT ROWID;
+
+CREATE TABLE t2(
+  ts BIGINT,
+  dur BIGINT,
+  PRIMARY KEY (ts)
+) WITHOUT ROWID;
+
+INSERT INTO t2(ts, dur)
+VALUES
+(100, 400),
+(500, 50),
+(600, 100);
+
+CREATE VIRTUAL TABLE sp USING span_outer_join(t1 PARTITIONED part, t2);
+
+SELECT * FROM sp;
+""",
+        out=Csv("""
+"ts","dur","part"
+"""))
+
+  def test_span_outer_join_mixed_left_empty_rev(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query="""
+CREATE TABLE t1(
+  ts BIGINT,
+  dur BIGINT,
+  part BIGINT,
+  PRIMARY KEY (part, ts)
+) WITHOUT ROWID;
+
+CREATE TABLE t2(
+  ts BIGINT,
+  dur BIGINT,
+  PRIMARY KEY (ts)
+) WITHOUT ROWID;
+
+INSERT INTO t1(ts, dur, part)
+VALUES
+(100, 400, 0),
+(100, 50, 1),
+(600, 100, 1);
+
+CREATE VIRTUAL TABLE sp USING span_outer_join(t2, t1 PARTITIONED part);
+
+SELECT * FROM sp;
+""",
+        out=Csv("""
+"ts","dur","part"
+100,400,0
+100,50,1
+600,100,1
+"""))
+
+  def test_span_outer_join_mixed_right_empty(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query="""
+CREATE TABLE t1(
+  ts BIGINT,
+  dur BIGINT,
+  part BIGINT,
+  PRIMARY KEY (part, ts)
+) WITHOUT ROWID;
+
+CREATE TABLE t2(
+  ts BIGINT,
+  dur BIGINT,
+  b BIGINT,
+  PRIMARY KEY (ts)
+) WITHOUT ROWID;
+
+INSERT INTO t1(ts, dur, part)
+VALUES
+(100, 400, 0),
+(100, 50, 1),
+(600, 100, 1);
+
+CREATE VIRTUAL TABLE sp USING span_outer_join(t1 PARTITIONED part, t2);
+
+SELECT * FROM sp;
+""",
+        out=Csv("""
+"ts","dur","part","b"
+100,400,0,"[NULL]"
+100,50,1,"[NULL]"
+600,100,1,"[NULL]"
+"""))
+
+  def test_span_outer_join_mixed_right_empty_rev(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query="""
+CREATE TABLE t1(
+  ts BIGINT,
+  dur BIGINT,
+  part BIGINT,
+  PRIMARY KEY (part, ts)
+) WITHOUT ROWID;
+
+CREATE TABLE t2(
+  ts BIGINT,
+  dur BIGINT,
+  b BIGINT,
+  PRIMARY KEY (ts)
+) WITHOUT ROWID;
+
+INSERT INTO t2(ts, dur)
+VALUES
+(100, 400),
+(500, 50),
+(600, 100);
+
+CREATE VIRTUAL TABLE sp USING span_outer_join(t2, t1 PARTITIONED part);
+
+SELECT * FROM sp;
+""",
+        out=Csv("""
+"ts","dur","part","b"
+"""))
+
+  def test_span_outer_join_mixed_2(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query=Path('span_outer_join_mixed_test.sql'),
+        out=Path('span_outer_join_mixed.out'))
+
+  def test_span_left_join(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query=Path('span_left_join_test.sql'),
+        out=Path('span_left_join.out'))
+
+  def test_span_left_join_unpartitioned(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query=Path('span_left_join_unpartitioned_test.sql'),
+        out=Path('span_left_join_unpartitioned.out'))
+
+  def test_span_left_join_left_unpartitioned(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query=Path('span_left_join_left_unpartitioned_test.sql'),
+        out=Path('span_left_join_left_unpartitioned.out'))
+
+  def test_span_left_join_left_partitioned(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query=Path('span_left_join_left_partitioned_test.sql'),
+        out=Path('span_left_join_left_partitioned.out'))
+
+  def test_span_left_join_empty_right(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query="""
+CREATE TABLE t1(
+  ts BIGINT,
+  dur BIGINT,
+  part BIGINT,
+  PRIMARY KEY (part, ts)
+) WITHOUT ROWID;
+
+CREATE TABLE t2(
+  ts BIGINT,
+  dur BIGINT,
+  part BIGINT,
+  PRIMARY KEY (part, ts)
+) WITHOUT ROWID;
+
+INSERT INTO t1(ts, dur, part)
+VALUES
+(500, 500, 100);
+
+CREATE VIRTUAL TABLE sp USING span_left_join(t1 PARTITIONED part,
+                                             t2 PARTITIONED part);
+
+SELECT * FROM sp;
+""",
+        out=Csv("""
+"ts","dur","part"
+500,500,100
+"""))
+
+  def test_span_left_join_unordered_android_sched_and_ps(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query="""
+CREATE TABLE t1(
+  ts BIGINT,
+  dur BIGINT,
+  part BIGINT,
+  PRIMARY KEY (part, ts)
+) WITHOUT ROWID;
+
+CREATE TABLE t2(
+  ts BIGINT,
+  dur BIGINT,
+  part BIGINT,
+  PRIMARY KEY (part, ts)
+) WITHOUT ROWID;
+
+INSERT INTO t1(ts, dur, part)
+VALUES (500, 100, 10);
+
+INSERT INTO t2(ts, dur, part)
+VALUES (500, 100, 5);
+
+CREATE VIRTUAL TABLE sp USING span_left_join(t1 PARTITIONED part,
+                                             t2 PARTITIONED part);
+
+SELECT * FROM sp;
+""",
+        out=Csv("""
+"ts","dur","part"
+500,100,10
+"""))
diff --git a/test/trace_processor/diff_tests/span_join/tests_left_join.py b/test/trace_processor/diff_tests/span_join/tests_left_join.py
new file mode 100644
index 0000000..2bd2e58
--- /dev/null
+++ b/test/trace_processor/diff_tests/span_join/tests_left_join.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class SpanJoinLeftJoin(TestSuite):
+
+  def test_span_left_join(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query=Path('span_left_join_test.sql'),
+        out=Path('span_left_join.out'))
+
+  def test_span_left_join_unpartitioned(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query=Path('span_left_join_unpartitioned_test.sql'),
+        out=Path('span_left_join_unpartitioned.out'))
+
+  def test_span_left_join_left_unpartitioned(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query=Path('span_left_join_left_unpartitioned_test.sql'),
+        out=Path('span_left_join_left_unpartitioned.out'))
+
+  def test_span_left_join_left_partitioned(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query=Path('span_left_join_left_partitioned_test.sql'),
+        out=Path('span_left_join_left_partitioned.out'))
+
+  def test_span_left_join_empty_right(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query="""
+        CREATE TABLE t1(
+          ts BIGINT,
+          dur BIGINT,
+          part BIGINT,
+          PRIMARY KEY (part, ts)
+        ) WITHOUT ROWID;
+
+        CREATE TABLE t2(
+          ts BIGINT,
+          dur BIGINT,
+          part BIGINT,
+          PRIMARY KEY (part, ts)
+        ) WITHOUT ROWID;
+
+        INSERT INTO t1(ts, dur, part)
+        VALUES
+        (500, 500, 100);
+
+        CREATE VIRTUAL TABLE sp USING span_left_join(t1 PARTITIONED part,
+                                                     t2 PARTITIONED part);
+
+        SELECT * FROM sp;
+        """,
+        out=Csv("""
+        "ts","dur","part"
+        500,500,100
+        """))
+
+  def test_span_left_join_unordered_android_sched_and_ps(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query="""
+        CREATE TABLE t1(
+          ts BIGINT,
+          dur BIGINT,
+          part BIGINT,
+          PRIMARY KEY (part, ts)
+        ) WITHOUT ROWID;
+
+        CREATE TABLE t2(
+          ts BIGINT,
+          dur BIGINT,
+          part BIGINT,
+          PRIMARY KEY (part, ts)
+        ) WITHOUT ROWID;
+
+        INSERT INTO t1(ts, dur, part)
+        VALUES (500, 100, 10);
+
+        INSERT INTO t2(ts, dur, part)
+        VALUES (500, 100, 5);
+
+        CREATE VIRTUAL TABLE sp USING span_left_join(t1 PARTITIONED part,
+                                                     t2 PARTITIONED part);
+
+        SELECT * FROM sp;
+        """,
+        out=Csv("""
+        "ts","dur","part"
+        500,100,10
+        """))
diff --git a/test/trace_processor/diff_tests/span_join/tests_outer_join.py b/test/trace_processor/diff_tests/span_join/tests_outer_join.py
new file mode 100644
index 0000000..c2ac4f5
--- /dev/null
+++ b/test/trace_processor/diff_tests/span_join/tests_outer_join.py
@@ -0,0 +1,321 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class SpanJoinOuterJoin(TestSuite):
+
+  def test_span_outer_join(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query=Path('span_outer_join_test.sql'),
+        out=Path('span_outer_join.out'))
+
+  def test_span_outer_join_empty(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query="""
+        CREATE TABLE t1(
+          ts BIGINT,
+          dur BIGINT,
+          part BIGINT,
+          PRIMARY KEY (part, ts)
+        ) WITHOUT ROWID;
+
+        CREATE TABLE t2(
+          ts BIGINT,
+          dur BIGINT,
+          part BIGINT,
+          PRIMARY KEY (part, ts)
+        ) WITHOUT ROWID;
+
+        INSERT INTO t1(ts, dur, part)
+        VALUES (500, 100, 10);
+
+
+        CREATE VIRTUAL TABLE sp USING span_outer_join(t1 PARTITIONED part,
+                                                      t2 PARTITIONED part);
+
+        SELECT * FROM sp;
+        """,
+        out=Csv("""
+        "ts","dur","part"
+        500,100,10
+        """))
+
+  def test_span_outer_join_unpartitioned_empty(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query="""
+        CREATE TABLE t1(
+          ts BIGINT,
+          dur BIGINT,
+          PRIMARY KEY (ts)
+        ) WITHOUT ROWID;
+
+        CREATE TABLE t2(
+          ts BIGINT,
+          dur BIGINT,
+          PRIMARY KEY (ts)
+        ) WITHOUT ROWID;
+
+
+        CREATE VIRTUAL TABLE sp USING span_outer_join(t1, t2);
+
+        SELECT * FROM sp;
+        """,
+        out=Csv("""
+        "ts","dur"
+        """))
+
+  def test_span_outer_join_unpartitioned_left_empty(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query="""
+        CREATE TABLE t1(
+          ts BIGINT,
+          dur BIGINT,
+          PRIMARY KEY (ts)
+        ) WITHOUT ROWID;
+
+        CREATE TABLE t2(
+          ts BIGINT,
+          dur BIGINT,
+          PRIMARY KEY (ts)
+        ) WITHOUT ROWID;
+
+        INSERT INTO t2(ts, dur)
+        VALUES
+        (100, 400),
+        (500, 50),
+        (600, 100);
+
+        CREATE VIRTUAL TABLE sp USING span_outer_join(t1, t2);
+
+        SELECT * FROM sp;
+        """,
+        out=Csv("""
+        "ts","dur"
+        100,400
+        500,50
+        600,100
+        """))
+
+  def test_span_outer_join_unpartitioned_right_empty(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query="""
+        CREATE TABLE t1(
+          ts BIGINT,
+          dur BIGINT,
+          PRIMARY KEY (ts)
+        ) WITHOUT ROWID;
+
+        CREATE TABLE t2(
+          ts BIGINT,
+          dur BIGINT,
+          PRIMARY KEY (ts)
+        ) WITHOUT ROWID;
+
+        INSERT INTO t1(ts, dur)
+        VALUES
+        (100, 400),
+        (500, 50),
+        (600, 100);
+
+        CREATE VIRTUAL TABLE sp USING span_outer_join(t1, t2);
+
+        SELECT * FROM sp;
+        """,
+        out=Csv("""
+        "ts","dur"
+        100,400
+        500,50
+        600,100
+        """))
+
+  def test_span_outer_join_mixed(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query=Path('span_outer_join_mixed_test.sql'),
+        out=Path('span_outer_join_mixed.out'))
+
+  def test_span_outer_join_mixed_empty(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query="""
+        CREATE TABLE t1(
+          ts BIGINT,
+          dur BIGINT,
+          part BIGINT,
+          PRIMARY KEY (part, ts)
+        ) WITHOUT ROWID;
+
+        CREATE TABLE t2(
+          ts BIGINT,
+          dur BIGINT,
+          PRIMARY KEY (ts)
+        ) WITHOUT ROWID;
+
+
+        CREATE VIRTUAL TABLE sp USING span_outer_join(t1 PARTITIONED part, t2);
+
+        SELECT * FROM sp;
+        """,
+        out=Csv("""
+        "ts","dur","part"
+        """))
+
+  def test_span_outer_join_mixed_left_empty(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query="""
+        CREATE TABLE t1(
+          ts BIGINT,
+          dur BIGINT,
+          part BIGINT,
+          PRIMARY KEY (part, ts)
+        ) WITHOUT ROWID;
+
+        CREATE TABLE t2(
+          ts BIGINT,
+          dur BIGINT,
+          PRIMARY KEY (ts)
+        ) WITHOUT ROWID;
+
+        INSERT INTO t2(ts, dur)
+        VALUES
+        (100, 400),
+        (500, 50),
+        (600, 100);
+
+        CREATE VIRTUAL TABLE sp USING span_outer_join(t1 PARTITIONED part, t2);
+
+        SELECT * FROM sp;
+        """,
+        out=Csv("""
+        "ts","dur","part"
+        """))
+
+  def test_span_outer_join_mixed_left_empty_rev(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query="""
+        CREATE TABLE t1(
+          ts BIGINT,
+          dur BIGINT,
+          part BIGINT,
+          PRIMARY KEY (part, ts)
+        ) WITHOUT ROWID;
+
+        CREATE TABLE t2(
+          ts BIGINT,
+          dur BIGINT,
+          PRIMARY KEY (ts)
+        ) WITHOUT ROWID;
+
+        INSERT INTO t1(ts, dur, part)
+        VALUES
+        (100, 400, 0),
+        (100, 50, 1),
+        (600, 100, 1);
+
+        CREATE VIRTUAL TABLE sp USING span_outer_join(t2, t1 PARTITIONED part);
+
+        SELECT * FROM sp;
+        """,
+        out=Csv("""
+        "ts","dur","part"
+        100,400,0
+        100,50,1
+        600,100,1
+        """))
+
+  def test_span_outer_join_mixed_right_empty(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query="""
+        CREATE TABLE t1(
+          ts BIGINT,
+          dur BIGINT,
+          part BIGINT,
+          PRIMARY KEY (part, ts)
+        ) WITHOUT ROWID;
+
+        CREATE TABLE t2(
+          ts BIGINT,
+          dur BIGINT,
+          b BIGINT,
+          PRIMARY KEY (ts)
+        ) WITHOUT ROWID;
+
+        INSERT INTO t1(ts, dur, part)
+        VALUES
+        (100, 400, 0),
+        (100, 50, 1),
+        (600, 100, 1);
+
+        CREATE VIRTUAL TABLE sp USING span_outer_join(t1 PARTITIONED part, t2);
+
+        SELECT * FROM sp;
+        """,
+        out=Csv("""
+        "ts","dur","part","b"
+        100,400,0,"[NULL]"
+        100,50,1,"[NULL]"
+        600,100,1,"[NULL]"
+        """))
+
+  def test_span_outer_join_mixed_right_empty_rev(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query="""
+        CREATE TABLE t1(
+          ts BIGINT,
+          dur BIGINT,
+          part BIGINT,
+          PRIMARY KEY (part, ts)
+        ) WITHOUT ROWID;
+
+        CREATE TABLE t2(
+          ts BIGINT,
+          dur BIGINT,
+          b BIGINT,
+          PRIMARY KEY (ts)
+        ) WITHOUT ROWID;
+
+        INSERT INTO t2(ts, dur)
+        VALUES
+        (100, 400),
+        (500, 50),
+        (600, 100);
+
+        CREATE VIRTUAL TABLE sp USING span_outer_join(t2, t1 PARTITIONED part);
+
+        SELECT * FROM sp;
+        """,
+        out=Csv("""
+        "ts","dur","part","b"
+        """))
+
+  def test_span_outer_join_mixed_2(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query=Path('span_outer_join_mixed_test.sql'),
+        out=Path('span_outer_join_mixed.out'))
diff --git a/test/trace_processor/diff_tests/span_join/tests_regression.py b/test/trace_processor/diff_tests/span_join/tests_regression.py
new file mode 100644
index 0000000..31d667e
--- /dev/null
+++ b/test/trace_processor/diff_tests/span_join/tests_regression.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class SpanJoinRegression(TestSuite):
+
+  def test_android_sched_and_ps_slice_span_join_b118665515(self):
+    return DiffTestBlueprint(
+        trace=DataPath('android_sched_and_ps.pb'),
+        query=Path('slice_span_join_b118665515_test.sql'),
+        out=Path('android_sched_and_ps_slice_span_join_b118665515.out'))
+
+  def test_span_join_unpartitioned_empty(self):
+    return DiffTestBlueprint(
+        trace=DataPath('android_sched_and_ps.pb'),
+        query="""
+        CREATE TABLE t1(
+          ts BIGINT,
+          dur BIGINT,
+          PRIMARY KEY (ts, dur)
+        ) WITHOUT ROWID;
+
+        CREATE TABLE t2(
+          ts BIGINT,
+          dur BIGINT,
+          PRIMARY KEY (ts, dur)
+        ) WITHOUT ROWID;
+
+        INSERT INTO t2(ts, dur)
+        VALUES
+        (1, 2),
+        (5, 0),
+        (1, 1);
+
+        CREATE VIRTUAL TABLE sp USING span_join(t1, t2);
+
+        SELECT ts, dur FROM sp;
+        """,
+        out=Csv("""
+        "ts","dur"
+        """))
diff --git a/test/trace_processor/diff_tests/span_join/tests_smoke.py b/test/trace_processor/diff_tests/span_join/tests_smoke.py
new file mode 100644
index 0000000..e94a6c8
--- /dev/null
+++ b/test/trace_processor/diff_tests/span_join/tests_smoke.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class SpanJoinSmoke(TestSuite):
+
+  def test_span_join_unordered_cols_synth_1(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query=Path('span_join_unordered_cols_test.sql'),
+        out=Csv("""
+        "ts","dur","part","b1","b2","b3","a1","a2","a3"
+        10,90,0,"A",10,100,"B",2,101
+        100,1,0,"B",90,200,"C",3,102
+        5,5,1,"A",10,100,"A",1,100
+        10,40,1,"A",10,100,"B",2,101
+        50,40,1,"B",90,200,"B",2,101
+        90,10,1,"C",1,300,"B",2,101
+        100,1,1,"C",1,300,"C",3,102
+        """))
+
+  def test_span_join_unordered_cols_synth_1_2(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query=Path('span_join_unordered_cols_reverse_test.sql'),
+        out=Csv("""
+        "ts","dur","part","b1","b2","b3","a1","a2","a3"
+        10,90,0,"A",10,100,"B",2,101
+        100,1,0,"B",90,200,"C",3,102
+        5,5,1,"A",10,100,"A",1,100
+        10,40,1,"A",10,100,"B",2,101
+        50,40,1,"B",90,200,"B",2,101
+        90,10,1,"C",1,300,"B",2,101
+        100,1,1,"C",1,300,"C",3,102
+        """))
+
+  def test_span_join_zero_negative_dur(self):
+    return DiffTestBlueprint(
+        trace=DataPath('android_sched_and_ps.pb'),
+        query=Path('span_join_zero_negative_dur_test.sql'),
+        out=Csv("""
+        "ts","dur","part"
+        1,0,0
+        1,2,0
+        5,-1,0
+        5,-1,0
+        1,1,1
+        2,0,1
+        """))
diff --git a/test/trace_processor/diff_tests/startup/android_startup.out b/test/trace_processor/diff_tests/startup/android_startup.out
new file mode 100644
index 0000000..752db7b
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/android_startup.out
@@ -0,0 +1,67 @@
+android_startup {
+  startup {
+    startup_id: 2
+    package_name: "com.google.android.calendar"
+    process_name: "com.google.android.calendar"
+    zygote_new_process: false
+    to_first_frame {
+      dur_ns: 108
+      main_thread_by_task_state {
+        running_dur_ns: 10
+        runnable_dur_ns: 90
+        uninterruptible_sleep_dur_ns: 0
+        interruptible_sleep_dur_ns: 10
+        uninterruptible_io_sleep_dur_ns: 0
+        uninterruptible_non_io_sleep_dur_ns: 0
+      }
+      other_processes_spawned_count: 1
+      time_activity_manager {
+        dur_ns: 8
+        dur_ms: 8e-06
+      }
+      time_activity_start {
+        dur_ns: 2
+        dur_ms: 2e-06
+      }
+      time_activity_resume {
+        dur_ns: 1
+        dur_ms: 1e-06
+      }
+      dur_ms: 0.000108
+    }
+    activity_hosting_process_count: 1
+    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
+      }
+    }
+    report_fully_drawn {
+      dur_ns: 198
+      dur_ms: 0.000198
+    }
+    event_timestamps {
+      intent_received: 102
+      first_frame: 210
+    }
+    system_state {
+      dex2oat_running: false
+      installd_running: false
+      broadcast_dispatched_count: 0
+      broadcast_received_count: 0
+      most_active_non_launch_processes: "init"
+      most_active_non_launch_processes: "com.google.android.calendar"
+      installd_dur_ns: 0
+      dex2oat_dur_ns: 0
+    }
+    startup_type: "warm"
+  }
+}
diff --git a/test/trace_processor/startup/android_startup.py b/test/trace_processor/diff_tests/startup/android_startup.py
similarity index 100%
rename from test/trace_processor/startup/android_startup.py
rename to test/trace_processor/diff_tests/startup/android_startup.py
diff --git a/test/trace_processor/diff_tests/startup/android_startup_attribution.out b/test/trace_processor/diff_tests/startup/android_startup_attribution.out
new file mode 100644
index 0000000..46a279e
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/android_startup_attribution.out
@@ -0,0 +1,101 @@
+android_startup {
+  startup {
+    startup_id: 1
+    package_name: "com.some.app"
+    process_name: "com.some.app"
+    zygote_new_process: false
+    to_first_frame {
+      dur_ns: 999999900
+      main_thread_by_task_state {
+        running_dur_ns: 0
+        runnable_dur_ns: 0
+        uninterruptible_sleep_dur_ns: 0
+        interruptible_sleep_dur_ns: 0
+        uninterruptible_io_sleep_dur_ns: 0
+        uninterruptible_non_io_sleep_dur_ns: 0
+      }
+      other_processes_spawned_count: 0
+      time_activity_manager {
+        dur_ns: 2
+        dur_ms: 2e-06
+      }
+      time_activity_resume {
+        dur_ns: 5
+        dur_ms: 5e-06
+      }
+      dur_ms: 999.9999
+      time_dex_open {
+        dur_ns: 20
+        dur_ms: 2e-05
+      }
+      time_verify_class {
+        dur_ns: 40
+        dur_ms: 4e-05
+      }
+      jit_compiled_methods: 2
+      time_jit_thread_pool_on_cpu {
+        dur_ns: 20
+        dur_ms: 2e-05
+      }
+      time_gc_total {
+        dur_ns: 130
+        dur_ms: 0.00013
+      }
+      time_gc_on_cpu {
+        dur_ns: 50
+        dur_ms: 5e-05
+      }
+    }
+    activity_hosting_process_count: 1
+    process {
+      name: "com.some.app"
+      uid: 10001
+      package {
+        package_name: "com.some.app"
+        apk_version_code: 123
+        debuggable: false
+      }
+      packages_for_uid {
+        package_name: "com.some.app"
+        apk_version_code: 123
+        debuggable: false
+      }
+    }
+    event_timestamps {
+      intent_received: 100
+      first_frame: 1000000000
+    }
+    long_binder_transactions {
+      duration {
+        dur_ns: 100000000
+        dur_ms: 100.0
+      }
+      thread: "Binder"
+      destination_process: "system_server"
+      flags: "0x00 No Flags Set"
+      code: "0x00 Java Layer Dependent"
+    }
+    long_binder_transactions {
+      duration {
+        dur_ns: 200000000
+        dur_ms: 200.0
+      }
+      thread: "fonts"
+      destination_thread: "Binder"
+      destination_process: "com.some.app"
+      flags: "0x00 No Flags Set"
+      code: "0x00 Java Layer Dependent"
+    }
+    system_state {
+      dex2oat_running: false
+      installd_running: false
+      broadcast_dispatched_count: 0
+      broadcast_received_count: 0
+      installd_dur_ns: 0
+      dex2oat_dur_ns: 0
+    }
+    startup_type: "hot"
+    slow_start_reason: "GC Activity"
+    slow_start_reason: "Main Thread - Binder transactions blocked"
+  }
+}
diff --git a/test/trace_processor/diff_tests/startup/android_startup_attribution.py b/test/trace_processor/diff_tests/startup/android_startup_attribution.py
new file mode 100644
index 0000000..b921201
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/android_startup_attribution.py
@@ -0,0 +1,181 @@
+#!/usr/bin/env python3
+# 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.
+
+from os import sys, path
+
+import synth_common
+
+APP_PID = 3
+APP_TID = APP_PID
+SECOND_APP_TID = 3
+JIT_TID = 4
+GC_TID = 5
+GC2_TID = 6
+BINDER_TID = 7
+FONTS_TID = 8
+SYSTEM_SERVER_PID = 2
+SYSTEM_SERVER_TID = 2
+LAUNCH_START_TS = 100
+LAUNCH_END_TS = 10**9
+
+trace = synth_common.create_trace()
+trace.add_packet()
+trace.add_process(1, 0, 'init')
+trace.add_process(SYSTEM_SERVER_PID, 1, 'system_server')
+trace.add_process(APP_PID, 1, 'com.some.app', uid=10001)
+trace.add_thread(tid=SECOND_APP_TID, tgid=APP_PID, cmdline='second_thread')
+trace.add_thread(
+    tid=JIT_TID,
+    tgid=APP_PID,
+    cmdline='Jit thread pool',
+    name='Jit thread pool')
+trace.add_thread(
+    tid=GC_TID, tgid=APP_PID, cmdline='HeapTaskDaemon', name='HeapTaskDaemon')
+trace.add_thread(
+    tid=GC2_TID, tgid=APP_PID, cmdline='HeapTaskDaemon', name='HeapTaskDaemon')
+trace.add_thread(tid=BINDER_TID, tgid=APP_PID, cmdline='Binder', name='Binder')
+trace.add_thread(tid=FONTS_TID, tgid=APP_PID, cmdline='fonts', name='fonts')
+
+trace.add_package_list(ts=99, name='com.some.app', uid=10001, version_code=123)
+
+trace.add_ftrace_packet(cpu=0)
+# Start intent.
+trace.add_atrace_begin(
+    ts=LAUNCH_START_TS,
+    pid=SYSTEM_SERVER_PID,
+    tid=SYSTEM_SERVER_TID,
+    buf='MetricsLogger:launchObserverNotifyIntentStarted')
+trace.add_atrace_end(
+    ts=LAUNCH_START_TS + 1, tid=SYSTEM_SERVER_TID, pid=SYSTEM_SERVER_PID)
+
+# System server launching the app.
+trace.add_atrace_async_begin(
+    ts=LAUNCH_START_TS + 2,
+    pid=SYSTEM_SERVER_PID,
+    tid=SYSTEM_SERVER_TID,
+    buf='launching: com.some.app')
+
+# Emulate a hot start (and therefore that we only see activityResume).
+trace.add_atrace_begin(ts=125, tid=APP_TID, pid=APP_PID, buf='activityResume')
+trace.add_atrace_end(ts=130, tid=APP_TID, pid=APP_PID)
+
+# OpenDex slices within the startup.
+trace.add_atrace_begin(
+    ts=150, pid=APP_PID, tid=APP_TID, buf='OpenDexFilesFromOat(something)')
+trace.add_atrace_end(ts=165, pid=APP_PID, tid=APP_TID)
+
+trace.add_atrace_begin(
+    ts=170, pid=APP_PID, tid=APP_TID, buf='OpenDexFilesFromOat(something else)')
+trace.add_atrace_end(ts=175, pid=APP_PID, tid=APP_TID)
+
+# OpenDex slice outside the startup.
+trace.add_atrace_begin(
+    ts=5, pid=APP_PID, tid=APP_TID, buf='OpenDexFilesFromOat(nothing)')
+trace.add_atrace_end(ts=35, pid=APP_PID, tid=APP_TID)
+
+trace.add_atrace_async_end(
+    ts=LAUNCH_END_TS,
+    tid=SYSTEM_SERVER_TID,
+    pid=SYSTEM_SERVER_PID,
+    buf='launching: com.some.app')
+
+# VerifyClass slices within the startup.
+trace.add_atrace_begin(ts=250, pid=APP_PID, tid=APP_TID, buf='VerifyClass vr')
+trace.add_atrace_end(ts=265, pid=APP_PID, tid=APP_TID)
+
+trace.add_atrace_begin(ts=270, pid=APP_PID, tid=APP_TID, buf='VerifyClass dl')
+trace.add_atrace_end(ts=275, pid=APP_PID, tid=APP_TID)
+
+# VerifyClass slice outside the startup.
+trace.add_atrace_begin(ts=55, pid=APP_PID, tid=APP_TID, buf='VerifyClass xf')
+trace.add_atrace_end(ts=65, pid=APP_PID, tid=APP_TID)
+
+# VerifyClass slice on a different thread, overlapping with the other slices.
+trace.add_atrace_begin(
+    ts=260, pid=APP_PID, tid=SECOND_APP_TID, buf='VerifyClass vp')
+trace.add_atrace_end(ts=280, pid=APP_PID, tid=SECOND_APP_TID)
+
+# JIT compilation slices
+trace.add_atrace_begin(
+    ts=150, pid=APP_PID, tid=JIT_TID, buf='JIT compiling someting')
+trace.add_atrace_end(ts=160, pid=APP_PID, tid=JIT_TID)
+
+trace.add_sched(ts=155, prev_pid=0, next_pid=JIT_TID)
+trace.add_sched(ts=165, prev_pid=JIT_TID, next_pid=0)
+
+trace.add_atrace_begin(
+    ts=170, pid=APP_PID, tid=JIT_TID, buf='JIT compiling something else')
+trace.add_atrace_end(ts=190, pid=APP_PID, tid=JIT_TID)
+
+trace.add_sched(ts=170, prev_pid=0, next_pid=JIT_TID)
+trace.add_sched(ts=175, prev_pid=JIT_TID, next_pid=0, prev_state='R')
+trace.add_sched(ts=185, prev_pid=0, next_pid=JIT_TID)
+trace.add_sched(ts=190, prev_pid=JIT_TID, next_pid=0)
+
+# JIT slice, but not on JIT thread.
+trace.add_atrace_begin(
+    ts=200, pid=APP_PID, tid=SECOND_APP_TID, buf='JIT compiling nothing')
+trace.add_atrace_end(ts=210, pid=APP_PID, tid=SECOND_APP_TID)
+
+# Slice on JIT thread, but name doesn't match
+trace.add_atrace_begin(
+    ts=200, pid=APP_PID, tid=JIT_TID, buf='JIT compiled something')
+trace.add_atrace_end(ts=210, pid=APP_PID, tid=JIT_TID)
+
+# GC slices.
+trace.add_atrace_begin(
+    ts=300, pid=APP_PID, tid=GC_TID, buf='Background concurrent copying GC')
+trace.add_atrace_end(ts=330, pid=APP_PID, tid=GC_TID)
+
+trace.add_atrace_begin(
+    ts=340, pid=APP_PID, tid=GC_TID, buf='CollectorTransition mark sweep GC')
+trace.add_atrace_end(ts=390, pid=APP_PID, tid=GC_TID)
+
+trace.add_atrace_begin(ts=320, pid=APP_PID, tid=GC2_TID, buf='semispace GC')
+trace.add_atrace_end(ts=370, pid=APP_PID, tid=GC2_TID)
+
+# Start running copying slice on the first thread
+trace.add_sched(ts=310, prev_pid=0, next_pid=GC_TID)
+# Switch to the second thread to run semispace slice
+trace.add_sched(ts=325, prev_pid=GC_TID, next_pid=GC2_TID)
+# Switch back to the first thread to run mark sweep slice
+trace.add_sched(ts=350, prev_pid=GC2_TID, next_pid=GC_TID)
+# Finish running for GC.
+trace.add_sched(ts=360, prev_pid=GC_TID, next_pid=0)
+
+# Long binder transactions.
+trace.add_binder_transaction(1, 10**8, 2 * (10**8), BINDER_TID, APP_PID, 2,
+                             10**8 + 1, 2 * (10**8) - 1, SYSTEM_SERVER_TID,
+                             SYSTEM_SERVER_PID)
+
+trace.add_binder_transaction(3, 3 * (10**8), 5 * (10**8), FONTS_TID, APP_PID, 4,
+                             3 * (10**8) + 1, 5 * (10**8) - 1, BINDER_TID,
+                             APP_PID)
+
+# A short binder transaction.
+trace.add_binder_transaction(5, 10**7, 5 * (10**7), APP_TID, APP_PID, 6,
+                             10**7 + 1, 5 * (10**7) - 1, SYSTEM_SERVER_TID,
+                             SYSTEM_SERVER_PID)
+
+# Intent successful.
+trace.add_atrace_begin(
+    ts=LAUNCH_END_TS + 1,
+    pid=SYSTEM_SERVER_PID,
+    tid=SYSTEM_SERVER_TID,
+    buf='MetricsLogger:launchObserverNotifyActivityLaunchFinished')
+trace.add_atrace_end(
+    ts=LAUNCH_END_TS + 2, tid=SYSTEM_SERVER_TID, pid=SYSTEM_SERVER_PID)
+
+sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/diff_tests/startup/android_startup_attribution_slow.out b/test/trace_processor/diff_tests/startup/android_startup_attribution_slow.out
new file mode 100644
index 0000000..2257cb0
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/android_startup_attribution_slow.out
@@ -0,0 +1,83 @@
+android_startup {
+  startup {
+    startup_id: 1
+    package_name: "com.some.app"
+    process_name: "com.some.app"
+    zygote_new_process: false
+    to_first_frame {
+      dur_ns: 999999900000000000
+      main_thread_by_task_state {
+        running_dur_ns: 0
+        runnable_dur_ns: 0
+        uninterruptible_sleep_dur_ns: 0
+        interruptible_sleep_dur_ns: 0
+        uninterruptible_io_sleep_dur_ns: 0
+        uninterruptible_non_io_sleep_dur_ns: 0
+      }
+      other_processes_spawned_count: 0
+      time_activity_manager {
+        dur_ns: 2000000000
+        dur_ms: 2000
+      }
+      time_activity_resume {
+        dur_ns: 5000000000
+        dur_ms: 5000
+      }
+      dur_ms: 999999900000.0
+      time_dex_open {
+        dur_ns: 20000000000
+        dur_ms: 20000
+      }
+      time_verify_class {
+        dur_ns: 40000000000
+        dur_ms: 40000
+      }
+      jit_compiled_methods: 41
+      time_jit_thread_pool_on_cpu {
+        dur_ns: 20000000000
+        dur_ms: 20000
+      }
+      time_gc_total {
+        dur_ns: 130000000000
+        dur_ms: 130000
+      }
+      time_gc_on_cpu {
+        dur_ns: 50000000000
+        dur_ms: 50000
+      }
+    }
+    activity_hosting_process_count: 1
+    process {
+      name: "com.some.app"
+      uid: 10001
+      package {
+        package_name: "com.some.app"
+        apk_version_code: 123
+        debuggable: false
+      }
+      packages_for_uid {
+        package_name: "com.some.app"
+        apk_version_code: 123
+        debuggable: false
+      }
+    }
+    event_timestamps {
+      intent_received: 100000000000
+      first_frame: 1000000000000000000
+    }
+    system_state {
+      dex2oat_running: false
+      installd_running: false
+      broadcast_dispatched_count: 0
+      broadcast_received_count: 0
+      installd_dur_ns: 0
+      dex2oat_dur_ns: 0
+    }
+    startup_type: "hot"
+    slow_start_reason: "Time spent in OpenDexFilesFromOat*"
+    slow_start_reason: "Time spent verifying classes"
+    slow_start_reason: "JIT Activity"
+    slow_start_reason: "GC Activity"
+    slow_start_reason: "JIT compiled methods"
+  }
+}
diff --git a/test/trace_processor/diff_tests/startup/android_startup_attribution_slow.py b/test/trace_processor/diff_tests/startup/android_startup_attribution_slow.py
new file mode 100644
index 0000000..aa81cca
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/android_startup_attribution_slow.py
@@ -0,0 +1,206 @@
+#!/usr/bin/env python3
+# 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.
+
+from os import sys, path
+
+import synth_common
+
+
+def to_s(ts):
+  return ts * 1000 * 1000 * 1000
+
+
+APP_PID = 3
+APP_TID = APP_PID
+SECOND_APP_TID = 3
+JIT_TID = 4
+GC_TID = 5
+GC2_TID = 6
+BINDER_TID = 7
+FONTS_TID = 8
+SYSTEM_SERVER_PID = 2
+SYSTEM_SERVER_TID = 2
+LAUNCH_START_TS = 100
+LAUNCH_END_TS = 10**9
+
+trace = synth_common.create_trace()
+trace.add_packet()
+trace.add_process(1, 0, 'init')
+trace.add_process(SYSTEM_SERVER_PID, 1, 'system_server')
+trace.add_process(APP_PID, 1, 'com.some.app', uid=10001)
+trace.add_thread(tid=SECOND_APP_TID, tgid=APP_PID, cmdline='second_thread')
+trace.add_thread(
+    tid=JIT_TID,
+    tgid=APP_PID,
+    cmdline='Jit thread pool',
+    name='Jit thread pool')
+trace.add_thread(
+    tid=GC_TID, tgid=APP_PID, cmdline='HeapTaskDaemon', name='HeapTaskDaemon')
+trace.add_thread(
+    tid=GC2_TID, tgid=APP_PID, cmdline='HeapTaskDaemon', name='HeapTaskDaemon')
+trace.add_thread(tid=BINDER_TID, tgid=APP_PID, cmdline='Binder', name='Binder')
+trace.add_thread(tid=FONTS_TID, tgid=APP_PID, cmdline='fonts', name='fonts')
+
+trace.add_package_list(
+    ts=to_s(99), name='com.some.app', uid=10001, version_code=123)
+
+trace.add_ftrace_packet(cpu=0)
+# Start intent.
+trace.add_atrace_begin(
+    ts=to_s(LAUNCH_START_TS),
+    pid=SYSTEM_SERVER_PID,
+    tid=SYSTEM_SERVER_TID,
+    buf='MetricsLogger:launchObserverNotifyIntentStarted')
+trace.add_atrace_end(
+    ts=to_s(LAUNCH_START_TS + 1), tid=SYSTEM_SERVER_TID, pid=SYSTEM_SERVER_PID)
+
+# System server launching the app.
+trace.add_atrace_async_begin(
+    ts=to_s(LAUNCH_START_TS + 2),
+    pid=SYSTEM_SERVER_PID,
+    tid=SYSTEM_SERVER_TID,
+    buf='launching: com.some.app')
+
+# Emulate a hot start (and therefore that we only see activityResume).
+trace.add_atrace_begin(
+    ts=to_s(125), tid=APP_TID, pid=APP_PID, buf='activityResume')
+trace.add_atrace_end(ts=to_s(130), tid=APP_TID, pid=APP_PID)
+
+# OpenDex slices within the startup.
+trace.add_atrace_begin(
+    ts=to_s(150),
+    pid=APP_PID,
+    tid=APP_TID,
+    buf='OpenDexFilesFromOat(something)')
+trace.add_atrace_end(ts=to_s(165), pid=APP_PID, tid=APP_TID)
+
+trace.add_atrace_begin(
+    ts=to_s(170),
+    pid=APP_PID,
+    tid=APP_TID,
+    buf='OpenDexFilesFromOat(something else)')
+trace.add_atrace_end(ts=to_s(175), pid=APP_PID, tid=APP_TID)
+
+# OpenDex slice outside the startup.
+trace.add_atrace_begin(
+    ts=to_s(5), pid=APP_PID, tid=APP_TID, buf='OpenDexFilesFromOat(nothing)')
+trace.add_atrace_end(ts=to_s(35), pid=APP_PID, tid=APP_TID)
+
+trace.add_atrace_async_end(
+    ts=to_s(LAUNCH_END_TS),
+    tid=SYSTEM_SERVER_TID,
+    pid=SYSTEM_SERVER_PID,
+    buf='launching: com.some.app')
+
+# VerifyClass slices within the startup.
+trace.add_atrace_begin(
+    ts=to_s(250), pid=APP_PID, tid=APP_TID, buf='VerifyClass vr')
+trace.add_atrace_end(ts=to_s(265), pid=APP_PID, tid=APP_TID)
+
+trace.add_atrace_begin(
+    ts=to_s(270), pid=APP_PID, tid=APP_TID, buf='VerifyClass dl')
+trace.add_atrace_end(ts=to_s(275), pid=APP_PID, tid=APP_TID)
+
+# VerifyClass slice outside the startup.
+trace.add_atrace_begin(
+    ts=to_s(55), pid=APP_PID, tid=APP_TID, buf='VerifyClass xf')
+trace.add_atrace_end(ts=to_s(65), pid=APP_PID, tid=APP_TID)
+
+# VerifyClass slice on a different thread, overlapping with the other slices.
+trace.add_atrace_begin(
+    ts=to_s(260), pid=APP_PID, tid=SECOND_APP_TID, buf='VerifyClass vp')
+trace.add_atrace_end(ts=to_s(280), pid=APP_PID, tid=SECOND_APP_TID)
+
+for t in range(100, 160, 2):
+  # JIT compilation slices
+  trace.add_atrace_begin(
+      ts=to_s(t), pid=APP_PID, tid=JIT_TID, buf='JIT compiling someting')
+  trace.add_atrace_end(ts=to_s(t + 1), pid=APP_PID, tid=JIT_TID)
+
+trace.add_sched(ts=to_s(155), prev_pid=0, next_pid=JIT_TID)
+trace.add_sched(ts=to_s(165), prev_pid=JIT_TID, next_pid=0)
+
+for t in range(168, 190, 2):
+  trace.add_atrace_begin(
+      ts=to_s(t), pid=APP_PID, tid=JIT_TID, buf='JIT compiling something else')
+  trace.add_atrace_end(ts=to_s(t + 1), pid=APP_PID, tid=JIT_TID)
+
+trace.add_sched(ts=to_s(170), prev_pid=0, next_pid=JIT_TID)
+trace.add_sched(ts=to_s(175), prev_pid=JIT_TID, next_pid=0, prev_state='R')
+trace.add_sched(ts=to_s(185), prev_pid=0, next_pid=JIT_TID)
+trace.add_sched(ts=to_s(190), prev_pid=JIT_TID, next_pid=0)
+
+# JIT slice, but not on JIT thread.
+trace.add_atrace_begin(
+    ts=to_s(200), pid=APP_PID, tid=SECOND_APP_TID, buf='JIT compiling nothing')
+trace.add_atrace_end(ts=to_s(210), pid=APP_PID, tid=SECOND_APP_TID)
+
+# Slice on JIT thread, but name doesn't match
+trace.add_atrace_begin(
+    ts=to_s(200), pid=APP_PID, tid=JIT_TID, buf='JIT compiled something')
+trace.add_atrace_end(ts=to_s(210), pid=APP_PID, tid=JIT_TID)
+
+# GC slices.
+trace.add_atrace_begin(
+    ts=to_s(300),
+    pid=APP_PID,
+    tid=GC_TID,
+    buf='Background concurrent copying GC')
+trace.add_atrace_end(ts=to_s(330), pid=APP_PID, tid=GC_TID)
+
+trace.add_atrace_begin(
+    ts=to_s(340),
+    pid=APP_PID,
+    tid=GC_TID,
+    buf='CollectorTransition mark sweep GC')
+trace.add_atrace_end(ts=to_s(390), pid=APP_PID, tid=GC_TID)
+
+trace.add_atrace_begin(
+    ts=to_s(320), pid=APP_PID, tid=GC2_TID, buf='semispace GC')
+trace.add_atrace_end(ts=to_s(370), pid=APP_PID, tid=GC2_TID)
+
+# Start running copying slice on the first thread
+trace.add_sched(ts=to_s(310), prev_pid=0, next_pid=GC_TID)
+# Switch to the second thread to run semispace slice
+trace.add_sched(ts=to_s(325), prev_pid=GC_TID, next_pid=GC2_TID)
+# Switch back to the first thread to run mark sweep slice
+trace.add_sched(ts=to_s(350), prev_pid=GC2_TID, next_pid=GC_TID)
+# Finish running for GC.
+trace.add_sched(ts=to_s(360), prev_pid=GC_TID, next_pid=0)
+
+# Long binder transactions.
+trace.add_binder_transaction(1, 10**8, 2 * (10**8), BINDER_TID, APP_PID, 2,
+                             10**8 + 1, 2 * (10**8) - 1, SYSTEM_SERVER_TID,
+                             SYSTEM_SERVER_PID)
+
+trace.add_binder_transaction(3, 3 * (10**8), 5 * (10**8), FONTS_TID, APP_PID, 4,
+                             3 * (10**8) + 1, 5 * (10**8) - 1, BINDER_TID,
+                             APP_PID)
+
+# A short binder transaction.
+trace.add_binder_transaction(5, 10**7, 5 * (10**7), BINDER_TID, APP_TID, 6,
+                             10**7 + 1, 5 * (10**7) - 1, SYSTEM_SERVER_TID,
+                             SYSTEM_SERVER_PID)
+
+# Intent successful.
+trace.add_atrace_begin(
+    ts=to_s(LAUNCH_END_TS + 1),
+    pid=SYSTEM_SERVER_PID,
+    tid=SYSTEM_SERVER_TID,
+    buf='MetricsLogger:launchObserverNotifyActivityLaunchFinished')
+trace.add_atrace_end(
+    ts=to_s(LAUNCH_END_TS + 2), tid=SYSTEM_SERVER_TID, pid=SYSTEM_SERVER_PID)
+
+sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/startup/android_startup_battery.py b/test/trace_processor/diff_tests/startup/android_startup_battery.py
similarity index 100%
rename from test/trace_processor/startup/android_startup_battery.py
rename to test/trace_processor/diff_tests/startup/android_startup_battery.py
diff --git a/test/trace_processor/diff_tests/startup/android_startup_breakdown.out b/test/trace_processor/diff_tests/startup/android_startup_breakdown.out
new file mode 100644
index 0000000..8b35bb5
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/android_startup_breakdown.out
@@ -0,0 +1,115 @@
+android_startup {
+  startup {
+    startup_id: 1
+    package_name: "com.google.android.calendar"
+    process_name: "com.google.android.calendar"
+    zygote_new_process: true
+    to_first_frame {
+      dur_ns: 108000000000
+      main_thread_by_task_state {
+        running_dur_ns: 25000000000
+        runnable_dur_ns: 30000000000
+        uninterruptible_sleep_dur_ns: 0
+        interruptible_sleep_dur_ns: 0
+        uninterruptible_io_sleep_dur_ns: 0
+        uninterruptible_non_io_sleep_dur_ns: 0
+      }
+      other_processes_spawned_count: 0
+      time_activity_manager {
+        dur_ns: 8000000000
+        dur_ms: 8000.0
+      }
+      time_bind_application {
+        dur_ns: 2000000000
+        dur_ms: 2000.0
+      }
+      time_activity_start {
+        dur_ns: 1000000000
+        dur_ms: 1000.0
+      }
+      time_activity_resume {
+        dur_ns: 1000000000
+        dur_ms: 1000.0
+      }
+      time_before_start_process {
+        dur_ns: 18000000000
+        dur_ms: 18000.0
+      }
+      time_during_start_process {
+        dur_ns: 35000000000
+        dur_ms: 35000.0
+      }
+      dur_ms: 108000.0
+      to_bind_application {
+        dur_ns: 83000000000
+        dur_ms: 83000.0
+      }
+      time_inflate {
+        dur_ns: 2000000000
+        dur_ms: 2000.0
+      }
+      time_get_resources {
+        dur_ns: 1000000000
+        dur_ms: 1000.0
+      }
+      mcycles_by_core_type {
+        unknown: 103
+      }
+    }
+    activity_hosting_process_count: 1
+    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
+      }
+    }
+    activities {
+      name: "com.google.android.calendar.MainActivity"
+      method: "performCreate"
+      ts_method_start: 188000000000
+    }
+    optimization_status {
+      odex_status: "up-to-date"
+      compilation_filter: "speed"
+      compilation_reason: "install-dm"
+      location: "/system/framework/oat/arm/com.google.android.calendar.odex"
+    }
+    optimization_status {
+      odex_status: "io-error-no-oat"
+      compilation_filter: "run-from-apk"
+      compilation_reason: "unknown"
+      location: "error"
+    }
+    event_timestamps {
+      intent_received: 102000000000
+      first_frame: 210000000000
+    }
+    system_state {
+      dex2oat_running: false
+      installd_running: false
+      broadcast_dispatched_count: 0
+      broadcast_received_count: 0
+      most_active_non_launch_processes: "init"
+      most_active_non_launch_processes: "system_server"
+      installd_dur_ns: 0
+      dex2oat_dur_ns: 0
+    }
+    slow_start_reason: "Main Thread - Time spent in Running state"
+    slow_start_reason: "Main Thread - Time spent in Runnable state"
+    slow_start_reason: "Time spent in bindApplication"
+    slow_start_reason: "Time spent in view inflation"
+    slow_start_reason: "Time spent in ResourcesManager#getResources"
+    slow_start_reason: "Potential CPU contention with init"
+    slow_start_reason: "No baseline or cloud profiles"
+    slow_start_reason: "Optimized artifacts missing, run from apk"
+    startup_type: "cold"
+  }
+}
diff --git a/test/trace_processor/diff_tests/startup/android_startup_breakdown.py b/test/trace_processor/diff_tests/startup/android_startup_breakdown.py
new file mode 100644
index 0000000..5a5b4a4
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/android_startup_breakdown.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python3
+# 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.
+
+from os import sys, path
+
+import synth_common
+
+
+def to_s(ts):
+  return ts * 1000 * 1000 * 1000
+
+
+trace = synth_common.create_trace()
+trace.add_packet()
+trace.add_process(1, 0, 'init')
+trace.add_process(2, 1, 'system_server')
+trace.add_process(3, 1, 'com.google.android.calendar', uid=10001)
+
+trace.add_package_list(
+    ts=100, name='com.google.android.calendar', uid=10001, version_code=123)
+
+trace.add_ftrace_packet(cpu=0)
+
+# Start intent for a successful launch of calendar
+trace.add_atrace_begin(
+    ts=to_s(102),
+    tid=2,
+    pid=2,
+    buf='MetricsLogger:launchObserverNotifyIntentStarted')
+trace.add_atrace_end(ts=to_s(103), tid=2, pid=2)
+
+trace.add_atrace_async_begin(
+    ts=to_s(110), tid=2, pid=2, buf='launching: com.google.android.calendar')
+
+trace.add_atrace_begin(
+    ts=to_s(120), tid=2, pid=2, buf='Start proc: com.google.android.calendar')
+trace.add_atrace_end(ts=to_s(155), tid=2, pid=2)
+
+# Unrelated process binding, ignored
+trace.add_atrace_begin(ts=to_s(125), tid=1, pid=1, buf='bindApplication')
+trace.add_atrace_end(ts=to_s(195), tid=1, pid=1)
+
+trace.add_atrace_begin(ts=to_s(185), tid=3, pid=3, buf='bindApplication')
+trace.add_atrace_begin(
+    ts=to_s(188),
+    tid=3,
+    pid=3,
+    buf='performCreate:com.google.android.calendar.MainActivity')
+trace.add_atrace_begin(ts=to_s(188), tid=3, pid=3, buf='inflate')
+trace.add_atrace_end(ts=to_s(189), tid=3, pid=3)
+trace.add_atrace_begin(
+    ts=to_s(188), tid=3, pid=3, buf='ResourcesManager#getResources')
+trace.add_atrace_end(ts=to_s(189), tid=3, pid=3)
+trace.add_atrace_begin(ts=to_s(191), tid=3, pid=3, buf='inflate')
+trace.add_atrace_end(ts=to_s(192), tid=3, pid=3)
+trace.add_atrace_end(ts=to_s(192), tid=3, pid=3)
+trace.add_atrace_begin(
+    ts=193,
+    tid=3,
+    pid=3,
+    buf='performResume:com.google.android.calendar.MainActivity')
+trace.add_atrace_end(ts=to_s(187), tid=3, pid=3)
+trace.add_atrace_end(ts=to_s(195), tid=3, pid=3)
+
+trace.add_atrace_begin(ts=to_s(195), tid=3, pid=3, buf='activityStart')
+trace.add_atrace_end(ts=to_s(196), tid=3, pid=3)
+
+trace.add_atrace_begin(ts=to_s(196), tid=3, pid=3, buf='activityResume')
+trace.add_atrace_end(ts=to_s(197), tid=3, pid=3)
+
+trace.add_atrace_begin(
+    ts=to_s(200),
+    tid=3,
+    pid=3,
+    buf='location=error status=io-error-no-oat ' \
+        'filter=run-from-apk reason=unknown')
+trace.add_atrace_end(ts=to_s(202), tid=3, pid=3)
+trace.add_atrace_begin(
+    ts=to_s(204),
+    tid=3,
+    pid=3,
+    buf='location=/system/framework/oat/arm/com.google.android.calendar' \
+        '.odex status=up-to-date filter=speed reason=install-dm')
+trace.add_atrace_end(ts=to_s(205), tid=3, pid=3)
+
+trace.add_atrace_async_end(
+    ts=to_s(210), tid=2, pid=2, buf='launching: com.google.android.calendar')
+trace.add_atrace_begin(
+    ts=to_s(211),
+    tid=2,
+    pid=2,
+    buf='MetricsLogger:launchObserverNotifyActivityLaunchFinished')
+trace.add_atrace_end(ts=to_s(212), tid=2, pid=2)
+
+# Add the scheduling data to match the timestamps of events above but with
+# some idle time inbetween to make the computation more realisitic.
+trace.add_cpufreq(ts=to_s(50), freq=1000, cpu=0)
+trace.add_sched(ts=to_s(100), prev_pid=0, next_pid=2)
+trace.add_sched(ts=to_s(115), prev_pid=2, next_pid=0)
+trace.add_sched(ts=to_s(120), prev_pid=0, next_pid=2)
+trace.add_sched(ts=to_s(125), prev_pid=2, next_pid=1)
+trace.add_sched(ts=to_s(150), prev_pid=1, next_pid=2)
+trace.add_sched(ts=to_s(160), prev_pid=2, next_pid=1)
+trace.add_sched(ts=to_s(180), prev_pid=1, next_pid=3)
+trace.add_sched(ts=to_s(205), prev_pid=3, next_pid=2)
+trace.add_sched(ts=to_s(220), prev_pid=2, next_pid=0)
+
+sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/diff_tests/startup/android_startup_breakdown_slow.out b/test/trace_processor/diff_tests/startup/android_startup_breakdown_slow.out
new file mode 100644
index 0000000..3ed8ce3
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/android_startup_breakdown_slow.out
@@ -0,0 +1,114 @@
+android_startup {
+  startup {
+    startup_id: 1
+    package_name: "com.google.android.calendar"
+    process_name: "com.google.android.calendar"
+    zygote_new_process: true
+    to_first_frame {
+      dur_ns: 108000000000
+      main_thread_by_task_state {
+        running_dur_ns: 25000000000
+        runnable_dur_ns: 30000000000
+        uninterruptible_sleep_dur_ns: 0
+        interruptible_sleep_dur_ns: 0
+        uninterruptible_io_sleep_dur_ns: 0
+        uninterruptible_non_io_sleep_dur_ns: 0
+      }
+      other_processes_spawned_count: 0
+      time_activity_manager {
+        dur_ns: 8000000000
+        dur_ms: 8000.0
+      }
+      time_bind_application {
+        dur_ns: 9000000000
+        dur_ms: 9000.0
+      }
+      time_activity_start {
+        dur_ns: 1000000000
+        dur_ms: 1000.0
+      }
+      time_activity_resume {
+        dur_ns: 1000000000
+        dur_ms: 1000.0
+      }
+      time_before_start_process {
+        dur_ns: 18000000000
+        dur_ms: 18000.0
+      }
+      time_during_start_process {
+        dur_ns: 35000000000
+        dur_ms: 35000.0
+      }
+      dur_ms: 108000.0
+      to_bind_application {
+        dur_ns: 83000000000
+        dur_ms: 83000.0
+      }
+      time_inflate {
+        dur_ns: 3000000000
+        dur_ms: 3000.0
+      }
+      time_get_resources {
+        dur_ns: 5000000000
+        dur_ms: 5000.0
+      }
+      mcycles_by_core_type {
+        unknown: 103
+      }
+    }
+    activity_hosting_process_count: 1
+    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
+      }
+    }
+    activities {
+      name: "com.google.android.calendar.MainActivity"
+      method: "performCreate"
+      ts_method_start: 188000000000
+    }
+    optimization_status {
+      odex_status: "up-to-date"
+      compilation_filter: "speed-profile"
+      compilation_reason: "install"
+      location: "/system/framework/oat/arm/com.android.location.provider.odex"
+    }
+    optimization_status {
+      odex_status: "io-error-no-oat"
+      compilation_filter: "run-from-apk"
+      compilation_reason: "unknown"
+      location: "error"
+    }
+    event_timestamps {
+      intent_received: 102000000000
+      first_frame: 210000000000
+    }
+    system_state {
+      dex2oat_running: false
+      installd_running: false
+      broadcast_dispatched_count: 0
+      broadcast_received_count: 0
+      most_active_non_launch_processes: "init"
+      most_active_non_launch_processes: "system_server"
+      installd_dur_ns: 0
+      dex2oat_dur_ns: 0
+    }
+    slow_start_reason: "Main Thread - Time spent in Running state"
+    slow_start_reason: "Main Thread - Time spent in Runnable state"
+    slow_start_reason: "Time spent in bindApplication"
+    slow_start_reason: "Time spent in view inflation"
+    slow_start_reason: "Time spent in ResourcesManager#getResources"
+    slow_start_reason: "Potential CPU contention with init"
+    slow_start_reason: "Optimized artifacts missing, run from apk"
+    startup_type: "cold"
+  }
+}
diff --git a/test/trace_processor/diff_tests/startup/android_startup_breakdown_slow.py b/test/trace_processor/diff_tests/startup/android_startup_breakdown_slow.py
new file mode 100644
index 0000000..06422a2
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/android_startup_breakdown_slow.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python3
+# 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.
+
+from os import sys, path
+
+import synth_common
+
+
+def to_s(ts):
+  return ts * 1000 * 1000 * 1000
+
+
+trace = synth_common.create_trace()
+trace.add_packet()
+trace.add_process(1, 0, 'init')
+trace.add_process(2, 1, 'system_server')
+trace.add_process(3, 1, 'com.google.android.calendar', uid=10001)
+
+trace.add_package_list(
+    ts=100, name='com.google.android.calendar', uid=10001, version_code=123)
+
+trace.add_ftrace_packet(cpu=0)
+
+# Start intent for a successful launch of calendar
+trace.add_atrace_begin(
+    ts=to_s(102),
+    tid=2,
+    pid=2,
+    buf='MetricsLogger:launchObserverNotifyIntentStarted')
+trace.add_atrace_end(ts=to_s(103), tid=2, pid=2)
+
+trace.add_atrace_async_begin(
+    ts=to_s(110), tid=2, pid=2, buf='launching: com.google.android.calendar')
+
+trace.add_atrace_begin(
+    ts=to_s(120), tid=2, pid=2, buf='Start proc: com.google.android.calendar')
+trace.add_atrace_end(ts=to_s(155), tid=2, pid=2)
+
+# Unrelated process binding, ignored
+trace.add_atrace_begin(ts=to_s(125), tid=1, pid=1, buf='bindApplication')
+trace.add_atrace_end(ts=to_s(195), tid=1, pid=1)
+
+trace.add_atrace_begin(ts=to_s(185), tid=3, pid=3, buf='bindApplication')
+trace.add_atrace_begin(
+    ts=to_s(188),
+    tid=3,
+    pid=3,
+    buf='performCreate:com.google.android.calendar.MainActivity')
+trace.add_atrace_begin(ts=to_s(188), tid=3, pid=3, buf='inflate')
+trace.add_atrace_end(ts=to_s(189), tid=3, pid=3)
+trace.add_atrace_begin(
+    ts=to_s(187), tid=3, pid=3, buf='ResourcesManager#getResources')
+trace.add_atrace_end(ts=to_s(189), tid=3, pid=3)
+trace.add_atrace_begin(ts=to_s(190), tid=3, pid=3, buf='inflate')
+trace.add_atrace_end(ts=to_s(192), tid=3, pid=3)
+trace.add_atrace_end(ts=to_s(192), tid=3, pid=3)
+trace.add_atrace_begin(
+    ts=193,
+    tid=3,
+    pid=3,
+    buf='performResume:com.google.android.calendar.MainActivity')
+trace.add_atrace_end(ts=to_s(194), tid=3, pid=3)
+trace.add_atrace_end(ts=to_s(195), tid=3, pid=3)
+
+trace.add_atrace_begin(ts=to_s(195), tid=3, pid=3, buf='activityStart')
+trace.add_atrace_end(ts=to_s(196), tid=3, pid=3)
+
+trace.add_atrace_begin(ts=to_s(196), tid=3, pid=3, buf='activityResume')
+trace.add_atrace_end(ts=to_s(197), tid=3, pid=3)
+
+trace.add_atrace_begin(
+    ts=to_s(200),
+    tid=3,
+    pid=3,
+    buf='location=error status=io-error-no-oat ' \
+        'filter=run-from-apk reason=unknown')
+trace.add_atrace_end(ts=to_s(202), tid=3, pid=3)
+trace.add_atrace_begin(
+    ts=to_s(204),
+    tid=3,
+    pid=3,
+    buf='location=/system/framework/oat/arm/com.android.location.provider' \
+        '.odex status=up-to-date filter=speed-profile reason=install')
+trace.add_atrace_end(ts=to_s(205), tid=3, pid=3)
+
+trace.add_atrace_async_end(
+    ts=to_s(210), tid=2, pid=2, buf='launching: com.google.android.calendar')
+trace.add_atrace_begin(
+    ts=to_s(211),
+    tid=2,
+    pid=2,
+    buf='MetricsLogger:launchObserverNotifyActivityLaunchFinished')
+trace.add_atrace_end(ts=to_s(212), tid=2, pid=2)
+
+# Add the scheduling data to match the timestamps of events above but with
+# some idle time inbetween to make the computation more realisitic.
+trace.add_cpufreq(ts=to_s(50), freq=1000, cpu=0)
+trace.add_sched(ts=to_s(100), prev_pid=0, next_pid=2)
+trace.add_sched(ts=to_s(115), prev_pid=2, next_pid=0)
+trace.add_sched(ts=to_s(120), prev_pid=0, next_pid=2)
+trace.add_sched(ts=to_s(125), prev_pid=2, next_pid=1)
+trace.add_sched(ts=to_s(150), prev_pid=1, next_pid=2)
+trace.add_sched(ts=to_s(160), prev_pid=2, next_pid=1)
+trace.add_sched(ts=to_s(180), prev_pid=1, next_pid=3)
+trace.add_sched(ts=to_s(205), prev_pid=3, next_pid=2)
+trace.add_sched(ts=to_s(220), prev_pid=2, next_pid=0)
+
+sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/diff_tests/startup/android_startup_broadcast.out b/test/trace_processor/diff_tests/startup/android_startup_broadcast.out
new file mode 100644
index 0000000..a0753b7
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/android_startup_broadcast.out
@@ -0,0 +1,33 @@
+android_startup {
+  startup {
+    startup_id: 1
+    package_name: "com.google.android.calendar"
+    zygote_new_process: false
+    to_first_frame {
+      dur_ns: 100
+      main_thread_by_task_state {
+        running_dur_ns: 0
+        runnable_dur_ns: 0
+        uninterruptible_sleep_dur_ns: 0
+        interruptible_sleep_dur_ns: 0
+        uninterruptible_io_sleep_dur_ns: 0
+        uninterruptible_non_io_sleep_dur_ns: 0
+      }
+      other_processes_spawned_count: 0
+      dur_ms: 0.0001
+    }
+    activity_hosting_process_count: 0
+    event_timestamps {
+      intent_received: 100
+      first_frame: 200
+    }
+    system_state {
+      dex2oat_running: false
+      installd_running: false
+      broadcast_dispatched_count: 1
+      broadcast_received_count: 1
+      installd_dur_ns: 0
+      dex2oat_dur_ns: 0
+    }
+  }
+}
diff --git a/test/trace_processor/startup/android_startup_broadcast.py b/test/trace_processor/diff_tests/startup/android_startup_broadcast.py
similarity index 100%
rename from test/trace_processor/startup/android_startup_broadcast.py
rename to test/trace_processor/diff_tests/startup/android_startup_broadcast.py
diff --git a/test/trace_processor/diff_tests/startup/android_startup_broadcast_multiple.out b/test/trace_processor/diff_tests/startup/android_startup_broadcast_multiple.out
new file mode 100644
index 0000000..8f50a7d
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/android_startup_broadcast_multiple.out
@@ -0,0 +1,35 @@
+android_startup {
+  startup {
+    startup_id: 1
+    package_name: "com.google.android.calendar"
+    zygote_new_process: false
+    to_first_frame {
+      dur_ns: 100
+      main_thread_by_task_state {
+        running_dur_ns: 0
+        runnable_dur_ns: 0
+        uninterruptible_sleep_dur_ns: 0
+        interruptible_sleep_dur_ns: 0
+        uninterruptible_io_sleep_dur_ns: 0
+        uninterruptible_non_io_sleep_dur_ns: 0
+      }
+      other_processes_spawned_count: 0
+      dur_ms: 0.0001
+    }
+    activity_hosting_process_count: 0
+    event_timestamps {
+      intent_received: 100
+      first_frame: 200
+    }
+    system_state {
+      dex2oat_running: false
+      installd_running: false
+      broadcast_dispatched_count: 12
+      broadcast_received_count: 11
+      installd_dur_ns: 0
+      dex2oat_dur_ns: 0
+    }
+    slow_start_reason: "Broadcast dispatched count"
+    slow_start_reason: "Broadcast received count"
+  }
+}
diff --git a/test/trace_processor/diff_tests/startup/android_startup_broadcast_multiple.py b/test/trace_processor/diff_tests/startup/android_startup_broadcast_multiple.py
new file mode 100644
index 0000000..4b5f5b2
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/android_startup_broadcast_multiple.py
@@ -0,0 +1,52 @@
+#!/usr/bin/env python3
+# 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.
+
+from os import sys
+
+import synth_common
+
+trace = synth_common.create_trace()
+trace.add_packet()
+trace.add_process(1, 0, 'init')
+trace.add_process(2, 1, 'system_server')
+trace.add_process(3, 1, 'com.google.android.calendar', 10003)
+
+trace.add_package_list(
+    ts=1, name='com.google.android.calendar', uid=10003, version_code=123)
+
+trace.add_ftrace_packet(cpu=0)
+
+trace.add_atrace_async_begin(ts=100, tid=2, pid=2, buf='launchingActivity#1')
+trace.add_atrace_async_end(ts=200, tid=2, pid=2, buf='launchingActivity#1')
+
+for t in range(105, 129, 2):
+  trace.add_atrace_begin(
+      ts=t,
+      tid=1,
+      pid=1,
+      buf='Broadcast dispatched from android (2005:system/1000) x')
+  trace.add_atrace_end(ts=t + 1, tid=1, pid=1)
+
+for t in range(130, 152, 2):
+  trace.add_atrace_begin(ts=t, tid=2, pid=2, buf='broadcastReceiveReg: x')
+  trace.add_atrace_end(ts=t + 1, tid=2, pid=2)
+
+trace.add_atrace_instant(
+    ts=201,
+    tid=2,
+    pid=2,
+    buf='launchingActivity#1:completed:com.google.android.calendar')
+
+sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/startup/android_startup_cpu.out b/test/trace_processor/diff_tests/startup/android_startup_cpu.out
similarity index 100%
rename from test/trace_processor/startup/android_startup_cpu.out
rename to test/trace_processor/diff_tests/startup/android_startup_cpu.out
diff --git a/test/trace_processor/startup/android_startup_cpu.py b/test/trace_processor/diff_tests/startup/android_startup_cpu.py
similarity index 100%
rename from test/trace_processor/startup/android_startup_cpu.py
rename to test/trace_processor/diff_tests/startup/android_startup_cpu.py
diff --git a/test/trace_processor/diff_tests/startup/android_startup_installd_dex2oat.out b/test/trace_processor/diff_tests/startup/android_startup_installd_dex2oat.out
new file mode 100644
index 0000000..48aa205
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/android_startup_installd_dex2oat.out
@@ -0,0 +1,130 @@
+android_startup {
+  startup {
+    startup_id: 1
+    package_name: "com.google.android.calendar"
+    zygote_new_process: false
+    to_first_frame {
+      dur_ns: 100
+      main_thread_by_task_state {
+        running_dur_ns: 0
+        runnable_dur_ns: 0
+        uninterruptible_sleep_dur_ns: 0
+        interruptible_sleep_dur_ns: 0
+        uninterruptible_io_sleep_dur_ns: 0
+        uninterruptible_non_io_sleep_dur_ns: 0
+      }
+      other_processes_spawned_count: 0
+      dur_ms: 0.0001
+    }
+    activity_hosting_process_count: 0
+    event_timestamps {
+      intent_received: 100
+      first_frame: 200
+    }
+    system_state {
+      dex2oat_running: false
+      installd_running: false
+      broadcast_dispatched_count: 0
+      broadcast_received_count: 0
+      installd_dur_ns: 0
+      dex2oat_dur_ns: 0
+    }
+  }
+  startup {
+    startup_id: 2
+    package_name: "com.google.android.calculator"
+    zygote_new_process: false
+    to_first_frame {
+      dur_ns: 100
+      main_thread_by_task_state {
+        running_dur_ns: 0
+        runnable_dur_ns: 0
+        uninterruptible_sleep_dur_ns: 0
+        interruptible_sleep_dur_ns: 0
+        uninterruptible_io_sleep_dur_ns: 0
+        uninterruptible_non_io_sleep_dur_ns: 0
+      }
+      other_processes_spawned_count: 0
+      dur_ms: 0.0001
+    }
+    activity_hosting_process_count: 0
+    event_timestamps {
+      intent_received: 300
+      first_frame: 400
+    }
+    system_state {
+      dex2oat_running: true
+      installd_running: false
+      broadcast_dispatched_count: 0
+      broadcast_received_count: 0
+      most_active_non_launch_processes: "dex2oat64"
+      installd_dur_ns: 0
+      dex2oat_dur_ns: 5
+    }
+  }
+  startup {
+    startup_id: 3
+    package_name: "com.google.android.deskclock"
+    zygote_new_process: false
+    to_first_frame {
+      dur_ns: 100
+      main_thread_by_task_state {
+        running_dur_ns: 0
+        runnable_dur_ns: 0
+        uninterruptible_sleep_dur_ns: 0
+        interruptible_sleep_dur_ns: 0
+        uninterruptible_io_sleep_dur_ns: 0
+        uninterruptible_non_io_sleep_dur_ns: 0
+      }
+      other_processes_spawned_count: 0
+      dur_ms: 0.0001
+    }
+    activity_hosting_process_count: 0
+    event_timestamps {
+      intent_received: 500
+      first_frame: 600
+    }
+    system_state {
+      dex2oat_running: false
+      installd_running: true
+      broadcast_dispatched_count: 0
+      broadcast_received_count: 0
+      most_active_non_launch_processes: "installd"
+      installd_dur_ns: 5
+      dex2oat_dur_ns: 0
+    }
+  }
+  startup {
+    startup_id: 4
+    package_name: "com.google.android.gm"
+    zygote_new_process: false
+    to_first_frame {
+      dur_ns: 100
+      main_thread_by_task_state {
+        running_dur_ns: 0
+        runnable_dur_ns: 0
+        uninterruptible_sleep_dur_ns: 0
+        interruptible_sleep_dur_ns: 0
+        uninterruptible_io_sleep_dur_ns: 0
+        uninterruptible_non_io_sleep_dur_ns: 0
+      }
+      other_processes_spawned_count: 0
+      dur_ms: 0.0001
+    }
+    activity_hosting_process_count: 0
+    event_timestamps {
+      intent_received: 700
+      first_frame: 800
+    }
+    system_state {
+      dex2oat_running: true
+      installd_running: true
+      broadcast_dispatched_count: 0
+      broadcast_received_count: 0
+      most_active_non_launch_processes: "dex2oat64"
+      most_active_non_launch_processes: "installd"
+      installd_dur_ns: 5
+      dex2oat_dur_ns: 5
+    }
+  }
+}
diff --git a/test/trace_processor/startup/android_startup_installd_dex2oat.py b/test/trace_processor/diff_tests/startup/android_startup_installd_dex2oat.py
similarity index 100%
rename from test/trace_processor/startup/android_startup_installd_dex2oat.py
rename to test/trace_processor/diff_tests/startup/android_startup_installd_dex2oat.py
diff --git a/test/trace_processor/diff_tests/startup/android_startup_installd_dex2oat_slow.out b/test/trace_processor/diff_tests/startup/android_startup_installd_dex2oat_slow.out
new file mode 100644
index 0000000..18039c2
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/android_startup_installd_dex2oat_slow.out
@@ -0,0 +1,138 @@
+android_startup {
+  startup {
+    startup_id: 1
+    package_name: "com.google.android.calendar"
+    zygote_new_process: false
+    to_first_frame {
+      dur_ns: 100000000000
+      main_thread_by_task_state {
+        running_dur_ns: 0
+        runnable_dur_ns: 0
+        uninterruptible_sleep_dur_ns: 0
+        interruptible_sleep_dur_ns: 0
+        uninterruptible_io_sleep_dur_ns: 0
+        uninterruptible_non_io_sleep_dur_ns: 0
+      }
+      other_processes_spawned_count: 0
+      dur_ms: 100000.0
+    }
+    activity_hosting_process_count: 0
+    event_timestamps {
+      intent_received: 100000000000
+      first_frame: 200000000000
+    }
+    system_state {
+      dex2oat_running: false
+      installd_running: false
+      broadcast_dispatched_count: 0
+      broadcast_received_count: 0
+      installd_dur_ns: 0
+      dex2oat_dur_ns: 0
+    }
+  }
+  startup {
+    startup_id: 2
+    package_name: "com.google.android.calculator"
+    zygote_new_process: false
+    to_first_frame {
+      dur_ns: 100000000000
+      main_thread_by_task_state {
+        running_dur_ns: 0
+        runnable_dur_ns: 0
+        uninterruptible_sleep_dur_ns: 0
+        interruptible_sleep_dur_ns: 0
+        uninterruptible_io_sleep_dur_ns: 0
+        uninterruptible_non_io_sleep_dur_ns: 0
+      }
+      other_processes_spawned_count: 0
+      dur_ms: 100000.0
+    }
+    activity_hosting_process_count: 0
+    event_timestamps {
+      intent_received: 300000000000
+      first_frame: 400000000000
+    }
+    system_state {
+      dex2oat_running: true
+      installd_running: false
+      broadcast_dispatched_count: 0
+      broadcast_received_count: 0
+      most_active_non_launch_processes: "dex2oat64"
+      installd_dur_ns: 0
+      dex2oat_dur_ns: 5000000000
+    }
+    slow_start_reason: "dex2oat running during launch"
+  }
+  startup {
+    startup_id: 3
+    package_name: "com.google.android.deskclock"
+    zygote_new_process: false
+    to_first_frame {
+      dur_ns: 250000000000
+      main_thread_by_task_state {
+        running_dur_ns: 0
+        runnable_dur_ns: 0
+        uninterruptible_sleep_dur_ns: 0
+        interruptible_sleep_dur_ns: 0
+        uninterruptible_io_sleep_dur_ns: 0
+        uninterruptible_non_io_sleep_dur_ns: 0
+      }
+      other_processes_spawned_count: 0
+      dur_ms: 250000.0
+    }
+    activity_hosting_process_count: 0
+    event_timestamps {
+      intent_received: 500000000000
+      first_frame: 750000000000
+    }
+    system_state {
+      dex2oat_running: true
+      installd_running: true
+      broadcast_dispatched_count: 0
+      broadcast_received_count: 0
+      most_active_non_launch_processes: "dex2oat64"
+      most_active_non_launch_processes: "installd"
+      installd_dur_ns: 10000000000
+      dex2oat_dur_ns: 5000000000
+    }
+    slow_start_reason: "dex2oat running during launch"
+    slow_start_reason: "installd running during launch"
+    slow_start_reason: "Startup running concurrent to launch"
+    startup_concurrent_to_launch: "com.google.android.gm"
+  }
+  startup {
+    startup_id: 4
+    package_name: "com.google.android.gm"
+    zygote_new_process: false
+    to_first_frame {
+      dur_ns: 100000000000
+      main_thread_by_task_state {
+        running_dur_ns: 0
+        runnable_dur_ns: 0
+        uninterruptible_sleep_dur_ns: 0
+        interruptible_sleep_dur_ns: 0
+        uninterruptible_io_sleep_dur_ns: 0
+        uninterruptible_non_io_sleep_dur_ns: 0
+      }
+      other_processes_spawned_count: 0
+      dur_ms: 100000.0
+    }
+    activity_hosting_process_count: 0
+    event_timestamps {
+      intent_received: 700000000000
+      first_frame: 800000000000
+    }
+    system_state {
+      dex2oat_running: true
+      installd_running: true
+      broadcast_dispatched_count: 0
+      broadcast_received_count: 0
+      installd_dur_ns: 5000000000
+      dex2oat_dur_ns: 5000000000
+    }
+    slow_start_reason: "dex2oat running during launch"
+    slow_start_reason: "installd running during launch"
+    slow_start_reason: "Startup running concurrent to launch"
+    startup_concurrent_to_launch: "com.google.android.deskclock"
+  }
+}
diff --git a/test/trace_processor/diff_tests/startup/android_startup_installd_dex2oat_slow.py b/test/trace_processor/diff_tests/startup/android_startup_installd_dex2oat_slow.py
new file mode 100644
index 0000000..3e59d25
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/android_startup_installd_dex2oat_slow.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+# 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.
+
+from os import sys
+
+import synth_common
+
+
+def to_s(ts):
+  return ts * 1000 * 1000 * 1000
+
+
+trace = synth_common.create_trace()
+trace.add_packet()
+trace.add_process(1, 0, 'init')
+trace.add_process(2, 1, 'system_server')
+trace.add_process(3, 1, 'com.google.android.calendar', 10003)
+trace.add_process(4, 1, 'com.google.android.calculator', 10004)
+trace.add_process(5, 1, 'com.google.android.deskclock', 10005)
+trace.add_process(6, 1, 'com.google.android.gm', 10006)
+trace.add_process(10, 1, 'dex2oat64')
+trace.add_process(11, 1, 'installd')
+
+trace.add_package_list(
+    ts=to_s(1), name='com.google.android.calendar', uid=10003, version_code=123)
+trace.add_package_list(
+    ts=to_s(2),
+    name='com.google.android.calculator',
+    uid=10004,
+    version_code=123)
+trace.add_package_list(
+    ts=to_s(3),
+    name='com.google.android.deskclock',
+    uid=10005,
+    version_code=123)
+trace.add_package_list(
+    ts=to_s(4), name='com.google.android.gm', uid=10006, version_code=123)
+
+trace.add_ftrace_packet(cpu=0)
+
+# First launch: don't have either dex2oat or installd
+trace.add_atrace_async_begin(
+    ts=to_s(100), tid=2, pid=2, buf='launchingActivity#1')
+trace.add_atrace_async_end(
+    ts=to_s(200), tid=2, pid=2, buf='launchingActivity#1')
+trace.add_atrace_instant(
+    ts=to_s(201),
+    tid=2,
+    pid=2,
+    buf='launchingActivity#1:completed:com.google.android.calendar')
+
+# Second launch: just dex2oat
+trace.add_atrace_async_begin(
+    ts=to_s(300), tid=2, pid=2, buf='launchingActivity#2')
+trace.add_sched(ts=to_s(305), prev_pid=0, next_pid=10)
+trace.add_sched(ts=to_s(310), prev_pid=10, next_pid=0)
+trace.add_atrace_async_end(
+    ts=to_s(400), tid=2, pid=2, buf='launchingActivity#2')
+trace.add_atrace_instant(
+    ts=to_s(401),
+    tid=2,
+    pid=2,
+    buf='launchingActivity#2:completed:com.google.android.calculator')
+
+# Third launch: just installd
+trace.add_atrace_async_begin(
+    ts=to_s(500), tid=2, pid=2, buf='launchingActivity#3')
+trace.add_sched(ts=to_s(505), prev_pid=0, next_pid=11)
+trace.add_sched(ts=to_s(510), prev_pid=11, next_pid=0)
+trace.add_atrace_async_end(
+    ts=to_s(750), tid=2, pid=2, buf='launchingActivity#3')
+trace.add_atrace_instant(
+    ts=to_s(751),
+    tid=2,
+    pid=2,
+    buf='launchingActivity#3:completed:com.google.android.deskclock')
+
+# Third launch: just installd
+trace.add_atrace_async_begin(
+    ts=to_s(700), tid=2, pid=2, buf='launchingActivity#4')
+trace.add_sched(ts=to_s(705), prev_pid=0, next_pid=10)
+trace.add_sched(ts=to_s(710), prev_pid=10, next_pid=0)
+trace.add_sched(ts=to_s(715), prev_pid=0, next_pid=11)
+trace.add_sched(ts=to_s(720), prev_pid=11, next_pid=0)
+trace.add_atrace_async_end(
+    ts=to_s(800), tid=2, pid=2, buf='launchingActivity#4')
+trace.add_atrace_instant(
+    ts=to_s(801),
+    tid=2,
+    pid=2,
+    buf='launchingActivity#4:completed:com.google.android.gm')
+
+sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/diff_tests/startup/android_startup_lock_contention.out b/test/trace_processor/diff_tests/startup/android_startup_lock_contention.out
new file mode 100644
index 0000000..e570b22
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/android_startup_lock_contention.out
@@ -0,0 +1,73 @@
+android_startup {
+  startup {
+    startup_id: 1
+    package_name: "com.google.android.calendar"
+    process_name: "com.google.android.calendar"
+    zygote_new_process: false
+    to_first_frame {
+      dur_ns: 100
+      main_thread_by_task_state {
+        running_dur_ns: 0
+        runnable_dur_ns: 0
+        uninterruptible_sleep_dur_ns: 0
+        interruptible_sleep_dur_ns: 0
+        uninterruptible_io_sleep_dur_ns: 0
+        uninterruptible_non_io_sleep_dur_ns: 0
+      }
+      other_processes_spawned_count: 0
+      time_bind_application {
+        dur_ns: 3
+        dur_ms: 3e-06
+      }
+      time_activity_start {
+        dur_ns: 1
+        dur_ms: 1e-06
+      }
+      time_activity_resume {
+        dur_ns: 1
+        dur_ms: 1e-06
+      }
+      dur_ms: 0.0001
+      to_bind_application {
+        dur_ns: 2
+        dur_ms: 2e-06
+      }
+      time_lock_contention_thread_main {
+        dur_ns: 20
+        dur_ms: 2e-05
+      }
+      time_monitor_contention_thread_main {
+        dur_ns: 10
+        dur_ms: 1e-05
+      }
+    }
+    activity_hosting_process_count: 1
+    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
+      }
+    }
+    event_timestamps {
+      intent_received: 110
+      first_frame: 210
+    }
+    system_state {
+      dex2oat_running: false
+      installd_running: false
+      broadcast_dispatched_count: 0
+      broadcast_received_count: 0
+      installd_dur_ns: 0
+      dex2oat_dur_ns: 0
+    }
+    startup_type: "cold"
+  }
+}
diff --git a/test/trace_processor/startup/android_startup_lock_contention.py b/test/trace_processor/diff_tests/startup/android_startup_lock_contention.py
similarity index 100%
rename from test/trace_processor/startup/android_startup_lock_contention.py
rename to test/trace_processor/diff_tests/startup/android_startup_lock_contention.py
diff --git a/test/trace_processor/diff_tests/startup/android_startup_lock_contention_slow.out b/test/trace_processor/diff_tests/startup/android_startup_lock_contention_slow.out
new file mode 100644
index 0000000..09a342a
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/android_startup_lock_contention_slow.out
@@ -0,0 +1,76 @@
+android_startup {
+  startup {
+    startup_id: 1
+    package_name: "com.google.android.calendar"
+    process_name: "com.google.android.calendar"
+    zygote_new_process: false
+    to_first_frame {
+      dur_ns: 100000000000
+      main_thread_by_task_state {
+        running_dur_ns: 0
+        runnable_dur_ns: 0
+        uninterruptible_sleep_dur_ns: 0
+        interruptible_sleep_dur_ns: 0
+	uninterruptible_io_sleep_dur_ns: 0
+	uninterruptible_non_io_sleep_dur_ns: 0
+      }
+      other_processes_spawned_count: 0
+      time_bind_application {
+        dur_ns: 3000000000
+        dur_ms: 3000.0
+      }
+      time_activity_start {
+        dur_ns: 1000000000
+        dur_ms: 1000.0
+      }
+      time_activity_resume {
+        dur_ns: 1000000000
+        dur_ms: 1000.0
+      }
+      dur_ms: 100000.0
+      to_bind_application {
+        dur_ns: 2000000000
+        dur_ms: 2000.0
+      }
+      time_lock_contention_thread_main {
+        dur_ns: 20000000000
+        dur_ms: 20000.0
+      }
+      time_monitor_contention_thread_main {
+        dur_ns: 10000000000
+        dur_ms: 10000.0
+      }
+    }
+    activity_hosting_process_count: 1
+    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
+      }
+    }
+    event_timestamps {
+      intent_received: 110000000000
+      first_frame: 210000000000
+    }
+    system_state {
+      dex2oat_running: false
+      installd_running: false
+      broadcast_dispatched_count: 0
+      broadcast_received_count: 0
+      installd_dur_ns: 0
+      dex2oat_dur_ns: 0
+    }
+    slow_start_reason: "Time spent in bindApplication"
+    slow_start_reason: "Main Thread - Lock contention"
+    slow_start_reason: "Main Thread - Monitor contention"
+    startup_type: "cold"
+  }
+}
diff --git a/test/trace_processor/diff_tests/startup/android_startup_lock_contention_slow.py b/test/trace_processor/diff_tests/startup/android_startup_lock_contention_slow.py
new file mode 100644
index 0000000..c447b08
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/android_startup_lock_contention_slow.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python3
+# Copyright (C) 2022 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
+
+import synth_common
+
+
+def to_s(ts):
+  return ts * 1000 * 1000 * 1000
+
+
+trace = synth_common.create_trace()
+trace.add_packet()
+trace.add_process(1, 0, 'init')
+trace.add_process(2, 1, 'system_server')
+trace.add_process(3, 1, 'com.google.android.calendar', 10001)
+trace.add_thread(4, 3, 'Binder')
+
+trace.add_package_list(
+    ts=to_s(1), name='com.google.android.calendar', uid=10001, version_code=123)
+
+trace.add_ftrace_packet(cpu=0)
+trace.add_atrace_async_begin(
+    ts=to_s(110), tid=2, pid=2, buf='launchingActivity#1')
+trace.add_atrace_async_end(
+    ts=to_s(210), tid=2, pid=2, buf='launchingActivity#1')
+
+# Required so we know this process is the one being started up.
+trace.add_atrace_begin(ts=to_s(112), tid=3, pid=3, buf='bindApplication')
+trace.add_atrace_end(ts=to_s(115), tid=3, pid=3)
+trace.add_atrace_begin(ts=to_s(115), tid=3, pid=3, buf='activityStart')
+trace.add_atrace_end(ts=to_s(116), tid=3, pid=3)
+trace.add_atrace_begin(ts=to_s(116), tid=3, pid=3, buf='activityResume')
+trace.add_atrace_end(ts=to_s(117), tid=3, pid=3)
+
+# Add some non-monitor lock contention.
+trace.add_atrace_begin(
+    ts=to_s(120),
+    tid=3,
+    pid=3,
+    buf='Lock contention on thread list lock (owner tid: 2)')
+trace.add_atrace_end(ts=to_s(130), tid=3, pid=3)
+
+# Add monitor contention
+trace.add_atrace_begin(
+    ts=to_s(140),
+    tid=3,
+    pid=3,
+    buf='Lock contention on a monitor lock (owner tid: 2)')
+trace.add_atrace_end(ts=to_s(150), tid=3, pid=3)
+
+# Lock contention on non-main thread should not be counted.
+trace.add_atrace_begin(
+    ts=to_s(155),
+    tid=4,
+    pid=3,
+    buf='Lock contention on a monitor lock (owner tid: 3)')
+trace.add_atrace_end(ts=to_s(160), tid=4, pid=3)
+
+# Lock contention in other process should not be counted.
+trace.add_atrace_begin(
+    ts=to_s(175),
+    tid=2,
+    pid=2,
+    buf='Lock contention on a monitor lock (owner tid: 3)')
+trace.add_atrace_end(ts=to_s(180), tid=2, pid=2)
+
+trace.add_atrace_instant(
+    ts=to_s(211),
+    tid=2,
+    pid=2,
+    buf='launchingActivity#1:completed:com.google.android.calendar')
+
+sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/diff_tests/startup/android_startup_minsdk33.out b/test/trace_processor/diff_tests/startup/android_startup_minsdk33.out
new file mode 100644
index 0000000..7a6d2eb
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/android_startup_minsdk33.out
@@ -0,0 +1,80 @@
+android_startup {
+  startup {
+    startup_id: 1
+    package_name: "com.google.android.calendar"
+    zygote_new_process: false
+    to_first_frame {
+      dur_ns: 100
+      main_thread_by_task_state {
+        running_dur_ns: 0
+        runnable_dur_ns: 0
+        uninterruptible_sleep_dur_ns: 0
+        interruptible_sleep_dur_ns: 0
+	uninterruptible_io_sleep_dur_ns: 0
+	uninterruptible_non_io_sleep_dur_ns: 0
+      }
+      other_processes_spawned_count: 0
+      dur_ms: 0.0001
+    }
+    activity_hosting_process_count: 0
+    event_timestamps {
+      intent_received: 110
+      first_frame: 210
+    }
+    system_state {
+      dex2oat_running: false
+      installd_running: false
+      broadcast_dispatched_count: 0
+      broadcast_received_count: 0
+      installd_dur_ns: 0
+      dex2oat_dur_ns: 0
+    }
+  }
+  startup {
+    startup_id: 2
+    package_name: "com.google.android.calendar"
+    process_name: "com.google.android.calendar"
+    zygote_new_process: false
+    to_first_frame {
+      dur_ns: 10
+      main_thread_by_task_state {
+        running_dur_ns: 0
+        runnable_dur_ns: 0
+        uninterruptible_sleep_dur_ns: 0
+        interruptible_sleep_dur_ns: 0
+        uninterruptible_io_sleep_dur_ns: 0
+        uninterruptible_non_io_sleep_dur_ns: 0
+      }
+      other_processes_spawned_count: 0
+      dur_ms: 1e-05
+    }
+    activity_hosting_process_count: 1
+    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
+      }
+    }
+    event_timestamps {
+      intent_received: 220
+      first_frame: 230
+    }
+    system_state {
+      dex2oat_running: false
+      installd_running: false
+      broadcast_dispatched_count: 0
+      broadcast_received_count: 0
+      installd_dur_ns: 0
+      dex2oat_dur_ns: 0
+    }
+    startup_type: "hot"
+  }
+}
diff --git a/test/trace_processor/diff_tests/startup/android_startup_minsdk33.py b/test/trace_processor/diff_tests/startup/android_startup_minsdk33.py
new file mode 100644
index 0000000..682b546
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/android_startup_minsdk33.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python3
+# 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.
+
+from os import sys, path
+
+import synth_common
+
+trace = synth_common.create_trace()
+trace.add_packet()
+trace.add_process(1, 0, 'init')
+trace.add_process(2, 1, 'system_server')
+trace.add_process(3, 1, 'com.google.android.calendar', 10001)
+
+trace.add_package_list(
+    ts=1, name='com.google.android.calendar', uid=10001, version_code=123)
+
+trace.add_ftrace_packet(cpu=0)
+trace.add_atrace_async_begin(ts=110, tid=2, pid=2, buf='launchingActivity#1')
+trace.add_atrace_async_end(ts=210, tid=2, pid=2, buf='launchingActivity#1')
+trace.add_atrace_instant(
+    ts=211,
+    tid=2,
+    pid=2,
+    buf='launchingActivity#1:completed:com.google.android.calendar')
+
+trace.add_atrace_async_begin(ts=220, tid=2, pid=2, buf='launchingActivity#2')
+trace.add_atrace_async_end(ts=230, tid=2, pid=2, buf='launchingActivity#2')
+trace.add_atrace_instant(
+    ts=231,
+    tid=2,
+    pid=2,
+    buf='launchingActivity#2:completed-hot:com.google.android.calendar')
+
+sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/startup/android_startup_powrails.out b/test/trace_processor/diff_tests/startup/android_startup_powrails.out
similarity index 100%
rename from test/trace_processor/startup/android_startup_powrails.out
rename to test/trace_processor/diff_tests/startup/android_startup_powrails.out
diff --git a/test/trace_processor/startup/android_startup_powrails.py b/test/trace_processor/diff_tests/startup/android_startup_powrails.py
similarity index 100%
rename from test/trace_processor/startup/android_startup_powrails.py
rename to test/trace_processor/diff_tests/startup/android_startup_powrails.py
diff --git a/test/trace_processor/diff_tests/startup/android_startup_process_track.out b/test/trace_processor/diff_tests/startup/android_startup_process_track.out
new file mode 100644
index 0000000..41b26e5
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/android_startup_process_track.out
@@ -0,0 +1,136 @@
+android_startup {
+  startup {
+    startup_id: 1
+    package_name: "com.google.android.calendar"
+    process_name: "com.google.android.calendar:debug"
+    zygote_new_process: false
+    to_first_frame {
+      dur_ns: 7
+      main_thread_by_task_state {
+        running_dur_ns: 0
+        runnable_dur_ns: 0
+        uninterruptible_sleep_dur_ns: 0
+        interruptible_sleep_dur_ns: 0
+        uninterruptible_io_sleep_dur_ns: 0
+        uninterruptible_non_io_sleep_dur_ns: 0
+      }
+      other_processes_spawned_count: 0
+      time_activity_manager {
+        dur_ns: 2
+        dur_ms: 2e-06
+      }
+      time_bind_application {
+        dur_ns: 1
+        dur_ms: 1e-06
+      }
+      time_activity_start {
+        dur_ns: 1
+        dur_ms: 1e-06
+      }
+      time_activity_resume {
+        dur_ns: 1
+        dur_ms: 1e-06
+      }
+      dur_ms: 7e-06
+      to_bind_application {
+        dur_ns: 3
+        dur_ms: 3e-06
+      }
+    }
+    activity_hosting_process_count: 1
+    process {
+      name: "com.google.android.calendar:debug"
+      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
+      }
+    }
+    event_timestamps {
+      intent_received: 100
+      first_frame: 107
+    }
+    system_state {
+      dex2oat_running: false
+      installd_running: false
+      broadcast_dispatched_count: 0
+      broadcast_received_count: 0
+      installd_dur_ns: 0
+      dex2oat_dur_ns: 0
+    }
+    startup_type: "cold"
+  }
+  startup {
+    startup_id: 2
+    package_name: "com.google.android.calendar"
+    process_name: "com.google.android.calendar"
+    zygote_new_process: false
+    to_first_frame {
+      dur_ns: 7
+      main_thread_by_task_state {
+        running_dur_ns: 0
+        runnable_dur_ns: 0
+        uninterruptible_sleep_dur_ns: 0
+        interruptible_sleep_dur_ns: 0
+        uninterruptible_io_sleep_dur_ns: 0
+        uninterruptible_non_io_sleep_dur_ns: 0
+      }
+      other_processes_spawned_count: 0
+      time_activity_manager {
+        dur_ns: 2
+        dur_ms: 2e-06
+      }
+      time_bind_application {
+        dur_ns: 1
+        dur_ms: 1e-06
+      }
+      time_activity_start {
+        dur_ns: 1
+        dur_ms: 1e-06
+      }
+      time_activity_resume {
+        dur_ns: 1
+        dur_ms: 1e-06
+      }
+      dur_ms: 7e-06
+      to_bind_application {
+        dur_ns: 3
+        dur_ms: 3e-06
+      }
+    }
+    activity_hosting_process_count: 1
+    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
+      }
+    }
+    event_timestamps {
+      intent_received: 200
+      first_frame: 207
+    }
+    system_state {
+      dex2oat_running: false
+      installd_running: false
+      broadcast_dispatched_count: 0
+      broadcast_received_count: 0
+      installd_dur_ns: 0
+      dex2oat_dur_ns: 0
+    }
+    startup_type: "cold"
+  }
+}
diff --git a/test/trace_processor/startup/android_startup_process_track.py b/test/trace_processor/diff_tests/startup/android_startup_process_track.py
similarity index 100%
rename from test/trace_processor/startup/android_startup_process_track.py
rename to test/trace_processor/diff_tests/startup/android_startup_process_track.py
diff --git a/test/trace_processor/diff_tests/startup/android_startup_slow.out b/test/trace_processor/diff_tests/startup/android_startup_slow.out
new file mode 100644
index 0000000..333883a
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/android_startup_slow.out
@@ -0,0 +1,72 @@
+android_startup {
+  startup {
+    startup_id: 2
+    package_name: "com.google.android.calendar"
+    process_name: "com.google.android.calendar"
+    zygote_new_process: false
+    to_first_frame {
+      dur_ns: 108000000000
+      main_thread_by_task_state {
+        running_dur_ns: 10000000000
+        runnable_dur_ns: 90000000000
+        uninterruptible_sleep_dur_ns: 5000000000
+        interruptible_sleep_dur_ns: 5000000000
+        uninterruptible_io_sleep_dur_ns: 5000000000
+        uninterruptible_non_io_sleep_dur_ns: 0
+      }
+      other_processes_spawned_count: 1
+      time_activity_manager {
+        dur_ns: 8000000000
+        dur_ms: 8000
+      }
+      time_activity_start {
+        dur_ns: 2000000000
+        dur_ms: 2000
+      }
+      time_activity_resume {
+        dur_ns: 1000000000
+        dur_ms: 1000
+      }
+      dur_ms: 108000
+    }
+    activity_hosting_process_count: 1
+    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
+      }
+    }
+    report_fully_drawn {
+      dur_ns: 198000000000
+      dur_ms: 198000
+    }
+    event_timestamps {
+      intent_received: 102000000000
+      first_frame: 210000000000
+    }
+    system_state {
+      dex2oat_running: false
+      installd_running: false
+      broadcast_dispatched_count: 0
+      broadcast_received_count: 0
+      most_active_non_launch_processes: "init"
+      most_active_non_launch_processes: "com.google.android.calendar"
+      installd_dur_ns: 0
+      dex2oat_dur_ns: 0
+    }
+    startup_type: "warm"
+    slow_start_reason: "Main Thread - Time spent in Running state"
+    slow_start_reason: "Main Thread - Time spent in Runnable state"
+    slow_start_reason: "Main Thread - Time spent in interruptible sleep state"
+    slow_start_reason: "Main Thread - Time spent in Blocking I/O"
+    slow_start_reason: "Potential CPU contention with init"
+  }
+}
diff --git a/test/trace_processor/diff_tests/startup/android_startup_slow.py b/test/trace_processor/diff_tests/startup/android_startup_slow.py
new file mode 100644
index 0000000..c85d320
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/android_startup_slow.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python3
+# 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.
+
+from os import sys, path
+
+import synth_common
+
+
+def to_s(ts):
+  return ts * 1000 * 1000 * 1000
+
+
+trace = synth_common.create_trace()
+trace.add_packet()
+trace.add_process(1, 0, 'init')
+trace.add_process(2, 1, 'system_server')
+trace.add_process(3, 1, 'com.google.android.calendar', 10001)
+trace.add_process(4, 3, 'com.google.android.calendar', 10001)
+
+trace.add_package_list(
+    ts=to_s(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(
+    ts=to_s(100),
+    tid=2,
+    pid=2,
+    buf='MetricsLogger:launchObserverNotifyIntentStarted')
+trace.add_atrace_end(ts=to_s(101), tid=2, pid=2)
+
+# Start intent for a successful launch of calendar
+trace.add_atrace_begin(
+    ts=to_s(102),
+    tid=2,
+    pid=2,
+    buf='MetricsLogger:launchObserverNotifyIntentStarted')
+trace.add_atrace_end(ts=to_s(103), tid=2, pid=2)
+
+trace.add_atrace_async_begin(
+    ts=to_s(110), tid=2, pid=2, buf='launching: com.google.android.calendar')
+
+trace.add_sched(ts=to_s(110), prev_pid=0, next_pid=3)
+
+# As the process already existed before intent started, this is a
+# warm/hot start (we choose warm). Therefore, emit an activityStart
+# slice.
+trace.add_atrace_begin(ts=to_s(115), tid=3, pid=3, buf='activityStart')
+trace.add_atrace_end(ts=to_s(117), tid=3, pid=3)
+trace.add_atrace_begin(ts=to_s(117), tid=3, pid=3, buf='activityResume')
+trace.add_atrace_end(ts=to_s(118), tid=3, pid=3)
+
+# P1: 5s interruptable sleep
+trace.add_sched(ts=to_s(120), prev_pid=3, next_pid=0, prev_state='S')
+trace.add_sched(ts=to_s(125), prev_pid=0, next_pid=3)
+# P1: 5s blocking I/O state
+trace.add_sched(ts=to_s(125), prev_pid=3, next_pid=0, prev_state='D')
+trace.add_sched_blocked_reason(ts=to_s(127), pid=3, io_wait=1, unblock_pid=4)
+trace.add_sched(ts=to_s(130), prev_pid=0, next_pid=3)
+
+trace.add_sched(ts=to_s(130), prev_pid=3, next_pid=4)
+
+# Create an unrelated task
+trace.add_newtask(ts=to_s(155), tid=1, new_tid=5, new_comm='', flags=0)
+
+# P2: 30ns running
+trace.add_sched(ts=to_s(160), prev_pid=4, next_pid=0, prev_state='R')
+# P2: 49ns runnable
+trace.add_sched(ts=to_s(209), prev_pid=0, next_pid=4)
+# P2: 1ns running
+trace.add_sched(ts=to_s(210), prev_pid=4, next_pid=0)
+
+trace.add_atrace_async_end(
+    ts=to_s(210), tid=2, pid=2, buf='launching: com.google.android.calendar')
+trace.add_atrace_begin(
+    ts=to_s(211),
+    tid=2,
+    pid=2,
+    buf='MetricsLogger:launchObserverNotifyActivityLaunchFinished')
+trace.add_atrace_end(ts=to_s(212), tid=2, pid=2)
+
+# Some time after, add a slice for fully drawn frame.
+trace.add_atrace_begin(
+    ts=to_s(300),
+    tid=3,
+    pid=3,
+    buf='reportFullyDrawn() for \{com.google.android.calendar\}')
+trace.add_atrace_end(ts=to_s(305), tid=2, pid=2)
+
+# Start intent for calendar, we failed to launch the activity.
+trace.add_atrace_begin(
+    ts=to_s(402),
+    tid=2,
+    pid=2,
+    buf='MetricsLogger:launchObserverNotifyIntentStarted')
+trace.add_atrace_end(ts=to_s(403), tid=2, pid=2)
+
+trace.add_atrace_async_begin(
+    ts=to_s(410), tid=2, pid=2, buf='launching: com.google.android.calendar')
+
+trace.add_atrace_async_end(
+    ts=to_s(510),
+    tid=2,
+    pid=2,
+    buf='launching: com.google.android.apps.nexuslauncher')
+
+trace.add_ftrace_packet(cpu=1)
+trace.add_sched(ts=to_s(160), prev_pid=0, next_pid=1)
+trace.add_sched(ts=to_s(200), prev_pid=1, next_pid=0)
+
+sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/diff_tests/startup/android_startup_unlock.out b/test/trace_processor/diff_tests/startup/android_startup_unlock.out
new file mode 100644
index 0000000..ba5d58f
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/android_startup_unlock.out
@@ -0,0 +1,34 @@
+android_startup {
+  startup {
+    startup_id: 1
+    package_name: "com.google.android.calendar"
+    zygote_new_process: false
+    to_first_frame {
+      dur_ns: 100
+      main_thread_by_task_state {
+        running_dur_ns: 0
+        runnable_dur_ns: 0
+        uninterruptible_sleep_dur_ns: 0
+        interruptible_sleep_dur_ns: 0
+        uninterruptible_io_sleep_dur_ns: 0
+        uninterruptible_non_io_sleep_dur_ns: 0
+      }
+      other_processes_spawned_count: 0
+      dur_ms: 0.0001
+    }
+    activity_hosting_process_count: 0
+    event_timestamps {
+      intent_received: 100
+      first_frame: 200
+    }
+    system_state {
+      dex2oat_running: false
+      installd_running: false
+      broadcast_dispatched_count: 0
+      broadcast_received_count: 0
+      installd_dur_ns: 0
+      dex2oat_dur_ns: 0
+    }
+    slow_start_reason: "Unlock running during launch"
+  }
+}
diff --git a/test/trace_processor/diff_tests/startup/android_startup_unlock.py b/test/trace_processor/diff_tests/startup/android_startup_unlock.py
new file mode 100644
index 0000000..59d2d45
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/android_startup_unlock.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python3
+# 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.
+
+from os import sys
+
+import synth_common
+
+trace = synth_common.create_trace()
+trace.add_packet()
+trace.add_process(1, 0, 'init')
+trace.add_process(2, 1, 'system_server')
+trace.add_process(3, 1, 'com.google.android.calendar', 10003)
+trace.add_process(4, 1, 'com.android.systemui', 10004)
+
+trace.add_package_list(
+    ts=1, name='com.google.android.calendar', uid=10003, version_code=123)
+trace.add_package_list(
+    ts=1, name='com.android.systemui', uid=10004, version_code=123)
+
+trace.add_ftrace_packet(cpu=0)
+
+trace.add_atrace_async_begin(ts=100, tid=2, pid=2, buf='launchingActivity#1')
+trace.add_atrace_async_end(ts=200, tid=2, pid=2, buf='launchingActivity#1')
+
+trace.add_atrace_begin(
+    ts=130, tid=2, pid=4, buf='KeyguardUpdateMonitor#onAuthenticationSucceeded')
+trace.add_atrace_end(ts=133, tid=2, pid=4)
+
+trace.add_atrace_instant(
+    ts=201,
+    tid=2,
+    pid=2,
+    buf='launchingActivity#1:completed:com.google.android.calendar')
+
+sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/diff_tests/startup/tests.py b/test/trace_processor/diff_tests/startup/tests.py
new file mode 100644
index 0000000..b365fcf
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/tests.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class Startup(TestSuite):
+  # Contains tests related to the startup of Android apps. Test that
+  # running in parallel are flagged.
+  def test_android_startup_installd_dex2oat(self):
+    return DiffTestBlueprint(
+        trace=Path('android_startup_installd_dex2oat.py'),
+        query=Metric('android_startup'),
+        out=Path('android_startup_installd_dex2oat.out'))
+
+  def test_android_startup_installd_dex2oat_slow(self):
+    return DiffTestBlueprint(
+        trace=Path('android_startup_installd_dex2oat_slow.py'),
+        query=Metric('android_startup'),
+        out=Path('android_startup_installd_dex2oat_slow.out'))
+
+  # Test that unlocks running in parallel are flagged.
+  def test_android_startup_unlock(self):
+    return DiffTestBlueprint(
+        trace=Path('android_startup_unlock.py'),
+        query=Metric('android_startup'),
+        out=Path('android_startup_unlock.out'))
diff --git a/test/trace_processor/diff_tests/startup/tests_broadcasts.py b/test/trace_processor/diff_tests/startup/tests_broadcasts.py
new file mode 100644
index 0000000..34b6756
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/tests_broadcasts.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class StartupBroadcasts(TestSuite):
+  # Test that broadcasts are correctly counted.
+  def test_android_startup_broadcast(self):
+    return DiffTestBlueprint(
+        trace=Path('android_startup_broadcast.py'),
+        query=Metric('android_startup'),
+        out=Path('android_startup_broadcast.out'))
+
+  def test_android_startup_broadcast_multiple(self):
+    return DiffTestBlueprint(
+        trace=Path('android_startup_broadcast_multiple.py'),
+        query=Metric('android_startup'),
+        out=Path('android_startup_broadcast_multiple.out'))
diff --git a/test/trace_processor/diff_tests/startup/tests_lock_contention.py b/test/trace_processor/diff_tests/startup/tests_lock_contention.py
new file mode 100644
index 0000000..f41bfe3
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/tests_lock_contention.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class StartupLockContention(TestSuite):
+  # Test lock contention is correctly attributed.
+  def test_android_startup_lock_contention(self):
+    return DiffTestBlueprint(
+        trace=Path('android_startup_lock_contention.py'),
+        query=Metric('android_startup'),
+        out=Path('android_startup_lock_contention.out'))
+
+  def test_android_startup_lock_contention_slow(self):
+    return DiffTestBlueprint(
+        trace=Path('android_startup_lock_contention_slow.py'),
+        query=Metric('android_startup'),
+        out=Path('android_startup_lock_contention_slow.out'))
diff --git a/test/trace_processor/diff_tests/startup/tests_metrics.py b/test/trace_processor/diff_tests/startup/tests_metrics.py
new file mode 100644
index 0000000..99911da
--- /dev/null
+++ b/test/trace_processor/diff_tests/startup/tests_metrics.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class StartupMetrics(TestSuite):
+
+  def test_android_startup(self):
+    return DiffTestBlueprint(
+        trace=Path('android_startup.py'),
+        query=Metric('android_startup'),
+        out=Path('android_startup.out'))
+
+  def test_android_startup_slow(self):
+    return DiffTestBlueprint(
+        trace=Path('android_startup_slow.py'),
+        query=Metric('android_startup'),
+        out=Path('android_startup_slow.out'))
+
+  def test_android_startup_minsdk33(self):
+    return DiffTestBlueprint(
+        trace=Path('android_startup_minsdk33.py'),
+        query=Metric('android_startup'),
+        out=Path('android_startup_minsdk33.out'))
+
+  def test_android_startup_breakdown(self):
+    return DiffTestBlueprint(
+        trace=Path('android_startup_breakdown.py'),
+        query=Metric('android_startup'),
+        out=Path('android_startup_breakdown.out'))
+
+  def test_android_startup_breakdown_slow(self):
+    return DiffTestBlueprint(
+        trace=Path('android_startup_breakdown_slow.py'),
+        query=Metric('android_startup'),
+        out=Path('android_startup_breakdown_slow.out'))
+
+  def test_android_startup_process_track(self):
+    return DiffTestBlueprint(
+        trace=Path('android_startup_process_track.py'),
+        query=Metric('android_startup'),
+        out=Path('android_startup_process_track.out'))
+
+  def test_android_startup_attribution(self):
+    return DiffTestBlueprint(
+        trace=Path('android_startup_attribution.py'),
+        query=Metric('android_startup'),
+        out=Path('android_startup_attribution.out'))
+
+  def test_android_startup_attribution_slow(self):
+    return DiffTestBlueprint(
+        trace=Path('android_startup_attribution_slow.py'),
+        query=Metric('android_startup'),
+        out=Path('android_startup_attribution_slow.out'))
+
+  # Other metrics associated with startup.
+  def test_android_batt_counters(self):
+    return DiffTestBlueprint(
+        trace=Path('android_startup_battery.py'),
+        query=Metric('android_batt'),
+        out=TextProto(r"""
+        android_batt{
+           battery_counters{
+              timestamp_ns: 20
+              charge_counter_uah: 52
+              capacity_percent: 0.2
+              current_ua: 10
+              current_avg_ua: 12
+           }
+           battery_counters {
+              timestamp_ns: 52
+              charge_counter_uah: 32
+              capacity_percent: 0.8
+              current_ua: 8
+              current_avg_ua: 93
+           }
+           battery_counters {
+              timestamp_ns: 80
+              charge_counter_uah: 15
+              capacity_percent: 0.5
+              current_ua: 9
+              current_avg_ua: 5
+           }
+           battery_counters {
+              timestamp_ns: 92
+              charge_counter_uah: 21
+              capacity_percent: 0.3
+              current_avg_ua: 25
+           }
+        }
+        """))
+
+  def test_android_startup_cpu(self):
+    return DiffTestBlueprint(
+        trace=Path('android_startup_cpu.py'),
+        query=Metric('android_cpu'),
+        out=Path('android_startup_cpu.out'))
+
+  def test_android_startup_powrails(self):
+    return DiffTestBlueprint(
+        trace=Path('android_startup_powrails.py'),
+        query=Metric('android_powrails'),
+        out=Path('android_startup_powrails.out'))
diff --git a/test/trace_processor/diff_tests/tables/counter_dur_test.sql b/test/trace_processor/diff_tests/tables/counter_dur_test.sql
new file mode 100644
index 0000000..67a60b9
--- /dev/null
+++ b/test/trace_processor/diff_tests/tables/counter_dur_test.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/tables/counters_group_by_freq.py b/test/trace_processor/diff_tests/tables/counters_group_by_freq.py
similarity index 100%
rename from test/trace_processor/tables/counters_group_by_freq.py
rename to test/trace_processor/diff_tests/tables/counters_group_by_freq.py
diff --git a/test/trace_processor/tables/counters_where_cpu.py b/test/trace_processor/diff_tests/tables/counters_where_cpu.py
similarity index 100%
rename from test/trace_processor/tables/counters_where_cpu.py
rename to test/trace_processor/diff_tests/tables/counters_where_cpu.py
diff --git a/test/trace_processor/tables/filter_row_vector_example_android_trace_30s.out b/test/trace_processor/diff_tests/tables/filter_row_vector_example_android_trace_30s.out
similarity index 100%
rename from test/trace_processor/tables/filter_row_vector_example_android_trace_30s.out
rename to test/trace_processor/diff_tests/tables/filter_row_vector_example_android_trace_30s.out
diff --git a/test/trace_processor/tables/nulls.out b/test/trace_processor/diff_tests/tables/nulls.out
similarity index 100%
rename from test/trace_processor/tables/nulls.out
rename to test/trace_processor/diff_tests/tables/nulls.out
diff --git a/test/trace_processor/diff_tests/tables/tests.py b/test/trace_processor/diff_tests/tables/tests.py
new file mode 100644
index 0000000..2f2e926
--- /dev/null
+++ b/test/trace_processor/diff_tests/tables/tests.py
@@ -0,0 +1,213 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class Tables(TestSuite):
+  # Contains tests for the handling of tables by trace processor. The focus of
+  # here is to check that trace processor is correctly returning and handling
+  # on the really important tables in trace processor.  Note: It's generally
+  # advisable to add tests here. Check the guidance provided by
+  # for choosing which folder to add a new test to. Window table
+  def test_android_sched_and_ps_smoke_window(self):
+    return DiffTestBlueprint(
+        trace=DataPath('android_sched_and_ps.pb'),
+        query="""
+        SELECT * FROM "window";
+        """,
+        out=Csv("""
+        "ts","dur","quantum_ts"
+        0,9223372036854775807,0
+        """))
+
+  # Null printing
+  def test_nulls(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query="""
+        CREATE TABLE null_test (
+          primary_key INTEGER PRIMARY KEY,
+          int_nulls INTEGER,
+          string_nulls STRING,
+          double_nulls DOUBLE,
+          start_int_nulls INTEGER,
+          start_string_nulls STRING,
+          start_double_nulls DOUBLE,
+          all_nulls INTEGER
+        );
+
+        INSERT INTO null_test(
+          int_nulls,
+          string_nulls,
+          double_nulls,
+          start_int_nulls,
+          start_string_nulls,
+          start_double_nulls
+        )
+        VALUES
+        (1, "test", 2.0, NULL, NULL, NULL),
+        (2, NULL, NULL, NULL, "test", NULL),
+        (1, "other", NULL, NULL, NULL, NULL),
+        (4, NULL, NULL, NULL, NULL, 1.0),
+        (NULL, "test", 1.0, 1, NULL, NULL);
+
+        SELECT * FROM null_test;
+        """,
+        out=Path('nulls.out'))
+
+  # Thread table
+  def test_thread_main_thread(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          timestamp: 1
+          process_tree {
+            processes {
+              pid: 5
+              ppid: 1
+              cmdline: "com.google.pid5"
+            }
+            threads {
+              tid: 5
+              tgid: 5
+            }
+            threads {
+              tid: 7
+              tgid: 5
+              name: "tid7"
+            }
+            processes {
+              pid: 11
+              ppid: 1
+              cmdline: "com.google.pid11"
+            }
+            threads {
+              tid: 11
+              tgid: 11
+              name: "tid11"
+            }
+            threads {
+              tid: 12
+              tgid: 11
+              name: "tid12"
+            }
+          }
+        }
+        packet {
+          timestamp: 2
+          ftrace_events {
+            cpu: 0
+            event {
+              timestamp: 2
+              pid: 99
+              lowmemory_kill {
+                pid: 99
+              }
+            }
+          }
+        }
+        """),
+        query="""
+        SELECT
+          tid,
+          is_main_thread
+        FROM thread
+        WHERE tid IN (5, 7, 11, 12, 99)
+        ORDER BY tid;
+        """,
+        out=Csv("""
+        "tid","is_main_thread"
+        5,1
+        7,0
+        11,1
+        12,0
+        99,"[NULL]"
+        """))
+
+  # Json output
+  def test_trace_metadata(self):
+    return DiffTestBlueprint(
+        trace=DataPath('memory_counters.pb'),
+        query=Metric('trace_metadata'),
+        out=Path('trace_metadata.json.out'))
+
+  # Processes as a metric
+  def test_android_task_names(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        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
+            }
+          }
+        }
+        """),
+        query=Metric('android_task_names'),
+        out=TextProto(r"""
+        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"
+          }
+        }
+        """))
+
+  # Ftrace stats imports in metadata and stats tables
+  def test_ftrace_setup_errors(self):
+    return DiffTestBlueprint(
+        trace=DataPath('ftrace_error_stats.pftrace'),
+        query="""
+        SELECT value FROM stats WHERE name = 'ftrace_setup_errors'
+        UNION ALL
+        SELECT str_value FROM metadata WHERE name = 'ftrace_setup_errors';
+        """,
+        out=Csv("""
+        "value"
+        3
+        "Ftrace event unknown: foo/bar
+        Ftrace event unknown: sched/foobar
+        Atrace failures: error: unknown tracing category "bar"
+        error enabling tracing category "bar"
+        "
+        """))
diff --git a/test/trace_processor/diff_tests/tables/tests_counters.py b/test/trace_processor/diff_tests/tables/tests_counters.py
new file mode 100644
index 0000000..8ed753f
--- /dev/null
+++ b/test/trace_processor/diff_tests/tables/tests_counters.py
@@ -0,0 +1,127 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class TablesCounters(TestSuite):
+  # Counters table
+  def test_synth_1_filter_counter(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query="""
+        SELECT COUNT(*)
+        FROM counter
+        WHERE
+          track_id = 0;
+        """,
+        out=Csv("""
+        "COUNT(*)"
+        2
+        """))
+
+  def test_memory_counters_b120278869_neg_ts_end(self):
+    return DiffTestBlueprint(
+        trace=DataPath('memory_counters.pb'),
+        query="""
+        SELECT count(*) FROM counters WHERE -1 < ts;
+        """,
+        out=Csv("""
+        "count(*)"
+        98688
+        """))
+
+  def test_counters_where_cpu_counters_where_cpu(self):
+    return DiffTestBlueprint(
+        trace=Path('counters_where_cpu.py'),
+        query="""
+        SELECT
+          ts,
+          lead(ts, 1, ts) OVER (PARTITION BY name ORDER BY ts) - ts AS dur,
+          value
+        FROM counter c
+        JOIN cpu_counter_track t ON t.id = c.track_id
+        WHERE cpu = 1;
+        """,
+        out=Csv("""
+        "ts","dur","value"
+        1000,1,3000.000000
+        1001,0,4000.000000
+        """))
+
+  def test_counters_group_by_freq_counters_group_by_freq(self):
+    return DiffTestBlueprint(
+        trace=Path('counters_group_by_freq.py'),
+        query="""
+        SELECT
+          value,
+          sum(dur) AS dur_sum
+        FROM (
+          SELECT value,
+            lead(ts) OVER (PARTITION BY name, track_id ORDER BY ts) - ts AS dur
+          FROM counter
+          JOIN counter_track ON counter.track_id = counter_track.id
+        )
+        WHERE value > 0
+        GROUP BY value
+        ORDER BY dur_sum DESC;
+        """,
+        out=Csv("""
+        "value","dur_sum"
+        4000.000000,2
+        3000.000000,1
+        """))
+
+  def test_filter_row_vector_example_android_trace_30s(self):
+    return DiffTestBlueprint(
+        trace=DataPath('example_android_trace_30s.pb'),
+        query="""
+        SELECT ts
+        FROM counter
+        WHERE
+          ts > 72563651549
+          AND track_id = (
+            SELECT t.id
+            FROM process_counter_track t
+            JOIN process p USING (upid)
+            WHERE
+              t.name = 'Heap size (KB)'
+              AND p.pid = 1204
+          )
+          AND value != 17952.000000
+        LIMIT 20;
+        """,
+        out=Path('filter_row_vector_example_android_trace_30s.out'))
+
+  def test_counter_dur_example_android_trace_30s(self):
+    return DiffTestBlueprint(
+        trace=DataPath('example_android_trace_30s.pb'),
+        query=Path('counter_dur_test.sql'),
+        out=Csv("""
+        "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/diff_tests/tables/tests_sched.py b/test/trace_processor/diff_tests/tables/tests_sched.py
new file mode 100644
index 0000000..1f1ee49
--- /dev/null
+++ b/test/trace_processor/diff_tests/tables/tests_sched.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class TablesSched(TestSuite):
+  # Sched table
+  def test_synth_1_filter_sched(self):
+    return DiffTestBlueprint(
+        trace=Path('../common/synth_1.py'),
+        query="""
+        SELECT ts, cpu, dur FROM sched
+        WHERE
+          cpu = 1
+          AND dur > 50
+          AND dur <= 100
+          AND ts >= 100
+          AND ts <= 400;
+        """,
+        out=Csv("""
+        "ts","cpu","dur"
+        170,1,80
+        """))
+
+  def test_android_sched_and_ps_b119496959(self):
+    return DiffTestBlueprint(
+        trace=DataPath('android_sched_and_ps.pb'),
+        query="""
+        SELECT ts, cpu FROM sched WHERE ts >= 81473797418963 LIMIT 10;
+        """,
+        out=Csv("""
+        "ts","cpu"
+        81473797824982,3
+        81473797942847,3
+        81473798135399,0
+        81473798786857,2
+        81473798875451,3
+        81473799019930,2
+        81473799079982,0
+        81473800089357,3
+        81473800144461,3
+        81473800441805,3
+        """))
+
+  def test_android_sched_and_ps_b119301023(self):
+    return DiffTestBlueprint(
+        trace=DataPath('android_sched_and_ps.pb'),
+        query="""
+        SELECT ts FROM sched
+        WHERE ts > 0.1 + 1e9
+        LIMIT 10;
+        """,
+        out=Csv("""
+        "ts"
+        81473010031230
+        81473010109251
+        81473010121751
+        81473010179772
+        81473010203886
+        81473010234720
+        81473010278522
+        81473010308470
+        81473010341386
+        81473010352792
+        """))
diff --git a/test/trace_processor/tables/trace_metadata.json.out b/test/trace_processor/diff_tests/tables/trace_metadata.json.out
similarity index 100%
rename from test/trace_processor/tables/trace_metadata.json.out
rename to test/trace_processor/diff_tests/tables/trace_metadata.json.out
diff --git a/test/trace_processor/track_event/experimental_slice_layout_depth.py b/test/trace_processor/diff_tests/track_event/experimental_slice_layout_depth.py
similarity index 100%
rename from test/trace_processor/track_event/experimental_slice_layout_depth.py
rename to test/trace_processor/diff_tests/track_event/experimental_slice_layout_depth.py
diff --git a/test/trace_processor/track_event/flow_events_proto_v1.textproto b/test/trace_processor/diff_tests/track_event/flow_events_proto_v1.textproto
similarity index 100%
rename from test/trace_processor/track_event/flow_events_proto_v1.textproto
rename to test/trace_processor/diff_tests/track_event/flow_events_proto_v1.textproto
diff --git a/test/trace_processor/track_event/flow_events_proto_v2.textproto b/test/trace_processor/diff_tests/track_event/flow_events_proto_v2.textproto
similarity index 100%
rename from test/trace_processor/track_event/flow_events_proto_v2.textproto
rename to test/trace_processor/diff_tests/track_event/flow_events_proto_v2.textproto
diff --git a/test/trace_processor/track_event/flow_events_track_event.textproto b/test/trace_processor/diff_tests/track_event/flow_events_track_event.textproto
similarity index 100%
rename from test/trace_processor/track_event/flow_events_track_event.textproto
rename to test/trace_processor/diff_tests/track_event/flow_events_track_event.textproto
diff --git a/test/trace_processor/track_event/legacy_async_event.out b/test/trace_processor/diff_tests/track_event/legacy_async_event.out
similarity index 100%
rename from test/trace_processor/track_event/legacy_async_event.out
rename to test/trace_processor/diff_tests/track_event/legacy_async_event.out
diff --git a/test/trace_processor/track_event/legacy_async_event.textproto b/test/trace_processor/diff_tests/track_event/legacy_async_event.textproto
similarity index 100%
rename from test/trace_processor/track_event/legacy_async_event.textproto
rename to test/trace_processor/diff_tests/track_event/legacy_async_event.textproto
diff --git a/test/trace_processor/diff_tests/track_event/range_of_interest.textproto b/test/trace_processor/diff_tests/track_event/range_of_interest.textproto
new file mode 100644
index 0000000..96b1da5
--- /dev/null
+++ b/test/trace_processor/diff_tests/track_event/range_of_interest.textproto
@@ -0,0 +1,117 @@
+packet {
+  timestamp: 0
+  trusted_packet_sequence_id: 1
+  incremental_state_cleared: true
+  track_event_range_of_interest {
+    start_us: 12
+  }
+}
+
+# Track for slice begin/end events.
+packet {
+  timestamp: 0
+  trusted_packet_sequence_id: 1
+  track_descriptor {
+    uuid: 12345
+    thread {
+      pid: 123
+      tid: 345
+    }
+    parent_uuid: 0
+    chrome_thread {
+      thread_type: THREAD_POOL_FG_WORKER
+    }
+  }
+}
+
+# Slice begins. The first two slices starts before the range of interest, the
+
+# rest are in the range.
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 10000
+  track_event {
+    track_uuid: 12345
+    categories: "cat1"
+    type: 1
+    name: "slice1"
+  }
+}
+
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 11000
+  track_event {
+    track_uuid: 12345
+    categories: "cat1"
+    type: 1
+    name: "slice2"
+  }
+}
+
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 12000
+  track_event {
+    track_uuid: 12345
+    categories: "cat1"
+    type: 1
+    name: "slice3"
+  }
+}
+
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 13000
+  track_event {
+    track_uuid: 12345
+    categories: "cat1"
+    type: 1
+    name: "slice4"
+  }
+}
+
+# Slice ends
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 6000000
+  track_event {
+    track_uuid: 12345
+    categories: "cat1"
+    name: "slice4"
+    type: 2
+  }
+}
+
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 6001000
+  track_event {
+    track_uuid: 12345
+    categories: "cat1"
+    name: "slice2"
+    type: 2
+  }
+}
+
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 6002000
+  track_event {
+    track_uuid: 12345
+    categories: "cat1"
+    name: "slice2"
+    type: 2
+  }
+}
+
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 6003000
+  track_event {
+    track_uuid: 12345
+    categories: "cat1"
+    name: "slice1"
+    type: 2
+  }
+}
diff --git a/test/trace_processor/diff_tests/track_event/tests.py b/test/trace_processor/diff_tests/track_event/tests.py
new file mode 100644
index 0000000..884f17e
--- /dev/null
+++ b/test/trace_processor/diff_tests/track_event/tests.py
@@ -0,0 +1,665 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class TrackEvent(TestSuite):
+  # Contains tests on the parsing and ingestion of TrackEvent packets. Same
+  # handling
+  def test_track_event_same_tids_threads(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        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: 1
+          timestamp: 0
+          track_descriptor {
+            uuid: 2
+            thread {
+              pid: 10
+              tid: 1
+              thread_name: "t2"
+            }
+          }
+        }
+        packet {
+          trusted_packet_sequence_id: 1
+          timestamp: 1000
+          track_event {
+            track_uuid: 1
+            categories: "cat"
+            name: "name1"
+            type: 3
+          }
+        }
+        packet {
+          trusted_packet_sequence_id: 1
+          timestamp: 2000
+          track_event {
+            track_uuid: 2
+            categories: "cat"
+            name: "name2"
+            type: 3
+          }
+        }
+        """),
+        query="""
+        SELECT tid, pid, process.name AS pname, thread.name AS tname
+        FROM thread
+        LEFT JOIN process USING(upid)
+        WHERE tid > 0
+        ORDER BY tid;
+        """,
+        out=Csv("""
+        "tid","pid","pname","tname"
+        1,5,"[NULL]","t1"
+        1,10,"[NULL]","t2"
+        5,5,"[NULL]","[NULL]"
+        10,10,"[NULL]","[NULL]"
+        """))
+
+  def test_track_event_same_tids_slices(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        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: 1
+          timestamp: 0
+          track_descriptor {
+            uuid: 2
+            thread {
+              pid: 10
+              tid: 1
+              thread_name: "t2"
+            }
+          }
+        }
+        packet {
+          trusted_packet_sequence_id: 1
+          timestamp: 1000
+          track_event {
+            track_uuid: 1
+            categories: "cat"
+            name: "name1"
+            type: 3
+          }
+        }
+        packet {
+          trusted_packet_sequence_id: 1
+          timestamp: 2000
+          track_event {
+            track_uuid: 2
+            categories: "cat"
+            name: "name2"
+            type: 3
+          }
+        }
+        """),
+        query="""
+        SELECT
+          track.name AS track,
+          process.name AS process,
+          thread.name AS thread,
+          thread_process.name AS thread_process,
+          slice.ts,
+          slice.dur,
+          slice.category,
+          slice.name
+        FROM slice
+        LEFT JOIN track ON slice.track_id = track.id
+        LEFT JOIN process_track ON slice.track_id = process_track.id
+        LEFT JOIN process ON process_track.upid = process.upid
+        LEFT JOIN thread_track ON slice.track_id = thread_track.id
+        LEFT JOIN thread ON thread_track.utid = thread.utid
+        LEFT JOIN process thread_process ON thread.upid = thread_process.upid
+        ORDER BY ts ASC;
+        """,
+        out=Csv("""
+        "track","process","thread","thread_process","ts","dur","category","name"
+        "[NULL]","[NULL]","t1","[NULL]",1000,0,"cat","name1"
+        "[NULL]","[NULL]","t2","[NULL]",2000,0,"cat","name2"
+        """))
+
+  # Typed args
+  def test_track_event_typed_args_slices(self):
+    return DiffTestBlueprint(
+        trace=Path('track_event_typed_args.textproto'),
+        query="""
+        SELECT
+          track.name AS track,
+          process.name AS process,
+          thread.name AS thread,
+          thread_process.name AS thread_process,
+          slice.ts,
+          slice.dur,
+          slice.category,
+          slice.name
+        FROM slice
+        LEFT JOIN track ON slice.track_id = track.id
+        LEFT JOIN process_track ON slice.track_id = process_track.id
+        LEFT JOIN process ON process_track.upid = process.upid
+        LEFT JOIN thread_track ON slice.track_id = thread_track.id
+        LEFT JOIN thread ON thread_track.utid = thread.utid
+        LEFT JOIN process thread_process ON thread.upid = thread_process.upid
+        ORDER BY ts ASC;
+        """,
+        out=Csv("""
+        "track","process","thread","thread_process","ts","dur","category","name"
+        "[NULL]","[NULL]","t1","[NULL]",1000,0,"cat","name1"
+        "[NULL]","[NULL]","t1","[NULL]",2000,0,"cat","name2"
+        "[NULL]","[NULL]","t1","[NULL]",3000,0,"cat","name3"
+        "[NULL]","[NULL]","t1","[NULL]",4000,0,"cat","name4"
+        "[NULL]","[NULL]","t1","[NULL]",6000,0,"cat","name5"
+        "[NULL]","[NULL]","t1","[NULL]",7000,0,"cat","name6"
+        """))
+
+  def test_track_event_typed_args_args(self):
+    return DiffTestBlueprint(
+        trace=Path('track_event_typed_args.textproto'),
+        query=Path('track_event_args_test.sql'),
+        out=Path('track_event_typed_args_args.out'))
+
+  # Track handling
+  def test_track_event_tracks_slices(self):
+    return DiffTestBlueprint(
+        trace=Path('track_event_tracks.textproto'),
+        query="""
+        SELECT
+          track.name AS track,
+          process.name AS process,
+          thread.name AS thread,
+          thread_process.name AS thread_process,
+          slice.ts,
+          slice.dur,
+          slice.category,
+          slice.name
+        FROM slice
+        LEFT JOIN track ON slice.track_id = track.id
+        LEFT JOIN process_track ON slice.track_id = process_track.id
+        LEFT JOIN process ON process_track.upid = process.upid
+        LEFT JOIN thread_track ON slice.track_id = thread_track.id
+        LEFT JOIN thread ON thread_track.utid = thread.utid
+        LEFT JOIN process thread_process ON thread.upid = thread_process.upid
+        ORDER BY ts ASC;
+        """,
+        out=Path('track_event_tracks_slices.out'))
+
+  def test_track_event_tracks_processes(self):
+    return DiffTestBlueprint(
+        trace=Path('track_event_tracks.textproto'),
+        query="""
+        SELECT
+          id,
+          name,
+          extract_arg(arg_set_id, "chrome.host_app_package_name") AS host_app
+        FROM process;
+        """,
+        out=Csv("""
+        "id","name","host_app"
+        0,"[NULL]","[NULL]"
+        1,"p1","host_app"
+        2,"p2","[NULL]"
+        """))
+
+  def test_track_event_tracks(self):
+    return DiffTestBlueprint(
+        trace=Path('track_event_tracks.textproto'),
+        query="""
+        WITH track_with_name AS (
+          SELECT
+            COALESCE(
+              t1.name,
+              'thread=' || thread.name,
+              'process=' || process.name,
+              'tid=' || thread.tid,
+              'pid=' || process.pid
+            ) AS full_name,
+            *
+          FROM track t1
+          LEFT JOIN thread_track t2 USING (id)
+          LEFT JOIN thread USING (utid)
+          LEFT JOIN process_track t3 USING (id)
+          LEFT JOIN process ON t3.upid = process.id
+          ORDER BY id
+        )
+        SELECT t1.full_name AS name, t2.full_name AS parent_name,
+               EXTRACT_ARG(t1.source_arg_set_id, 'has_first_packet_on_sequence')
+               AS has_first_packet_on_sequence
+        FROM track_with_name t1
+        LEFT JOIN track_with_name t2 ON t1.parent_id = t2.id
+        ORDER BY 1, 2;
+        """,
+        out=Csv("""
+        "name","parent_name","has_first_packet_on_sequence"
+        "Default Track","[NULL]","[NULL]"
+        "async","process=p1",1
+        "async2","process=p1",1
+        "async3","thread=t2",1
+        "event_and_track_async3","process=p1",1
+        "process=p1","[NULL]","[NULL]"
+        "process=p2","[NULL]","[NULL]"
+        "process=p2","[NULL]","[NULL]"
+        "thread=t1","process=p1",1
+        "thread=t2","process=p1",1
+        "thread=t3","process=p1",1
+        "thread=t4","process=p2","[NULL]"
+        "tid=1","[NULL]","[NULL]"
+        """))
+
+  # Instant events
+  def test_track_event_instant_slices(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        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
+            }
+          }
+        }
+        packet {
+          trusted_packet_sequence_id: 1
+          timestamp: 1000
+          track_event {
+            categories: "cat"
+            name: "instant_on_t1"
+            type: 3
+          }
+        }
+        packet {
+          trusted_packet_sequence_id: 1
+          timestamp: 2000
+          track_event {
+            categories: "cat"
+            name: "legacy_instant_on_t1"
+            legacy_event {
+              phase: 73               # 'I'
+            }
+          }
+        }
+        packet {
+          trusted_packet_sequence_id: 1
+          timestamp: 3000
+          track_event {
+            categories: "cat"
+            name: "legacy_mark_on_t1"
+            legacy_event {
+              phase: 82               # 'R'
+            }
+          }
+        }
+        """),
+        query="""
+        SELECT
+          track.name AS track,
+          process.name AS process,
+          thread.name AS thread,
+          thread_process.name AS thread_process,
+          slice.ts,
+          slice.dur,
+          slice.category,
+          slice.name
+        FROM slice
+        LEFT JOIN track ON slice.track_id = track.id
+        LEFT JOIN process_track ON slice.track_id = process_track.id
+        LEFT JOIN process ON process_track.upid = process.upid
+        LEFT JOIN thread_track ON slice.track_id = thread_track.id
+        LEFT JOIN thread ON thread_track.utid = thread.utid
+        LEFT JOIN process thread_process ON thread.upid = thread_process.upid
+        ORDER BY ts ASC;
+        """,
+        out=Csv("""
+        "track","process","thread","thread_process","ts","dur","category","name"
+        "[NULL]","[NULL]","t1","[NULL]",1000,0,"cat","instant_on_t1"
+        "[NULL]","[NULL]","t1","[NULL]",2000,0,"cat","legacy_instant_on_t1"
+        "[NULL]","[NULL]","t1","[NULL]",3000,0,"cat","legacy_mark_on_t1"
+        """))
+
+  # Legacy async events
+  def test_legacy_async_event(self):
+    return DiffTestBlueprint(
+        trace=Path('legacy_async_event.textproto'),
+        query="""
+        SELECT
+          track.name AS track,
+          process.name AS process,
+          thread.name AS thread,
+          thread_process.name AS thread_process,
+          slice.ts,
+          slice.dur,
+          slice.category,
+          slice.name,
+          args.key,
+          args.string_value,
+          args.int_value
+        FROM slice
+        LEFT JOIN track ON slice.track_id = track.id
+        LEFT JOIN process_track ON slice.track_id = process_track.id
+        LEFT JOIN process ON process_track.upid = process.upid
+        LEFT JOIN thread_track ON slice.track_id = thread_track.id
+        LEFT JOIN thread ON thread_track.utid = thread.utid
+        LEFT JOIN process thread_process ON thread.upid = thread_process.upid
+        LEFT JOIN args ON slice.arg_set_id = args.arg_set_id
+        ORDER BY ts ASC;
+        """,
+        out=Path('legacy_async_event.out'))
+
+  # Legacy atrace
+  def test_track_event_with_atrace(self):
+    return DiffTestBlueprint(
+        trace=Path('track_event_with_atrace.textproto'),
+        query="""
+        SELECT
+          track.name AS track,
+          process.name AS process,
+          thread.name AS thread,
+          thread_process.name AS thread_process,
+          slice.ts,
+          slice.dur,
+          slice.category,
+          slice.name
+        FROM slice
+        LEFT JOIN track ON slice.track_id = track.id
+        LEFT JOIN process_track ON slice.track_id = process_track.id
+        LEFT JOIN process ON process_track.upid = process.upid
+        LEFT JOIN thread_track ON slice.track_id = thread_track.id
+        LEFT JOIN thread ON thread_track.utid = thread.utid
+        LEFT JOIN process thread_process ON thread.upid = thread_process.upid
+        ORDER BY ts ASC;
+        """,
+        out=Csv("""
+        "track","process","thread","thread_process","ts","dur","category","name"
+        "[NULL]","[NULL]","t1","[NULL]",10000,1000,"cat","event1"
+        "[NULL]","[NULL]","t1","[NULL]",20000,8000,"cat","event2"
+        "[NULL]","[NULL]","t1","[NULL]",21000,7000,"[NULL]","atrace"
+        """))
+
+  # Debug annotations
+  def test_track_event_merged_debug_annotations_args(self):
+    return DiffTestBlueprint(
+        trace=Path('track_event_merged_debug_annotations.textproto'),
+        query=Path('track_event_args_test.sql'),
+        out=Path('track_event_merged_debug_annotations_args.out'))
+
+  # Counters
+  def test_track_event_counters_slices(self):
+    return DiffTestBlueprint(
+        trace=Path('track_event_counters.textproto'),
+        query="""
+        SELECT
+          track.name AS track,
+          process.name AS process,
+          thread.name AS thread,
+          thread_process.name AS thread_process,
+          slice.ts,
+          slice.dur,
+          slice.category,
+          slice.name
+        FROM slice
+        LEFT JOIN track ON slice.track_id = track.id
+        LEFT JOIN process_track ON slice.track_id = process_track.id
+        LEFT JOIN process ON process_track.upid = process.upid
+        LEFT JOIN thread_track ON slice.track_id = thread_track.id
+        LEFT JOIN thread ON thread_track.utid = thread.utid
+        LEFT JOIN process thread_process ON thread.upid = thread_process.upid
+        ORDER BY ts ASC;
+        """,
+        out=Csv("""
+        "track","process","thread","thread_process","ts","dur","category","name"
+        "[NULL]","[NULL]","t1","Browser",1000,100,"cat","event1_on_t1"
+        "[NULL]","[NULL]","t1","Browser",2000,200,"cat","event2_on_t1"
+        "[NULL]","[NULL]","t1","Browser",2000,200,"cat","event3_on_t1"
+        "[NULL]","[NULL]","t1","Browser",4000,0,"cat","event4_on_t1"
+        "[NULL]","[NULL]","t4","Browser",4000,100,"cat","event1_on_t3"
+        "[NULL]","[NULL]","t1","Browser",4300,0,"cat","float_counter_on_t1"
+        "[NULL]","[NULL]","t1","Browser",4500,0,"cat","float_counter_on_t1"
+        """))
+
+  def test_track_event_counters_counters(self):
+    return DiffTestBlueprint(
+        trace=Path('track_event_counters.textproto'),
+        query="""
+        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.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;
+        """,
+        out=Path('track_event_counters_counters.out'))
+
+  # Clock handling
+  def test_track_event_monotonic_trace_clock_slices(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          trusted_packet_sequence_id: 1
+          timestamp: 0
+          clock_snapshot {
+            primary_trace_clock: 3  # BUILTIN_CLOCK_MONOTONIC
+            clocks {
+              clock_id: 3  # BUILTIN_CLOCK_MONOTONIC
+              timestamp: 1000
+            }
+            clocks {
+              clock_id: 6  # BUILTIN_CLOCK_BOOTTIME
+              timestamp: 11000
+            }
+          }
+        }
+        packet {
+          trusted_packet_sequence_id: 1
+          timestamp: 1000
+          timestamp_clock_id: 3  # BUILTIN_CLOCK_MONOTONIC
+          track_event {
+            track_uuid: 1
+            categories: "cat"
+            name: "name1"
+            type: 3
+          }
+        }
+        packet {
+          trusted_packet_sequence_id: 1
+          timestamp: 12000
+          timestamp_clock_id: 6  # BUILTIN_CLOCK_BOOTTIME
+          track_event {
+            track_uuid: 1
+            categories: "cat"
+            name: "name2"
+            type: 3
+          }
+        }
+        """),
+        query="""
+        SELECT
+          track.name AS track,
+          process.name AS process,
+          thread.name AS thread,
+          thread_process.name AS thread_process,
+          slice.ts,
+          slice.dur,
+          slice.category,
+          slice.name
+        FROM slice
+        LEFT JOIN track ON slice.track_id = track.id
+        LEFT JOIN process_track ON slice.track_id = process_track.id
+        LEFT JOIN process ON process_track.upid = process.upid
+        LEFT JOIN thread_track ON slice.track_id = thread_track.id
+        LEFT JOIN thread ON thread_track.utid = thread.utid
+        LEFT JOIN process thread_process ON thread.upid = thread_process.upid
+        ORDER BY ts ASC;
+        """,
+        out=Csv("""
+        "track","process","thread","thread_process","ts","dur","category","name"
+        "name1","[NULL]","[NULL]","[NULL]",1000,0,"cat","name1"
+        "name1","[NULL]","[NULL]","[NULL]",2000,0,"cat","name2"
+        """))
+
+  # HistogramName interning
+  def test_track_event_chrome_histogram_sample_args(self):
+    return DiffTestBlueprint(
+        trace=Path('track_event_chrome_histogram_sample.textproto'),
+        query=Path('track_event_args_test.sql'),
+        out=Path('track_event_chrome_histogram_sample_args.out'))
+
+  # Flow events importing from proto
+  def test_flow_events_track_event(self):
+    return DiffTestBlueprint(
+        trace=Path('flow_events_track_event.textproto'),
+        query="""
+        SELECT t1.name AS slice_out, t2.name AS slice_in FROM flow t
+        JOIN slice t1 ON t.slice_out = t1.slice_id
+        JOIN slice t2 ON t.slice_in = t2.slice_id;
+        """,
+        out=Csv("""
+        "slice_out","slice_in"
+        "FlowSlice1Start","FlowSlice1End"
+        "FlowSlice1Start2Start","FlowSlice1End"
+        "FlowSlice1Start2Start","FlowSlice2End"
+        "FlowSlice3Begin","FlowSlice3End4Begin"
+        "FlowSlice3End4Begin","FlowSlice4Step"
+        "FlowSlice4Step","FlowSlice4Step2_FlowIdOnAsyncEndEvent"
+        "FlowSlice4Step2_FlowIdOnAsyncEndEvent","FlowSlice4End"
+        """))
+
+  def test_flow_events_proto_v2(self):
+    return DiffTestBlueprint(
+        trace=Path('flow_events_proto_v2.textproto'),
+        query="""
+        SELECT t1.name AS slice_out, t2.name AS slice_in FROM flow t
+        JOIN slice t1 ON t.slice_out = t1.slice_id
+        JOIN slice t2 ON t.slice_in = t2.slice_id;
+        """,
+        out=Csv("""
+        "slice_out","slice_in"
+        "FlowBeginSlice","FlowEndSlice_1"
+        "FlowBeginSlice","FlowStepSlice"
+        "FlowStepSlice","FlowEndSlice_2"
+        """))
+
+  def test_flow_events_proto_v1(self):
+    return DiffTestBlueprint(
+        trace=Path('flow_events_proto_v1.textproto'),
+        query="""
+        SELECT t1.name AS slice_out, t2.name AS slice_in FROM flow t
+        JOIN slice t1 ON t.slice_out = t1.slice_id
+        JOIN slice t2 ON t.slice_in = t2.slice_id;
+        """,
+        out=Csv("""
+        "slice_out","slice_in"
+        "FlowBeginSlice","FlowEndSlice_1"
+        "FlowEndSlice_1","FlowStepSlice"
+        "FlowStepSlice","FlowEndSlice_2"
+        """))
+
+  # Async slices starting and ending at the same time
+  def test_experimental_slice_layout_depth(self):
+    return DiffTestBlueprint(
+        trace=Path('experimental_slice_layout_depth.py'),
+        query="""
+        SELECT layout_depth FROM experimental_slice_layout
+        WHERE filter_track_ids = (SELECT group_concat(track_id, ',') FROM slice);
+        """,
+        out=Csv("""
+        "layout_depth"
+        0
+        0
+        0
+        """))
+
+  # Descriptor merging regression test (bug: b/197203390)
+  def test_merging_regression(self):
+    return DiffTestBlueprint(
+        trace=DataPath('trace_with_descriptor.pftrace'),
+        query="""
+        SELECT ts FROM slice ORDER BY ts LIMIT 10;
+        """,
+        out=Csv("""
+        "ts"
+        605361018360000
+        605361018360000
+        605361028265000
+        605361028265000
+        605361028361000
+        605361028878000
+        605361033445000
+        605361033445000
+        605361034257000
+        605361035040000
+        """))
+
+  # Range of interest
+  def test_range_of_interest(self):
+    return DiffTestBlueprint(
+        trace=Path('range_of_interest.textproto'),
+        query="""
+        SELECT ts, name
+        FROM slice
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "ts","name"
+        12000,"slice3"
+        13000,"slice4"
+        """))
diff --git a/test/trace_processor/diff_tests/track_event/track_event_args_test.sql b/test/trace_processor/diff_tests/track_event/track_event_args_test.sql
new file mode 100644
index 0000000..7d8ced6
--- /dev/null
+++ b/test/trace_processor/diff_tests/track_event/track_event_args_test.sql
@@ -0,0 +1,16 @@
+--
+-- 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 flat_key, key, int_value, string_value FROM args ORDER BY key, display_value, arg_set_id, key ASC;
diff --git a/test/trace_processor/track_event/track_event_chrome_histogram_sample.textproto b/test/trace_processor/diff_tests/track_event/track_event_chrome_histogram_sample.textproto
similarity index 100%
rename from test/trace_processor/track_event/track_event_chrome_histogram_sample.textproto
rename to test/trace_processor/diff_tests/track_event/track_event_chrome_histogram_sample.textproto
diff --git a/test/trace_processor/diff_tests/track_event/track_event_chrome_histogram_sample_args.out b/test/trace_processor/diff_tests/track_event/track_event_chrome_histogram_sample_args.out
new file mode 100644
index 0000000..4cf08d4
--- /dev/null
+++ b/test/trace_processor/diff_tests/track_event/track_event_chrome_histogram_sample_args.out
@@ -0,0 +1,26 @@
+"flat_key","key","int_value","string_value"
+"chrome_histogram_sample.name","chrome_histogram_sample.name","[NULL]","Compositing.Display.DrawToSwapUs"
+"chrome_histogram_sample.name","chrome_histogram_sample.name","[NULL]","CompositorLatency.TotalLatency"
+"chrome_histogram_sample.name","chrome_histogram_sample.name","[NULL]","Graphics.Smoothness.Checkerboarding.MainThreadAnimation"
+"chrome_histogram_sample.name","chrome_histogram_sample.name","[NULL]","Memory.GPU.PeakMemoryUsage.PageLoad"
+"chrome_histogram_sample.name_hash","chrome_histogram_sample.name_hash",10,"[NULL]"
+"chrome_histogram_sample.name_hash","chrome_histogram_sample.name_hash",20,"[NULL]"
+"chrome_histogram_sample.name_hash","chrome_histogram_sample.name_hash",30,"[NULL]"
+"chrome_histogram_sample.name_hash","chrome_histogram_sample.name_hash",40,"[NULL]"
+"chrome_histogram_sample.name_hash","chrome_histogram_sample.name_hash",50,"[NULL]"
+"chrome_histogram_sample.name_hash","chrome_histogram_sample.name_hash",60,"[NULL]"
+"chrome_histogram_sample.name_iid","chrome_histogram_sample.name_iid",1,"[NULL]"
+"chrome_histogram_sample.name_iid","chrome_histogram_sample.name_iid",2,"[NULL]"
+"chrome_histogram_sample.name_iid","chrome_histogram_sample.name_iid",3,"[NULL]"
+"chrome_histogram_sample.name_iid","chrome_histogram_sample.name_iid",4,"[NULL]"
+"chrome_histogram_sample.sample","chrome_histogram_sample.sample",100,"[NULL]"
+"chrome_histogram_sample.sample","chrome_histogram_sample.sample",200,"[NULL]"
+"chrome_histogram_sample.sample","chrome_histogram_sample.sample",300,"[NULL]"
+"chrome_histogram_sample.sample","chrome_histogram_sample.sample",400,"[NULL]"
+"chrome_histogram_sample.sample","chrome_histogram_sample.sample",500,"[NULL]"
+"chrome_histogram_sample.sample","chrome_histogram_sample.sample",600,"[NULL]"
+"event.category","event.category","[NULL]","disabled-by-default-histogram_samples"
+"event.name","event.name","[NULL]","[NULL]"
+"is_root_in_scope","is_root_in_scope",1,"[NULL]"
+"source","source","[NULL]","descriptor"
+"source_id","source_id",0,"[NULL]"
diff --git a/test/trace_processor/track_event/track_event_counters.textproto b/test/trace_processor/diff_tests/track_event/track_event_counters.textproto
similarity index 100%
rename from test/trace_processor/track_event/track_event_counters.textproto
rename to test/trace_processor/diff_tests/track_event/track_event_counters.textproto
diff --git a/test/trace_processor/track_event/track_event_counters_counters.out b/test/trace_processor/diff_tests/track_event/track_event_counters_counters.out
similarity index 100%
rename from test/trace_processor/track_event/track_event_counters_counters.out
rename to test/trace_processor/diff_tests/track_event/track_event_counters_counters.out
diff --git a/test/trace_processor/track_event/track_event_merged_debug_annotations.textproto b/test/trace_processor/diff_tests/track_event/track_event_merged_debug_annotations.textproto
similarity index 100%
rename from test/trace_processor/track_event/track_event_merged_debug_annotations.textproto
rename to test/trace_processor/diff_tests/track_event/track_event_merged_debug_annotations.textproto
diff --git a/test/trace_processor/diff_tests/track_event/track_event_merged_debug_annotations_args.out b/test/trace_processor/diff_tests/track_event/track_event_merged_debug_annotations_args.out
new file mode 100644
index 0000000..a4e2871
--- /dev/null
+++ b/test/trace_processor/diff_tests/track_event/track_event_merged_debug_annotations_args.out
@@ -0,0 +1,31 @@
+"flat_key","key","int_value","string_value"
+"debug.debug1.key1","debug.debug1.key1",10,"[NULL]"
+"debug.debug1.key2","debug.debug1.key2[0]",20,"[NULL]"
+"debug.debug1.key2","debug.debug1.key2[1]",21,"[NULL]"
+"debug.debug1.key2","debug.debug1.key2[2]",22,"[NULL]"
+"debug.debug1.key2","debug.debug1.key2[3]",23,"[NULL]"
+"debug.debug1.key3","debug.debug1.key3",30,"[NULL]"
+"debug.debug2.key1","debug.debug2.key1",10,"[NULL]"
+"debug.debug2.key2","debug.debug2.key2[0]",20,"[NULL]"
+"debug.debug2.key2","debug.debug2.key2[1]",21,"[NULL]"
+"debug.debug2.key2","debug.debug2.key2[2]",22,"[NULL]"
+"debug.debug2.key2","debug.debug2.key2[3]",23,"[NULL]"
+"debug.debug2.key3.key31","debug.debug2.key3.key31",31,"[NULL]"
+"debug.debug2.key3.key32","debug.debug2.key3.key32",32,"[NULL]"
+"debug.debug2.key4","debug.debug2.key4",40,"[NULL]"
+"debug.debug3","debug.debug3",32,"[NULL]"
+"debug.debug4.key1","debug.debug4.key1",10,"[NULL]"
+"debug.debug4.key2","debug.debug4.key2[0]",20,"[NULL]"
+"debug.debug4.key2","debug.debug4.key2[1]",21,"[NULL]"
+"event.category","event.category","[NULL]","cat"
+"event.category","event.category","[NULL]","cat"
+"event.name","event.name","[NULL]","[NULL]"
+"event.name","event.name","[NULL]","name1"
+"is_root_in_scope","is_root_in_scope",1,"[NULL]"
+"legacy_event.passthrough_utid","legacy_event.passthrough_utid",1,"[NULL]"
+"source","source","[NULL]","chrome"
+"source","source","[NULL]","descriptor"
+"source_id","source_id",1,"[NULL]"
+"source_id","source_id",1234,"[NULL]"
+"source_id_is_process_scoped","source_id_is_process_scoped",0,"[NULL]"
+"source_scope","source_scope","[NULL]","cat"
diff --git a/test/trace_processor/diff_tests/track_event/track_event_tracks.textproto b/test/trace_processor/diff_tests/track_event/track_event_tracks.textproto
new file mode 100644
index 0000000..779998b
--- /dev/null
+++ b/test/trace_processor/diff_tests/track_event/track_event_tracks.textproto
@@ -0,0 +1,333 @@
+# Sequence 1 defaults to track for "t1".
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 0
+  incremental_state_cleared: true
+  first_packet_on_sequence: true
+  track_descriptor {
+    uuid: 1
+    parent_uuid: 10
+    thread {
+      pid: 5
+      tid: 1
+      thread_name: "t1"
+    }
+  }
+  trace_packet_defaults {
+    track_event_defaults {
+      track_uuid: 1
+    }
+  }
+}
+# Sequence 2 defaults to track for "t2".
+packet {
+  trusted_packet_sequence_id: 2
+  timestamp: 0
+  incremental_state_cleared: true
+  first_packet_on_sequence: true
+  track_descriptor {
+    uuid: 2
+    parent_uuid: 10
+    thread {
+      pid: 5
+      tid: 2
+      thread_name: "t2"
+    }
+  }
+  trace_packet_defaults {
+    track_event_defaults {
+      track_uuid: 2
+    }
+  }
+}
+# Both thread tracks are nested underneath this process track.
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 0
+  track_descriptor {
+    uuid: 10
+    process {
+      pid: 5
+      process_name: "p1"
+    }
+    chrome_process {
+      host_app_package_name: "host_app"
+    }
+  }
+}
+# And we have an async track underneath the process too.
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 0
+  track_descriptor {
+    uuid: 11
+    parent_uuid: 10
+    name: "async"
+  }
+}
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 100
+  track_descriptor {
+    uuid: 12
+    parent_uuid: 10
+    name: "async2"
+  }
+}
+packet {
+  trusted_packet_sequence_id: 2
+  timestamp: 200
+  track_descriptor {
+    uuid: 12
+    parent_uuid: 10
+    name: "async2"
+  }
+}
+
+# Threads also can have child async tracks.
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 200
+  track_descriptor {
+    uuid: 14
+    parent_uuid: 2
+    name: "async3"
+  }
+}
+
+# Should appear on default track "t1".
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 1000
+  track_event {
+    categories: "cat"
+    name: "event1_on_t1"
+    type: 3
+  }
+}
+# Should appear on default track "t2".
+packet {
+  trusted_packet_sequence_id: 2
+  timestamp: 2000
+  track_event {
+    categories: "cat"
+    name: "event1_on_t2"
+    type: 3
+  }
+}
+# Should appear on overridden track "t2".
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 3000
+  track_event {
+    track_uuid: 2
+    categories: "cat"
+    name: "event2_on_t2"
+    type: 3
+  }
+}
+# Should appear on process track.
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 4000
+  track_event {
+    track_uuid: 10
+    categories: "cat"
+    name: "event1_on_p1"
+    type: 3
+  }
+}
+# Should appear on async track.
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 5000
+  track_event {
+    track_uuid: 11
+    categories: "cat"
+    name: "event1_on_async"
+    type: 3
+  }
+}
+# Event for the "async2" track starting on one thread and ending on another.
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 5100
+  track_event {
+    track_uuid: 12
+    categories: "cat"
+    name: "event1_on_async2"
+    type: 1
+  }
+}
+packet {
+  trusted_packet_sequence_id: 2
+  timestamp: 5200
+  track_event {
+    track_uuid: 12
+    categories: "cat"
+    name: "event1_on_async2"
+    type: 2
+  }
+}
+
+# If we later see another track descriptor for tid 1, but with a different uuid,
+# we should detect tid reuse and start a new thread.
+packet {
+  trusted_packet_sequence_id: 3
+  timestamp: 10000
+  incremental_state_cleared: true
+  first_packet_on_sequence: true
+  track_descriptor {
+    uuid: 3
+    parent_uuid: 10
+    thread {
+      pid: 5
+      tid: 1
+      thread_name: "t3"
+    }
+  }
+}
+# Should appear on t3.
+packet {
+  trusted_packet_sequence_id: 3
+  timestamp: 11000
+  track_event {
+    track_uuid: 3
+    categories: "cat"
+    name: "event1_on_t3"
+    type: 3
+  }
+}
+
+# If we later see another track descriptor for pid 5, but with a different uuid,
+# we should detect pid reuse and start a new process.
+packet {
+  trusted_packet_sequence_id: 4
+  timestamp: 20000
+  incremental_state_cleared: true
+  track_descriptor {
+    uuid: 20
+    process {
+      pid: 5
+      process_name: "p2"
+    }
+  }
+}
+# Should appear on p2.
+packet {
+  trusted_packet_sequence_id: 4
+  timestamp: 21000
+  track_event {
+    track_uuid: 20
+    categories: "cat"
+    name: "event1_on_p2"
+    type: 3
+  }
+}
+# Another thread t4 in the new process.
+packet {
+  trusted_packet_sequence_id: 4
+  timestamp: 22000
+  incremental_state_cleared: true
+  track_descriptor {
+    uuid: 21
+    parent_uuid: 20
+    thread {
+      pid: 5
+      tid: 4
+      thread_name: "t4"
+    }
+  }
+}
+# Should appear on t4.
+packet {
+  trusted_packet_sequence_id: 4
+  timestamp: 22000
+  track_event {
+    track_uuid: 21
+    categories: "cat"
+    name: "event1_on_t4"
+    type: 3
+  }
+}
+
+# Another packet for a thread track in the old process, badly sorted.
+packet {
+  trusted_packet_sequence_id: 2
+  timestamp: 6000
+  track_event {
+    track_uuid: 1
+    categories: "cat"
+    name: "event3_on_t1"
+    type: 3
+  }
+}
+
+# Override the track to the default descriptor track for an event with a
+# TrackEvent type. Should appear on the default descriptor track instead of
+# "t1".
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 30000
+  track_event {
+    track_uuid: 0
+    categories: "cat"
+    name: "event1_on_t1"
+    type: 3
+  }
+}
+
+# But a legacy event without TrackEvent type falls back to legacy tracks (based
+# on ThreadDescriptor / async IDs / legacy instant scopes). This instant event
+# should appear on the process track "p2".
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 31000
+  track_event {
+    track_uuid: 0
+    categories: "cat"
+    name: "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
+    }
+  }
+}
+
+# Track descriptor without name and process/thread association derives its
+# name from the first event on the track.
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 40000
+  track_descriptor {
+    uuid: 13
+    parent_uuid: 10
+  }
+}
+
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 40000
+  track_event {
+    track_uuid: 13
+    categories: "cat"
+    name: "event_and_track_async3"
+    type: 3
+  }
+}
diff --git a/test/trace_processor/track_event/track_event_tracks_slices.out b/test/trace_processor/diff_tests/track_event/track_event_tracks_slices.out
similarity index 100%
rename from test/trace_processor/track_event/track_event_tracks_slices.out
rename to test/trace_processor/diff_tests/track_event/track_event_tracks_slices.out
diff --git a/test/trace_processor/track_event/track_event_typed_args.textproto b/test/trace_processor/diff_tests/track_event/track_event_typed_args.textproto
similarity index 100%
rename from test/trace_processor/track_event/track_event_typed_args.textproto
rename to test/trace_processor/diff_tests/track_event/track_event_typed_args.textproto
diff --git a/test/trace_processor/diff_tests/track_event/track_event_typed_args_args.out b/test/trace_processor/diff_tests/track_event/track_event_typed_args_args.out
new file mode 100644
index 0000000..cb6ade4
--- /dev/null
+++ b/test/trace_processor/diff_tests/track_event/track_event_typed_args_args.out
@@ -0,0 +1,39 @@
+"flat_key","key","int_value","string_value"
+"chrome_app_state","chrome_app_state","[NULL]","APP_STATE_FOREGROUND"
+"chrome_keyed_service.name","chrome_keyed_service.name","[NULL]","MediaRouter"
+"chrome_latency_info.component_info.component_type","chrome_latency_info.component_info[0].component_type","[NULL]","COMPONENT_INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL"
+"chrome_latency_info.component_info.time_us","chrome_latency_info.component_info[0].time_us",1201,"[NULL]"
+"chrome_latency_info.component_info.time_us","chrome_latency_info.component_info[1].time_us",928310,"[NULL]"
+"chrome_latency_info.is_coalesced","chrome_latency_info.is_coalesced",1,"[NULL]"
+"chrome_latency_info.trace_id","chrome_latency_info.trace_id",7,"[NULL]"
+"chrome_legacy_ipc.message_class","chrome_legacy_ipc.message_class","[NULL]","CLASS_AUTOMATION"
+"chrome_legacy_ipc.message_line","chrome_legacy_ipc.message_line",10,"[NULL]"
+"chrome_memory_pressure_notification.file_name","chrome_memory_pressure_notification.file_name","[NULL]","another_source.cc"
+"chrome_memory_pressure_notification.function_name","chrome_memory_pressure_notification.function_name","[NULL]","AnotherSourceFunction"
+"chrome_memory_pressure_notification.line_number","chrome_memory_pressure_notification.line_number",1337,"[NULL]"
+"chrome_user_event.action","chrome_user_event.action","[NULL]","NewTab"
+"event.category","event.category","[NULL]","cat"
+"event.category","event.category","[NULL]","cat"
+"event.category","event.category","[NULL]","cat"
+"event.category","event.category","[NULL]","cat"
+"event.category","event.category","[NULL]","cat"
+"event.category","event.category","[NULL]","cat"
+"event.name","event.name","[NULL]","name1"
+"event.name","event.name","[NULL]","name2"
+"event.name","event.name","[NULL]","name3"
+"event.name","event.name","[NULL]","name4"
+"event.name","event.name","[NULL]","name5"
+"event.name","event.name","[NULL]","name6"
+"int_extension_for_testing","int_extension_for_testing[0]",42,"[NULL]"
+"int_extension_for_testing","int_extension_for_testing[1]",1337,"[NULL]"
+"is_root_in_scope","is_root_in_scope",1,"[NULL]"
+"nested_message_extension_for_testing.arg1","nested_message_extension_for_testing.arg1","[NULL]","value"
+"nested_message_extension_for_testing.arg2.key","nested_message_extension_for_testing.arg2.key","[NULL]","value"
+"nested_message_extension_for_testing.child_field_for_testing","nested_message_extension_for_testing.child_field_for_testing","[NULL]","nesting test"
+"source","source","[NULL]","descriptor"
+"source.file_name","source.file_name","[NULL]","source.cc"
+"source.function_name","source.function_name","[NULL]","SourceFunction"
+"source.line_number","source.line_number",0,"[NULL]"
+"source_id","source_id",1,"[NULL]"
+"source_location_iid","source_location_iid",1,"[NULL]"
+"string_extension_for_testing","string_extension_for_testing","[NULL]","an extension string!"
diff --git a/test/trace_processor/track_event/track_event_with_atrace.textproto b/test/trace_processor/diff_tests/track_event/track_event_with_atrace.textproto
similarity index 100%
rename from test/trace_processor/track_event/track_event_with_atrace.textproto
rename to test/trace_processor/diff_tests/track_event/track_event_with_atrace.textproto
diff --git a/test/trace_processor/diff_tests/translation/chrome_args_test.sql b/test/trace_processor/diff_tests/translation/chrome_args_test.sql
new file mode 100644
index 0000000..542df14
--- /dev/null
+++ b/test/trace_processor/diff_tests/translation/chrome_args_test.sql
@@ -0,0 +1,16 @@
+--
+-- Copyright 2022 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 flat_key, key, int_value, string_value FROM args ORDER BY key, display_value, arg_set_id ASC;
diff --git a/test/trace_processor/diff_tests/translation/chrome_histogram.out b/test/trace_processor/diff_tests/translation/chrome_histogram.out
new file mode 100644
index 0000000..1e4b7a2
--- /dev/null
+++ b/test/trace_processor/diff_tests/translation/chrome_histogram.out
@@ -0,0 +1,16 @@
+"flat_key","key","int_value","string_value"
+"chrome_histogram_sample.name","chrome_histogram_sample.name","[NULL]","histogram_name1"
+"chrome_histogram_sample.name","chrome_histogram_sample.name","[NULL]","histogram_name2"
+"chrome_histogram_sample.name_hash","chrome_histogram_sample.name_hash",10,"[NULL]"
+"chrome_histogram_sample.name_hash","chrome_histogram_sample.name_hash",20,"[NULL]"
+"chrome_histogram_sample.name_hash","chrome_histogram_sample.name_hash",30,"[NULL]"
+"chrome_histogram_sample.sample","chrome_histogram_sample.sample",100,"[NULL]"
+"event.category","event.category","[NULL]","cat1"
+"event.category","event.category","[NULL]","cat2"
+"event.category","event.category","[NULL]","cat3"
+"event.name","event.name","[NULL]","slice1"
+"event.name","event.name","[NULL]","slice2"
+"event.name","event.name","[NULL]","slice3"
+"is_root_in_scope","is_root_in_scope",1,"[NULL]"
+"source","source","[NULL]","descriptor"
+"source_id","source_id",12345,"[NULL]"
diff --git a/test/trace_processor/translation/chrome_histogram.textproto b/test/trace_processor/diff_tests/translation/chrome_histogram.textproto
similarity index 100%
rename from test/trace_processor/translation/chrome_histogram.textproto
rename to test/trace_processor/diff_tests/translation/chrome_histogram.textproto
diff --git a/test/trace_processor/diff_tests/translation/chrome_performance_mark.out b/test/trace_processor/diff_tests/translation/chrome_performance_mark.out
new file mode 100644
index 0000000..c3afe4c
--- /dev/null
+++ b/test/trace_processor/diff_tests/translation/chrome_performance_mark.out
@@ -0,0 +1,10 @@
+"flat_key","key","int_value","string_value"
+"chrome_hashed_performance_mark.mark","chrome_hashed_performance_mark.mark","[NULL]","mark2"
+"chrome_hashed_performance_mark.mark_hash","chrome_hashed_performance_mark.mark_hash",20,"[NULL]"
+"chrome_hashed_performance_mark.site","chrome_hashed_performance_mark.site","[NULL]","site1"
+"chrome_hashed_performance_mark.site_hash","chrome_hashed_performance_mark.site_hash",10,"[NULL]"
+"event.category","event.category","[NULL]","cat1"
+"event.name","event.name","[NULL]","slice1"
+"is_root_in_scope","is_root_in_scope",1,"[NULL]"
+"source","source","[NULL]","descriptor"
+"source_id","source_id",12345,"[NULL]"
diff --git a/test/trace_processor/diff_tests/translation/chrome_user_event.out b/test/trace_processor/diff_tests/translation/chrome_user_event.out
new file mode 100644
index 0000000..a198369
--- /dev/null
+++ b/test/trace_processor/diff_tests/translation/chrome_user_event.out
@@ -0,0 +1,15 @@
+"flat_key","key","int_value","string_value"
+"chrome_user_event.action","chrome_user_event.action","[NULL]","action1"
+"chrome_user_event.action","chrome_user_event.action","[NULL]","action2"
+"chrome_user_event.action_hash","chrome_user_event.action_hash",10,"[NULL]"
+"chrome_user_event.action_hash","chrome_user_event.action_hash",20,"[NULL]"
+"chrome_user_event.action_hash","chrome_user_event.action_hash",30,"[NULL]"
+"event.category","event.category","[NULL]","cat1"
+"event.category","event.category","[NULL]","cat2"
+"event.category","event.category","[NULL]","cat3"
+"event.name","event.name","[NULL]","slice1"
+"event.name","event.name","[NULL]","slice2"
+"event.name","event.name","[NULL]","slice3"
+"is_root_in_scope","is_root_in_scope",1,"[NULL]"
+"source","source","[NULL]","descriptor"
+"source_id","source_id",12345,"[NULL]"
diff --git a/test/trace_processor/translation/chrome_user_event.textproto b/test/trace_processor/diff_tests/translation/chrome_user_event.textproto
similarity index 100%
rename from test/trace_processor/translation/chrome_user_event.textproto
rename to test/trace_processor/diff_tests/translation/chrome_user_event.textproto
diff --git a/test/trace_processor/diff_tests/translation/java_class_name_arg.out b/test/trace_processor/diff_tests/translation/java_class_name_arg.out
new file mode 100644
index 0000000..057e63f
--- /dev/null
+++ b/test/trace_processor/diff_tests/translation/java_class_name_arg.out
@@ -0,0 +1,12 @@
+"flat_key","key","int_value","string_value"
+"android_view_dump.activity.name","android_view_dump.activity[0].name","[NULL]","A1"
+"android_view_dump.activity.view.class_name","android_view_dump.activity[0].view[0].class_name","[NULL]","class_A"
+"android_view_dump.activity.view.class_name","android_view_dump.activity[0].view[1].class_name","[NULL]","def"
+"android_view_dump.activity.view.class_name","android_view_dump.activity[0].view[2].class_name","[NULL]","ghi"
+"android_view_dump.activity.name","android_view_dump.activity[1].name","[NULL]","B1"
+"android_view_dump.activity.view.class_name","android_view_dump.activity[1].view[0].class_name","[NULL]","class_J"
+"event.category","event.category","[NULL]","cat1"
+"event.name","event.name","[NULL]","name1"
+"is_root_in_scope","is_root_in_scope",1,"[NULL]"
+"source","source","[NULL]","descriptor"
+"source_id","source_id",0,"[NULL]"
diff --git a/test/trace_processor/diff_tests/translation/java_class_name_arg.textproto b/test/trace_processor/diff_tests/translation/java_class_name_arg.textproto
new file mode 100644
index 0000000..3a633f6
--- /dev/null
+++ b/test/trace_processor/diff_tests/translation/java_class_name_arg.textproto
@@ -0,0 +1,54 @@
+packet {
+  packages_list {
+    packages {
+      name: "package_name_1"
+      version_code: 123
+    }
+  }
+}
+# Packet with arguments
+packet {
+  trusted_packet_sequence_id: 1
+  timestamp: 0
+  incremental_state_cleared: true
+  track_event {
+    categories: "cat1"
+    type: 3
+    name: "name1"
+    [perfetto.protos.ChromeTrackEvent.android_view_dump] {
+      activity {
+        name: "A1"
+        view {
+          class_name: "abc"
+        },
+        view {
+          class_name: "def"
+        },
+        view {
+          class_name: "ghi"
+        }
+      }
+      activity {
+        name: "B1"
+        view {
+          class_name: "jkl"
+        }
+      }
+    }
+  }
+}
+# Packet with DeobfuscationMapping
+packet {
+  deobfuscation_mapping {
+    package_name: "package_name_1"
+    version_code: 123
+    obfuscated_classes {
+        obfuscated_name: "abc"
+        deobfuscated_name: "class_A"
+    }
+    obfuscated_classes {
+        obfuscated_name: "jkl"
+        deobfuscated_name: "class_J"
+    }
+  }
+}
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/translation/native_symbol_arg.out b/test/trace_processor/diff_tests/translation/native_symbol_arg.out
new file mode 100644
index 0000000..2c00ad2
--- /dev/null
+++ b/test/trace_processor/diff_tests/translation/native_symbol_arg.out
@@ -0,0 +1,20 @@
+"flat_key","key","int_value","string_value"
+"chrome_mojo_event_info.mojo_interface_tag","chrome_mojo_event_info.mojo_interface_tag","[NULL]","Class::Method"
+"chrome_mojo_event_info.mojo_interface_tag","chrome_mojo_event_info.mojo_interface_tag","[NULL]","Func"
+"chrome_mojo_event_info.mojo_interface_tag","chrome_mojo_event_info.mojo_interface_tag","[NULL]","storage.mojom.Directory"
+"chrome_mojo_event_info.mojo_interface_tag","chrome_mojo_event_info.mojo_interface_tag","[NULL]","storage.mojom.File"
+"chrome_mojo_event_info.mojo_method_name","chrome_mojo_event_info.mojo_method_name","[NULL]","Class::Method"
+"chrome_mojo_event_info.mojo_method_name","chrome_mojo_event_info.mojo_method_name","[NULL]","Func"
+"chrome_mojo_event_info.mojo_method_name","chrome_mojo_event_info.mojo_method_name","[NULL]","storage::mojom::Directory::OpenFile"
+"chrome_mojo_event_info.mojo_method_name","chrome_mojo_event_info.mojo_method_name","[NULL]","storage::mojom::File::Close"
+"event.category","event.category","[NULL]","cat1"
+"event.category","event.category","[NULL]","cat1"
+"event.category","event.category","[NULL]","cat1"
+"event.category","event.category","[NULL]","cat1"
+"event.name","event.name","[NULL]","slice1"
+"event.name","event.name","[NULL]","slice2"
+"event.name","event.name","[NULL]","slice3"
+"event.name","event.name","[NULL]","slice4"
+"is_root_in_scope","is_root_in_scope",1,"[NULL]"
+"source","source","[NULL]","descriptor"
+"source_id","source_id",12345,"[NULL]"
diff --git a/test/trace_processor/translation/native_symbol_arg.textproto b/test/trace_processor/diff_tests/translation/native_symbol_arg.textproto
similarity index 100%
rename from test/trace_processor/translation/native_symbol_arg.textproto
rename to test/trace_processor/diff_tests/translation/native_symbol_arg.textproto
diff --git a/test/trace_processor/translation/native_symbol_arg_incomplete.textproto b/test/trace_processor/diff_tests/translation/native_symbol_arg_incomplete.textproto
similarity index 100%
rename from test/trace_processor/translation/native_symbol_arg_incomplete.textproto
rename to test/trace_processor/diff_tests/translation/native_symbol_arg_incomplete.textproto
diff --git a/test/trace_processor/translation/slice_name.textproto b/test/trace_processor/diff_tests/translation/slice_name.textproto
similarity index 100%
rename from test/trace_processor/translation/slice_name.textproto
rename to test/trace_processor/diff_tests/translation/slice_name.textproto
diff --git a/test/trace_processor/translation/slice_name_negative_timestamp.textproto b/test/trace_processor/diff_tests/translation/slice_name_negative_timestamp.textproto
similarity index 100%
rename from test/trace_processor/translation/slice_name_negative_timestamp.textproto
rename to test/trace_processor/diff_tests/translation/slice_name_negative_timestamp.textproto
diff --git a/test/trace_processor/diff_tests/translation/tests.py b/test/trace_processor/diff_tests/translation/tests.py
new file mode 100644
index 0000000..9856205
--- /dev/null
+++ b/test/trace_processor/diff_tests/translation/tests.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class Translation(TestSuite):
+
+  def test_java_class_name_arg(self):
+    return DiffTestBlueprint(
+        trace=Path('java_class_name_arg.textproto'),
+        query=Path('chrome_args_test.sql'),
+        out=Path('java_class_name_arg.out'))
+
+  def test_chrome_histogram(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_histogram.textproto'),
+        query=Path('chrome_args_test.sql'),
+        out=Path('chrome_histogram.out'))
+
+  def test_chrome_user_event(self):
+    return DiffTestBlueprint(
+        trace=Path('chrome_user_event.textproto'),
+        query=Path('chrome_args_test.sql'),
+        out=Path('chrome_user_event.out'))
+
+  def test_chrome_performance_mark(self):
+    return DiffTestBlueprint(
+        trace=TextProto(r"""
+        packet {
+          translation_table {
+            chrome_performance_mark {
+              site_hash_to_name { key: 10 value: "site1" }
+              mark_hash_to_name { key: 20 value: "mark2" }
+            }
+          }
+        }
+        packet {
+          timestamp: 0
+          trusted_packet_sequence_id: 1
+          track_descriptor {
+            uuid: 12345
+            thread {
+              pid: 123
+              tid: 345
+            }
+            parent_uuid: 0
+            chrome_thread {
+              thread_type: THREAD_POOL_FG_WORKER
+            }
+          }
+        }
+        packet {
+          trusted_packet_sequence_id: 1
+          timestamp: 1
+          track_event {
+            categories: "cat1"
+            track_uuid: 12345
+            type: 1
+            name: "slice1"
+            [perfetto.protos.ChromeTrackEvent.chrome_hashed_performance_mark] {
+              site_hash: 10
+              mark_hash: 20
+            }
+          }
+        }
+        packet {
+          trusted_packet_sequence_id: 1
+          timestamp: 6000
+          track_event {
+            track_uuid: 12345
+            categories: "cat1"
+            name: "slice1"
+            type: 2
+          }
+        }
+        """),
+        query=Path('chrome_args_test.sql'),
+        out=Path('chrome_performance_mark.out'))
+
+  def test_slice_name(self):
+    return DiffTestBlueprint(
+        trace=Path('slice_name.textproto'),
+        query="""
+        SELECT name FROM slice ORDER BY name;
+        """,
+        out=Csv("""
+        "name"
+        "mapped_name1"
+        "mapped_name2"
+        "raw_name3"
+        "slice_begin"
+        """))
+
+  def test_slice_name_2(self):
+    return DiffTestBlueprint(
+        trace=Path('slice_name_negative_timestamp.textproto'),
+        query="""
+        SELECT name FROM slice ORDER BY name;
+        """,
+        out=Csv("""
+        "name"
+        "mapped_name1"
+        "mapped_name2"
+        "raw_name3"
+        "slice_begin"
+        """))
+
+  def test_native_symbol_arg(self):
+    return DiffTestBlueprint(
+        trace=Path('native_symbol_arg.textproto'),
+        query=Path('chrome_args_test.sql'),
+        out=Path('native_symbol_arg.out'))
+
+  def test_native_symbol_arg_2(self):
+    return DiffTestBlueprint(
+        trace=Path('native_symbol_arg_incomplete.textproto'),
+        query=Path('chrome_args_test.sql'),
+        out=Path('native_symbol_arg.out'))
diff --git a/test/trace_processor/diff_tests/ufs/tests.py b/test/trace_processor/diff_tests/ufs/tests.py
new file mode 100644
index 0000000..0e318aa
--- /dev/null
+++ b/test/trace_processor/diff_tests/ufs/tests.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python3
+# Copyright (C) 2023 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 a
+#
+#      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 python.generators.diff_tests.testing import Path, DataPath, Metric
+from python.generators.diff_tests.testing import Csv, Json, TextProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class Ufs(TestSuite):
+  # UFS command
+  def test_ufshcd_command(self):
+    return DiffTestBlueprint(
+        trace=Path('ufshcd_command.textproto'),
+        query="""
+        SELECT
+          ts,
+          value
+        FROM
+          counter AS c
+        JOIN
+          counter_track AS ct
+          ON c.track_id = ct.id
+        WHERE
+          ct.name = "io.ufs.command.count"
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "ts","value"
+        10000,1.000000
+        10008,2.000000
+        10010,3.000000
+        10011,1.000000
+        """))
+
+  def test_ufshcd_command_tag(self):
+    return DiffTestBlueprint(
+        trace=Path('ufshcd_command_tag.textproto'),
+        query="""
+        SELECT ts, dur, slice.name
+        FROM slice
+        JOIN track ON slice.track_id = track.id
+        WHERE track.name GLOB 'io.ufs.command.tag*'
+        ORDER BY ts;
+        """,
+        out=Csv("""
+        "ts","dur","name"
+        10000,800,"READ (10)"
+        10900,50,"WRITE (10) (GID=0x16)"
+        """))
diff --git a/test/trace_processor/io/ufshcd_command.textproto b/test/trace_processor/diff_tests/ufs/ufshcd_command.textproto
similarity index 100%
rename from test/trace_processor/io/ufshcd_command.textproto
rename to test/trace_processor/diff_tests/ufs/ufshcd_command.textproto
diff --git a/test/trace_processor/io/ufshcd_command_tag.textproto b/test/trace_processor/diff_tests/ufs/ufshcd_command_tag.textproto
similarity index 100%
rename from test/trace_processor/io/ufshcd_command_tag.textproto
rename to test/trace_processor/diff_tests/ufs/ufshcd_command_tag.textproto
diff --git a/test/trace_processor/dynamic/abs_time_str_test.sql b/test/trace_processor/dynamic/abs_time_str_test.sql
deleted file mode 100644
index 2cbae92..0000000
--- a/test/trace_processor/dynamic/abs_time_str_test.sql
+++ /dev/null
@@ -1,4 +0,0 @@
-SELECT
-  ABS_TIME_STR(15) as t15,
-  ABS_TIME_STR(25) as t25,
-  ABS_TIME_STR(35) as t35;
diff --git a/test/trace_processor/dynamic/ancestor_slice_by_stack.out b/test/trace_processor/dynamic/ancestor_slice_by_stack.out
deleted file mode 100644
index 13881bc..0000000
--- a/test/trace_processor/dynamic/ancestor_slice_by_stack.out
+++ /dev/null
@@ -1,5 +0,0 @@
-"ts","name"
-1000,"event_depth_0"
-2000,"event_depth_1"
-8000,"event_depth_0"
-9000,"event_depth_1"
diff --git a/test/trace_processor/dynamic/ancestor_slice_by_stack_test.sql b/test/trace_processor/dynamic/ancestor_slice_by_stack_test.sql
deleted file mode 100644
index 3b5089e..0000000
--- a/test/trace_processor/dynamic/ancestor_slice_by_stack_test.sql
+++ /dev/null
@@ -1,5 +0,0 @@
-SELECT ts, name FROM ancestor_slice_by_stack((
-  SELECT stack_id FROM slice
-  WHERE name = 'event_depth_2'
-  LIMIT 1
-));
diff --git a/test/trace_processor/dynamic/ancestor_slice_test.sql b/test/trace_processor/dynamic/ancestor_slice_test.sql
deleted file mode 100644
index 44ff972..0000000
--- a/test/trace_processor/dynamic/ancestor_slice_test.sql
+++ /dev/null
@@ -1,3 +0,0 @@
-SELECT slice.name AS currentSliceName, ancestor.name AS ancestorSliceName
-FROM slice LEFT JOIN ancestor_slice(slice.id) AS ancestor
-ORDER BY slice.ts ASC, ancestor.ts ASC, slice.name ASC, ancestor.name ASC;
diff --git a/test/trace_processor/dynamic/annotated_callstack_test.sql b/test/trace_processor/dynamic/annotated_callstack_test.sql
deleted file mode 100644
index a23f3cc..0000000
--- a/test/trace_processor/dynamic/annotated_callstack_test.sql
+++ /dev/null
@@ -1,25 +0,0 @@
---
--- Copyright 2021 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 eac.id, eac.depth, eac.frame_id, eac.annotation,
-       spf.name
-from experimental_annotated_callstack eac
-join perf_sample ps
-  on (eac.start_id == ps.callsite_id)
-join stack_profile_frame spf
-  on (eac.frame_id == spf.id)
-order by eac.start_id asc, eac.depth asc;
-
diff --git a/test/trace_processor/dynamic/connected_flow_test.sql b/test/trace_processor/dynamic/connected_flow_test.sql
deleted file mode 100644
index 96f079f..0000000
--- a/test/trace_processor/dynamic/connected_flow_test.sql
+++ /dev/null
@@ -1,30 +0,0 @@
---
--- 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 "directly_connected" as type, s.name, s1.name as start_name, s2.name as end_name  FROM slice s
-JOIN DIRECTLY_CONNECTED_FLOW(s.id) c
-JOIN slice s1 ON s1.id = c.slice_out
-JOIN slice s2 ON s2.id = c.slice_in
-UNION
-SELECT "following" as type, s.name, s1.name as start_name, s2.name as end_name  FROM slice s
-JOIN FOLLOWING_FLOW(s.id) c
-JOIN slice s1 ON s1.id = c.slice_out
-JOIN slice s2 ON s2.id = c.slice_in
-UNION
-SELECT "preceding" as type, s.name, s1.name as start_name, s2.name as end_name  FROM slice s
-JOIN PRECEDING_FLOW(s.id) c
-JOIN slice s1 ON s1.id = c.slice_out
-JOIN slice s2 ON s2.id = c.slice_in
-ORDER BY type, s.name, s1.name, s2.name ASC
diff --git a/test/trace_processor/dynamic/descendant_slice_by_stack.out b/test/trace_processor/dynamic/descendant_slice_by_stack.out
deleted file mode 100644
index c32f639..0000000
--- a/test/trace_processor/dynamic/descendant_slice_by_stack.out
+++ /dev/null
@@ -1,5 +0,0 @@
-"ts","name"
-2000,"event_depth_1"
-3000,"event_depth_2"
-9000,"event_depth_1"
-10000,"event_depth_2"
diff --git a/test/trace_processor/dynamic/descendant_slice_by_stack_test.sql b/test/trace_processor/dynamic/descendant_slice_by_stack_test.sql
deleted file mode 100644
index ac42bda..0000000
--- a/test/trace_processor/dynamic/descendant_slice_by_stack_test.sql
+++ /dev/null
@@ -1,6 +0,0 @@
-SELECT ts, name FROM descendant_slice_by_stack((
-  SELECT stack_id FROM slice
-  WHERE name = 'event_depth_0'
-  LIMIT 1
-));
-
diff --git a/test/trace_processor/dynamic/descendant_slice_test.sql b/test/trace_processor/dynamic/descendant_slice_test.sql
deleted file mode 100644
index 918dd38..0000000
--- a/test/trace_processor/dynamic/descendant_slice_test.sql
+++ /dev/null
@@ -1,3 +0,0 @@
-SELECT slice.name AS currentSliceName, descendant.name AS descendantSliceName
-FROM slice LEFT JOIN descendant_slice(slice.id) AS descendant
-ORDER BY slice.ts ASC, descendant.ts ASC, slice.name ASC, descendant.name ASC;
diff --git a/test/trace_processor/dynamic/empty_abs_time_str.out b/test/trace_processor/dynamic/empty_abs_time_str.out
deleted file mode 100644
index 4671214..0000000
--- a/test/trace_processor/dynamic/empty_abs_time_str.out
+++ /dev/null
@@ -1,2 +0,0 @@
-"t15","t25","t35"
-"[NULL]","[NULL]","[NULL]"
diff --git a/test/trace_processor/dynamic/index b/test/trace_processor/dynamic/index
deleted file mode 100644
index f4740ee..0000000
--- a/test/trace_processor/dynamic/index
+++ /dev/null
@@ -1,23 +0,0 @@
-# Tests for custom dynamic tables.
-
-# Ancestor slice table.
-relationship_tables.textproto ancestor_slice_test.sql ancestor_slice.out
-
-# Descendant slice table.
-relationship_tables.textproto descendant_slice_test.sql descendant_slice.out
-
-# Ancestor slice by stack table.
-slice_stacks.textproto ancestor_slice_by_stack_test.sql ancestor_slice_by_stack.out
-
-# Descendant slice by stack table.
-slice_stacks.textproto descendant_slice_by_stack_test.sql descendant_slice_by_stack.out
-
-# Connected/Following/Perceeding flow table.
-connected_flow_data.json connected_flow_test.sql connected_flow.out
-
-# Annotated callstacks.
-../../data/perf_sample_sc.pb annotated_callstack_test.sql perf_sample_sc_annotated_callstack.out
-
-# ABS_TIME_STR function
-various_clocks.textproto abs_time_str_test.sql various_clocks_abs_time_str.out
-../common/empty.textproto abs_time_str_test.sql empty_abs_time_str.out
diff --git a/test/trace_processor/dynamic/various_clocks.textproto b/test/trace_processor/dynamic/various_clocks.textproto
deleted file mode 100644
index c8de99f..0000000
--- a/test/trace_processor/dynamic/various_clocks.textproto
+++ /dev/null
@@ -1,42 +0,0 @@
-packet {
-  clock_snapshot {
-    clocks {
-      clock_id: 1
-      timestamp: 0
-    }
-    clocks {
-      clock_id: 6
-      timestamp: 10
-    }
-  }
-  trusted_packet_sequence_id: 1
-  timestamp: 10
-}
-packet {
-  clock_snapshot {
-    clocks {
-      clock_id: 1
-      timestamp: 1652904000000000000
-    }
-    clocks {
-      clock_id: 6
-      timestamp: 20
-    }
-  }
-  trusted_packet_sequence_id: 1
-  timestamp: 20
-}
-packet {
-  clock_snapshot {
-    clocks {
-      clock_id: 1
-      timestamp: 1652903999999999995
-    }
-    clocks {
-      clock_id: 6
-      timestamp: 30
-    }
-  }
-  trusted_packet_sequence_id: 1
-  timestamp: 30
-}
diff --git a/test/trace_processor/fs/f2fs_iostat_latency_test.sql b/test/trace_processor/fs/f2fs_iostat_latency_test.sql
deleted file mode 100644
index fa20398..0000000
--- a/test/trace_processor/fs/f2fs_iostat_latency_test.sql
+++ /dev/null
@@ -1,10 +0,0 @@
-SELECT
-  name,
-  ts,
-  value
-FROM
-  counter AS c
-  JOIN
-  counter_track AS ct
-  ON c.track_id = ct.id
-ORDER BY name,ts
diff --git a/test/trace_processor/fs/f2fs_iostat_test.sql b/test/trace_processor/fs/f2fs_iostat_test.sql
deleted file mode 100644
index fa20398..0000000
--- a/test/trace_processor/fs/f2fs_iostat_test.sql
+++ /dev/null
@@ -1,10 +0,0 @@
-SELECT
-  name,
-  ts,
-  value
-FROM
-  counter AS c
-  JOIN
-  counter_track AS ct
-  ON c.track_id = ct.id
-ORDER BY name,ts
diff --git a/test/trace_processor/fs/index b/test/trace_processor/fs/index
deleted file mode 100644
index b416990..0000000
--- a/test/trace_processor/fs/index
+++ /dev/null
@@ -1,2 +0,0 @@
-f2fs_iostat.textproto f2fs_iostat_test.sql f2fs_iostat.out
-f2fs_iostat_latency.textproto f2fs_iostat_latency_test.sql f2fs_iostat_latency.out
diff --git a/test/trace_processor/fuchsia/fuchsia_smoke.out b/test/trace_processor/fuchsia/fuchsia_smoke.out
deleted file mode 100644
index a62db85..0000000
--- a/test/trace_processor/fuchsia/fuchsia_smoke.out
+++ /dev/null
@@ -1,11 +0,0 @@
-"ts","cpu","dur","end_state","priority","tid"
-19675868967,2,79022,"S",20,4344
-19676000188,3,504797,"S",20,6547
-19676504985,3,42877,"S",20,6525
-19676582005,0,48467,"S",20,11566
-19676989045,2,138116,"S",20,9949
-19677162311,3,48655,"S",20,6525
-19677305405,3,48814,"S",20,6525
-19677412330,0,177220,"S",20,4344
-19677680485,2,91422,"S",20,6537
-19677791779,3,96082,"S",20,1680
diff --git a/test/trace_processor/fuchsia/fuchsia_smoke_counters.out b/test/trace_processor/fuchsia/fuchsia_smoke_counters.out
deleted file mode 100644
index 19882a1..0000000
--- a/test/trace_processor/fuchsia/fuchsia_smoke_counters.out
+++ /dev/null
@@ -1,6 +0,0 @@
-"ts","value","name"
-20329439768,30.331177,"cpu_usage:average_cpu_percentage"
-21331281870,7.829745,"cpu_usage:average_cpu_percentage"
-22332302017,9.669818,"cpu_usage:average_cpu_percentage"
-23332974162,6.421237,"cpu_usage:average_cpu_percentage"
-24333405767,12.079849,"cpu_usage:average_cpu_percentage"
diff --git a/test/trace_processor/fuchsia/fuchsia_smoke_flow.out b/test/trace_processor/fuchsia/fuchsia_smoke_flow.out
deleted file mode 100644
index 6726a35..0000000
--- a/test/trace_processor/fuchsia/fuchsia_smoke_flow.out
+++ /dev/null
@@ -1,11 +0,0 @@
-"id","slice_out","slice_in"
-0,0,1
-1,2,3
-2,4,5
-3,6,7
-4,8,9
-5,10,11
-6,12,13
-7,14,15
-8,16,17
-9,18,19
diff --git a/test/trace_processor/fuchsia/fuchsia_smoke_instants.out b/test/trace_processor/fuchsia/fuchsia_smoke_instants.out
deleted file mode 100644
index 87964ab..0000000
--- a/test/trace_processor/fuchsia/fuchsia_smoke_instants.out
+++ /dev/null
@@ -1,11 +0,0 @@
-"ts","name"
-21442756010,"task_start"
-21446583438,"task_end"
-21448366538,"task_start"
-21450363277,"task_end"
-21454255741,"task_start"
-21457834528,"task_end"
-21459006408,"task_start"
-21460601866,"task_end"
-21461282720,"task_start"
-21462998487,"task_end"
diff --git a/test/trace_processor/fuchsia/fuchsia_smoke_slices.out b/test/trace_processor/fuchsia/fuchsia_smoke_slices.out
deleted file mode 100644
index 39c37a0..0000000
--- a/test/trace_processor/fuchsia/fuchsia_smoke_slices.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"type","depth","count"
-"thread_track",0,2153
-"thread_track",1,1004
diff --git a/test/trace_processor/fuchsia/fuchsia_smoke_type.out b/test/trace_processor/fuchsia/fuchsia_smoke_type.out
deleted file mode 100644
index ba17fcf..0000000
--- a/test/trace_processor/fuchsia/fuchsia_smoke_type.out
+++ /dev/null
@@ -1,11 +0,0 @@
-"id","name","type"
-0,"[NULL]","thread_track"
-1,"[NULL]","thread_track"
-2,"[NULL]","thread_track"
-3,"[NULL]","thread_track"
-4,"[NULL]","thread_track"
-5,"cpu_usage:average_cpu_percentage","process_counter_track"
-6,"[NULL]","thread_track"
-7,"[NULL]","thread_track"
-8,"[NULL]","thread_track"
-9,"[NULL]","thread_track"
diff --git a/test/trace_processor/fuchsia/fuchsia_workstation_smoke_args.out b/test/trace_processor/fuchsia/fuchsia_workstation_smoke_args.out
deleted file mode 100644
index 3b83654..0000000
--- a/test/trace_processor/fuchsia/fuchsia_workstation_smoke_args.out
+++ /dev/null
@@ -1,11 +0,0 @@
-"key","COUNT(*)"
-"Dart Arguments",3
-"Escher frame number",33
-"Expected presentation time",17
-"Frame number",33
-"MinikinFontsCount",2
-"Predicted frame duration(ms)",21
-"Render time(ms)",21
-"Timestamp",917
-"Update time(ms)",21
-"Vsync interval",900
diff --git a/test/trace_processor/fuchsia/index b/test/trace_processor/fuchsia/index
deleted file mode 100644
index 80fb688..0000000
--- a/test/trace_processor/fuchsia/index
+++ /dev/null
@@ -1,13 +0,0 @@
-# Contains tests for parsing Fuchsia traces.
-
-# Smoke test a bunch of different event types.
-../../data/fuchsia_trace.fxt ../common/smoke_test.sql fuchsia_smoke.out
-../../data/fuchsia_trace.fxt ../common/smoke_slices_test.sql fuchsia_smoke_slices.out
-../../data/fuchsia_trace.fxt smoke_instants_test.sql fuchsia_smoke_instants.out
-../../data/fuchsia_trace.fxt smoke_counters_test.sql fuchsia_smoke_counters.out
-../../data/fuchsia_trace.fxt smoke_flow_test.sql fuchsia_smoke_flow.out
-../../data/fuchsia_trace.fxt smoke_type_test.sql fuchsia_smoke_type.out
-
-# Smoke test a high-CPU trace.
-../../data/fuchsia_workstation.fxt ../common/smoke_slices_test.sql fuchsia_workstation_smoke_slices.out
-../../data/fuchsia_workstation.fxt smoke_args_test.sql fuchsia_workstation_smoke_args.out
diff --git a/test/trace_processor/fuchsia/smoke_args_test.sql b/test/trace_processor/fuchsia/smoke_args_test.sql
deleted file mode 100644
index 522f538..0000000
--- a/test/trace_processor/fuchsia/smoke_args_test.sql
+++ /dev/null
@@ -1,21 +0,0 @@
---
--- 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
-  key,
-  COUNT(*)
-from args
-group by key
-limit 10;
diff --git a/test/trace_processor/fuchsia/smoke_counters_test.sql b/test/trace_processor/fuchsia/smoke_counters_test.sql
deleted file mode 100644
index b4dc7ed..0000000
--- a/test/trace_processor/fuchsia/smoke_counters_test.sql
+++ /dev/null
@@ -1,21 +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
-  ts,
-  value,
-  name
-from counters
-limit 10;
diff --git a/test/trace_processor/fuchsia/smoke_flow_test.sql b/test/trace_processor/fuchsia/smoke_flow_test.sql
deleted file mode 100644
index e0b8af4..0000000
--- a/test/trace_processor/fuchsia/smoke_flow_test.sql
+++ /dev/null
@@ -1,21 +0,0 @@
---
--- 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
-  id,
-  slice_out,
-  slice_in
-from flow
-limit 10;
diff --git a/test/trace_processor/fuchsia/smoke_instants_test.sql b/test/trace_processor/fuchsia/smoke_instants_test.sql
deleted file mode 100644
index cc5c40e..0000000
--- a/test/trace_processor/fuchsia/smoke_instants_test.sql
+++ /dev/null
@@ -1,22 +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
-  ts,
-  name
-from slice
-where
-  dur = 0
-limit 10;
diff --git a/test/trace_processor/fuchsia/smoke_type_test.sql b/test/trace_processor/fuchsia/smoke_type_test.sql
deleted file mode 100644
index 4a29c12..0000000
--- a/test/trace_processor/fuchsia/smoke_type_test.sql
+++ /dev/null
@@ -1,21 +0,0 @@
---
--- Copyright 2022 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
-  id,
-  name,
-  type
-from track
-limit 10;
diff --git a/test/trace_processor/graphics/actual_frame_timeline_events_test.sql b/test/trace_processor/graphics/actual_frame_timeline_events_test.sql
deleted file mode 100644
index 412f4b7..0000000
--- a/test/trace_processor/graphics/actual_frame_timeline_events_test.sql
+++ /dev/null
@@ -1,24 +0,0 @@
---
--- 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 ts, dur, process.pid as pid, display_frame_token, surface_frame_token, layer_name,
-    present_type, on_time_finish, gpu_composition, jank_type, prediction_type, jank_tag
-from
-  (select t.*, process_track.name as track_name from
-    process_track left join actual_frame_timeline_slice t
-    on process_track.id = t.track_id) s
-join process using(upid)
-where s.track_name = 'Actual Timeline'
-order by ts
diff --git a/test/trace_processor/graphics/android_jank_cuj_query_test.sql b/test/trace_processor/graphics/android_jank_cuj_query_test.sql
deleted file mode 100644
index af5c4ee..0000000
--- a/test/trace_processor/graphics/android_jank_cuj_query_test.sql
+++ /dev/null
@@ -1,66 +0,0 @@
---
--- Copyright 2022 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/android_jank_cuj.sql') AS suppress_query_output;
-
-
--- First query to look at `binder transaction` on the Main Thread
-
-DROP VIEW IF EXISTS android_jank_cuj_query_test_binder;
-CREATE VIEW android_jank_cuj_query_test_binder AS
-SELECT * FROM android_jank_cuj_slice
-WHERE name = 'binder transaction';
-
-SELECT ANDROID_JANK_CORRELATE_FRAME_SLICE('MainThread', 'android_jank_cuj_query_test_binder') AS suppress_query_output;
-
-
--- Second query to look at `JIT compiling` slices on JIT threadpool
-
-DROP VIEW IF EXISTS android_jank_cuj_query_test_jit;
-CREATE VIEW android_jank_cuj_query_test_jit AS
-SELECT * FROM android_jank_cuj_slice
-WHERE name GLOB 'JIT compiling*';
-
-SELECT ANDROID_JANK_CORRELATE_FRAME_SLICE_IMPL('App threads', 'android_jank_cuj_query_test_jit', 'jank_query_jit') AS suppress_query_output;
-
---- Third query to look at 'sf binder' slices on SF main thread
-
-DROP VIEW IF EXISTS android_jank_cuj_query_test_sf_binder;
-CREATE VIEW android_jank_cuj_query_test_sf_binder AS
-SELECT * FROM android_jank_cuj_sf_slice
-WHERE name = 'sf binder';
-
-SELECT ANDROID_JANK_CORRELATE_FRAME_SLICE_IMPL('SF MainThread', 'android_jank_cuj_query_test_sf_binder', 'jank_query_sf_binder') AS suppress_query_output;
-
-
---- Fourth query to look at 'shader compile' slices on SF RenderEngine
-
-DROP VIEW IF EXISTS android_jank_cuj_query_test_re;
-CREATE VIEW android_jank_cuj_query_test_re AS
-SELECT * FROM android_jank_cuj_sf_slice
-WHERE name = 'shader compile';
-
-SELECT ANDROID_JANK_CORRELATE_FRAME_SLICE_IMPL('SF RenderEngine', 'android_jank_cuj_query_test_re', 'jank_query_re') AS suppress_query_output;
-
-
--- UNION ALL results from all queries.
-SELECT 'JIT compiling' AS slice, * FROM jank_query_jit_slice_in_frame_agg
-UNION ALL
-SELECT 'binder transaction' AS slice, * FROM jank_query_slice_in_frame_agg
-UNION ALL
-SELECT 'sf binder' AS slice, * FROM jank_query_sf_binder_slice_in_frame_agg
-UNION ALL
-SELECT 'shader compile' AS slice, * FROM jank_query_re_slice_in_frame_agg
-ORDER BY slice, cuj_id, vsync;
diff --git a/test/trace_processor/graphics/android_sysui_cuj.out b/test/trace_processor/graphics/android_sysui_cuj.out
deleted file mode 100644
index 0614d02..0000000
--- a/test/trace_processor/graphics/android_sysui_cuj.out
+++ /dev/null
@@ -1,110 +0,0 @@
-android_sysui_cuj {
- cuj_name: "SHADE_ROW_EXPAND"
-  cuj_start: 10
-  cuj_dur: 901000000
-  process {
-    name: "com.android.systemui"
-    uid: 10001
-    package {
-      package_name: "com.android.systemui"
-      apk_version_code: 1
-      debuggable: false
-    }
-    packages_for_uid {
-      package_name: "com.android.systemui"
-      apk_version_code: 1
-      debuggable: false
-    }
- }
- frames {
-   number: 1
-   ts: 0
-   dur: 15000000
-   vsync: 10
- }
- frames {
-   number: 2
-   ts: 8000000
-   dur: 27000000
-   jank_cause: "MainThread - binder transaction time"
-   jank_cause: "SurfaceFlinger Scheduling"
-   vsync: 20
- }
- frames {
-   number: 3
-   ts: 30000000
-   dur: 22000000
-   jank_cause: "RenderThread - long flush layers"
-   jank_cause: "MainThread - binder calls count"
-   vsync: 30
- }
- frames {
-   number: 4
-   ts: 40000000
-   dur: 38000000
-   jank_cause: "GPU completion - long completion time"
-   jank_cause: "Long running time"
-   jank_cause: "JIT compiling"
-   vsync: 40
- }
- frames {
-   number: 5
-   ts: 70000000
-   dur: 18000000
-   jank_cause: "RenderThread - scheduler"
-   vsync: 60
- }
-  frames {
-    number: 6
-    ts: 100000000
-    dur: 22000000
-    jank_cause: "GPU completion - long completion time"
-    vsync: 90
-  }
-  frames {
-    number: 7
-    ts: 200000000
-    dur: 10000000
-    jank_cause: "SurfaceFlinger GPU Deadline Missed"
-    jank_cause: "SurfaceFlinger Scheduling"
-    vsync: 100
-  }
-  frames {
-    number: 8
-    ts: 300000000
-    dur: 4000000
-    vsync: 110
-  }
-  frames {
-    number: 9
-    ts: 400000000
-    dur: 10000000
-    vsync: 120
-  }
-  frames {
-    number: 10
-    ts: 500000000
-    dur: 56000000
-    vsync: 130
-    jank_cause: "MainThread - binder transaction time"
-  }
-  frames {
-    number: 11
-    ts: 600000000
-    dur: 26000000
-    vsync: 140
-    jank_cause: "MainThread - binder transaction time"
-  }
-  frames {
-    number: 12
-    ts: 700000000
-    dur: 15000000
-    vsync: 150
-  }
-  frames {
-    number: 13
-    ts: 800000000
-    dur: 2000000
-    vsync: 160
-  }
-}
diff --git a/test/trace_processor/graphics/android_sysui_cuj_event.out b/test/trace_processor/graphics/android_sysui_cuj_event.out
deleted file mode 100644
index e5104e8..0000000
--- a/test/trace_processor/graphics/android_sysui_cuj_event.out
+++ /dev/null
@@ -1,10 +0,0 @@
-
-"track_type","track_name","ts","dur","slice_name"
-"slice","SHADE_ROW_EXPAND - jank cause",8000000,15000000,"MainThread - binder transaction time,SurfaceFlinger Scheduling"
-"slice","SHADE_ROW_EXPAND - jank cause",30000000,3000000,"MainThread - binder calls count,RenderThread - long flush layers"
-"slice","SHADE_ROW_EXPAND - jank cause",40000000,13000000,"GPU completion - long completion time,JIT compiling,Long running time"
-"slice","SHADE_ROW_EXPAND - jank cause",70000000,10000000,"RenderThread - scheduler"
-"slice","SHADE_ROW_EXPAND - jank cause",100000000,15000000,"GPU completion - long completion time"
-"slice","SHADE_ROW_EXPAND - jank cause",200000000,15000000,"SurfaceFlinger GPU Deadline Missed,SurfaceFlinger Scheduling"
-"slice","SHADE_ROW_EXPAND - jank cause",500000000,55000000,"MainThread - binder transaction time"
-"slice","SHADE_ROW_EXPAND - jank cause",600000000,10000000,"MainThread - binder transaction time"
diff --git a/test/trace_processor/graphics/android_sysui_cuj_event_test.sql b/test/trace_processor/graphics/android_sysui_cuj_event_test.sql
deleted file mode 100644
index e1d1271..0000000
--- a/test/trace_processor/graphics/android_sysui_cuj_event_test.sql
+++ /dev/null
@@ -1,18 +0,0 @@
---
--- Copyright 2021 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/android_sysui_cuj.sql') AS suppress_query_output;
-
-SELECT * FROM android_sysui_cuj_event;
\ No newline at end of file
diff --git a/test/trace_processor/graphics/clock_sync.out b/test/trace_processor/graphics/clock_sync.out
deleted file mode 100644
index aca328a..0000000
--- a/test/trace_processor/graphics/clock_sync.out
+++ /dev/null
@@ -1,10 +0,0 @@
-"ts","int_value"
-1,3
-102,5
-1003,7
-1005,9
-2006,11
-2010,12
-2013,13
-3007,14
-3010,15
diff --git a/test/trace_processor/graphics/clock_sync.py b/test/trace_processor/graphics/clock_sync.py
deleted file mode 100644
index 690c736..0000000
--- a/test/trace_processor/graphics/clock_sync.py
+++ /dev/null
@@ -1,91 +0,0 @@
-#!/usr/bin/env python3
-# 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 the clock-sync logic. It synthesizes a trace with
-# (i) builtin clocks, (ii) custom global clocks, (iii) sequence-scoped clocks.
-# It uses gpu counters because that is a quite simple packet and doesn't have
-# special treatement. We can't use ftrace because ftrace events use nested
-# per-event timestamps and they are assumed to be in the CLOCK_MONOTONIC
-# domains regardless of the TracePacket's timestamp_clock_id.
-
-from os import sys, path
-
-import synth_common
-from synth_common import CLONE_THREAD
-
-# Clock IDs in the range [64, 128) are sequence-scoped. See comments in
-# clock_snapshot.proto.
-CLOCK_MONOTONIC = 3  # Builtin clock, see clock_snapshot.proto.
-CLOCK_BOOTTIME = 6  # Builtin clock, see clock_snapshot.proto.
-GLOBAL_CLK1 = 128
-GLOBAL_CLK2 = 129
-SEQ_CLOCK1 = 64
-
-trace = synth_common.create_trace()
-
-# The default trace clock domain is CLOCK_BOOTTIME.
-trace.add_gpu_counter(ts=1, counter_id=42, value=3)
-
-# Emit a ClockSnapshot that sets BOOTTIME = MONOTONIC + 100.
-trace.add_clock_snapshot(clocks={CLOCK_MONOTONIC: 1, CLOCK_BOOTTIME: 101})
-
-# Emit a counter synced against the global built-in clock CLOCK_MONOTONIC.
-# This should be translated, at import time, to BOOTTIME = 2 + 100 = 102.
-trace.add_gpu_counter(ts=2, clock_id=CLOCK_MONOTONIC, counter_id=42, value=5)
-
-# Use two global custom clocks. We sync them as follows:
-# BOOTTIME = GLOBAL_CLK1 + 1000
-# GLOBAL_CLK1 = GLOBAL_CLK2 + 1
-# Hence, recursively:
-# BOOTTIME = GLOBAL_CLK2 + 1000 + 1
-trace.add_clock_snapshot(clocks={GLOBAL_CLK1: 1, CLOCK_BOOTTIME: 1001})
-trace.add_clock_snapshot(clocks={GLOBAL_CLK1: 2, GLOBAL_CLK2: 1})
-
-# This counter should be translated, at import time, to BOOTTIME = 3 + 1000
-trace.add_gpu_counter(ts=3, clock_id=GLOBAL_CLK1, counter_id=42, value=7)
-
-# This one instead to BOOTTIME = 4 + 1000 + 1 = 1005
-trace.add_gpu_counter(ts=4, clock_id=GLOBAL_CLK2, counter_id=42, value=9)
-
-# Use a sequence-scoped clock on two differents sequences.
-# On seq 2, BOOTTIME = SEQ_CLOCK1 + 2000
-# On seq 3, BOOTTIME = SEQ_CLOCK1 + 3000
-trace.add_clock_snapshot(seq_id=2, clocks={SEQ_CLOCK1: 1, CLOCK_BOOTTIME: 2001})
-trace.add_clock_snapshot(seq_id=3, clocks={SEQ_CLOCK1: 1, CLOCK_BOOTTIME: 3001})
-
-# This counter should be translated @ BOOTTIME : 3000 + 7
-trace.add_gpu_counter(
-    ts=7, clock_id=SEQ_CLOCK1, counter_id=42, value=14, seq_id=3)
-
-# This counter should be translated @ BOOTTIME : 2000 + 6
-trace.add_gpu_counter(
-    ts=6, clock_id=SEQ_CLOCK1, seq_id=2, counter_id=42, value=11)
-
-# Set default clock for sequence 2.
-defaults_packet = trace.add_packet()
-defaults_packet.trusted_packet_sequence_id = 2
-defaults_packet.trace_packet_defaults.timestamp_clock_id = SEQ_CLOCK1
-
-# This counter should be translated @ BOOTTIME : 2000 + 10
-trace.add_gpu_counter(ts=10, seq_id=2, counter_id=42, value=12)
-
-# Manually specified clock_id overrides the default clock.
-trace.add_gpu_counter(
-    ts=2013, clock_id=CLOCK_BOOTTIME, seq_id=2, counter_id=42, value=13)
-
-# Other sequence's default clock isn't changed, so this should be in BOOTTIME.
-trace.add_gpu_counter(ts=3010, counter_id=42, value=15, seq_id=3)
-
-sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/graphics/clock_sync_test.sql b/test/trace_processor/graphics/clock_sync_test.sql
deleted file mode 100644
index ac9e48a..0000000
--- a/test/trace_processor/graphics/clock_sync_test.sql
+++ /dev/null
@@ -1,18 +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 ts, cast(value as integer) as int_value
-from counters
-where name GLOB 'gpu_counter*'
\ No newline at end of file
diff --git a/test/trace_processor/graphics/composer_execution.out b/test/trace_processor/graphics/composer_execution.out
deleted file mode 100644
index 1ec79a8..0000000
--- a/test/trace_processor/graphics/composer_execution.out
+++ /dev/null
@@ -1,5 +0,0 @@
-
-"validation_type","count","total"
-"separated_validation",1,200
-"skipped_validation",2,200
-"unskipped_validation",1,200
diff --git a/test/trace_processor/graphics/composer_execution_test.sql b/test/trace_processor/graphics/composer_execution_test.sql
deleted file mode 100644
index 874e12b..0000000
--- a/test/trace_processor/graphics/composer_execution_test.sql
+++ /dev/null
@@ -1,25 +0,0 @@
---
--- Copyright 2021 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/composer_execution.sql',
-  'output', 'hwc_execution_spans') AS suppress_query_output;
-
-SELECT
-  validation_type,
-  COUNT(*) as count,
-  SUM(execution_time_ns) as total
-FROM hwc_execution_spans
-GROUP BY validation_type
-ORDER BY validation_type;
diff --git a/test/trace_processor/graphics/composition_layer_count.out b/test/trace_processor/graphics/composition_layer_count.out
deleted file mode 100644
index 6c47b9e..0000000
--- a/test/trace_processor/graphics/composition_layer_count.out
+++ /dev/null
@@ -1,3 +0,0 @@
-
-"AVG(value)"
-3.000000
diff --git a/test/trace_processor/graphics/composition_layer_count_test.sql b/test/trace_processor/graphics/composition_layer_count_test.sql
deleted file mode 100644
index 30a92e6..0000000
--- a/test/trace_processor/graphics/composition_layer_count_test.sql
+++ /dev/null
@@ -1,19 +0,0 @@
---
--- Copyright 2021 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/android_hwcomposer.sql') AS suppress_query_output;
-
-SELECT AVG(value)
-FROM total_layers;
diff --git a/test/trace_processor/graphics/dpu_vote_clock_bw.out b/test/trace_processor/graphics/dpu_vote_clock_bw.out
deleted file mode 100644
index 78ad37b..0000000
--- a/test/trace_processor/graphics/dpu_vote_clock_bw.out
+++ /dev/null
@@ -1,17 +0,0 @@
-android_hwcomposer {
-  skipped_validation_count: 0
-  unskipped_validation_count: 0
-  separated_validation_count: 0
-  unknown_validation_count: 0
-  dpu_vote_metrics {
-    tid: 237
-    avg_dpu_vote_clock: 206250
-    avg_dpu_vote_avg_bw: 210000
-    avg_dpu_vote_peak_bw: 205000
-    avg_dpu_vote_rt_bw: 271000
-  }
-  dpu_vote_metrics {
-    tid: 299
-    avg_dpu_vote_clock: 250000
-  }
-}
diff --git a/test/trace_processor/graphics/drm_dma_fence_gpu_track.out b/test/trace_processor/graphics/drm_dma_fence_gpu_track.out
deleted file mode 100644
index 5ec5f6d..0000000
--- a/test/trace_processor/graphics/drm_dma_fence_gpu_track.out
+++ /dev/null
@@ -1,5 +0,0 @@
-"name","ts","dur","name","flat_key","int_value","string_value"
-"fence-gpu-ring-0-1",11303602488073,12813,"fence","fence seqno",16665,"[NULL]"
-"fence-gpu-ring-0-1",11303602500886,4805626,"fence","fence seqno",16665,"[NULL]"
-"fence-gpu-ring-0-1",11303607306512,3850783,"fence","fence seqno",16666,"[NULL]"
-"fence-ring0-9",11303702681699,4868387,"fence","fence seqno",5065,"[NULL]"
diff --git a/test/trace_processor/graphics/drm_dma_fence_thread_track.out b/test/trace_processor/graphics/drm_dma_fence_thread_track.out
deleted file mode 100644
index c4d5bda..0000000
--- a/test/trace_processor/graphics/drm_dma_fence_thread_track.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"utid","ts","dur","name","flat_key","int_value","string_value"
-3,11303702851231,4867658,"dma_fence_wait","fence context",9,"[NULL]"
-3,11303702851231,4867658,"dma_fence_wait","fence seqno",5065,"[NULL]"
diff --git a/test/trace_processor/graphics/drm_gpu_track_test.sql b/test/trace_processor/graphics/drm_gpu_track_test.sql
deleted file mode 100644
index b1554a8..0000000
--- a/test/trace_processor/graphics/drm_gpu_track_test.sql
+++ /dev/null
@@ -1,15 +0,0 @@
-SELECT
-  gpu_track.name,
-  ts,
-  dur,
-  slice.name,
-  flat_key,
-  int_value,
-  string_value
-FROM
-  gpu_track
-  JOIN slice
-  ON slice.track_id = gpu_track.id
-  JOIN args
-  ON slice.arg_set_id = args.arg_set_id
-ORDER BY ts
diff --git a/test/trace_processor/graphics/drm_sched_gpu_track.out b/test/trace_processor/graphics/drm_sched_gpu_track.out
deleted file mode 100644
index e9533da..0000000
--- a/test/trace_processor/graphics/drm_sched_gpu_track.out
+++ /dev/null
@@ -1,5 +0,0 @@
-"name","ts","dur","name","flat_key","int_value","string_value"
-"sched-ring0",9246165349383,4729073,"job","gpu sched job",13481,"[NULL]"
-"sched-ring0",9246170078456,3941571,"job","gpu sched job",13482,"[NULL]"
-"sched-ring0",9246174020027,25156,"job","gpu sched job",13483,"[NULL]"
-"sched-ring0",9246181933273,4726312,"job","gpu sched job",13484,"[NULL]"
diff --git a/test/trace_processor/graphics/drm_sched_thread_track.out b/test/trace_processor/graphics/drm_sched_thread_track.out
deleted file mode 100644
index 6e9e577..0000000
--- a/test/trace_processor/graphics/drm_sched_thread_track.out
+++ /dev/null
@@ -1,9 +0,0 @@
-"utid","ts","dur","name","flat_key","int_value","string_value"
-1,9246165326050,0,"drm_sched_job","gpu sched ring","[NULL]","ring0"
-1,9246165326050,0,"drm_sched_job","gpu sched job",13481,"[NULL]"
-3,9246166957616,0,"drm_sched_job","gpu sched ring","[NULL]","ring0"
-3,9246166957616,0,"drm_sched_job","gpu sched job",13482,"[NULL]"
-3,9246167272512,0,"drm_sched_job","gpu sched ring","[NULL]","ring0"
-3,9246167272512,0,"drm_sched_job","gpu sched job",13483,"[NULL]"
-1,9246181907439,0,"drm_sched_job","gpu sched ring","[NULL]","ring0"
-1,9246181907439,0,"drm_sched_job","gpu sched job",13484,"[NULL]"
diff --git a/test/trace_processor/graphics/drm_thread_track_test.sql b/test/trace_processor/graphics/drm_thread_track_test.sql
deleted file mode 100644
index 4c879fb..0000000
--- a/test/trace_processor/graphics/drm_thread_track_test.sql
+++ /dev/null
@@ -1,15 +0,0 @@
-SELECT
-  utid,
-  ts,
-  dur,
-  slice.name,
-  flat_key,
-  int_value,
-  string_value
-FROM
-  thread_track
-  JOIN slice
-  ON slice.track_id = thread_track.id
-  JOIN args
-  ON slice.arg_set_id = args.arg_set_id
-ORDER BY ts
diff --git a/test/trace_processor/graphics/drm_vblank.textproto b/test/trace_processor/graphics/drm_vblank.textproto
deleted file mode 100644
index 71817fc..0000000
--- a/test/trace_processor/graphics/drm_vblank.textproto
+++ /dev/null
@@ -1,29 +0,0 @@
-packet {
-  ftrace_events {
-    cpu: 0
-    event {
-      timestamp: 6159770881976
-      pid: 0
-      drm_vblank_event {
-        crtc: 0
-        high_prec: 1
-        seq: 3551
-        time: 6159771267407
-      }
-    }
-  }
-}
-packet {
-  ftrace_events {
-    cpu: 4
-    event {
-      timestamp: 6159770993376
-      pid: 144
-      drm_vblank_event_delivered {
-        crtc: 0
-        file: 18446743526216291840
-        seq: 3551
-      }
-    }
-  }
-}
diff --git a/test/trace_processor/graphics/drm_vblank_gpu_track.out b/test/trace_processor/graphics/drm_vblank_gpu_track.out
deleted file mode 100644
index 1578c8e..0000000
--- a/test/trace_processor/graphics/drm_vblank_gpu_track.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"name","ts","dur","name","flat_key","int_value","string_value"
-"vblank-0",6159770881976,0,"signal","vblank seqno",3551,"[NULL]"
-"vblank-0",6159770993376,0,"deliver","vblank seqno",3551,"[NULL]"
diff --git a/test/trace_processor/graphics/expected_frame_timeline_events.out b/test/trace_processor/graphics/expected_frame_timeline_events.out
deleted file mode 100644
index bbfb5bd..0000000
--- a/test/trace_processor/graphics/expected_frame_timeline_events.out
+++ /dev/null
@@ -1,13 +0,0 @@
-"ts","dur","pid","display_frame_token","surface_frame_token","layer_name"
-20,6,666,2,0,"[NULL]"
-21,15,1000,4,1,"Layer1"
-40,6,666,4,0,"[NULL]"
-41,15,1000,6,5,"Layer1"
-80,6,666,6,0,"[NULL]"
-90,16,1000,8,7,"Layer1"
-120,6,666,8,0,"[NULL]"
-140,6,666,12,0,"[NULL]"
-150,20,1000,15,14,"Layer1"
-170,6,666,15,0,"[NULL]"
-200,6,666,17,0,"[NULL]"
-220,10,666,18,0,"[NULL]"
diff --git a/test/trace_processor/graphics/expected_frame_timeline_events_test.sql b/test/trace_processor/graphics/expected_frame_timeline_events_test.sql
deleted file mode 100644
index 31669c2..0000000
--- a/test/trace_processor/graphics/expected_frame_timeline_events_test.sql
+++ /dev/null
@@ -1,23 +0,0 @@
---
--- 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 ts, dur, process.pid as pid, display_frame_token, surface_frame_token, layer_name
-from
-  (select t.*, process_track.name as track_name from
-    process_track left join expected_frame_timeline_slice t
-    on process_track.id = t.track_id) s
-join process using(upid)
-where s.track_name = 'Expected Timeline'
-order by ts
diff --git a/test/trace_processor/graphics/frame_missed_event_frame_missed.out b/test/trace_processor/graphics/frame_missed_event_frame_missed.out
deleted file mode 100644
index 03bdcb3..0000000
--- a/test/trace_processor/graphics/frame_missed_event_frame_missed.out
+++ /dev/null
@@ -1,5 +0,0 @@
-
-"ts","dur"
-100,1
-102,1
-103,1
diff --git a/test/trace_processor/graphics/frame_missed_event_test.sql b/test/trace_processor/graphics/frame_missed_event_test.sql
deleted file mode 100644
index 4e79c51..0000000
--- a/test/trace_processor/graphics/frame_missed_event_test.sql
+++ /dev/null
@@ -1,19 +0,0 @@
---
--- 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/android_surfaceflinger.sql') AS suppress_query_output;
-
-SELECT ts, dur
-FROM android_surfaceflinger_event;
\ No newline at end of file
diff --git a/test/trace_processor/graphics/frame_missed_metrics.out b/test/trace_processor/graphics/frame_missed_metrics.out
deleted file mode 100644
index aad41d2..0000000
--- a/test/trace_processor/graphics/frame_missed_metrics.out
+++ /dev/null
@@ -1,7 +0,0 @@
-android_surfaceflinger {
-  missed_frames: 3
-  missed_hwc_frames: 0
-  missed_gpu_frames: 0
-  missed_frame_rate: 0.42857142857142855 # = 3/7
-  gpu_invocations: 0
-}
diff --git a/test/trace_processor/graphics/gpu_counter_specs.out b/test/trace_processor/graphics/gpu_counter_specs.out
deleted file mode 100644
index 85ec94d..0000000
--- a/test/trace_processor/graphics/gpu_counter_specs.out
+++ /dev/null
@@ -1,6 +0,0 @@
-"group_id","name","description","unit"
-0,"GPU Frequency","clock speed","/22"
-3,"Fragments / vertex","Number of fragments per vertex","39/25"
-2,"Fragments / vertex","Number of fragments per vertex","39/25"
-3,"Fragment / Second","Number of fragments per second","26/22"
-4,"Triangle Acceleration","Number of triangles per ms-ms","27/21:21"
diff --git a/test/trace_processor/graphics/gpu_counter_specs_test.sql b/test/trace_processor/graphics/gpu_counter_specs_test.sql
deleted file mode 100644
index 9faa557..0000000
--- a/test/trace_processor/graphics/gpu_counter_specs_test.sql
+++ /dev/null
@@ -1,18 +0,0 @@
--- 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 group_id,c.name,c.description,unit
-from gpu_counter_group as g
-join gpu_counter_track as c
-  on g.track_id = c.id
diff --git a/test/trace_processor/graphics/gpu_counters.out b/test/trace_processor/graphics/gpu_counters.out
deleted file mode 100644
index 17789fd..0000000
--- a/test/trace_processor/graphics/gpu_counters.out
+++ /dev/null
@@ -1,13 +0,0 @@
-"ts","value","name","gpu_id","description","unit"
-11,5.000000,"Vertex / Second",0,"Number of vertices per second","25/22"
-12,7.000000,"Fragment / Second",0,"Number of fragments per second","26/22"
-13,8.000000,"gpu_counter(33)",0,"[NULL]","[NULL]"
-14,0.000000,"Triangle Acceleration",0,"Number of triangles per ms-ms","27/21:21"
-21,10.000000,"Vertex / Second",0,"Number of vertices per second","25/22"
-22,14.000000,"Fragment / Second",0,"Number of fragments per second","26/22"
-23,16.000000,"gpu_counter(33)",0,"[NULL]","[NULL]"
-24,9.000000,"Triangle Acceleration",0,"Number of triangles per ms-ms","27/21:21"
-31,15.000000,"Vertex / Second",0,"Number of vertices per second","25/22"
-32,21.000000,"Fragment / Second",0,"Number of fragments per second","26/22"
-33,25.000000,"gpu_counter(33)",0,"[NULL]","[NULL]"
-34,7.000000,"Triangle Acceleration",0,"Number of triangles per ms-ms","27/21:21"
diff --git a/test/trace_processor/graphics/gpu_counters_test.sql b/test/trace_processor/graphics/gpu_counters_test.sql
deleted file mode 100644
index 1d8c1c6..0000000
--- a/test/trace_processor/graphics/gpu_counters_test.sql
+++ /dev/null
@@ -1,20 +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 "ts","value","name","gpu_id","description","unit"
-from counter
-join gpu_counter_track
-  on counter.track_id = gpu_counter_track.id
-order by "ts";
diff --git a/test/trace_processor/graphics/gpu_log.out b/test/trace_processor/graphics/gpu_log.out
deleted file mode 100644
index 61b1c09..0000000
--- a/test/trace_processor/graphics/gpu_log.out
+++ /dev/null
@@ -1,13 +0,0 @@
-"scope","track_name","ts","dur","slice_name","key","value"
-"gpu_log","GPU Log",1,0,"VERBOSE","message","message0"
-"gpu_log","GPU Log",1,0,"VERBOSE","tag","tag0"
-"gpu_log","GPU Log",2,0,"DEBUG","message","message1"
-"gpu_log","GPU Log",2,0,"DEBUG","tag","tag0"
-"gpu_log","GPU Log",3,0,"INFO","message","message2"
-"gpu_log","GPU Log",3,0,"INFO","tag","tag0"
-"gpu_log","GPU Log",4,0,"ERROR","message","message4"
-"gpu_log","GPU Log",4,0,"ERROR","tag","tag0"
-"gpu_log","GPU Log",4,0,"WARNING","message","message3"
-"gpu_log","GPU Log",4,0,"WARNING","tag","tag0"
-"gpu_log","GPU Log",5,0,"VERBOSE","message","message5"
-"gpu_log","GPU Log",5,0,"VERBOSE","tag","tag1"
diff --git a/test/trace_processor/graphics/gpu_log_test.sql b/test/trace_processor/graphics/gpu_log_test.sql
deleted file mode 100644
index 2e48384..0000000
--- a/test/trace_processor/graphics/gpu_log_test.sql
+++ /dev/null
@@ -1,22 +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 scope, track.name as track_name, ts, dur, gpu_slice.name as slice_name,
-    key, string_value as value
-from gpu_track
-left join track using (id)
-left join gpu_slice on gpu_track.id=gpu_slice.track_id
-left join args using (arg_set_id)
-order by ts, slice_name, key
diff --git a/test/trace_processor/graphics/gpu_mem_total.out b/test/trace_processor/graphics/gpu_mem_total.out
deleted file mode 100644
index e39c9a0..0000000
--- a/test/trace_processor/graphics/gpu_mem_total.out
+++ /dev/null
@@ -1,7 +0,0 @@
-"name","unit","description","ts","pid","value"
-"GPU Memory","7","Total GPU memory used by the entire system",0,"[NULL]",123
-"GPU Memory","7","Total GPU memory used by this process",0,1,100
-"GPU Memory","7","Total GPU memory used by the entire system",5,"[NULL]",256
-"GPU Memory","7","Total GPU memory used by this process",5,1,233
-"GPU Memory","7","Total GPU memory used by the entire system",10,"[NULL]",123
-"GPU Memory","7","Total GPU memory used by this process",10,1,0
diff --git a/test/trace_processor/graphics/gpu_mem_total_after_free_gpu_mem_total.out b/test/trace_processor/graphics/gpu_mem_total_after_free_gpu_mem_total.out
deleted file mode 100644
index b88ad31..0000000
--- a/test/trace_processor/graphics/gpu_mem_total_after_free_gpu_mem_total.out
+++ /dev/null
@@ -1,4 +0,0 @@
-"name","unit","description","ts","pid","value"
-"GPU Memory","7","Total GPU memory used by this process",0,1,100
-"GPU Memory","7","Total GPU memory used by this process",5,1,233
-"GPU Memory","7","Total GPU memory used by this process",10,1,50
diff --git a/test/trace_processor/graphics/gpu_mem_total_test.sql b/test/trace_processor/graphics/gpu_mem_total_test.sql
deleted file mode 100644
index be985aa..0000000
--- a/test/trace_processor/graphics/gpu_mem_total_test.sql
+++ /dev/null
@@ -1,6 +0,0 @@
-SELECT ct.name, ct.unit, ct.description, c.ts, p.pid, CAST(c.value as INT) as value
-FROM counter_track ct
-LEFT JOIN process_counter_track pct USING (id)
-LEFT JOIN process p USING (upid)
-LEFT JOIN counter c ON c.track_id = ct.id
-ORDER BY ts
diff --git a/test/trace_processor/graphics/gpu_metric.out b/test/trace_processor/graphics/gpu_metric.out
deleted file mode 100644
index d0ca395..0000000
--- a/test/trace_processor/graphics/gpu_metric.out
+++ /dev/null
@@ -1,17 +0,0 @@
-android_gpu {
-  processes {
-    name: "app_1"
-    mem_max: 8
-    mem_min: 2
-    mem_avg: 3
-  }
-  processes {
-    name: "app_2"
-    mem_max: 10
-    mem_min: 6
-    mem_avg: 8
-  }
-  mem_max: 4
-  mem_min: 1
-  mem_avg: 2
-}
diff --git a/test/trace_processor/graphics/gpu_render_stages_test.sql b/test/trace_processor/graphics/gpu_render_stages_test.sql
deleted file mode 100644
index 50b887c..0000000
--- a/test/trace_processor/graphics/gpu_render_stages_test.sql
+++ /dev/null
@@ -1,24 +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 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_target_name, render_pass, render_pass_name,
-    command_buffer, command_buffer_name, submission_id, hw_queue_id, render_subpasses
-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/test/trace_processor/graphics/graphics_frame_events_test.sql b/test/trace_processor/graphics/graphics_frame_events_test.sql
deleted file mode 100644
index 3e42f53..0000000
--- a/test/trace_processor/graphics/graphics_frame_events_test.sql
+++ /dev/null
@@ -1,21 +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 ts, gpu_track.name as track_name, dur, frame_slice.name as slice_name,
-    frame_number, layer_name
-from gpu_track
-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/graphics/index b/test/trace_processor/graphics/index
deleted file mode 100644
index e26f1b8..0000000
--- a/test/trace_processor/graphics/index
+++ /dev/null
@@ -1,65 +0,0 @@
-# Contains tests for graphics related events and tables.
-
-# GPU trace tests.
-gpu_counters.py gpu_counters_test.sql gpu_counters.out
-gpu_counter_specs.textproto gpu_counter_specs_test.sql gpu_counter_specs.out
-gpu_render_stages.py gpu_render_stages_test.sql gpu_render_stages.out
-gpu_render_stages_interned_spec.textproto gpu_render_stages_test.sql gpu_render_stages_interned_spec.out
-vulkan_api_events.py vulkan_api_events_test.sql vulkan_api_events.out
-gpu_log.py gpu_log_test.sql gpu_log.out
-
-# Graphics frame event trace tests.
-graphics_frame_events.py graphics_frame_events_test.sql graphics_frame_events.out
-
-# GPU Memory ftrace packets
-gpu_mem_total.py gpu_mem_total_test.sql gpu_mem_total.out
-gpu_mem_total_after_free.py gpu_mem_total_test.sql gpu_mem_total_after_free_gpu_mem_total.out
-
-# Clock sync
-clock_sync.py clock_sync_test.sql clock_sync.out
-
-# Android SurfaceFlinger metrics
-frame_missed.py frame_missed_event_test.sql frame_missed_event_frame_missed.out
-frame_missed.py android_surfaceflinger frame_missed_metrics.out
-surfaceflinger_gpu_invocation.py android_surfaceflinger surfaceflinger_gpu_invocation.out
-
-# GPU metrics
-gpu_metric.py android_gpu gpu_metric.out
-gpu_frequency_metric.textproto android_gpu gpu_frequency_metric.out
-
-# Android SysUI CUJs metrics
-android_sysui_cuj.py android_sysui_cuj android_sysui_cuj.out
-android_sysui_cuj.py android_sysui_cuj_event_test.sql android_sysui_cuj_event.out
-
-# Android Jank CUJ metric
-android_sysui_cuj.py android_jank_cuj android_jank_cuj.out
-android_sysui_cuj.py android_jank_cuj_query_test.sql android_jank_cuj_query.out
-
-# Frame Timeline event trace tests
-frame_timeline_events.py expected_frame_timeline_events_test.sql expected_frame_timeline_events.out
-frame_timeline_events.py actual_frame_timeline_events_test.sql actual_frame_timeline_events.out
-
-# Composition layer
-composition_layer.py composition_layer_count_test.sql composition_layer_count.out
-
-# G2D metrics
-# TODO(rsavitski): find a real trace and double-check that the textproto is
-# realistic. One kernel's source I checked had tgid=0 for all counter events.
-# Initial support was added/discussed in b/171296908.
-g2d_metrics.textproto g2d g2d_metrics.out
-
-# Composer execution
-composer_execution.py composer_execution_test.sql composer_execution.out
-
-# Display metrics
-panel_fps.py display_metrics panel_fps.out
-
-# DPU vote clock and bandwidth
-dpu_vote_clock_bw.textproto android_hwcomposer dpu_vote_clock_bw.out
-
-# DRM-related ftrace events
-drm_vblank.textproto drm_gpu_track_test.sql drm_vblank_gpu_track.out
-drm_sched.textproto drm_gpu_track_test.sql drm_sched_gpu_track.out
-drm_sched.textproto drm_thread_track_test.sql drm_sched_thread_track.out
-drm_dma_fence.textproto drm_gpu_track_test.sql drm_dma_fence_gpu_track.out
-drm_dma_fence.textproto drm_thread_track_test.sql drm_dma_fence_thread_track.out
diff --git a/test/trace_processor/graphics/panel_fps.out b/test/trace_processor/graphics/panel_fps.out
deleted file mode 100644
index c4e06d9..0000000
--- a/test/trace_processor/graphics/panel_fps.out
+++ /dev/null
@@ -1,24 +0,0 @@
-display_metrics {
-  total_duplicate_frames: 0
-  duplicate_frames_logged: 0
-  total_dpu_underrun_count: 0
-  refresh_rate_switches: 5
-  refresh_rate_stats {
-    refresh_rate_fps: 60
-    count: 2
-    total_dur_ms: 2
-    avg_dur_ms: 1
-  }
-  refresh_rate_stats {
-    refresh_rate_fps: 90
-    count: 2
-    total_dur_ms: 2
-    avg_dur_ms: 1
-  }
-  refresh_rate_stats {
-    refresh_rate_fps: 120
-    count: 1
-    total_dur_ms: 2
-    avg_dur_ms: 2
-  }
-}
diff --git a/test/trace_processor/graphics/panel_fps.py b/test/trace_processor/graphics/panel_fps.py
deleted file mode 100644
index 0c9e293..0000000
--- a/test/trace_processor/graphics/panel_fps.py
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/usr/bin/env python3
-# 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 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
-
-import synth_common
-
-trace = synth_common.create_trace()
-
-trace.add_packet(ts=1)
-trace.add_process(10, 1, "parent_process")
-trace.add_process(11, 10, "child_process")
-
-trace.add_ftrace_packet(1)
-
-trace.add_print(ts=99_000_000, tid=11, buf='C|10|panel_fps|60')
-trace.add_print(ts=100_000_000, tid=11, buf='C|10|panel_fps|90')
-trace.add_print(ts=101_000_000, tid=11, buf='C|10|panel_fps|60')
-trace.add_print(ts=102_000_000, tid=11, buf='C|10|panel_fps|120')
-
-# The duplicated fps will be ignored
-trace.add_print(ts=103_000_000, tid=11, buf='C|10|panel_fps|120')
-
-trace.add_print(ts=104_000_000, tid=11, buf='C|10|panel_fps|90')
-
-# The last fps and its following duplicates will be ignored, and will
-# only be used for the calculation of duration of the previous fps
-trace.add_print(ts=105_000_000, tid=11, buf='C|10|panel_fps|24')
-trace.add_print(ts=106_000_000, tid=11, buf='C|10|panel_fps|24')
-
-sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/graphics/surfaceflinger_gpu_invocation.out b/test/trace_processor/graphics/surfaceflinger_gpu_invocation.out
deleted file mode 100644
index 9495fed..0000000
--- a/test/trace_processor/graphics/surfaceflinger_gpu_invocation.out
+++ /dev/null
@@ -1,8 +0,0 @@
-android_surfaceflinger {
-  missed_frames: 0
-  missed_hwc_frames: 0
-  missed_gpu_frames: 0
-  gpu_invocations: 4
-  avg_gpu_waiting_dur_ms: 4
-  total_non_empty_gpu_waiting_dur_ms: 11
-}
diff --git a/test/trace_processor/graphics/vulkan_api_events_test.sql b/test/trace_processor/graphics/vulkan_api_events_test.sql
deleted file mode 100644
index 89fccd9..0000000
--- a/test/trace_processor/graphics/vulkan_api_events_test.sql
+++ /dev/null
@@ -1,23 +0,0 @@
---
--- 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/test/trace_processor/include_index b/test/trace_processor/include_index
deleted file mode 100644
index e4eee90..0000000
--- a/test/trace_processor/include_index
+++ /dev/null
@@ -1,24 +0,0 @@
-android/index
-atrace/index
-camera/index
-chrome/index
-cros/index
-dynamic/index
-fs/index
-fuchsia/index
-graphics/index
-io/index
-memory/index
-network/index
-parsing/index
-performance/index
-power/index
-process_tracking/index
-profiling/index
-smoke/index
-span_join/index
-startup/index
-tables/index
-track_event/index
-translation/index
-scheduler/index
diff --git a/test/trace_processor/io/index b/test/trace_processor/io/index
deleted file mode 100644
index 3852a30..0000000
--- a/test/trace_processor/io/index
+++ /dev/null
@@ -1,3 +0,0 @@
-# UFS command
-ufshcd_command.textproto ufshcd_command_test.sql ufshcd_command.out
-ufshcd_command_tag.textproto ufshcd_command_tag_test.sql ufshcd_command_tag.out
diff --git a/test/trace_processor/io/ufshcd_command.out b/test/trace_processor/io/ufshcd_command.out
deleted file mode 100644
index 6be82e6..0000000
--- a/test/trace_processor/io/ufshcd_command.out
+++ /dev/null
@@ -1,5 +0,0 @@
-"ts","value"
-10000,1.000000
-10008,2.000000
-10010,3.000000
-10011,1.000000
diff --git a/test/trace_processor/io/ufshcd_command_tag.out b/test/trace_processor/io/ufshcd_command_tag.out
deleted file mode 100644
index 3002aab..0000000
--- a/test/trace_processor/io/ufshcd_command_tag.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"ts","dur","name"
-10000,800,"READ (10)"
-10900,50,"WRITE (10) (GID=0x16)"
diff --git a/test/trace_processor/io/ufshcd_command_tag_test.sql b/test/trace_processor/io/ufshcd_command_tag_test.sql
deleted file mode 100644
index 8d3e994..0000000
--- a/test/trace_processor/io/ufshcd_command_tag_test.sql
+++ /dev/null
@@ -1,5 +0,0 @@
-SELECT ts, dur, slice.name
-FROM slice
-JOIN track on slice.track_id = track.id
-WHERE track.name GLOB 'io.ufs.command.tag*'
-ORDER BY ts
diff --git a/test/trace_processor/io/ufshcd_command_test.sql b/test/trace_processor/io/ufshcd_command_test.sql
deleted file mode 100644
index 2f706bb..0000000
--- a/test/trace_processor/io/ufshcd_command_test.sql
+++ /dev/null
@@ -1,11 +0,0 @@
-SELECT
-  ts,
-  value
-FROM
-  counter AS c
-  JOIN
-  counter_track AS ct
-  ON c.track_id = ct.id
-WHERE
-  ct.name = "io.ufs.command.count"
-ORDER BY ts
diff --git a/test/trace_processor/memory/android_dma_buffer_tracks.out b/test/trace_processor/memory/android_dma_buffer_tracks.out
deleted file mode 100644
index 39463cd..0000000
--- a/test/trace_processor/memory/android_dma_buffer_tracks.out
+++ /dev/null
@@ -1,2 +0,0 @@
-"name","ts","dur","name"
-"mem.dma_buffer",100,100,"1 kB"
diff --git a/test/trace_processor/memory/android_dma_heap_stat.out b/test/trace_processor/memory/android_dma_heap_stat.out
deleted file mode 100644
index 71b8f40..0000000
--- a/test/trace_processor/memory/android_dma_heap_stat.out
+++ /dev/null
@@ -1,6 +0,0 @@
-android_dma_heap {
-    avg_size_bytes: 2048.0
-    min_size_bytes: 1024.0
-    max_size_bytes: 2048.0
-    total_alloc_size_bytes: 1024.0
-}
diff --git a/test/trace_processor/memory/android_dma_heap_stat.textproto b/test/trace_processor/memory/android_dma_heap_stat.textproto
deleted file mode 100644
index 6c091aa..0000000
--- a/test/trace_processor/memory/android_dma_heap_stat.textproto
+++ /dev/null
@@ -1,28 +0,0 @@
-packet {
-  ftrace_events {
-    cpu: 0
-    event {
-      timestamp: 100
-      pid: 1
-      dma_heap_stat {
-        inode: 123
-        len: 1024
-        total_allocated: 2048
-      }
-    }
-  }
-}
-packet {
-  ftrace_events {
-    cpu: 0
-    event {
-      timestamp: 200
-      pid: 1
-      dma_heap_stat {
-        inode: 123
-        len: -1024
-        total_allocated: 1024
-      }
-    }
-  }
-}
diff --git a/test/trace_processor/memory/android_fastrpc_dma_stat.out b/test/trace_processor/memory/android_fastrpc_dma_stat.out
deleted file mode 100644
index c16f833..0000000
--- a/test/trace_processor/memory/android_fastrpc_dma_stat.out
+++ /dev/null
@@ -1,9 +0,0 @@
-android_fastrpc {
-  subsystem {
-    name: "MDSP"
-    avg_size_bytes: 2000.0
-    min_size_bytes: 1000.0
-    max_size_bytes: 2000.0
-    total_alloc_size_bytes: 1000.0
-  }
-}
diff --git a/test/trace_processor/memory/android_fastrpc_dma_stat.textproto b/test/trace_processor/memory/android_fastrpc_dma_stat.textproto
deleted file mode 100644
index 9157736..0000000
--- a/test/trace_processor/memory/android_fastrpc_dma_stat.textproto
+++ /dev/null
@@ -1,28 +0,0 @@
-packet {
-  ftrace_events {
-    cpu: 0
-    event {
-      timestamp: 100
-      pid: 1
-      fastrpc_dma_stat {
-        cid: 1
-        len: 1000
-        total_allocated: 2000
-      }
-    }
-  }
-}
-packet {
-  ftrace_events {
-    cpu: 0
-    event {
-      timestamp: 200
-      pid: 1
-      fastrpc_dma_stat {
-        cid: 1
-        len: -1000
-        total_allocated: 1000
-      }
-    }
-  }
-}
diff --git a/test/trace_processor/memory/android_ion.out b/test/trace_processor/memory/android_ion.out
deleted file mode 100644
index 5116e55..0000000
--- a/test/trace_processor/memory/android_ion.out
+++ /dev/null
@@ -1,16 +0,0 @@
-android_ion {
-  buffer {
-    name: "adsp"
-    avg_size_bytes: 1000.0
-    min_size_bytes: 1000.0
-    max_size_bytes: 1100.0
-    total_alloc_size_bytes: 1100.0
-  }
-  buffer {
-    name: "system"
-    avg_size_bytes: 1497.4874371859296
-    min_size_bytes: 1000.0
-    max_size_bytes: 2000.0
-    total_alloc_size_bytes: 2000.0
-  }
-}
diff --git a/test/trace_processor/memory/android_ion_stat.out b/test/trace_processor/memory/android_ion_stat.out
deleted file mode 100644
index f9ac727..0000000
--- a/test/trace_processor/memory/android_ion_stat.out
+++ /dev/null
@@ -1,9 +0,0 @@
-android_ion {
-  buffer {
-    name: "all"
-    avg_size_bytes: 2000.0
-    min_size_bytes: 1000.0
-    max_size_bytes: 2000.0
-    total_alloc_size_bytes: 1000.0
-  }
-}
\ No newline at end of file
diff --git a/test/trace_processor/memory/android_ion_stat.textproto b/test/trace_processor/memory/android_ion_stat.textproto
deleted file mode 100644
index e573712..0000000
--- a/test/trace_processor/memory/android_ion_stat.textproto
+++ /dev/null
@@ -1,28 +0,0 @@
-packet {
-  ftrace_events {
-    cpu: 0
-    event {
-      timestamp: 100
-      pid: 1
-      ion_stat {
-        buffer_id: 123
-        len: 1000
-        total_allocated: 2000
-      }
-    }
-  }
-}
-packet {
-  ftrace_events {
-    cpu: 0
-    event {
-      timestamp: 200
-      pid: 1
-      ion_stat {
-        buffer_id: 123
-        len: -1000
-        total_allocated: 1000
-      }
-    }
-  }
-}
diff --git a/test/trace_processor/memory/android_lmk_oom.out b/test/trace_processor/memory/android_lmk_oom.out
deleted file mode 100644
index 31f0df5..0000000
--- a/test/trace_processor/memory/android_lmk_oom.out
+++ /dev/null
@@ -1,4 +0,0 @@
-android_lmk {
-  total_count: 0
-  oom_victim_count: 1
-}
\ No newline at end of file
diff --git a/test/trace_processor/memory/android_mem_delta.out b/test/trace_processor/memory/android_mem_delta.out
deleted file mode 100644
index 7a43ef8..0000000
--- a/test/trace_processor/memory/android_mem_delta.out
+++ /dev/null
@@ -1,13 +0,0 @@
-android_mem {
-  process_metrics {
-    process_name: "com.my.pkg"
-    total_counters {
-      file_rss {
-        min: 2000.0
-        max: 10000.0
-        avg: 6666.666666666667
-        delta: 7000.0
-      }
-    }
-  }
-}
diff --git a/test/trace_processor/memory/android_mem_lmk.out b/test/trace_processor/memory/android_mem_lmk.out
deleted file mode 100644
index 762bdaf..0000000
--- a/test/trace_processor/memory/android_mem_lmk.out
+++ /dev/null
@@ -1,8 +0,0 @@
-android_lmk {
-  total_count: 1
-    by_oom_score {
-    oom_score_adj: 900
-    count: 1
-  }
-  oom_victim_count: 0
-}
diff --git a/test/trace_processor/memory/dma_buffer_tracks_test.sql b/test/trace_processor/memory/dma_buffer_tracks_test.sql
deleted file mode 100644
index 9815ce5..0000000
--- a/test/trace_processor/memory/dma_buffer_tracks_test.sql
+++ /dev/null
@@ -1,3 +0,0 @@
-SELECT track.name, slice.ts, slice.dur, slice.name
-FROM slice JOIN track ON slice.track_id = track.id
-WHERE track.name = 'mem.dma_buffer';
diff --git a/test/trace_processor/memory/index b/test/trace_processor/memory/index
deleted file mode 100644
index 87266cd..0000000
--- a/test/trace_processor/memory/index
+++ /dev/null
@@ -1,20 +0,0 @@
-# Contains test for Android memory metrics.
-
-# Memory metric
-../../data/memory_counters.pb android_mem android_mem_counters.out
-../../data/memory_counters.pb trace_metadata trace_metadata.out
-android_mem_by_priority.py android_mem android_mem_by_priority.out
-android_systrace_lmk.py android_lmk android_mem_lmk.out
-../common/oom_kill.textproto android_lmk android_lmk_oom.out
-android_mem_delta.py android_mem android_mem_delta.out
-
-# ION metric
-android_ion.py android_ion android_ion.out
-android_ion_stat.textproto android_ion android_ion_stat.out
-
-# DMA-BUF heap Metric
-android_dma_heap_stat.textproto android_dma_heap android_dma_heap_stat.out
-android_dma_heap_stat.textproto dma_buffer_tracks_test.sql android_dma_buffer_tracks.out
-
-# fastrpc metric
-android_fastrpc_dma_stat.textproto android_fastrpc android_fastrpc_dma_stat.out
diff --git a/test/trace_processor/network/index b/test/trace_processor/network/index
deleted file mode 100644
index 528db80..0000000
--- a/test/trace_processor/network/index
+++ /dev/null
@@ -1,8 +0,0 @@
-# Network performance
-netif_receive_skb.textproto netif_receive_skb_test.sql netif_receive_skb.out
-net_dev_xmit.textproto net_dev_xmit_test.sql net_dev_xmit.out
-netperf_metric.textproto android_netperf netperf_metric.out
-inet_sock_set_state.textproto inet_sock_set_state_test.sql inet_sock_set_state.out
-tcp_retransmit_skb.textproto tcp_retransmit_skb_test.sql tcp_retransmit_skb.out
-napi_gro_receive.textproto napi_gro_receive_test.sql napi_gro_receive.out
-kfree_skb.textproto kfree_skb_test.sql kfree_skb.out
diff --git a/test/trace_processor/network/inet_sock_set_state.out b/test/trace_processor/network/inet_sock_set_state.out
deleted file mode 100644
index fd67a19..0000000
--- a/test/trace_processor/network/inet_sock_set_state.out
+++ /dev/null
@@ -1,7 +0,0 @@
-"ts","name","dur","name"
-10000000,"TCP_SYN_SENT(pid=123)",100000000,"TCP stream#1"
-110000000,"TCP_ESTABLISHED(sport=56789,dport=5001)",500000000,"TCP stream#1"
-610000000,"TCP_CLOSE_WAIT",-1,"TCP stream#1"
-710000000,"TCP_SYN_SENT(pid=567)",10000000,"TCP stream#2"
-720000000,"TCP_ESTABLISHED(sport=56790,dport=5002)",300000000,"TCP stream#2"
-1020000000,"TCP_CLOSE_WAIT",-1,"TCP stream#2"
diff --git a/test/trace_processor/network/inet_sock_set_state_test.sql b/test/trace_processor/network/inet_sock_set_state_test.sql
deleted file mode 100644
index badba1e..0000000
--- a/test/trace_processor/network/inet_sock_set_state_test.sql
+++ /dev/null
@@ -1,12 +0,0 @@
-SELECT
-  ts,
-  s.name,
-  dur,
-  t.name
-FROM
-  slice AS s
-  LEFT JOIN track AS t
-  ON s.track_id = t.id
-WHERE
-  t.name GLOB "TCP stream#*"
-ORDER BY ts;
diff --git a/test/trace_processor/network/kfree_skb.out b/test/trace_processor/network/kfree_skb.out
deleted file mode 100644
index 6db1bd9..0000000
--- a/test/trace_processor/network/kfree_skb.out
+++ /dev/null
@@ -1,4 +0,0 @@
-"ts","value","prot"
-10000,1.000000,"IP"
-10020,2.000000,"IPV6"
-20020,3.000000,"IP"
diff --git a/test/trace_processor/network/kfree_skb.textproto b/test/trace_processor/network/kfree_skb.textproto
deleted file mode 100644
index 6956831..0000000
--- a/test/trace_processor/network/kfree_skb.textproto
+++ /dev/null
@@ -1,48 +0,0 @@
-packet {
-  ftrace_events {
-    cpu: 2
-    event {
-      timestamp: 10000
-      pid: 200
-      kfree_skb {
-        protocol: 2048
-      }
-    }
-  }
-}
-packet {
-  ftrace_events {
-    cpu: 2
-    event {
-      timestamp: 10020
-      pid: 300
-      kfree_skb {
-        protocol: 34525
-      }
-    }
-  }
-}
-packet {
-  ftrace_events {
-    cpu: 2
-    event {
-      timestamp: 20000
-      pid: 200
-      kfree_skb {
-        protocol: 1536
-      }
-    }
-  }
-}
-packet {
-  ftrace_events {
-    cpu: 2
-    event {
-      timestamp: 20020
-      pid: 300
-      kfree_skb {
-        protocol: 2048
-      }
-    }
-  }
-}
diff --git a/test/trace_processor/network/kfree_skb_test.sql b/test/trace_processor/network/kfree_skb_test.sql
deleted file mode 100644
index 6ec2056..0000000
--- a/test/trace_processor/network/kfree_skb_test.sql
+++ /dev/null
@@ -1,12 +0,0 @@
-SELECT
-  ts,
-  value,
-  EXTRACT_ARG(arg_set_id, 'protocol') AS prot
-FROM
-  counter AS c
-  LEFT JOIN
-  counter_track AS t
-  ON c.track_id = t.id
-WHERE
-  name GLOB "Kfree Skb IP Prot"
-ORDER BY ts;
diff --git a/test/trace_processor/network/napi_gro_receive.out b/test/trace_processor/network/napi_gro_receive.out
deleted file mode 100644
index 0648e41..0000000
--- a/test/trace_processor/network/napi_gro_receive.out
+++ /dev/null
@@ -1,4 +0,0 @@
-"ts","name","dur","cat","name","ret","len"
-10000,"rmnet0",20,"napi_gro","Napi Gro Cpu 2",2,1000
-20000,"rmnet0",20,"napi_gro","Napi Gro Cpu 2",1,1000
-30000,"wlan",20,"napi_gro","Napi Gro Cpu 4",3,500
diff --git a/test/trace_processor/network/napi_gro_receive_test.sql b/test/trace_processor/network/napi_gro_receive_test.sql
deleted file mode 100644
index 48ee38f..0000000
--- a/test/trace_processor/network/napi_gro_receive_test.sql
+++ /dev/null
@@ -1,16 +0,0 @@
-SELECT
-  ts,
-  s.name,
-  dur,
-  cat,
-  t.name,
-  EXTRACT_ARG(arg_set_id, 'ret') AS ret,
-  EXTRACT_ARG(arg_set_id, 'len') AS len
-FROM
-  slice AS s
-LEFT JOIN
-  track AS t
-  ON s.track_id = t.id
-WHERE
-  t.name GLOB "Napi Gro Cpu *"
-ORDER BY ts;
diff --git a/test/trace_processor/network/net_dev_xmit.out b/test/trace_processor/network/net_dev_xmit.out
deleted file mode 100644
index 020428b..0000000
--- a/test/trace_processor/network/net_dev_xmit.out
+++ /dev/null
@@ -1,5 +0,0 @@
-"ts","dev","cpu","len"
-10000,"rmnet0",0,1000
-10000,"rmnet0",1,1000
-10010,"rmnet0",0,1000
-12000,"wlan0",4,1300
diff --git a/test/trace_processor/network/net_dev_xmit_test.sql b/test/trace_processor/network/net_dev_xmit_test.sql
deleted file mode 100644
index 8c4fcd4..0000000
--- a/test/trace_processor/network/net_dev_xmit_test.sql
+++ /dev/null
@@ -1,13 +0,0 @@
-SELECT
-  ts,
-  REPLACE(name, " Transmitted KB", "") AS dev,
-  EXTRACT_ARG(arg_set_id, 'cpu') AS cpu,
-  EXTRACT_ARG(arg_set_id, 'len') AS len
-FROM
-  counter AS c
-  LEFT JOIN
-  counter_track AS t
-  ON c.track_id = t.id
-WHERE
-  name GLOB "* Transmitted KB"
-ORDER BY ts;
diff --git a/test/trace_processor/network/netif_receive_skb.out b/test/trace_processor/network/netif_receive_skb.out
deleted file mode 100644
index 468292b..0000000
--- a/test/trace_processor/network/netif_receive_skb.out
+++ /dev/null
@@ -1,6 +0,0 @@
-"ts","dev","cpu","len"
-10000,"rmnet0",0,1000
-10000,"rmnet0",1,1000
-10010,"rmnet0",0,1000
-10011,"rmnet0",1,1000
-12000,"wlan",4,1300
diff --git a/test/trace_processor/network/netif_receive_skb_test.sql b/test/trace_processor/network/netif_receive_skb_test.sql
deleted file mode 100644
index 20dc468..0000000
--- a/test/trace_processor/network/netif_receive_skb_test.sql
+++ /dev/null
@@ -1,13 +0,0 @@
-SELECT
-  ts,
-  REPLACE(name, " Received KB", "") AS dev,
-  EXTRACT_ARG(arg_set_id, 'cpu') AS cpu,
-  EXTRACT_ARG(arg_set_id, 'len') AS len
-FROM
-  counter AS c
-  LEFT JOIN
-  counter_track AS t
-  ON c.track_id = t.id
-WHERE
-  name GLOB "* Received KB"
-ORDER BY ts;
diff --git a/test/trace_processor/network/tcp_retransmit_skb.out b/test/trace_processor/network/tcp_retransmit_skb.out
deleted file mode 100644
index 9a3629f..0000000
--- a/test/trace_processor/network/tcp_retransmit_skb.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"ts","name","dur"
-110000000,"sport=56789,dport=5001",0
-720000000,"sport=56790,dport=5002",0
diff --git a/test/trace_processor/network/tcp_retransmit_skb.textproto b/test/trace_processor/network/tcp_retransmit_skb.textproto
deleted file mode 100644
index 3e96e75..0000000
--- a/test/trace_processor/network/tcp_retransmit_skb.textproto
+++ /dev/null
@@ -1,34 +0,0 @@
-packet {
-  ftrace_events {
-    cpu: 1
-    event {
-      timestamp: 110000000
-      pid: 234
-      tcp_retransmit_skb {
-        daddr: 19216801
-        saddr: 127001
-        dport: 5001
-        sport: 56789
-        state: 1
-        skaddr: 77889900
-      }
-    }
-  }
-}
-packet {
-  ftrace_events {
-    cpu: 1
-    event {
-      timestamp: 720000000
-      pid: 234
-      tcp_retransmit_skb {
-        daddr: 0
-        saddr: 0
-        dport: 5002
-        sport: 56790
-        state: 2
-        skaddr: 33445566
-      }
-    }
-  }
-}
diff --git a/test/trace_processor/network/tcp_retransmit_skb_test.sql b/test/trace_processor/network/tcp_retransmit_skb_test.sql
deleted file mode 100644
index 44dacb3..0000000
--- a/test/trace_processor/network/tcp_retransmit_skb_test.sql
+++ /dev/null
@@ -1,11 +0,0 @@
-SELECT
-  ts,
-  s.name,
-  dur
-FROM
-  slice AS s
-  LEFT JOIN track AS t
-  ON s.track_id = t.id
-WHERE
-  t.name = "TCP Retransmit Skb"
-ORDER BY ts;
diff --git a/test/trace_processor/parsing/android_binder.out b/test/trace_processor/parsing/android_binder.out
deleted file mode 100644
index ef53b12..0000000
--- a/test/trace_processor/parsing/android_binder.out
+++ /dev/null
@@ -1,20 +0,0 @@
-android_binder {
-  process_breakdown {
-    process_name: "test_process_a"
-    pid: 1
-    slice_name: "binder transaction"
-    count: 2
-  }
-  process_breakdown {
-    process_name: "test_process_b"
-    pid: 2
-    slice_name: "binder reply"
-    count: 1
-  }
-  process_breakdown {
-    process_name: "test_process_c"
-    pid: 3
-    slice_name: "binder reply"
-    count: 1
-  }
-}
\ No newline at end of file
diff --git a/test/trace_processor/parsing/android_log_counts.out b/test/trace_processor/parsing/android_log_counts.out
deleted file mode 100644
index e321ccd..0000000
--- a/test/trace_processor/parsing/android_log_counts.out
+++ /dev/null
@@ -1,8 +0,0 @@
-"cnt"
-2249
-431
-264
-2
-4
-31
-246
diff --git a/test/trace_processor/parsing/android_log_counts_test.sql b/test/trace_processor/parsing/android_log_counts_test.sql
deleted file mode 100644
index 35a2a47..0000000
--- a/test/trace_processor/parsing/android_log_counts_test.sql
+++ /dev/null
@@ -1,22 +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 count(*) as cnt from android_logs union all
-select count(*) as cnt from android_logs where prio = 3 union all
-select count(*) as cnt from android_logs where prio > 4 union all
-select count(*) as cnt from android_logs where tag = 'screen_toggled' union all
-select count(*) as cnt from android_logs where tag GLOB '*_pss' union all
-select count(*) as cnt from android_logs where msg GLOB '*i2c?write*' or msg GLOB '*I2C?Write*' union all
-select count(*) as cnt from android_logs where ts >= 1510113924391 and ts < 1512610021879;
diff --git a/test/trace_processor/parsing/android_log_msgs_test.sql b/test/trace_processor/parsing/android_log_msgs_test.sql
deleted file mode 100644
index 1666095..0000000
--- a/test/trace_processor/parsing/android_log_msgs_test.sql
+++ /dev/null
@@ -1,34 +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.
---
-create view v1 as select tag, count(*) from android_logs group by tag order by 2 desc limit 5;
-
-create view v2 as select tag, count(*) from android_logs group by tag order by 2 asc limit 5;
-
-create view v3 as
-select tag, count(*)
-from android_logs
-where msg GLOB '*wakelock*' or msg GLOB '*Wakelock*' or msg GLOB '*WakeLock*' or msg GLOB '*wakeLock*'
-group by tag;
-
-create view v4 as select msg, 1 from android_logs limit 10;
-
-select * from v1 union all
-select '-----', 0 union all
-select * from v2 union all
-select '-----', 0 union all
-select * from v3 union all
-select '-----', 0 union all
-select * from v4;
diff --git a/test/trace_processor/parsing/android_log_ring_buffer_mode.out b/test/trace_processor/parsing/android_log_ring_buffer_mode.out
deleted file mode 100644
index d431867..0000000
--- a/test/trace_processor/parsing/android_log_ring_buffer_mode.out
+++ /dev/null
@@ -1,2 +0,0 @@
-"count(*)"
-26
diff --git a/test/trace_processor/parsing/android_log_ring_buffer_mode_test.sql b/test/trace_processor/parsing/android_log_ring_buffer_mode_test.sql
deleted file mode 100644
index 2ce2bd1..0000000
--- a/test/trace_processor/parsing/android_log_ring_buffer_mode_test.sql
+++ /dev/null
@@ -1,16 +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 count(*) from android_logs;
diff --git a/test/trace_processor/parsing/android_multiuser_switch.out b/test/trace_processor/parsing/android_multiuser_switch.out
deleted file mode 100644
index a49f9e3..0000000
--- a/test/trace_processor/parsing/android_multiuser_switch.out
+++ /dev/null
@@ -1,5 +0,0 @@
-android_multiuser: {
-  user_switch: {
-    duration_ms: 4900
-  }
-}
\ No newline at end of file
diff --git a/test/trace_processor/parsing/android_package_list.out b/test/trace_processor/parsing/android_package_list.out
deleted file mode 100644
index d14f35b..0000000
--- a/test/trace_processor/parsing/android_package_list.out
+++ /dev/null
@@ -1,7 +0,0 @@
-android_package_list {
-  packages {
-    package_name: "com.my.pkg"
-    uid: 123
-    version_code: 456000
-  }
-}
diff --git a/test/trace_processor/parsing/android_sched_and_ps_end_reason_eq.out b/test/trace_processor/parsing/android_sched_and_ps_end_reason_eq.out
deleted file mode 100644
index 0a99aed..0000000
--- a/test/trace_processor/parsing/android_sched_and_ps_end_reason_eq.out
+++ /dev/null
@@ -1,2 +0,0 @@
-"end_state","count(*)"
-"D",10503
diff --git a/test/trace_processor/parsing/android_sched_and_ps_end_reason_neq.out b/test/trace_processor/parsing/android_sched_and_ps_end_reason_neq.out
deleted file mode 100644
index 3d9a00a..0000000
--- a/test/trace_processor/parsing/android_sched_and_ps_end_reason_neq.out
+++ /dev/null
@@ -1,6 +0,0 @@
-"end_state","count(*)"
-"DK",30
-"I",82
-"R",91189
-"R+",9428
-"S",110560
diff --git a/test/trace_processor/parsing/android_sched_and_ps_trace_size.out b/test/trace_processor/parsing/android_sched_and_ps_trace_size.out
deleted file mode 100644
index b2bd2ea..0000000
--- a/test/trace_processor/parsing/android_sched_and_ps_trace_size.out
+++ /dev/null
@@ -1,2 +0,0 @@
-"int_value"
-18761615
diff --git a/test/trace_processor/parsing/args_string_filter_null_test.sql b/test/trace_processor/parsing/args_string_filter_null_test.sql
deleted file mode 100644
index c42e2f3..0000000
--- a/test/trace_processor/parsing/args_string_filter_null_test.sql
+++ /dev/null
@@ -1,46 +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 string_value
-from args
-where string_value = NULL
-UNION
-select string_value
-from args
-where string_value != NULL
-UNION
-select string_value
-from args
-where string_value < NULL
-UNION
-select string_value
-from args
-where string_value <= NULL
-UNION
-select string_value
-from args
-where string_value > NULL
-UNION
-select string_value
-from args
-where string_value >= NULL
-UNION
-select string_value
-from args
-where string_value GLOB NULL
-UNION
-select string_value
-from args
-where string_value GLOB NULL
diff --git a/test/trace_processor/parsing/args_string_is_not_null_test.sql b/test/trace_processor/parsing/args_string_is_not_null_test.sql
deleted file mode 100644
index 971f47d..0000000
--- a/test/trace_processor/parsing/args_string_is_not_null_test.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 string_value
-from args
-where string_value IS NOT NULL
-limit 10
diff --git a/test/trace_processor/parsing/args_string_is_null_test.sql b/test/trace_processor/parsing/args_string_is_null_test.sql
deleted file mode 100644
index c1feb90..0000000
--- a/test/trace_processor/parsing/args_string_is_null_test.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 string_value
-from args
-where string_value IS NULL
-limit 10
diff --git a/test/trace_processor/parsing/atrace_compressed_sched_count.out b/test/trace_processor/parsing/atrace_compressed_sched_count.out
deleted file mode 100644
index c03bd11..0000000
--- a/test/trace_processor/parsing/atrace_compressed_sched_count.out
+++ /dev/null
@@ -1,2 +0,0 @@
-"COUNT(1)"
-1120
diff --git a/test/trace_processor/parsing/atrace_uncompressed_sched_count.out b/test/trace_processor/parsing/atrace_uncompressed_sched_count.out
deleted file mode 100644
index 41c340b..0000000
--- a/test/trace_processor/parsing/atrace_uncompressed_sched_count.out
+++ /dev/null
@@ -1,2 +0,0 @@
-"COUNT(1)"
-9
diff --git a/test/trace_processor/parsing/b120487929_test.sql b/test/trace_processor/parsing/b120487929_test.sql
deleted file mode 100644
index a266d7a..0000000
--- a/test/trace_processor/parsing/b120487929_test.sql
+++ /dev/null
@@ -1,71 +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.
---
-create view freq_view as
-  select
-    ts,
-    lead(ts) OVER (PARTITION BY track_id ORDER BY ts) - ts as dur,
-    cpu,
-    name as freq_name,
-    value as freq_value
-  from counter
-  inner join cpu_counter_track
-    on counter.track_id = cpu_counter_track.id
-  where name = 'cpufreq';
-
-create view idle_view
-  as select
-    ts,
-    lead(ts) OVER (PARTITION BY track_id ORDER BY ts) - ts as dur,
-    cpu,
-    name as idle_name,
-    value as idle_value
-  from counter
-  inner join cpu_counter_track
-    on counter.track_id = cpu_counter_track.id
-  where name = 'cpuidle';
-
-create virtual table freq_idle
-  using span_join(freq_view PARTITIONED cpu, idle_view PARTITIONED cpu);
-
-create virtual table window_freq_idle using window;
-
-create virtual table span_freq_idle
-  using span_join(freq_idle PARTITIONED cpu, window_freq_idle);
-
-update window_freq_idle
-  set
-    window_start=(select min(ts) from sched),
-    window_dur=(select max(ts) - min(ts) from sched),
-    quantum=1000000
-  where rowid = 0;
-
-create view counter_view
-  as select
-    ts,
-    dur,
-    quantum_ts,
-    cpu,
-    case idle_value
-      when 4294967295 then "freq"
-      else "idle"
-    end as name,
-    case idle_value
-      when 4294967295 then freq_value
-      else idle_value
-    end as value
-  from span_freq_idle;
-
-select cpu, name, value, sum(dur) from counter_view group by cpu, name, value;
diff --git a/test/trace_processor/parsing/b120605557_test.sql b/test/trace_processor/parsing/b120605557_test.sql
deleted file mode 100644
index 8af6cd9..0000000
--- a/test/trace_processor/parsing/b120605557_test.sql
+++ /dev/null
@@ -1,18 +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 count(*)
-from counter
-inner join counter_track on counter_track.id = counter.track_id
diff --git a/test/trace_processor/parsing/cgroup_attach_task_post_s.textproto b/test/trace_processor/parsing/cgroup_attach_task_post_s.textproto
deleted file mode 100644
index 6950e5a..0000000
--- a/test/trace_processor/parsing/cgroup_attach_task_post_s.textproto
+++ /dev/null
@@ -1,17 +0,0 @@
-packet {
-    ftrace_events {
-      cpu: 3
-      event {
-        timestamp: 74289018336
-        pid: 1
-        cgroup_attach_task {
-          dst_root: 1
-          dst_id: 2
-          pid: 3
-          comm: "foo"
-          dst_level: 4
-          dst_path: "bar"
-        }
-      }
-    }
-  }
\ No newline at end of file
diff --git a/test/trace_processor/parsing/cgroup_attach_task_pre_s.textproto b/test/trace_processor/parsing/cgroup_attach_task_pre_s.textproto
deleted file mode 100644
index 679b211..0000000
--- a/test/trace_processor/parsing/cgroup_attach_task_pre_s.textproto
+++ /dev/null
@@ -1,16 +0,0 @@
-packet {
-    ftrace_events {
-      cpu: 3
-      event {
-        timestamp: 74289018336
-        pid: 1
-        cgroup_attach_task {
-          dst_root: 1
-          dst_id: 2
-          pid: 3
-          comm: "foo"
-          cname: "bar"
-        }
-      }
-    }
-  }
\ No newline at end of file
diff --git a/test/trace_processor/parsing/chrome_metadata.out b/test/trace_processor/parsing/chrome_metadata.out
deleted file mode 100644
index 5ba298f..0000000
--- a/test/trace_processor/parsing/chrome_metadata.out
+++ /dev/null
@@ -1,6 +0,0 @@
-"id","type","name","key_type","int_value","str_value"
-0,"metadata","trace_uuid","single","[NULL]","00000000-0000-0000-707e-42fe4a525c33"
-1,"metadata","cr-background_tracing_metadata","single","[NULL]","CgA="
-2,"metadata","cr-playstore_version_code","single",101,"[NULL]"
-3,"metadata","cr-enabled_categories","single","[NULL]","cat1,cat2,cat3"
-4,"metadata","trace_size_bytes","single",54,"[NULL]"
diff --git a/test/trace_processor/parsing/chrome_metadata.textproto b/test/trace_processor/parsing/chrome_metadata.textproto
deleted file mode 100644
index e8ef860..0000000
--- a/test/trace_processor/parsing/chrome_metadata.textproto
+++ /dev/null
@@ -1,21 +0,0 @@
-packet {
-  clock_snapshot {
-    clocks {
-      clock_id: 6
-      timestamp: 101000002
-    }
-  }
-  trusted_packet_sequence_id: 1
-  timestamp: 101000002
-}
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 101000002
-  chrome_metadata {
-    background_tracing_metadata {
-      triggered_rule {}
-    }
-    chrome_version_code: 101
-    enabled_categories: "cat1,cat2,cat3"
-  }
-}
diff --git a/test/trace_processor/parsing/chrome_metadata_test.sql b/test/trace_processor/parsing/chrome_metadata_test.sql
deleted file mode 100644
index 3521cf1..0000000
--- a/test/trace_processor/parsing/chrome_metadata_test.sql
+++ /dev/null
@@ -1 +0,0 @@
-select * from metadata
diff --git a/test/trace_processor/parsing/config_metadata.out b/test/trace_processor/parsing/config_metadata.out
deleted file mode 100644
index 3a77f45..0000000
--- a/test/trace_processor/parsing/config_metadata.out
+++ /dev/null
@@ -1,5 +0,0 @@
-"name","str_value"
-"android_build_fingerprint","the fingerprint"
-"trace_config_pbtxt","trace_uuid_msb: 1314564453825188563
-trace_uuid_lsb: -6605018796207623390"
-"trace_uuid","123e4567-e89b-12d3-a456-426655443322"
diff --git a/test/trace_processor/parsing/config_metadata.textproto b/test/trace_processor/parsing/config_metadata.textproto
deleted file mode 100644
index e2c1766..0000000
--- a/test/trace_processor/parsing/config_metadata.textproto
+++ /dev/null
@@ -1,24 +0,0 @@
-packet {
-  clock_snapshot {
-    clocks {
-      clock_id: 6
-      timestamp: 101000002
-    }
-    clocks {
-      clock_id: 128
-      timestamp: 2
-    }
-  }
-  timestamp: 101000002
-}
-packet {
-  trace_config {
-    trace_uuid_msb: 1314564453825188563
-    trace_uuid_lsb: -6605018796207623390
-  }
-}
-packet {
-  system_info {
-    android_build_fingerprint: "the fingerprint"
-  }
-}
diff --git a/test/trace_processor/parsing/counters_json_counters.out b/test/trace_processor/parsing/counters_json_counters.out
deleted file mode 100644
index dfe4747d..0000000
--- a/test/trace_processor/parsing/counters_json_counters.out
+++ /dev/null
@@ -1,4 +0,0 @@
-"name","ts","value"
-"ctr cats",0,0.000000
-"ctr cats",10000,10.000000
-"ctr cats",20000,0.000000
diff --git a/test/trace_processor/parsing/cpu.out b/test/trace_processor/parsing/cpu.out
deleted file mode 100644
index 6e186ef..0000000
--- a/test/trace_processor/parsing/cpu.out
+++ /dev/null
@@ -1,9 +0,0 @@
-"id","cluster_id","processor"
-0,0,"AArch64 Processor rev 13 (aarch64)"
-1,0,"AArch64 Processor rev 13 (aarch64)"
-2,0,"AArch64 Processor rev 13 (aarch64)"
-3,0,"AArch64 Processor rev 13 (aarch64)"
-4,0,"AArch64 Processor rev 13 (aarch64)"
-5,0,"AArch64 Processor rev 13 (aarch64)"
-6,1,"AArch64 Processor rev 13 (aarch64)"
-7,1,"AArch64 Processor rev 13 (aarch64)"
diff --git a/test/trace_processor/parsing/cpu_freq_test.sql b/test/trace_processor/parsing/cpu_freq_test.sql
deleted file mode 100644
index 764283b..0000000
--- a/test/trace_processor/parsing/cpu_freq_test.sql
+++ /dev/null
@@ -1,6 +0,0 @@
-SELECT
-  freq,
-  GROUP_CONCAT(cpu_id) AS cpus
-FROM cpu_freq
-GROUP BY freq
-ORDER BY freq;
diff --git a/test/trace_processor/parsing/cpu_test.sql b/test/trace_processor/parsing/cpu_test.sql
deleted file mode 100644
index fad57fa..0000000
--- a/test/trace_processor/parsing/cpu_test.sql
+++ /dev/null
@@ -1,5 +0,0 @@
-SELECT
-  id,
-  cluster_id,
-  processor
-FROM cpu;
diff --git a/test/trace_processor/parsing/decimal_timestamp_slices.out b/test/trace_processor/parsing/decimal_timestamp_slices.out
deleted file mode 100644
index b64b606..0000000
--- a/test/trace_processor/parsing/decimal_timestamp_slices.out
+++ /dev/null
@@ -1,2 +0,0 @@
-"ts","dur","name"
-5100,500100,"name.exec"
diff --git a/test/trace_processor/parsing/display_time_unit_slices.out b/test/trace_processor/parsing/display_time_unit_slices.out
deleted file mode 100644
index cee485b..0000000
--- a/test/trace_processor/parsing/display_time_unit_slices.out
+++ /dev/null
@@ -1,2 +0,0 @@
-"ts","dur","name"
--7794778920422990592,211463000000,"add_graph"
diff --git a/test/trace_processor/parsing/end_reason_eq_test.sql b/test/trace_processor/parsing/end_reason_eq_test.sql
deleted file mode 100644
index e56f5ff..0000000
--- a/test/trace_processor/parsing/end_reason_eq_test.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 = 'D'
-group by end_state
diff --git a/test/trace_processor/parsing/end_reason_neq_test.sql b/test/trace_processor/parsing/end_reason_neq_test.sql
deleted file mode 100644
index 57e6092..0000000
--- a/test/trace_processor/parsing/end_reason_neq_test.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 != 'D'
-group by end_state
diff --git a/test/trace_processor/parsing/flow_events_json_v1.out b/test/trace_processor/parsing/flow_events_json_v1.out
deleted file mode 100644
index 3cef532..0000000
--- a/test/trace_processor/parsing/flow_events_json_v1.out
+++ /dev/null
@@ -1,4 +0,0 @@
-"slice_out","slice_in"
-"SenderB","Blergh"
-"SenderA","OtherSlice"
-"OtherSlice","SomeSlice"
diff --git a/test/trace_processor/parsing/flow_events_json_v2.out b/test/trace_processor/parsing/flow_events_json_v2.out
deleted file mode 100644
index 3090047..0000000
--- a/test/trace_processor/parsing/flow_events_json_v2.out
+++ /dev/null
@@ -1,5 +0,0 @@
-"slice_out","slice_in"
-"SenderB","Blergh"
-"SenderA","OtherSlice"
-"OtherSlice","SomeSlice"
-"OtherSlice","SomeOtherSlice"
diff --git a/test/trace_processor/parsing/flow_events_test.sql b/test/trace_processor/parsing/flow_events_test.sql
deleted file mode 100644
index 11ef1a9..0000000
--- a/test/trace_processor/parsing/flow_events_test.sql
+++ /dev/null
@@ -1,18 +0,0 @@
---
--- 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 t1.name as slice_out, t2.name as slice_in from flow t
-join slice t1 on t.slice_out == t1.slice_id
-join slice t2 on t.slice_in == t2.slice_id;
diff --git a/test/trace_processor/parsing/ftrace_with_tracing_start_list_sched_slice_spans.out b/test/trace_processor/parsing/ftrace_with_tracing_start_list_sched_slice_spans.out
deleted file mode 100644
index f6748c7..0000000
--- a/test/trace_processor/parsing/ftrace_with_tracing_start_list_sched_slice_spans.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"ts","dur","tid"
-100,10,1
-110,-1,2
diff --git a/test/trace_processor/parsing/global_memory_counter_memory_counters.out b/test/trace_processor/parsing/global_memory_counter_memory_counters.out
deleted file mode 100644
index a083fa0..0000000
--- a/test/trace_processor/parsing/global_memory_counter_memory_counters.out
+++ /dev/null
@@ -1,11 +0,0 @@
-"ts","value","name"
-22240334823167,2696392704.000000,"MemAvailable"
-22240356169836,2696392704.000000,"MemAvailable"
-22240468594483,2696392704.000000,"MemAvailable"
-22240566948190,2696392704.000000,"MemAvailable"
-22240667383304,2696392704.000000,"MemAvailable"
-22240766505085,2696392704.000000,"MemAvailable"
-22240866794106,2696392704.000000,"MemAvailable"
-22240968271928,2696392704.000000,"MemAvailable"
-22241065777407,2696392704.000000,"MemAvailable"
-22241165839708,2696392704.000000,"MemAvailable"
diff --git a/test/trace_processor/parsing/global_memory_counter_test.sql b/test/trace_processor/parsing/global_memory_counter_test.sql
deleted file mode 100644
index fdf24c2..0000000
--- a/test/trace_processor/parsing/global_memory_counter_test.sql
+++ /dev/null
@@ -1,20 +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 ts, value, name
-from counter
-inner join counter_track on counter.track_id = counter_track.id
-where name = 'MemAvailable' and counter_track.type = 'counter_track'
-limit 10
diff --git a/test/trace_processor/parsing/index b/test/trace_processor/parsing/index
deleted file mode 100644
index e1cbe46..0000000
--- a/test/trace_processor/parsing/index
+++ /dev/null
@@ -1,158 +0,0 @@
-# Contains tests for parsing events which are applicable to more than one vertical
-# "area". Generally, events here are of high importance (e.g. sched_switch is tested
-# here is and is used by every embedder of trace processor)
-#
-# Note: It's generally *not* advisable to add tests here. Check the guidance provided by
-# http://perfetto/dev/docs/analysis/trace-processor#diff-tests for choosing which folder
-# to add a new test to.
-
-# TODO(lalitm): some tests here should be moved out of here and into the area folders;
-# they are only here because they predate the modularisation of diff tests.
-
-# Sched
-../../data/android_sched_and_ps.pb ts_desc_filter_test.sql ts_desc_filter_android_sched_and_ps.out
-
-# Sched reason
-../../data/android_sched_and_ps.pb end_reason_eq_test.sql android_sched_and_ps_end_reason_eq.out
-../../data/android_sched_and_ps.pb end_reason_neq_test.sql android_sched_and_ps_end_reason_neq.out
-
-# CPU Frequency
-../../data/cpu_counters.pb b120487929_test.sql cpu_counters_b120487929.out
-
-# Test the filtering of ftrace events before tracing_start.
-ftrace_with_tracing_start.py list_sched_slice_spans_test.sql ftrace_with_tracing_start_list_sched_slice_spans.out
-
-# Rss stats
-rss_stat_mm_id.py rss_stat_test.sql rss_stat_mm_id.out
-rss_stat_mm_id_clone.py rss_stat_test.sql rss_stat_mm_id_clone.out
-rss_stat_mm_id_reuse.py rss_stat_test.sql rss_stat_mm_id_reuse.out
-rss_stat_legacy.py rss_stat_test.sql rss_stat_legacy.out
-rss_stat_after_free.py rss_stat_after_free_test.sql rss_stat_after_free.out
-
-# Memory counters
-../../data/memory_counters.pb args_string_filter_null_test.sql memory_counters_args_string_filter_null.out
-../../data/memory_counters.pb args_string_is_null_test.sql memory_counters_args_string_is_null.out
-../../data/memory_counters.pb args_string_is_not_null_test.sql memory_counters_args_string_is_not_null.out
-../../data/memory_counters.pb b120605557_test.sql memory_counters_b120605557.out
-../../data/memory_counters.pb global_memory_counter_test.sql global_memory_counter_memory_counters.out
-ion_stat.textproto ion_stat_test.sql ion_stat.out
-
-# Scheduling slices from sched_switch events. There are two tests, one for the
-# typical encoding of sched_switch events, and one for the same trace
-# re-encoded in the compact format. The output should be identical apart from
-# the latter having one slice fewer for each cpu (the first compact
-# sched_switch event doesn't start a slice). Six slices in this case.
-../../data/sched_switch_original.pb sched_slices_test.sql sched_slices_sched_switch_original.out
-../../data/sched_switch_compact.pb sched_slices_test.sql sched_slices_sched_switch_compact.out
-
-# Decoding of sched_waking events from a trace with compact scheduling events.
-# Verifies the contents of raw & instants tables.
-../../data/compact_sched.pb sched_waking_raw_test.sql sched_waking_raw_compact_sched.out
-../../data/compact_sched.pb sched_waking_instants_test.sql sched_waking_instants_compact_sched.out
-
-# Mm Event
-../../data/mm_event.pb mm_event_test.sql mm_event.out
-
-# Check the systrace conversion code in the raw table.
-# Print events
-../../data/lmk_userspace.pb print_systrace_test.sql print_systrace_lmk_userspace.out
-kernel_tmw_counter.textproto process_counter_and_track_test.sql kernel_tmw_counter_process_counter_and_track.out
-kernel_dpu_tmw_counter.textproto process_counter_and_track_test.sql kernel_dpu_tmw_counter_process_counter_and_track.out
-# Unsigned integers
-print_systrace_unsigned.py print_systrace_test.sql print_systrace_unsigned.out
-
-# cgroup_attach_task systrace conversion.
-cgroup_attach_task_pre_s.textproto print_systrace_test.sql cgroup_attach_task_pre_s_print_systrace.out
-cgroup_attach_task_post_s.textproto print_systrace_test.sql cgroup_attach_task_post_s_print_systrace.out
-
-# Parsing systrace files
-../../data/systrace.html systrace_html_test.sql systrace_html.out
-../../data/trailing_empty.systrace sched_smoke_test.sql sched_smoke_trailing_empty.out
-
-# LMK handling
-../../data/lmk_userspace.pb lmk_test.sql lmk_userspace_lmk.out
-../common/oom_kill.textproto oom_kill_test.sql oom_kill.out
-
-# Logcat
-../../data/android_log.pb android_log_counts_test.sql android_log_counts.out
-../../data/android_log.pb android_log_msgs_test.sql android_log_msgs.out
-../../data/android_log_ring_buffer_mode.pb android_log_ring_buffer_mode_test.sql android_log_ring_buffer_mode.out
-
-# Oom Score
-synth_oom.py oom_query_test.sql synth_oom_oom_query.out
-../../data/process_stats_poll.pb oom_score_poll_test.sql process_stats_poll_oom_score.out
-
-# Stats
-../../data/android_sched_and_ps.pb stats_test.sql android_sched_and_ps_stats.out
-
-# Syscalls
-syscall.py sys_test.sql sys_syscall.out
-
-# thread_slice tables.
-flow_events_json_v2.json thread_time_in_thread_slice_test.sql thread_time_in_thread_slice.out
-
-# Initial display state
-initial_display_state.textproto initial_display_state_test.sql initial_display_state.out
-
-# Config & metadata
-config_metadata.textproto metadata_test.sql config_metadata.out
-trigger_packet_trace.textproto triggers_packets_test.sql triggers_packets_trigger_packet_trace.out
-chrome_metadata.textproto chrome_metadata_test.sql chrome_metadata.out
-
-# CPU info
-cpu_info.textproto cpu_test.sql cpu.out
-cpu_info.textproto cpu_freq_test.sql cpu_freq.out
-
-# Trace size
-../../data/android_sched_and_ps.pb trace_size_test.sql android_sched_and_ps_trace_size.out
-
-# Package list handling
-android_package_list.py android_package_list android_package_list.out
-
-# Ensures process -> package matching works as expected.
-process_metadata_matching.textproto process_metadata_matching_test.sql process_metadata_matching.out
-
-# Flow events importing from json
-flow_events_json_v1.json flow_events_test.sql flow_events_json_v1.out
-flow_events_json_v2.json flow_events_test.sql flow_events_json_v2.out
-
-# Importing displayTimeUnit
-../../data/display_time_unit.json slices_test.sql display_time_unit_slices.out
-
-# Parsing sched_blocked_reason
-sched_blocked_proto.py sched_blocked_reason_test.sql sched_blocked_proto_sched_blocked_reason.out
-sched_blocked_systrace.systrace sched_blocked_reason_test.sql sched_blocked_systrace_sched_blocked_reason.out
-
-# Kernel symbolization
-sched_blocked_reason_symbolized.textproto sched_blocked_reason_function_test.sql sched_blocked_reason_symbolized_sched_blocked_reason_function.out
-sched_blocked_reason_symbolized.textproto ../common/to_systrace_test.sql sched_blocked_reason_symbolized_to_systrace.out
-
-# Floating point numbers
-../../data/decimal_timestamp.json slices_test.sql decimal_timestamp_slices.out
-
-# JSON instants and counters
-../../data/counters.json json_counters_test.sql counters_json_counters.out
-../../data/instants.json json_instants_test.sql instants_json_instants.out
-
-# Trace quality metric
-very_long_sched.py android_trace_quality very_long_sched_android_trace_quality.out
-
-# Regression test for b/193721088 (infra prepending " done\n" to atrace)
-../../data/atrace_b_193721088.atr sched_smoke_test.sql sched_smoke_trailing_empty.out
-
-# Multiuser
-android_multiuser_switch.textproto android_multiuser android_multiuser_switch.out
-
-# Output of atrace -z.
-../../data/atrace_compressed.ctrace sched_smoke_test.sql atrace_compressed_sched_count.out
-
-# Output of adb shell "atrace -t 1 sched" > out.txt". It has extra garbage
-# coming from stderr before the TRACE: marker. See b/208691037.
-../../data/atrace_uncompressed_b_208691037 sched_smoke_test.sql atrace_uncompressed_sched_count.out
-otheruuids.textproto android_other_traces otheruuids_android_other_traces.out
-
-# Per-process Binder transaction metrics
-android_binder.py android_binder android_binder.out
-
-# Statsd Atoms
-../../data/statsd_atoms.pb all_atoms_test.sql statsd_atoms_all_atoms.out
diff --git a/test/trace_processor/parsing/initial_display_state.out b/test/trace_processor/parsing/initial_display_state.out
deleted file mode 100644
index a3a19b0..0000000
--- a/test/trace_processor/parsing/initial_display_state.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"name","ts","value"
-"ScreenState",1,2.000000
-"ScreenState",1000,0.000000
diff --git a/test/trace_processor/parsing/initial_display_state.textproto b/test/trace_processor/parsing/initial_display_state.textproto
deleted file mode 100644
index 31cf253..0000000
--- a/test/trace_processor/parsing/initial_display_state.textproto
+++ /dev/null
@@ -1,19 +0,0 @@
-packet: {
-  timestamp: 1
-  initial_display_state: {
-    display_state: 2
-    brightness: 0.5
-  }
-}
-packet {
-  ftrace_events {
-    cpu: 0
-    event {
-      timestamp: 1000
-      pid: 1234
-      print {
-        buf: "C|5678|ScreenState|0\n"
-      }
-    }
-  }
-}
diff --git a/test/trace_processor/parsing/initial_display_state_test.sql b/test/trace_processor/parsing/initial_display_state_test.sql
deleted file mode 100644
index bec5a87..0000000
--- a/test/trace_processor/parsing/initial_display_state_test.sql
+++ /dev/null
@@ -1,6 +0,0 @@
-SELECT t.name,
-       c.ts,
-       c.value
-FROM counter_track t
-    JOIN counter c ON t.id = c.track_id
-WHERE t.name = 'ScreenState';
diff --git a/test/trace_processor/parsing/instants_json_instants.out b/test/trace_processor/parsing/instants_json_instants.out
deleted file mode 100644
index 926270d..0000000
--- a/test/trace_processor/parsing/instants_json_instants.out
+++ /dev/null
@@ -1,7 +0,0 @@
-"ts","slice_name","tid","pid"
-1234523300,"Thread",2347,"[NULL]"
-1235523300,"Global","[NULL]","[NULL]"
-1236523300,"Process","[NULL]",2320
-1237523300,"Nonei",6790,"[NULL]"
-1238523300,"NoneI",6790,"[NULL]"
-1239523300,"NoneR",6790,"[NULL]"
diff --git a/test/trace_processor/parsing/ion_stat.out b/test/trace_processor/parsing/ion_stat.out
deleted file mode 100644
index 90c127e..0000000
--- a/test/trace_processor/parsing/ion_stat.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"name","ts","value"
-"mem.ion",1234,200.000000
-"mem.ion_change",1234,100.000000
diff --git a/test/trace_processor/parsing/ion_stat.textproto b/test/trace_processor/parsing/ion_stat.textproto
deleted file mode 100644
index b65f5b0..0000000
--- a/test/trace_processor/parsing/ion_stat.textproto
+++ /dev/null
@@ -1,14 +0,0 @@
-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/parsing/ion_stat_test.sql b/test/trace_processor/parsing/ion_stat_test.sql
deleted file mode 100644
index 5cdc8d0..0000000
--- a/test/trace_processor/parsing/ion_stat_test.sql
+++ /dev/null
@@ -1,4 +0,0 @@
-SELECT t.name, c.ts, c.value
-FROM counter c
-JOIN track t ON c.track_id = t.id
-WHERE t.name GLOB 'mem.ion*';
diff --git a/test/trace_processor/parsing/json_counters_test.sql b/test/trace_processor/parsing/json_counters_test.sql
deleted file mode 100644
index 604c2c1..0000000
--- a/test/trace_processor/parsing/json_counters_test.sql
+++ /dev/null
@@ -1,6 +0,0 @@
-select
-  process_counter_track.name,
-  counter.ts,
-  counter.value
-from counter
-join process_counter_track on (counter.track_id = process_counter_track.id);
\ No newline at end of file
diff --git a/test/trace_processor/parsing/json_instants_test.sql b/test/trace_processor/parsing/json_instants_test.sql
deleted file mode 100644
index d76ef89..0000000
--- a/test/trace_processor/parsing/json_instants_test.sql
+++ /dev/null
@@ -1,12 +0,0 @@
-select
-  slice.ts,
-  slice.name as slice_name,
-  thread.tid,
-  process.pid
-from slice
-join track on (slice.track_id = track.id)
-left join thread_track on (slice.track_id = thread_track.id)
-left join thread on (thread_track.utid = thread.utid)
-left join process_track on (slice.track_id = process_track.id)
-left join process on (process_track.upid = process.upid)
-where dur = 0;
\ No newline at end of file
diff --git a/test/trace_processor/parsing/kernel_dpu_tmw_counter.textproto b/test/trace_processor/parsing/kernel_dpu_tmw_counter.textproto
deleted file mode 100644
index 32bd0cf..0000000
--- a/test/trace_processor/parsing/kernel_dpu_tmw_counter.textproto
+++ /dev/null
@@ -1,47 +0,0 @@
-packet {
-  ftrace_events {
-    cpu: 2
-    event {
-      timestamp: 795572805481
-      pid: 237
-      dpu_tracing_mark_write {
-        pid: 237
-        name: "dpu_vote_clock"
-        type: 67
-        value: 123
-      }
-    }
-    event {
-      timestamp: 795572870504
-      pid: 515
-      dpu_tracing_mark_write {
-        pid: 237
-        name: "dpu_vote_clock"
-        type: 67
-        value: 100
-      }
-    }
-    event {
-      timestamp: 795620516581
-      pid: 237
-      dpu_tracing_mark_write {
-        pid: 237
-        name: "dpu_vote_clock"
-        type: 67
-        value: 125
-      }
-    }
-    event {
-      timestamp: 795620943421
-      pid: 515
-      dpu_tracing_mark_write {
-        pid: 237
-        name: "dpu_vote_clock"
-        type: 67
-        value: 100
-      }
-    }
-  }
-  trusted_uid: 9999
-  trusted_packet_sequence_id: 3
-}
\ No newline at end of file
diff --git a/test/trace_processor/parsing/kernel_dpu_tmw_counter_process_counter_and_track.out b/test/trace_processor/parsing/kernel_dpu_tmw_counter_process_counter_and_track.out
deleted file mode 100644
index 4750603..0000000
--- a/test/trace_processor/parsing/kernel_dpu_tmw_counter_process_counter_and_track.out
+++ /dev/null
@@ -1,5 +0,0 @@
-"ts","name","value","pid"
-795572805481,"dpu_vote_clock",123.000000,237
-795572870504,"dpu_vote_clock",100.000000,237
-795620516581,"dpu_vote_clock",125.000000,237
-795620943421,"dpu_vote_clock",100.000000,237
diff --git a/test/trace_processor/parsing/kernel_tmw_counter_process_counter_and_track.out b/test/trace_processor/parsing/kernel_tmw_counter_process_counter_and_track.out
deleted file mode 100644
index 7f90a5a..0000000
--- a/test/trace_processor/parsing/kernel_tmw_counter_process_counter_and_track.out
+++ /dev/null
@@ -1,8 +0,0 @@
-"ts","name","value","pid"
-795572805481,"g2d_frame_hw#15",0.000000,237
-795572870504,"g2d_frame_sw#15",0.000000,237
-795620516581,"g2d_frame_sw#15",1.000000,237
-795620943421,"g2d_frame_hw#15",1.000000,237
-795623633810,"g2d_frame_hw#15",0.000000,237
-795623633810,"g2d_frame_hw#15",0.000000,237
-795623739848,"g2d_frame_sw#15",0.000000,237
diff --git a/test/trace_processor/parsing/list_sched_slice_spans_test.sql b/test/trace_processor/parsing/list_sched_slice_spans_test.sql
deleted file mode 100644
index e5122e9..0000000
--- a/test/trace_processor/parsing/list_sched_slice_spans_test.sql
+++ /dev/null
@@ -1,4 +0,0 @@
-select ts, dur, tid
-from sched
-join thread using(utid)
-order by ts
\ No newline at end of file
diff --git a/test/trace_processor/parsing/lmk_test.sql b/test/trace_processor/parsing/lmk_test.sql
deleted file mode 100644
index 5b913b3..0000000
--- a/test/trace_processor/parsing/lmk_test.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 ts, process.pid
-FROM instant
-JOIN process_track ON instant.track_id = process_track.id
-JOIN process USING (upid);
\ No newline at end of file
diff --git a/test/trace_processor/parsing/lmk_userspace_lmk.out b/test/trace_processor/parsing/lmk_userspace_lmk.out
deleted file mode 100644
index dc7ed0a..0000000
--- a/test/trace_processor/parsing/lmk_userspace_lmk.out
+++ /dev/null
@@ -1,5 +0,0 @@
-"ts","pid"
-732246100696424,17924
-732246180149452,21090
-732246388596557,21120
-732246415955101,21151
diff --git a/test/trace_processor/parsing/memory_counters_args_string_filter_null.out b/test/trace_processor/parsing/memory_counters_args_string_filter_null.out
deleted file mode 100644
index 683ea38..0000000
--- a/test/trace_processor/parsing/memory_counters_args_string_filter_null.out
+++ /dev/null
@@ -1 +0,0 @@
-"string_value"
diff --git a/test/trace_processor/parsing/memory_counters_args_string_is_not_null.out b/test/trace_processor/parsing/memory_counters_args_string_is_not_null.out
deleted file mode 100644
index bf80b37..0000000
--- a/test/trace_processor/parsing/memory_counters_args_string_is_not_null.out
+++ /dev/null
@@ -1,11 +0,0 @@
-"string_value"
-"traced_probes"
-"rcuos/0"
-"rcuos/0"
-"rcu_sched"
-"rcu_sched"
-"atrace"
-"atrace"
-"traced_probes"
-"swapper/1"
-"rcu_preempt"
diff --git a/test/trace_processor/parsing/memory_counters_args_string_is_null.out b/test/trace_processor/parsing/memory_counters_args_string_is_null.out
deleted file mode 100644
index 8932606..0000000
--- a/test/trace_processor/parsing/memory_counters_args_string_is_null.out
+++ /dev/null
@@ -1,11 +0,0 @@
-"string_value"
-"[NULL]"
-"[NULL]"
-"[NULL]"
-"[NULL]"
-"[NULL]"
-"[NULL]"
-"[NULL]"
-"[NULL]"
-"[NULL]"
-"[NULL]"
diff --git a/test/trace_processor/parsing/memory_counters_b120605557.out b/test/trace_processor/parsing/memory_counters_b120605557.out
deleted file mode 100644
index 99cb66b..0000000
--- a/test/trace_processor/parsing/memory_counters_b120605557.out
+++ /dev/null
@@ -1,2 +0,0 @@
-"count(*)"
-98688
diff --git a/test/trace_processor/parsing/metadata_test.sql b/test/trace_processor/parsing/metadata_test.sql
deleted file mode 100644
index 3943251..0000000
--- a/test/trace_processor/parsing/metadata_test.sql
+++ /dev/null
@@ -1,16 +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 name, str_value from metadata where str_value is not null order by name;
diff --git a/test/trace_processor/parsing/mm_event_test.sql b/test/trace_processor/parsing/mm_event_test.sql
deleted file mode 100644
index d1209c3..0000000
--- a/test/trace_processor/parsing/mm_event_test.sql
+++ /dev/null
@@ -1,22 +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 ts, name, value
-from counter
-inner join counter_track
-  on counter.track_id = counter_track.id
-where name glob 'mem.mm.*'
-order by ts
-limit 40
diff --git a/test/trace_processor/parsing/oom_kill.out b/test/trace_processor/parsing/oom_kill.out
deleted file mode 100644
index 7d54ced..0000000
--- a/test/trace_processor/parsing/oom_kill.out
+++ /dev/null
@@ -1,2 +0,0 @@
-"ts","name","pid","name"
-1234,"mem.oom_kill",1000,"com.google.android.gm"
diff --git a/test/trace_processor/parsing/oom_kill_test.sql b/test/trace_processor/parsing/oom_kill_test.sql
deleted file mode 100644
index d9f7fad..0000000
--- a/test/trace_processor/parsing/oom_kill_test.sql
+++ /dev/null
@@ -1,5 +0,0 @@
-SELECT ts, instant.name, process.pid, process.name
-FROM instant
-JOIN thread_track ON instant.track_id = thread_track.id
-JOIN thread USING (utid)
-JOIN process USING (upid);
diff --git a/test/trace_processor/parsing/oom_query_test.sql b/test/trace_processor/parsing/oom_query_test.sql
deleted file mode 100644
index 68c19ac..0000000
--- a/test/trace_processor/parsing/oom_query_test.sql
+++ /dev/null
@@ -1,105 +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.
---
-/* Create the file RSS table. */
-CREATE VIEW file_rss AS
-SELECT ts,
-       LEAD(ts, 1, ts) OVER(PARTITION BY track_id ORDER BY ts) - ts as dur,
-       upid,
-       value AS file
-FROM counter
-JOIN process_counter_track
-  ON counter.track_id = process_counter_track.id
-WHERE process_counter_track.name IN ("mem.rss.file", "rss_stat.mm_filepages");
-
-/* Create the anon RSS table. */
-create view anon_rss as
-select ts,
-       LEAD(ts, 1, ts) OVER(PARTITION BY track_id ORDER BY ts) - ts as dur,
-       upid,
-       value as anon
-FROM counter
-JOIN process_counter_track
-  ON counter.track_id = process_counter_track.id
-where process_counter_track.name in ("mem.rss.anon", "rss_stat.mm_anonpages");
-
-/* Create the oom adj table. */
-create view oom_adj as
-select ts,
-       LEAD(ts, 1, ts) OVER(PARTITION BY track_id ORDER BY ts) - ts as dur,
-       upid,
-       value as oom_score_adj
-FROM counter
-JOIN process_counter_track
-  ON counter.track_id = process_counter_track.id
-where process_counter_track.name = 'oom_score_adj';
-
-/* Harmonise the three tables above into a single table. */
-CREATE VIRTUAL TABLE anon_file USING span_join(anon_rss PARTITIONED upid, file_rss PARTITIONED upid);
-
-CREATE VIRTUAL TABLE anon_file_oom USING span_join(anon_file PARTITIONED upid, oom_adj PARTITIONED upid);
-
-/* For each span, compute the RSS (for a given upid) for each category of oom_adj */
-CREATE VIEW rss_spans_for_oom_upid AS
-SELECT ts,
-       dur,
-       upid,
-       CASE WHEN oom_score_adj < 0 THEN file + anon ELSE 0 END as rss_oom_lt_zero,
-       CASE WHEN oom_score_adj <= 0 THEN file + anon ELSE 0 END as rss_oom_eq_zero,
-       CASE WHEN oom_score_adj < 20 THEN file + anon ELSE 0 END as rss_fg,
-       CASE WHEN oom_score_adj < 700 THEN file + anon ELSE 0 END as rss_bg,
-       CASE WHEN oom_score_adj < 900 THEN file + anon ELSE 0 END as rss_low_bg,
-       file + anon as rss_cached
-FROM anon_file_oom;
-
-/* Convert the raw RSS values to the change in RSS (for a given upid) over time */
-CREATE VIEW rss_spans_rss_change AS
-SELECT ts,
-       dur,
-       upid,
-       rss_oom_lt_zero - lag(rss_oom_lt_zero, 1, 0) OVER win as rss_oom_lt_zero_diff,
-       rss_oom_eq_zero - lag(rss_oom_eq_zero, 1, 0) OVER win as rss_oom_eq_zero_diff,
-       rss_fg - lag(rss_fg, 1, 0) OVER win as rss_fg_diff,
-       rss_bg - lag(rss_bg, 1, 0) OVER win as rss_bg_diff,
-       rss_low_bg - lag(rss_low_bg, 1, 0) OVER win as rss_low_bg_diff,
-       rss_cached - lag(rss_cached, 1, 0) OVER win as rss_cached_diff
-FROM rss_spans_for_oom_upid
-WINDOW win AS (PARTITION BY upid ORDER BY ts);
-
-/*
- * Compute a rolling sum of anon + file for each category of process state.
- * (note: we no longer consider upid in the windows which means we are now
- * computing a rolling sum for all the processes).
- */
-CREATE VIEW output AS
-SELECT ts,
-       lead(ts, 1, ts + dur) over win - ts as dur,
-       SUM(rss_oom_lt_zero_diff) OVER win as rss_oom_lt_zero,
-       SUM(rss_oom_eq_zero_diff) OVER win as rss_oom_eq_zero,
-       SUM(rss_fg_diff) OVER win as rss_fg,
-       SUM(rss_bg_diff) OVER win as rss_bg,
-       SUM(rss_low_bg_diff) OVER win as rss_low_bg,
-       SUM(rss_cached_diff) OVER win as rss_cached
-FROM rss_spans_rss_change
-WINDOW win as (ORDER BY ts)
-ORDER BY ts;
-
-/*
- * Print out the final result (note: dur = 0 is excluded to account for times
- * where multiple processes report a new memory value at the same timestamp)
- */
-SELECT *
-FROM output
-WHERE dur > 0
diff --git a/test/trace_processor/parsing/oom_score_poll_test.sql b/test/trace_processor/parsing/oom_score_poll_test.sql
deleted file mode 100644
index f22624b..0000000
--- a/test/trace_processor/parsing/oom_score_poll_test.sql
+++ /dev/null
@@ -1,22 +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 ts, name, value, upid
-from counter c
-join process_counter_track t
-  on c.track_id = t.id
-where name = "oom_score_adj"
-order by ts
-limit 20
diff --git a/test/trace_processor/parsing/otheruuids_android_other_traces.out b/test/trace_processor/parsing/otheruuids_android_other_traces.out
deleted file mode 100644
index 61df982..0000000
--- a/test/trace_processor/parsing/otheruuids_android_other_traces.out
+++ /dev/null
@@ -1,5 +0,0 @@
-android_other_traces {
-  finalized_traces_uuid: "75e4c6d0-d8f6-4f82-fa4b-9e09c5512288"
-  finalized_traces_uuid: "ad836701-3113-3fb1-be4f-f7731e23fbbf"
-  finalized_traces_uuid: "0de1a010-efa1-a081-2345-969b1186a6ab"
-}
diff --git a/test/trace_processor/parsing/print_systrace_test.sql b/test/trace_processor/parsing/print_systrace_test.sql
deleted file mode 100644
index 7e834eb..0000000
--- a/test/trace_processor/parsing/print_systrace_test.sql
+++ /dev/null
@@ -1,17 +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 to_ftrace(id)
-from raw;
diff --git a/test/trace_processor/parsing/process_counter_and_track_test.sql b/test/trace_processor/parsing/process_counter_and_track_test.sql
deleted file mode 100644
index eaa29ec..0000000
--- a/test/trace_processor/parsing/process_counter_and_track_test.sql
+++ /dev/null
@@ -1,5 +0,0 @@
-select ts, pct.name, value, pid
-from counter c
-join process_counter_track pct on c.track_id = pct.id
-join process using (upid)
-order by ts;
diff --git a/test/trace_processor/parsing/process_metadata_matching.out b/test/trace_processor/parsing/process_metadata_matching.out
deleted file mode 100644
index e80bf61..0000000
--- a/test/trace_processor/parsing/process_metadata_matching.out
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-"upid","process_name","uid","shared_uid","package_name","version_code"
-1,"init",0,"[NULL]","[NULL]","[NULL]"
-2,"system_server",1000,"[NULL]","[NULL]","[NULL]"
-3,"com.google.android.gms",10100,1,"com.google.android.gms",1234
-4,"com.google.android.gms.persistent",10100,1,"com.google.android.gms",1234
-5,"com.google.android.gms",10100,1,"com.google.android.gms",1234
diff --git a/test/trace_processor/parsing/process_metadata_matching.textproto b/test/trace_processor/parsing/process_metadata_matching.textproto
deleted file mode 100644
index c372464..0000000
--- a/test/trace_processor/parsing/process_metadata_matching.textproto
+++ /dev/null
@@ -1,48 +0,0 @@
-packet {
-  process_tree {
-    processes {
-      pid: 1
-      ppid: 0
-      cmdline: "init"
-      uid: 0
-    }
-    processes {
-      pid: 2
-      ppid: 1
-      cmdline: "system_server"
-      uid: 1000
-    }
-    processes {
-      pid: 3
-      ppid: 1
-      cmdline: "com.google.android.gms"
-      uid: 10100
-    }
-    processes {
-      pid: 4
-      ppid: 1
-      cmdline: "com.google.android.gms.persistent"
-      uid: 10100
-    }
-    processes {
-      pid: 5
-      ppid: 1
-      cmdline: "com.google.android.gms"
-      uid: 1010100
-    }
-  }
-}
-packet {
-  packages_list {
-    packages {
-      name: "com.google.android.gms"
-      uid: 10100
-      version_code: 1234
-    }
-    packages {
-      name: "com.google.android.gsf"
-      uid: 10100
-      version_code: 1
-    }
-  }
-}
diff --git a/test/trace_processor/parsing/process_metadata_matching_test.sql b/test/trace_processor/parsing/process_metadata_matching_test.sql
deleted file mode 100644
index 759448e..0000000
--- a/test/trace_processor/parsing/process_metadata_matching_test.sql
+++ /dev/null
@@ -1,24 +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.
---
--- Create so that RUN_METRIC will run without outputting any rows.
-CREATE TABLE TEST_TMP AS
-SELECT RUN_METRIC('android/process_metadata.sql');
-
-DROP TABLE TEST_TMP;
-
-SELECT upid, process_name, uid, shared_uid, package_name, version_code
-FROM process_metadata_table
-WHERE upid != 0;
diff --git a/test/trace_processor/parsing/rss_stat_after_free.out b/test/trace_processor/parsing/rss_stat_after_free.out
deleted file mode 100644
index d5c690e..0000000
--- a/test/trace_processor/parsing/rss_stat_after_free.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"pid","last_rss","process_end"
-10,100,101
-11,90,"[NULL]"
diff --git a/test/trace_processor/parsing/rss_stat_after_free_test.sql b/test/trace_processor/parsing/rss_stat_after_free_test.sql
deleted file mode 100644
index d31fab7..0000000
--- a/test/trace_processor/parsing/rss_stat_after_free_test.sql
+++ /dev/null
@@ -1,8 +0,0 @@
-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/parsing/rss_stat_legacy.out b/test/trace_processor/parsing/rss_stat_legacy.out
deleted file mode 100644
index e40bb9c..0000000
--- a/test/trace_processor/parsing/rss_stat_legacy.out
+++ /dev/null
@@ -1,6 +0,0 @@
-"ts","name","pid","name","value"
-90,"mem.rss.file",3,"kthreadd_child",9.000000
-91,"mem.rss.file",3,"kthreadd_child",900.000000
-99,"mem.rss.file",10,"process",10.000000
-100,"mem.rss.file",10,"process",1000.000000
-101,"mem.rss.file",3,"kthreadd_child",900.000000
diff --git a/test/trace_processor/parsing/rss_stat_mm_id.out b/test/trace_processor/parsing/rss_stat_mm_id.out
deleted file mode 100644
index 08bbe17..0000000
--- a/test/trace_processor/parsing/rss_stat_mm_id.out
+++ /dev/null
@@ -1,5 +0,0 @@
-"ts","name","pid","name","value"
-90,"mem.rss.file",3,"kthreadd_child",9.000000
-99,"mem.rss.file",3,"kthreadd_child",10.000000
-100,"mem.rss.file",10,"process",1000.000000
-101,"mem.rss.file",10,"process",900.000000
diff --git a/test/trace_processor/parsing/rss_stat_mm_id_clone.out b/test/trace_processor/parsing/rss_stat_mm_id_clone.out
deleted file mode 100644
index 092557d..0000000
--- a/test/trace_processor/parsing/rss_stat_mm_id_clone.out
+++ /dev/null
@@ -1,9 +0,0 @@
-"ts","name","pid","name","value"
-100,"mem.rss.file",3,"kernel_thread",10.000000
-100,"mem.rss.file",10,"parent_process",100.000000
-102,"mem.rss.file",4,"kernel_thread2",20.000000
-102,"mem.rss.file",11,"child_process",90.000000
-104,"mem.rss.file",11,"child_process",10.000000
-105,"mem.rss.file",10,"parent_process",95.000000
-107,"mem.rss.file",10,"parent_process",105.000000
-108,"mem.rss.file",10,"parent_process",110.000000
diff --git a/test/trace_processor/parsing/rss_stat_mm_id_reuse.out b/test/trace_processor/parsing/rss_stat_mm_id_reuse.out
deleted file mode 100644
index 396f5f7..0000000
--- a/test/trace_processor/parsing/rss_stat_mm_id_reuse.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"ts","name","pid","name","value"
-100,"mem.rss.file",10,"parent_process",100.000000
-103,"mem.rss.file",10,"new_process",10.000000
diff --git a/test/trace_processor/parsing/rss_stat_test.sql b/test/trace_processor/parsing/rss_stat_test.sql
deleted file mode 100644
index 0458f0a..0000000
--- a/test/trace_processor/parsing/rss_stat_test.sql
+++ /dev/null
@@ -1,5 +0,0 @@
-SELECT c.ts, t.name, p.pid, p.name, c.value
-FROM counter c
-JOIN process_counter_track t ON c.track_id = t.id
-JOIN process p USING (upid)
-ORDER BY ts, pid
diff --git a/test/trace_processor/parsing/sched_blocked_proto_sched_blocked_reason.out b/test/trace_processor/parsing/sched_blocked_proto_sched_blocked_reason.out
deleted file mode 100644
index 910c51c..0000000
--- a/test/trace_processor/parsing/sched_blocked_proto_sched_blocked_reason.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"ts","tid","io_wait"
-100,1,0
-110,2,1
diff --git a/test/trace_processor/parsing/sched_blocked_reason_function_test.sql b/test/trace_processor/parsing/sched_blocked_reason_function_test.sql
deleted file mode 100644
index ffa1e10..0000000
--- a/test/trace_processor/parsing/sched_blocked_reason_function_test.sql
+++ /dev/null
@@ -1,8 +0,0 @@
-select
-  ts,
-  thread.tid as pid,
-  blocked_function as func
-from thread_state
-join thread USING (utid)
-where state = 'D'
-order by ts
diff --git a/test/trace_processor/parsing/sched_blocked_reason_symbolized_sched_blocked_reason_function.out b/test/trace_processor/parsing/sched_blocked_reason_symbolized_sched_blocked_reason_function.out
deleted file mode 100644
index c6a36fa..0000000
--- a/test/trace_processor/parsing/sched_blocked_reason_symbolized_sched_blocked_reason_function.out
+++ /dev/null
@@ -1,8 +0,0 @@
-"ts","pid","func"
-999000,105,"some_fn"
-999000,102,"filemap_fault"
-1000000,100,"filemap_fault"
-1001000,101,"[NULL]"
-1002000,103,"[NULL]"
-1003000,100,"some_other_fn"
-1005000,104,"filemap_fault"
diff --git a/test/trace_processor/parsing/sched_blocked_reason_test.sql b/test/trace_processor/parsing/sched_blocked_reason_test.sql
deleted file mode 100644
index 1761c97..0000000
--- a/test/trace_processor/parsing/sched_blocked_reason_test.sql
+++ /dev/null
@@ -1,5 +0,0 @@
-select ts, tid, io_wait
-from thread_state
-join thread using (utid)
-where state = 'D'
-order by ts
diff --git a/test/trace_processor/parsing/sched_blocked_systrace_sched_blocked_reason.out b/test/trace_processor/parsing/sched_blocked_systrace_sched_blocked_reason.out
deleted file mode 100644
index 87f718c..0000000
--- a/test/trace_processor/parsing/sched_blocked_systrace_sched_blocked_reason.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"ts","tid","io_wait"
-20258854000,269,0
-21123838000,2172,1
diff --git a/test/trace_processor/parsing/sched_slices_test.sql b/test/trace_processor/parsing/sched_slices_test.sql
deleted file mode 100644
index 15c1ec7..0000000
--- a/test/trace_processor/parsing/sched_slices_test.sql
+++ /dev/null
@@ -1,18 +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 ts, cpu, dur, ts_end, end_state, priority, tid, name
-FROM sched JOIN thread ON sched.utid == thread.utid
-ORDER BY cpu, sched.ts ASC;
diff --git a/test/trace_processor/parsing/sched_smoke_test.sql b/test/trace_processor/parsing/sched_smoke_test.sql
deleted file mode 100644
index 14b26f5..0000000
--- a/test/trace_processor/parsing/sched_smoke_test.sql
+++ /dev/null
@@ -1,2 +0,0 @@
-SELECT COUNT(1)
-FROM sched
\ No newline at end of file
diff --git a/test/trace_processor/parsing/sched_smoke_trailing_empty.out b/test/trace_processor/parsing/sched_smoke_trailing_empty.out
deleted file mode 100644
index 7c28e5e..0000000
--- a/test/trace_processor/parsing/sched_smoke_trailing_empty.out
+++ /dev/null
@@ -1,2 +0,0 @@
-"COUNT(1)"
-2
diff --git a/test/trace_processor/parsing/sched_waking_instants_test.sql b/test/trace_processor/parsing/sched_waking_instants_test.sql
deleted file mode 100644
index 9b5606b..0000000
--- a/test/trace_processor/parsing/sched_waking_instants_test.sql
+++ /dev/null
@@ -1,20 +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 ts, thread.name, thread.tid
-FROM thread_state
-JOIN thread USING (utid)
-WHERE state = 'R'
-ORDER BY ts
diff --git a/test/trace_processor/parsing/sched_waking_raw_compact_sched.out b/test/trace_processor/parsing/sched_waking_raw_compact_sched.out
deleted file mode 100644
index 7067a25..0000000
--- a/test/trace_processor/parsing/sched_waking_raw_compact_sched.out
+++ /dev/null
@@ -1,406 +0,0 @@
-"ts","name","cpu","key","int_value","string_value"
-250978451591891,"sched_waking",0,"comm","[NULL]","rcu_sched"
-250978451591891,"sched_waking",0,"pid",8,"[NULL]"
-250978451591891,"sched_waking",0,"prio",120,"[NULL]"
-250978451591891,"sched_waking",0,"success",1,"[NULL]"
-250978451591891,"sched_waking",0,"target_cpu",0,"[NULL]"
-250978451609131,"sched_waking",0,"comm","[NULL]","kworker/u16:18"
-250978451609131,"sched_waking",0,"pid",17473,"[NULL]"
-250978451609131,"sched_waking",0,"prio",120,"[NULL]"
-250978451609131,"sched_waking",0,"success",1,"[NULL]"
-250978451609131,"sched_waking",0,"target_cpu",0,"[NULL]"
-250978596565656,"sched_waking",0,"comm","[NULL]","kworker/2:0"
-250978596565656,"sched_waking",0,"pid",17438,"[NULL]"
-250978596565656,"sched_waking",0,"prio",120,"[NULL]"
-250978596565656,"sched_waking",0,"success",1,"[NULL]"
-250978596565656,"sched_waking",0,"target_cpu",2,"[NULL]"
-250978600598417,"sched_waking",0,"comm","[NULL]","kworker/2:0"
-250978600598417,"sched_waking",0,"pid",17438,"[NULL]"
-250978600598417,"sched_waking",0,"prio",120,"[NULL]"
-250978600598417,"sched_waking",0,"success",1,"[NULL]"
-250978600598417,"sched_waking",0,"target_cpu",2,"[NULL]"
-250978600639042,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
-250978600639042,"sched_waking",0,"pid",6,"[NULL]"
-250978600639042,"sched_waking",0,"prio",120,"[NULL]"
-250978600639042,"sched_waking",0,"success",1,"[NULL]"
-250978600639042,"sched_waking",0,"target_cpu",0,"[NULL]"
-250978604651907,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
-250978604651907,"sched_waking",0,"pid",6,"[NULL]"
-250978604651907,"sched_waking",0,"prio",120,"[NULL]"
-250978604651907,"sched_waking",0,"success",1,"[NULL]"
-250978604651907,"sched_waking",0,"target_cpu",0,"[NULL]"
-250978604739980,"sched_waking",0,"comm","[NULL]","kworker/2:0"
-250978604739980,"sched_waking",0,"pid",17438,"[NULL]"
-250978604739980,"sched_waking",0,"prio",120,"[NULL]"
-250978604739980,"sched_waking",0,"success",1,"[NULL]"
-250978604739980,"sched_waking",0,"target_cpu",2,"[NULL]"
-250978608903886,"sched_waking",0,"comm","[NULL]","kworker/2:0"
-250978608903886,"sched_waking",0,"pid",17438,"[NULL]"
-250978608903886,"sched_waking",0,"prio",120,"[NULL]"
-250978608903886,"sched_waking",0,"success",1,"[NULL]"
-250978608903886,"sched_waking",0,"target_cpu",2,"[NULL]"
-250978608942220,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
-250978608942220,"sched_waking",0,"pid",6,"[NULL]"
-250978608942220,"sched_waking",0,"prio",120,"[NULL]"
-250978608942220,"sched_waking",0,"success",1,"[NULL]"
-250978608942220,"sched_waking",0,"target_cpu",0,"[NULL]"
-250978612887272,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
-250978612887272,"sched_waking",0,"pid",6,"[NULL]"
-250978612887272,"sched_waking",0,"prio",120,"[NULL]"
-250978612887272,"sched_waking",0,"success",1,"[NULL]"
-250978612887272,"sched_waking",0,"target_cpu",0,"[NULL]"
-250978612971283,"sched_waking",0,"comm","[NULL]","kworker/2:0"
-250978612971283,"sched_waking",0,"pid",17438,"[NULL]"
-250978612971283,"sched_waking",0,"prio",120,"[NULL]"
-250978612971283,"sched_waking",0,"success",1,"[NULL]"
-250978612971283,"sched_waking",0,"target_cpu",2,"[NULL]"
-250978616876595,"sched_waking",0,"comm","[NULL]","kworker/2:0"
-250978616876595,"sched_waking",0,"pid",17438,"[NULL]"
-250978616876595,"sched_waking",0,"prio",120,"[NULL]"
-250978616876595,"sched_waking",0,"success",1,"[NULL]"
-250978616876595,"sched_waking",0,"target_cpu",2,"[NULL]"
-250978616921700,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
-250978616921700,"sched_waking",0,"pid",6,"[NULL]"
-250978616921700,"sched_waking",0,"prio",120,"[NULL]"
-250978616921700,"sched_waking",0,"success",1,"[NULL]"
-250978616921700,"sched_waking",0,"target_cpu",0,"[NULL]"
-250978617016804,"sched_waking",0,"comm","[NULL]","kworker/u16:18"
-250978617016804,"sched_waking",0,"pid",17473,"[NULL]"
-250978617016804,"sched_waking",0,"prio",120,"[NULL]"
-250978617016804,"sched_waking",0,"success",1,"[NULL]"
-250978617016804,"sched_waking",0,"target_cpu",2,"[NULL]"
-250978620859669,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
-250978620859669,"sched_waking",0,"pid",6,"[NULL]"
-250978620859669,"sched_waking",0,"prio",120,"[NULL]"
-250978620859669,"sched_waking",0,"success",1,"[NULL]"
-250978620859669,"sched_waking",0,"target_cpu",0,"[NULL]"
-250978620939148,"sched_waking",0,"comm","[NULL]","kworker/2:0"
-250978620939148,"sched_waking",0,"pid",17438,"[NULL]"
-250978620939148,"sched_waking",0,"prio",120,"[NULL]"
-250978620939148,"sched_waking",0,"success",1,"[NULL]"
-250978620939148,"sched_waking",0,"target_cpu",2,"[NULL]"
-250978624836805,"sched_waking",0,"comm","[NULL]","kworker/2:0"
-250978624836805,"sched_waking",0,"pid",17438,"[NULL]"
-250978624836805,"sched_waking",0,"prio",120,"[NULL]"
-250978624836805,"sched_waking",0,"success",1,"[NULL]"
-250978624836805,"sched_waking",0,"target_cpu",2,"[NULL]"
-250978624875398,"sched_waking",0,"comm","[NULL]","ksoftirqd/0"
-250978624875398,"sched_waking",0,"pid",6,"[NULL]"
-250978624875398,"sched_waking",0,"prio",120,"[NULL]"
-250978624875398,"sched_waking",0,"success",1,"[NULL]"
-250978624875398,"sched_waking",0,"target_cpu",0,"[NULL]"
-250978624978003,"sched_waking",0,"comm","[NULL]","kworker/u16:18"
-250978624978003,"sched_waking",0,"pid",17473,"[NULL]"
-250978624978003,"sched_waking",0,"prio",120,"[NULL]"
-250978624978003,"sched_waking",0,"success",1,"[NULL]"
-250978624978003,"sched_waking",0,"target_cpu",0,"[NULL]"
-250978441664547,"sched_waking",1,"comm","[NULL]","ksoftirqd/1"
-250978441664547,"sched_waking",1,"pid",18,"[NULL]"
-250978441664547,"sched_waking",1,"prio",120,"[NULL]"
-250978441664547,"sched_waking",1,"success",1,"[NULL]"
-250978441664547,"sched_waking",1,"target_cpu",1,"[NULL]"
-250978441702515,"sched_waking",1,"comm","[NULL]","kworker/u16:18"
-250978441702515,"sched_waking",1,"pid",17473,"[NULL]"
-250978441702515,"sched_waking",1,"prio",120,"[NULL]"
-250978441702515,"sched_waking",1,"success",1,"[NULL]"
-250978441702515,"sched_waking",1,"target_cpu",2,"[NULL]"
-250978805144375,"sched_waking",1,"comm","[NULL]","traced_probes"
-250978805144375,"sched_waking",1,"pid",17772,"[NULL]"
-250978805144375,"sched_waking",1,"prio",120,"[NULL]"
-250978805144375,"sched_waking",1,"success",1,"[NULL]"
-250978805144375,"sched_waking",1,"target_cpu",1,"[NULL]"
-250978808058698,"sched_waking",1,"comm","[NULL]","rcuop/0"
-250978808058698,"sched_waking",1,"pid",10,"[NULL]"
-250978808058698,"sched_waking",1,"prio",120,"[NULL]"
-250978808058698,"sched_waking",1,"success",1,"[NULL]"
-250978808058698,"sched_waking",1,"target_cpu",2,"[NULL]"
-250978884120216,"sched_waking",1,"comm","[NULL]","traced"
-250978884120216,"sched_waking",1,"pid",17771,"[NULL]"
-250978884120216,"sched_waking",1,"prio",120,"[NULL]"
-250978884120216,"sched_waking",1,"success",1,"[NULL]"
-250978884120216,"sched_waking",1,"target_cpu",1,"[NULL]"
-250978885260685,"sched_waking",1,"comm","[NULL]","rcuop/0"
-250978885260685,"sched_waking",1,"pid",10,"[NULL]"
-250978885260685,"sched_waking",1,"prio",120,"[NULL]"
-250978885260685,"sched_waking",1,"success",1,"[NULL]"
-250978885260685,"sched_waking",1,"target_cpu",2,"[NULL]"
-250978885786674,"sched_waking",1,"comm","[NULL]","traced_probes"
-250978885786674,"sched_waking",1,"pid",17772,"[NULL]"
-250978885786674,"sched_waking",1,"prio",120,"[NULL]"
-250978885786674,"sched_waking",1,"success",1,"[NULL]"
-250978885786674,"sched_waking",1,"target_cpu",1,"[NULL]"
-250978444958245,"sched_waking",2,"comm","[NULL]","rcuop/0"
-250978444958245,"sched_waking",2,"pid",10,"[NULL]"
-250978444958245,"sched_waking",2,"prio",120,"[NULL]"
-250978444958245,"sched_waking",2,"success",1,"[NULL]"
-250978444958245,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978444974443,"sched_waking",2,"comm","[NULL]","rcuop/1"
-250978444974443,"sched_waking",2,"pid",21,"[NULL]"
-250978444974443,"sched_waking",2,"prio",120,"[NULL]"
-250978444974443,"sched_waking",2,"success",1,"[NULL]"
-250978444974443,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978444984234,"sched_waking",2,"comm","[NULL]","rcu_preempt"
-250978444984234,"sched_waking",2,"pid",7,"[NULL]"
-250978444984234,"sched_waking",2,"prio",120,"[NULL]"
-250978444984234,"sched_waking",2,"success",1,"[NULL]"
-250978444984234,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978451590173,"sched_waking",2,"comm","[NULL]","rcu_preempt"
-250978451590173,"sched_waking",2,"pid",7,"[NULL]"
-250978451590173,"sched_waking",2,"prio",120,"[NULL]"
-250978451590173,"sched_waking",2,"success",1,"[NULL]"
-250978451590173,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978451646579,"sched_waking",2,"comm","[NULL]","rcuos/0"
-250978451646579,"sched_waking",2,"pid",11,"[NULL]"
-250978451646579,"sched_waking",2,"prio",120,"[NULL]"
-250978451646579,"sched_waking",2,"success",1,"[NULL]"
-250978451646579,"sched_waking",2,"target_cpu",4,"[NULL]"
-250978451716891,"sched_waking",2,"comm","[NULL]","rcuos/1"
-250978451716891,"sched_waking",2,"pid",22,"[NULL]"
-250978451716891,"sched_waking",2,"prio",120,"[NULL]"
-250978451716891,"sched_waking",2,"success",1,"[NULL]"
-250978451716891,"sched_waking",2,"target_cpu",1,"[NULL]"
-250978452047048,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
-250978452047048,"sched_waking",2,"pid",17473,"[NULL]"
-250978452047048,"sched_waking",2,"prio",120,"[NULL]"
-250978452047048,"sched_waking",2,"success",1,"[NULL]"
-250978452047048,"sched_waking",2,"target_cpu",0,"[NULL]"
-250978458337725,"sched_waking",2,"comm","[NULL]","sugov:0"
-250978458337725,"sched_waking",2,"pid",625,"[NULL]"
-250978458337725,"sched_waking",2,"prio",49,"[NULL]"
-250978458337725,"sched_waking",2,"success",1,"[NULL]"
-250978458337725,"sched_waking",2,"target_cpu",3,"[NULL]"
-250978458362100,"sched_waking",2,"comm","[NULL]","rcu_preempt"
-250978458362100,"sched_waking",2,"pid",7,"[NULL]"
-250978458362100,"sched_waking",2,"prio",120,"[NULL]"
-250978458362100,"sched_waking",2,"success",1,"[NULL]"
-250978458362100,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978458398455,"sched_waking",2,"comm","[NULL]","rcuop/0"
-250978458398455,"sched_waking",2,"pid",10,"[NULL]"
-250978458398455,"sched_waking",2,"prio",120,"[NULL]"
-250978458398455,"sched_waking",2,"success",1,"[NULL]"
-250978458398455,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978458419496,"sched_waking",2,"comm","[NULL]","rcuop/1"
-250978458419496,"sched_waking",2,"pid",21,"[NULL]"
-250978458419496,"sched_waking",2,"prio",120,"[NULL]"
-250978458419496,"sched_waking",2,"success",1,"[NULL]"
-250978458419496,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978459118038,"sched_waking",2,"comm","[NULL]","sugov:0"
-250978459118038,"sched_waking",2,"pid",625,"[NULL]"
-250978459118038,"sched_waking",2,"prio",49,"[NULL]"
-250978459118038,"sched_waking",2,"success",1,"[NULL]"
-250978459118038,"sched_waking",2,"target_cpu",3,"[NULL]"
-250978593466697,"sched_waking",2,"comm","[NULL]","kworker/2:0"
-250978593466697,"sched_waking",2,"pid",17438,"[NULL]"
-250978593466697,"sched_waking",2,"prio",120,"[NULL]"
-250978593466697,"sched_waking",2,"success",1,"[NULL]"
-250978593466697,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978593734510,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
-250978593734510,"sched_waking",2,"pid",17473,"[NULL]"
-250978593734510,"sched_waking",2,"prio",120,"[NULL]"
-250978593734510,"sched_waking",2,"success",1,"[NULL]"
-250978593734510,"sched_waking",2,"target_cpu",3,"[NULL]"
-250978595521125,"sched_waking",2,"comm","[NULL]","kworker/2:3"
-250978595521125,"sched_waking",2,"pid",17754,"[NULL]"
-250978595521125,"sched_waking",2,"prio",120,"[NULL]"
-250978595521125,"sched_waking",2,"success",1,"[NULL]"
-250978595521125,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978595696645,"sched_waking",2,"comm","[NULL]","kworker/2:0"
-250978595696645,"sched_waking",2,"pid",17438,"[NULL]"
-250978595696645,"sched_waking",2,"prio",120,"[NULL]"
-250978595696645,"sched_waking",2,"success",1,"[NULL]"
-250978595696645,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978596068468,"sched_waking",2,"comm","[NULL]","kworker/2:3"
-250978596068468,"sched_waking",2,"pid",17754,"[NULL]"
-250978596068468,"sched_waking",2,"prio",120,"[NULL]"
-250978596068468,"sched_waking",2,"success",1,"[NULL]"
-250978596068468,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978605185448,"sched_waking",2,"comm","[NULL]","ksoftirqd/2"
-250978605185448,"sched_waking",2,"pid",26,"[NULL]"
-250978605185448,"sched_waking",2,"prio",120,"[NULL]"
-250978605185448,"sched_waking",2,"success",1,"[NULL]"
-250978605185448,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978605260240,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
-250978605260240,"sched_waking",2,"pid",17473,"[NULL]"
-250978605260240,"sched_waking",2,"prio",120,"[NULL]"
-250978605260240,"sched_waking",2,"success",1,"[NULL]"
-250978605260240,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978628278107,"sched_waking",2,"comm","[NULL]","logd.klogd"
-250978628278107,"sched_waking",2,"pid",651,"[NULL]"
-250978628278107,"sched_waking",2,"prio",130,"[NULL]"
-250978628278107,"sched_waking",2,"success",1,"[NULL]"
-250978628278107,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978629289722,"sched_waking",2,"comm","[NULL]","rcuop/2"
-250978629289722,"sched_waking",2,"pid",29,"[NULL]"
-250978629289722,"sched_waking",2,"prio",120,"[NULL]"
-250978629289722,"sched_waking",2,"success",1,"[NULL]"
-250978629289722,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978629405763,"sched_waking",2,"comm","[NULL]","rcu_preempt"
-250978629405763,"sched_waking",2,"pid",7,"[NULL]"
-250978629405763,"sched_waking",2,"prio",120,"[NULL]"
-250978629405763,"sched_waking",2,"success",1,"[NULL]"
-250978629405763,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978635024462,"sched_waking",2,"comm","[NULL]","rcu_preempt"
-250978635024462,"sched_waking",2,"pid",7,"[NULL]"
-250978635024462,"sched_waking",2,"prio",120,"[NULL]"
-250978635024462,"sched_waking",2,"success",1,"[NULL]"
-250978635024462,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978635060191,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
-250978635060191,"sched_waking",2,"pid",17473,"[NULL]"
-250978635060191,"sched_waking",2,"prio",120,"[NULL]"
-250978635060191,"sched_waking",2,"success",1,"[NULL]"
-250978635060191,"sched_waking",2,"target_cpu",0,"[NULL]"
-250978635219514,"sched_waking",2,"comm","[NULL]","rcuop/2"
-250978635219514,"sched_waking",2,"pid",29,"[NULL]"
-250978635219514,"sched_waking",2,"prio",120,"[NULL]"
-250978635219514,"sched_waking",2,"success",1,"[NULL]"
-250978635219514,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978683441081,"sched_waking",2,"comm","[NULL]","kworker/2:0"
-250978683441081,"sched_waking",2,"pid",17438,"[NULL]"
-250978683441081,"sched_waking",2,"prio",120,"[NULL]"
-250978683441081,"sched_waking",2,"success",1,"[NULL]"
-250978683441081,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978683732331,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
-250978683732331,"sched_waking",2,"pid",17473,"[NULL]"
-250978683732331,"sched_waking",2,"prio",120,"[NULL]"
-250978683732331,"sched_waking",2,"success",1,"[NULL]"
-250978683732331,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978685694259,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
-250978685694259,"sched_waking",2,"pid",17473,"[NULL]"
-250978685694259,"sched_waking",2,"prio",120,"[NULL]"
-250978685694259,"sched_waking",2,"success",1,"[NULL]"
-250978685694259,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978740127441,"sched_waking",2,"comm","[NULL]","kworker/2:0"
-250978740127441,"sched_waking",2,"pid",17438,"[NULL]"
-250978740127441,"sched_waking",2,"prio",120,"[NULL]"
-250978740127441,"sched_waking",2,"success",1,"[NULL]"
-250978740127441,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978740425410,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
-250978740425410,"sched_waking",2,"pid",17473,"[NULL]"
-250978740425410,"sched_waking",2,"prio",120,"[NULL]"
-250978740425410,"sched_waking",2,"success",1,"[NULL]"
-250978740425410,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978743210358,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
-250978743210358,"sched_waking",2,"pid",17473,"[NULL]"
-250978743210358,"sched_waking",2,"prio",120,"[NULL]"
-250978743210358,"sched_waking",2,"success",1,"[NULL]"
-250978743210358,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978744945567,"sched_waking",2,"comm","[NULL]","logd.klogd"
-250978744945567,"sched_waking",2,"pid",651,"[NULL]"
-250978744945567,"sched_waking",2,"prio",130,"[NULL]"
-250978744945567,"sched_waking",2,"success",1,"[NULL]"
-250978744945567,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978746028431,"sched_waking",2,"comm","[NULL]","rcuop/2"
-250978746028431,"sched_waking",2,"pid",29,"[NULL]"
-250978746028431,"sched_waking",2,"prio",120,"[NULL]"
-250978746028431,"sched_waking",2,"success",1,"[NULL]"
-250978746028431,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978746173171,"sched_waking",2,"comm","[NULL]","rcu_preempt"
-250978746173171,"sched_waking",2,"pid",7,"[NULL]"
-250978746173171,"sched_waking",2,"prio",120,"[NULL]"
-250978746173171,"sched_waking",2,"success",1,"[NULL]"
-250978746173171,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978751622494,"sched_waking",2,"comm","[NULL]","rcu_preempt"
-250978751622494,"sched_waking",2,"pid",7,"[NULL]"
-250978751622494,"sched_waking",2,"prio",120,"[NULL]"
-250978751622494,"sched_waking",2,"success",1,"[NULL]"
-250978751622494,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978751661348,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
-250978751661348,"sched_waking",2,"pid",17473,"[NULL]"
-250978751661348,"sched_waking",2,"prio",120,"[NULL]"
-250978751661348,"sched_waking",2,"success",1,"[NULL]"
-250978751661348,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978751792859,"sched_waking",2,"comm","[NULL]","rcuop/2"
-250978751792859,"sched_waking",2,"pid",29,"[NULL]"
-250978751792859,"sched_waking",2,"prio",120,"[NULL]"
-250978751792859,"sched_waking",2,"success",1,"[NULL]"
-250978751792859,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978751971817,"sched_waking",2,"comm","[NULL]","rcu_preempt"
-250978751971817,"sched_waking",2,"pid",7,"[NULL]"
-250978751971817,"sched_waking",2,"prio",120,"[NULL]"
-250978751971817,"sched_waking",2,"success",1,"[NULL]"
-250978751971817,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978758784578,"sched_waking",2,"comm","[NULL]","rcu_preempt"
-250978758784578,"sched_waking",2,"pid",7,"[NULL]"
-250978758784578,"sched_waking",2,"prio",120,"[NULL]"
-250978758784578,"sched_waking",2,"success",1,"[NULL]"
-250978758784578,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978758873745,"sched_waking",2,"comm","[NULL]","rcuop/2"
-250978758873745,"sched_waking",2,"pid",29,"[NULL]"
-250978758873745,"sched_waking",2,"prio",120,"[NULL]"
-250978758873745,"sched_waking",2,"success",1,"[NULL]"
-250978758873745,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978808519271,"sched_waking",2,"comm","[NULL]","rcu_preempt"
-250978808519271,"sched_waking",2,"pid",7,"[NULL]"
-250978808519271,"sched_waking",2,"prio",120,"[NULL]"
-250978808519271,"sched_waking",2,"success",1,"[NULL]"
-250978808519271,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978815488490,"sched_waking",2,"comm","[NULL]","rcu_preempt"
-250978815488490,"sched_waking",2,"pid",7,"[NULL]"
-250978815488490,"sched_waking",2,"prio",120,"[NULL]"
-250978815488490,"sched_waking",2,"success",1,"[NULL]"
-250978815488490,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978815535678,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
-250978815535678,"sched_waking",2,"pid",17473,"[NULL]"
-250978815535678,"sched_waking",2,"prio",120,"[NULL]"
-250978815535678,"sched_waking",2,"success",1,"[NULL]"
-250978815535678,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978815675782,"sched_waking",2,"comm","[NULL]","rcuop/0"
-250978815675782,"sched_waking",2,"pid",10,"[NULL]"
-250978815675782,"sched_waking",2,"prio",120,"[NULL]"
-250978815675782,"sched_waking",2,"success",1,"[NULL]"
-250978815675782,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978815738022,"sched_waking",2,"comm","[NULL]","rcuop/1"
-250978815738022,"sched_waking",2,"pid",21,"[NULL]"
-250978815738022,"sched_waking",2,"prio",120,"[NULL]"
-250978815738022,"sched_waking",2,"success",1,"[NULL]"
-250978815738022,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978860203182,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
-250978860203182,"sched_waking",2,"pid",17473,"[NULL]"
-250978860203182,"sched_waking",2,"prio",120,"[NULL]"
-250978860203182,"sched_waking",2,"success",1,"[NULL]"
-250978860203182,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978880151205,"sched_waking",2,"comm","[NULL]","kworker/2:0"
-250978880151205,"sched_waking",2,"pid",17438,"[NULL]"
-250978880151205,"sched_waking",2,"prio",120,"[NULL]"
-250978880151205,"sched_waking",2,"success",1,"[NULL]"
-250978880151205,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978880432143,"sched_waking",2,"comm","[NULL]","kworker/u16:18"
-250978880432143,"sched_waking",2,"pid",17473,"[NULL]"
-250978880432143,"sched_waking",2,"prio",120,"[NULL]"
-250978880432143,"sched_waking",2,"success",1,"[NULL]"
-250978880432143,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978885784018,"sched_waking",2,"comm","[NULL]","rcu_preempt"
-250978885784018,"sched_waking",2,"pid",7,"[NULL]"
-250978885784018,"sched_waking",2,"prio",120,"[NULL]"
-250978885784018,"sched_waking",2,"success",1,"[NULL]"
-250978885784018,"sched_waking",2,"target_cpu",2,"[NULL]"
-250978465172309,"sched_waking",3,"comm","[NULL]","ksoftirqd/3"
-250978465172309,"sched_waking",3,"pid",34,"[NULL]"
-250978465172309,"sched_waking",3,"prio",120,"[NULL]"
-250978465172309,"sched_waking",3,"success",1,"[NULL]"
-250978465172309,"sched_waking",3,"target_cpu",3,"[NULL]"
-250978465266789,"sched_waking",3,"comm","[NULL]","kworker/u16:18"
-250978465266789,"sched_waking",3,"pid",17473,"[NULL]"
-250978465266789,"sched_waking",3,"prio",120,"[NULL]"
-250978465266789,"sched_waking",3,"success",1,"[NULL]"
-250978465266789,"sched_waking",3,"target_cpu",0,"[NULL]"
-250978439491994,"sched_waking",4,"comm","[NULL]","rcu_sched"
-250978439491994,"sched_waking",4,"pid",8,"[NULL]"
-250978439491994,"sched_waking",4,"prio",120,"[NULL]"
-250978439491994,"sched_waking",4,"success",1,"[NULL]"
-250978439491994,"sched_waking",4,"target_cpu",1,"[NULL]"
-250978445783141,"sched_waking",5,"comm","[NULL]","rcu_sched"
-250978445783141,"sched_waking",5,"pid",8,"[NULL]"
-250978445783141,"sched_waking",5,"prio",120,"[NULL]"
-250978445783141,"sched_waking",5,"success",1,"[NULL]"
-250978445783141,"sched_waking",5,"target_cpu",5,"[NULL]"
-250978806359896,"sched_waking",5,"comm","[NULL]","kworker/5:1"
-250978806359896,"sched_waking",5,"pid",17667,"[NULL]"
-250978806359896,"sched_waking",5,"prio",120,"[NULL]"
-250978806359896,"sched_waking",5,"success",1,"[NULL]"
-250978806359896,"sched_waking",5,"target_cpu",5,"[NULL]"
-250978806428646,"sched_waking",5,"comm","[NULL]","kworker/u16:18"
-250978806428646,"sched_waking",5,"pid",17473,"[NULL]"
-250978806428646,"sched_waking",5,"prio",120,"[NULL]"
-250978806428646,"sched_waking",5,"success",1,"[NULL]"
-250978806428646,"sched_waking",5,"target_cpu",2,"[NULL]"
diff --git a/test/trace_processor/parsing/sched_waking_raw_test.sql b/test/trace_processor/parsing/sched_waking_raw_test.sql
deleted file mode 100644
index 0c6a755..0000000
--- a/test/trace_processor/parsing/sched_waking_raw_test.sql
+++ /dev/null
@@ -1,16 +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 ts, name, cpu, key, int_value, string_value FROM raw JOIN args ON raw.arg_set_id == args.arg_set_id WHERE name == "sched_waking" ORDER BY cpu ASC, ts ASC;
diff --git a/test/trace_processor/parsing/slices_test.sql b/test/trace_processor/parsing/slices_test.sql
deleted file mode 100644
index d41bd3b..0000000
--- a/test/trace_processor/parsing/slices_test.sql
+++ /dev/null
@@ -1,17 +0,0 @@
---
--- 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 ts, dur, name from slice order by ts desc;
-
diff --git a/test/trace_processor/parsing/stats_test.sql b/test/trace_processor/parsing/stats_test.sql
deleted file mode 100644
index 02316b2..0000000
--- a/test/trace_processor/parsing/stats_test.sql
+++ /dev/null
@@ -1,17 +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 name, idx, severity, source, value
-from stats where name GLOB 'ftrace_cpu_*' or name GLOB 'traced_buf_*';
\ No newline at end of file
diff --git a/test/trace_processor/parsing/sys_syscall.out b/test/trace_processor/parsing/sys_syscall.out
deleted file mode 100644
index d781668..0000000
--- a/test/trace_processor/parsing/sys_syscall.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"ts","dur","name"
-100,6,"sys_io_setup"
-105,5,"sys_io_destroy"
diff --git a/test/trace_processor/parsing/sys_test.sql b/test/trace_processor/parsing/sys_test.sql
deleted file mode 100644
index 7d42be3..0000000
--- a/test/trace_processor/parsing/sys_test.sql
+++ /dev/null
@@ -1,18 +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 ts, dur, name
-from slices
-limit 10
diff --git a/test/trace_processor/parsing/systrace_html.out b/test/trace_processor/parsing/systrace_html.out
deleted file mode 100644
index a37df78..0000000
--- a/test/trace_processor/parsing/systrace_html.out
+++ /dev/null
@@ -1,3700 +0,0 @@
-"ts","cpu","dur","ts_end","utid","end_state","priority","upid","name","tid"
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824711826997000,1,21000,6824711827018000,18,"S",120,18,"rcuos/1",21
-6824711827018000,1,27430000,6824711854448000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824711827085000,2,1808000,6824711828893000,0,"R",120,0,"swapper",0
-6824711827100000,6,4000,6824711827104000,702,"S",120,702,"kworker/u16:7",19422
-6824711827104000,6,9000,6824711827113000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824711827144000,6,1680000,6824711828824000,0,"R",120,0,"swapper",0
-6824711827183000,4,899199000,6824712726382000,0,"R",120,0,"swapper",0
-6824711827287000,3,999000,6824711828286000,2731,"I",120,760,"sh",20447
-6824711828286000,3,281000,6824711828567000,2732,"I",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,0,"swapper",0
-6824711828867000,6,19000,6824711828886000,669,"S",120,669,"kworker/6:1",14833
-6824711828886000,6,64000,6824711828950000,0,"R",120,0,"swapper",0
-6824711828893000,2,28000,6824711828921000,2487,"S",120,739,"UsbFfs-worker",20308
-6824711828921000,2,72000,6824711828993000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824711829019000,2,208000,6824711829227000,0,"R",120,0,"swapper",0
-6824711829100000,6,34000,6824711829134000,630,"S",100,630,"kworker/u17:1",1134
-6824711829134000,6,38000,6824711829172000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824711829437000,6,7000,6824711829444000,630,"S",100,630,"kworker/u17:1",1134
-6824711829444000,6,452000,6824711829896000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824711829896000,6,16000,6824711829912000,669,"S",120,669,"kworker/6:1",14833
-6824711829912000,6,3511000,6824711833423000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824711834507000,5,6564000,6824711841071000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824711841098000,6,3082000,6824711844180000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824711844275000,6,572000,6824711844847000,0,"R",120,0,"swapper",0
-6824711844547000,3,172000,6824711844719000,77,"S",120,77,"smem_native_rpm",87
-6824711844719000,3,2190000,6824711846909000,0,"R",120,0,"swapper",0
-6824711844847000,6,13000,6824711844860000,702,"S",120,702,"kworker/u16:7",19422
-6824711844860000,6,1757000,6824711846617000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824711846815000,6,7290000,6824711854105000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824711847350000,2,8611000,6824711855961000,0,"R",120,0,"swapper",0
-6824711850532000,3,50000,6824711850582000,29,"S",120,29,"ksoftirqd/3",33
-6824711850582000,3,24018000,6824711874600000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824711854169000,0,120000,6824711854289000,743,"S",120,743,"kworker/0:5",20371
-6824711854289000,0,462000,6824711854751000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824711855078000,0,471000,6824711855549000,0,"R",120,0,"swapper",0
-6824711855178000,1,179000,6824711855357000,771,"S",97,493,"DispSync",676
-6824711855357000,1,545000,6824711855902000,0,"R",120,0,"swapper",0
-6824711855549000,0,272000,6824711855821000,773,"S",97,493,"app",678
-6824711855821000,0,1794000,6824711857615000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824711857615000,0,250000,6824711857865000,770,"S",120,493,"Binder:640_2",675
-6824711857673000,1,347000,6824711858020000,0,"R",120,0,"swapper",0
-6824711857865000,0,426000,6824711858291000,0,"R",120,0,"swapper",0
-6824711858020000,1,151000,6824711858171000,773,"S",97,493,"app",678
-6824711858171000,1,29595000,6824711887766000,0,"R",120,0,"swapper",0
-6824711858291000,0,79000,6824711858370000,771,"S",97,493,"DispSync",676
-6824711858370000,0,1693000,6824711860063000,0,"R",120,0,"swapper",0
-6824711860063000,0,48000,6824711860111000,3,"S",120,3,"ksoftirqd/0",3
-6824711860111000,0,5836000,6824711865947000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824711861384000,2,457000,6824711861841000,45,"S",120,45,"rcuop/5",52
-6824711861841000,2,27907000,6824711889748000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824711867021000,0,47000,6824711867068000,3,"S",120,3,"ksoftirqd/0",3
-6824711867068000,0,702000,6824711867770000,0,"R",120,0,"swapper",0
-6824711867770000,0,70000,6824711867840000,5,"S",120,5,"rcu_preempt",7
-6824711867840000,0,4623000,6824711872463000,0,"R",120,0,"swapper",0
-6824711872463000,0,52000,6824711872515000,3,"S",120,3,"ksoftirqd/0",3
-6824711872515000,0,868000,6824711873383000,0,"R",120,0,"swapper",0
-6824711873308000,6,68000,6824711873376000,702,"D",120,702,"kworker/u16:7",19422
-6824711873376000,6,665000,6824711874041000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824711874041000,6,477000,6824711874518000,702,"D",120,702,"kworker/u16:7",19422
-6824711874518000,6,672000,6824711875190000,0,"R",120,0,"swapper",0
-6824711874600000,3,138000,6824711874738000,77,"S",120,77,"smem_native_rpm",87
-6824711874738000,3,920000,6824711875658000,0,"R",120,0,"swapper",0
-6824711875190000,6,207000,6824711875397000,702,"D",120,702,"kworker/u16:7",19422
-6824711875397000,6,667000,6824711876064000,0,"R",120,0,"swapper",0
-6824711875658000,3,86000,6824711875744000,77,"S",120,77,"smem_native_rpm",87
-6824711875744000,3,729000,6824711876473000,0,"R",120,0,"swapper",0
-6824711876064000,6,202000,6824711876266000,702,"D",120,702,"kworker/u16:7",19422
-6824711876266000,6,110000,6824711876376000,0,"R",120,0,"swapper",0
-6824711876376000,6,96000,6824711876472000,702,"D",120,702,"kworker/u16:7",19422
-6824711876472000,6,704000,6824711877176000,0,"R",120,0,"swapper",0
-6824711876473000,3,71000,6824711876544000,77,"S",120,77,"smem_native_rpm",87
-6824711876544000,3,783000,6824711877327000,0,"R",120,0,"swapper",0
-6824711877176000,6,125000,6824711877301000,702,"D",120,702,"kworker/u16:7",19422
-6824711877301000,6,678000,6824711877979000,0,"R",120,0,"swapper",0
-6824711877327000,3,143000,6824711877470000,77,"S",120,77,"smem_native_rpm",87
-6824711877470000,3,716000,6824711878186000,0,"R",120,0,"swapper",0
-6824711877979000,6,198000,6824711878177000,702,"D",120,702,"kworker/u16:7",19422
-6824711878177000,6,472000,6824711878649000,0,"R",120,0,"swapper",0
-6824711878186000,3,193000,6824711878379000,77,"S",120,77,"smem_native_rpm",87
-6824711878379000,3,469000,6824711878848000,0,"R",120,0,"swapper",0
-6824711878649000,6,182000,6824711878831000,702,"D",120,702,"kworker/u16:7",19422
-6824711878831000,6,446000,6824711879277000,0,"R",120,0,"swapper",0
-6824711878848000,3,140000,6824711878988000,77,"S",120,77,"smem_native_rpm",87
-6824711878988000,3,666000,6824711879654000,0,"R",120,0,"swapper",0
-6824711879277000,6,161000,6824711879438000,702,"D",120,702,"kworker/u16:7",19422
-6824711879438000,6,112000,6824711879550000,0,"R",120,0,"swapper",0
-6824711879550000,6,82000,6824711879632000,702,"D",120,702,"kworker/u16:7",19422
-6824711879632000,6,169000,6824711879801000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824711879993000,6,231000,6824711880224000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824711880298000,3,94000,6824711880392000,0,"R",120,0,"swapper",0
-6824711880367000,6,186000,6824711880553000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824711880611000,6,5024000,6824711885635000,0,"R",120,0,"swapper",0
-6824711885635000,6,44000,6824711885679000,50,"S",120,50,"ksoftirqd/6",57
-6824711885679000,6,8649000,6824711894328000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824711887462000,0,118000,6824711887580000,743,"S",120,743,"kworker/0:5",20371
-6824711887580000,0,455000,6824711888035000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824711888231000,1,85000,6824711888316000,771,"S",97,493,"DispSync",676
-6824711888316000,1,979000,6824711889295000,0,"R",120,0,"swapper",0
-6824711888362000,0,604000,6824711888966000,0,"R",120,0,"swapper",0
-6824711888966000,0,178000,6824711889144000,771,"S",97,493,"DispSync",676
-6824711889144000,0,532000,6824711889676000,0,"R",120,0,"swapper",0
-6824711889295000,1,275000,6824711889570000,773,"S",97,493,"app",678
-6824711889570000,1,1823000,6824711891393000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824711891393000,1,280000,6824711891673000,770,"S",120,493,"Binder:640_2",675
-6824711891429000,0,393000,6824711891822000,0,"R",120,0,"swapper",0
-6824711891673000,1,472000,6824711892145000,0,"R",120,0,"swapper",0
-6824711891822000,0,178000,6824711892000000,773,"S",97,493,"app",678
-6824711892000000,0,1451000,6824711893451000,0,"R",120,0,"swapper",0
-6824711892145000,1,95000,6824711892240000,771,"S",97,493,"DispSync",676
-6824711892240000,1,12180000,6824711904420000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824711894328000,6,300000,6824711894628000,702,"D",120,702,"kworker/u16:7",19422
-6824711894628000,6,2368000,6824711896996000,0,"R",120,0,"swapper",0
-6824711894870000,3,77000,6824711894947000,77,"S",120,77,"smem_native_rpm",87
-6824711894947000,3,4289000,6824711899236000,0,"R",120,0,"swapper",0
-6824711896996000,6,306000,6824711897302000,702,"D",120,702,"kworker/u16:7",19422
-6824711897302000,6,48000,6824711897350000,0,"R",120,0,"swapper",0
-6824711897350000,6,43000,6824711897393000,50,"S",120,50,"ksoftirqd/6",57
-6824711897393000,6,3585000,6824711900978000,0,"R",120,0,"swapper",0
-6824711899236000,3,257000,6824711899493000,77,"S",120,77,"smem_native_rpm",87
-6824711899493000,3,4694000,6824711904187000,0,"R",120,0,"swapper",0
-6824711900978000,6,122000,6824711901100000,702,"S",120,702,"kworker/u16:7",19422
-6824711901100000,6,50000,6824711901150000,0,"R",120,0,"swapper",0
-6824711901150000,6,40000,6824711901190000,50,"S",120,50,"ksoftirqd/6",57
-6824711901190000,6,826872000,6824712728062000,0,"R",120,0,"swapper",0
-6824711904187000,3,212000,6824711904399000,77,"S",120,77,"smem_native_rpm",87
-6824711904399000,3,55334000,6824711959733000,0,"R",120,0,"swapper",0
-6824711904420000,1,72000,6824711904492000,15,"S",120,15,"ksoftirqd/1",17
-6824711904492000,1,14769000,6824711919261000,0,"R",120,0,"swapper",0
-6824711919261000,1,145000,6824711919406000,15,"S",120,15,"ksoftirqd/1",17
-6824711919406000,1,3219000,6824711922625000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824711922383000,0,57000,6824711922440000,3,"S",120,3,"ksoftirqd/0",3
-6824711922440000,0,585000,6824711923025000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824711923264000,0,1073000,6824711924337000,0,"R",120,0,"swapper",0
-6824711923686000,1,409000,6824711924095000,773,"S",97,493,"app",678
-6824711924095000,1,2591000,6824711926686000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824711926686000,1,341000,6824711927027000,770,"S",120,493,"Binder:640_2",675
-6824711926753000,0,601000,6824711927354000,0,"R",120,0,"swapper",0
-6824711927027000,1,795000,6824711927822000,0,"R",120,0,"swapper",0
-6824711927354000,0,186000,6824711927540000,773,"S",97,493,"app",678
-6824711927540000,0,30747000,6824711958287000,0,"R",120,0,"swapper",0
-6824711927822000,1,86000,6824711927908000,771,"S",97,493,"DispSync",676
-6824711927908000,1,30404000,6824711958312000,0,"R",120,0,"swapper",0
-6824711935090000,5,59000,6824711935149000,113,"S",120,113,"kswapd0",150
-6824711935149000,5,791709000,6824712726858000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824711960206000,2,86000,6824711960292000,771,"S",97,493,"DispSync",676
-6824711960292000,2,31867000,6824711992159000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824711963017000,0,225000,6824711963242000,77,"S",120,77,"smem_native_rpm",87
-6824711963242000,0,720000,6824711963962000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824711963962000,0,93000,6824711964055000,771,"S",97,493,"DispSync",676
-6824711964055000,0,26981000,6824711991036000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824711991963000,1,86000,6824711992049000,15,"S",120,15,"ksoftirqd/1",17
-6824711992049000,1,1109000,6824711993158000,0,"R",120,0,"swapper",0
-6824711992159000,2,539000,6824711992698000,773,"S",97,493,"app",678
-6824711992203000,0,811000,6824711993014000,0,"R",120,0,"swapper",0
-6824711992457000,3,561000,6824711993018000,777,"S",120,493,"HwBinder:640_1",721
-6824711992698000,2,31632000,6824712024330000,0,"R",120,0,"swapper",0
-6824711993014000,0,3047000,6824711996061000,644,"S",120,644,"ndroid.systemui",1664
-6824711993018000,3,29607000,6824712022625000,0,"R",120,0,"swapper",0
-6824711993158000,1,88000,6824711993246000,771,"S",97,493,"DispSync",676
-6824711993246000,1,2846000,6824711996092000,0,"R",120,0,"swapper",0
-6824711996061000,0,723000,6824711996784000,0,"R",120,0,"swapper",0
-6824711996092000,1,399000,6824711996491000,770,"S",120,493,"Binder:640_2",675
-6824711996491000,1,795000,6824711997286000,0,"R",120,0,"swapper",0
-6824711996784000,0,206000,6824711996990000,773,"S",97,493,"app",678
-6824711996990000,0,72000,6824711997062000,0,"R",120,0,"swapper",0
-6824711997062000,0,65000,6824711997127000,3,"S",120,3,"ksoftirqd/0",3
-6824711997127000,0,25255000,6824712022382000,0,"R",120,0,"swapper",0
-6824711997286000,1,89000,6824711997375000,771,"S",97,493,"DispSync",676
-6824711997375000,1,26567000,6824712023942000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712024330000,2,324000,6824712024654000,771,"S",97,493,"DispSync",676
-6824712024581000,1,1190000,6824712025771000,0,"R",120,0,"swapper",0
-6824712024654000,2,1208000,6824712025862000,0,"R",120,0,"swapper",0
-6824712024951000,0,531000,6824712025482000,773,"S",97,493,"app",678
-6824712025482000,0,3497000,6824712028979000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712028934000,1,717000,6824712029651000,0,"R",120,0,"swapper",0
-6824712028979000,0,393000,6824712029372000,770,"S",120,493,"Binder:640_2",675
-6824712029372000,0,798000,6824712030170000,0,"R",120,0,"swapper",0
-6824712029651000,1,204000,6824712029855000,773,"S",97,493,"app",678
-6824712029855000,1,29454000,6824712059309000,0,"R",120,0,"swapper",0
-6824712030170000,0,89000,6824712030259000,771,"S",97,493,"DispSync",676
-6824712030259000,0,70000,6824712030329000,0,"R",120,0,"swapper",0
-6824712030329000,0,70000,6824712030399000,3,"S",120,3,"ksoftirqd/0",3
-6824712030399000,0,28593000,6824712058992000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712060406000,3,546000,6824712060952000,777,"S",120,493,"HwBinder:640_1",721
-6824712060637000,2,31702000,6824712092339000,0,"R",120,0,"swapper",0
-6824712060952000,3,31540000,6824712092492000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712064440000,1,710000,6824712065150000,0,"R",120,0,"swapper",0
-6824712064472000,0,400000,6824712064872000,770,"S",120,493,"Binder:640_2",675
-6824712064872000,0,780000,6824712065652000,0,"R",120,0,"swapper",0
-6824712065150000,1,201000,6824712065351000,773,"S",97,493,"app",678
-6824712065351000,1,10437000,6824712075788000,0,"R",120,0,"swapper",0
-6824712065652000,0,89000,6824712065741000,771,"S",97,493,"DispSync",676
-6824712065741000,0,24857000,6824712090598000,0,"R",120,0,"swapper",0
-6824712075788000,1,292000,6824712076080000,1919,"S",120,667,"Executor-7",14762
-6824712076080000,1,88000,6824712076168000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712092146000,1,1144000,6824712093290000,0,"R",120,0,"swapper",0
-6824712092233000,0,79000,6824712092312000,3,"S",120,3,"ksoftirqd/0",3
-6824712092312000,0,820000,6824712093132000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712092979000,3,33505000,6824712126484000,0,"R",120,0,"swapper",0
-6824712093132000,0,90000,6824712093222000,771,"S",97,493,"DispSync",676
-6824712093222000,0,3125000,6824712096347000,0,"R",120,0,"swapper",0
-6824712093290000,1,3028000,6824712096318000,644,"S",120,644,"ndroid.systemui",1664
-6824712096318000,1,443000,6824712096761000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712096969000,1,28547000,6824712125516000,0,"R",120,0,"swapper",0
-6824712097293000,2,94000,6824712097387000,771,"S",97,493,"DispSync",676
-6824712097387000,2,27295000,6824712124682000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712126484000,3,712000,6824712127196000,777,"S",120,493,"HwBinder:640_1",721
-6824712127196000,3,32061000,6824712159257000,0,"R",120,0,"swapper",0
-6824712127359000,0,84000,6824712127443000,771,"S",97,493,"DispSync",676
-6824712127443000,0,1769000,6824712129212000,0,"R",120,0,"swapper",0
-6824712129212000,0,299000,6824712129511000,770,"S",120,493,"Binder:640_2",675
-6824712129255000,2,29843000,6824712159098000,0,"R",120,0,"swapper",0
-6824712129511000,0,530000,6824712130041000,0,"R",120,0,"swapper",0
-6824712129680000,1,177000,6824712129857000,773,"S",97,493,"app",678
-6824712129857000,1,28493000,6824712158350000,0,"R",120,0,"swapper",0
-6824712130041000,0,88000,6824712130129000,771,"S",97,493,"DispSync",676
-6824712130129000,0,64000,6824712130193000,0,"R",120,0,"swapper",0
-6824712130193000,0,56000,6824712130249000,3,"S",120,3,"ksoftirqd/0",3
-6824712130249000,0,496000,6824712130745000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712158889000,1,87000,6824712158976000,15,"S",120,15,"ksoftirqd/1",17
-6824712158976000,1,981000,6824712159957000,0,"R",120,0,"swapper",0
-6824712159015000,0,478000,6824712159493000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712159816000,3,33271000,6824712193087000,0,"R",120,0,"swapper",0
-6824712159957000,1,87000,6824712160044000,771,"S",97,493,"DispSync",676
-6824712160044000,1,3767000,6824712163811000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712163811000,1,405000,6824712164216000,770,"S",120,493,"Binder:640_2",675
-6824712164216000,1,792000,6824712165008000,0,"R",120,0,"swapper",0
-6824712164501000,0,203000,6824712164704000,773,"S",97,493,"app",678
-6824712164704000,0,26926000,6824712191630000,0,"R",120,0,"swapper",0
-6824712165008000,1,88000,6824712165096000,771,"S",97,493,"DispSync",676
-6824712165096000,1,26545000,6824712191641000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712193513000,0,1329000,6824712194842000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712194663000,1,2149000,6824712196812000,0,"R",120,0,"swapper",0
-6824712194842000,0,89000,6824712194931000,771,"S",97,493,"DispSync",676
-6824712194931000,0,1217000,6824712196148000,0,"R",120,0,"swapper",0
-6824712196116000,2,30414000,6824712226530000,0,"R",120,0,"swapper",0
-6824712196148000,0,385000,6824712196533000,770,"S",120,493,"Binder:640_2",675
-6824712196533000,0,781000,6824712197314000,0,"R",120,0,"swapper",0
-6824712196812000,1,200000,6824712197012000,773,"S",97,493,"app",678
-6824712197012000,1,28707000,6824712225719000,0,"R",120,0,"swapper",0
-6824712197314000,0,88000,6824712197402000,771,"S",97,493,"DispSync",676
-6824712197402000,0,28073000,6824712225475000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712226321000,1,87000,6824712226408000,15,"S",120,15,"ksoftirqd/1",17
-6824712226408000,1,1183000,6824712227591000,0,"R",120,0,"swapper",0
-6824712226530000,2,669000,6824712227199000,773,"S",97,493,"app",678
-6824712226595000,0,894000,6824712227489000,0,"R",120,0,"swapper",0
-6824712226848000,3,546000,6824712227394000,777,"S",120,493,"HwBinder:640_1",721
-6824712227199000,2,32486000,6824712259685000,0,"R",120,0,"swapper",0
-6824712227394000,3,101387000,6824712328781000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712230650000,0,717000,6824712231367000,0,"R",120,0,"swapper",0
-6824712230693000,1,394000,6824712231087000,770,"S",120,493,"Binder:640_2",675
-6824712231087000,1,782000,6824712231869000,0,"R",120,0,"swapper",0
-6824712231367000,0,200000,6824712231567000,773,"S",97,493,"app",678
-6824712231567000,0,13149000,6824712244716000,0,"R",120,0,"swapper",0
-6824712231869000,1,87000,6824712231956000,771,"S",97,493,"DispSync",676
-6824712231956000,1,12891000,6824712244847000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712245040000,1,14256000,6824712259296000,0,"R",120,0,"swapper",0
-6824712245127000,0,258000,6824712245385000,3,"S",120,3,"ksoftirqd/0",3
-6824712245385000,0,12652000,6824712258037000,0,"R",120,0,"swapper",0
-6824712247262000,7,203000,6824712247465000,711,"S",120,711,"kworker/u16:2",19725
-6824712247465000,7,71000,6824712247536000,0,"R",120,0,"swapper",0
-6824712247536000,7,62000,6824712247598000,57,"S",120,57,"ksoftirqd/7",65
-6824712247598000,7,14402000,6824712262000000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712259478000,0,86000,6824712259564000,3,"S",120,3,"ksoftirqd/0",3
-6824712259564000,0,904000,6824712260468000,0,"R",120,0,"swapper",0
-6824712259685000,2,648000,6824712260333000,771,"S",97,493,"DispSync",676
-6824712259922000,1,1420000,6824712261342000,0,"R",120,0,"swapper",0
-6824712260333000,2,128000,6824712260461000,694,"S",120,694,"kworker/2:0",18823
-6824712260461000,2,979000,6824712261440000,0,"R",120,0,"swapper",0
-6824712260468000,0,523000,6824712260991000,773,"S",97,493,"app",678
-6824712260991000,0,3489000,6824712264480000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712262000000,7,773000,6824712262773000,711,"S",120,711,"kworker/u16:2",19725
-6824712262773000,7,467225000,6824712729998000,0,"R",120,0,"swapper",0
-6824712264416000,1,745000,6824712265161000,0,"R",120,0,"swapper",0
-6824712264480000,0,399000,6824712264879000,770,"S",120,493,"Binder:640_2",675
-6824712264879000,0,791000,6824712265670000,0,"R",120,0,"swapper",0
-6824712265161000,1,209000,6824712265370000,773,"S",97,493,"app",678
-6824712265370000,1,26002000,6824712291372000,0,"R",120,0,"swapper",0
-6824712265670000,0,96000,6824712265766000,771,"S",97,493,"DispSync",676
-6824712265766000,0,19023000,6824712284789000,0,"R",120,0,"swapper",0
-6824712284789000,0,487000,6824712285276000,1694,"D",100,660,"thermal-engine",2490
-6824712285276000,0,89000,6824712285365000,0,"R",120,0,"swapper",0
-6824712285365000,0,85000,6824712285450000,3,"S",120,3,"ksoftirqd/0",3
-6824712285450000,0,2460000,6824712287910000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712288868000,0,79000,6824712288947000,3,"S",120,3,"ksoftirqd/0",3
-6824712288947000,0,1078000,6824712290025000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712291181000,0,48000,6824712291229000,3,"S",120,3,"ksoftirqd/0",3
-6824712291229000,0,733000,6824712291962000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712292278000,0,1237000,6824712293515000,0,"R",120,0,"swapper",0
-6824712292682000,2,555000,6824712293237000,773,"S",97,493,"app",678
-6824712293237000,2,4208000,6824712297445000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712296584000,0,315000,6824712296899000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712297146000,1,30662000,6824712327808000,0,"R",120,0,"swapper",0
-6824712297445000,2,91000,6824712297536000,771,"S",97,493,"DispSync",676
-6824712297536000,2,29403000,6824712326939000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712328559000,0,938000,6824712329497000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712329497000,0,87000,6824712329584000,771,"S",97,493,"DispSync",676
-6824712329584000,0,2242000,6824712331826000,0,"R",120,0,"swapper",0
-6824712331793000,2,29260000,6824712361053000,0,"R",120,0,"swapper",0
-6824712331826000,0,394000,6824712332220000,770,"S",120,493,"Binder:640_2",675
-6824712332220000,0,790000,6824712333010000,0,"R",120,0,"swapper",0
-6824712332509000,1,202000,6824712332711000,773,"S",97,493,"app",678
-6824712332711000,1,27609000,6824712360320000,0,"R",120,0,"swapper",0
-6824712333010000,0,85000,6824712333095000,771,"S",97,493,"DispSync",676
-6824712333095000,0,26074000,6824712359169000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712361444000,1,3028000,6824712364472000,644,"S",120,644,"ndroid.systemui",1664
-6824712361543000,2,32596000,6824712394139000,0,"R",120,0,"swapper",0
-6824712361634000,3,102000,6824712361736000,771,"S",97,493,"DispSync",676
-6824712361736000,3,33366000,6824712395102000,0,"R",120,0,"swapper",0
-6824712362118000,0,2398000,6824712364516000,0,"R",120,0,"swapper",0
-6824712364472000,1,724000,6824712365196000,0,"R",120,0,"swapper",0
-6824712364516000,0,394000,6824712364910000,770,"S",120,493,"Binder:640_2",675
-6824712364910000,0,797000,6824712365707000,0,"R",120,0,"swapper",0
-6824712365196000,1,209000,6824712365405000,773,"S",97,493,"app",678
-6824712365405000,1,27749000,6824712393154000,0,"R",120,0,"swapper",0
-6824712365707000,0,88000,6824712365795000,771,"S",97,493,"DispSync",676
-6824712365795000,0,27098000,6824712392893000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712394139000,2,561000,6824712394700000,773,"S",97,493,"app",678
-6824712394204000,0,745000,6824712394949000,0,"R",120,0,"swapper",0
-6824712394700000,2,32976000,6824712427676000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712395292000,1,2827000,6824712398119000,0,"R",120,0,"swapper",0
-6824712398082000,0,723000,6824712398805000,0,"R",120,0,"swapper",0
-6824712398119000,1,404000,6824712398523000,770,"S",120,493,"Binder:640_2",675
-6824712398523000,1,782000,6824712399305000,0,"R",120,0,"swapper",0
-6824712398805000,0,206000,6824712399011000,773,"S",97,493,"app",678
-6824712399011000,0,27138000,6824712426149000,0,"R",120,0,"swapper",0
-6824712399305000,1,88000,6824712399393000,771,"S",97,493,"DispSync",676
-6824712399393000,1,27547000,6824712426940000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712428005000,1,3964000,6824712431969000,0,"R",120,0,"swapper",0
-6824712428161000,2,33130000,6824712461291000,0,"R",120,0,"swapper",0
-6824712428251000,3,103000,6824712428354000,771,"S",97,493,"DispSync",676
-6824712428354000,3,33090000,6824712461444000,0,"R",120,0,"swapper",0
-6824712428804000,0,3132000,6824712431936000,644,"S",120,644,"ndroid.systemui",1664
-6824712431936000,0,704000,6824712432640000,0,"R",120,0,"swapper",0
-6824712431969000,1,397000,6824712432366000,770,"S",120,493,"Binder:640_2",675
-6824712432366000,1,792000,6824712433158000,0,"R",120,0,"swapper",0
-6824712432640000,0,216000,6824712432856000,773,"S",97,493,"app",678
-6824712432856000,0,26763000,6824712459619000,0,"R",120,0,"swapper",0
-6824712433158000,1,88000,6824712433246000,771,"S",97,493,"DispSync",676
-6824712433246000,1,27279000,6824712460525000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712465308000,1,737000,6824712466045000,0,"R",120,0,"swapper",0
-6824712465361000,0,404000,6824712465765000,770,"S",120,493,"Binder:640_2",675
-6824712465765000,0,787000,6824712466552000,0,"R",120,0,"swapper",0
-6824712466045000,1,204000,6824712466249000,773,"S",97,493,"app",678
-6824712466249000,1,27754000,6824712494003000,0,"R",120,0,"swapper",0
-6824712466552000,0,174000,6824712466726000,771,"S",97,493,"DispSync",676
-6824712466726000,0,26386000,6824712493112000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712495145000,1,3951000,6824712499096000,0,"R",120,0,"swapper",0
-6824712495270000,2,33176000,6824712528446000,0,"R",120,0,"swapper",0
-6824712495377000,3,105000,6824712495482000,771,"S",97,493,"DispSync",676
-6824712495482000,3,33062000,6824712528544000,0,"R",120,0,"swapper",0
-6824712495913000,0,3157000,6824712499070000,644,"S",120,644,"ndroid.systemui",1664
-6824712499070000,0,712000,6824712499782000,0,"R",120,0,"swapper",0
-6824712499096000,1,408000,6824712499504000,770,"S",120,493,"Binder:640_2",675
-6824712499504000,1,790000,6824712500294000,0,"R",120,0,"swapper",0
-6824712499782000,0,306000,6824712500088000,773,"S",97,493,"app",678
-6824712500088000,0,26796000,6824712526884000,0,"R",120,0,"swapper",0
-6824712500294000,1,92000,6824712500386000,771,"S",97,493,"DispSync",676
-6824712500386000,1,27266000,6824712527652000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712528168000,0,70000,6824712528238000,702,"S",120,702,"kworker/u16:7",19422
-6824712528238000,0,994000,6824712529232000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712528985000,2,78000,6824712529063000,773,"S",97,493,"app",678
-6824712529063000,2,32595000,6824712561658000,0,"R",120,0,"swapper",0
-6824712529164000,3,33240000,6824712562404000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712532591000,1,395000,6824712532986000,770,"S",120,493,"Binder:640_2",675
-6824712532986000,1,786000,6824712533772000,0,"R",120,0,"swapper",0
-6824712533274000,0,198000,6824712533472000,773,"S",97,493,"app",678
-6824712533472000,0,70000,6824712533542000,0,"R",120,0,"swapper",0
-6824712533542000,0,67000,6824712533609000,3,"S",120,3,"ksoftirqd/0",3
-6824712533609000,0,26476000,6824712560085000,0,"R",120,0,"swapper",0
-6824712533772000,1,89000,6824712533861000,771,"S",97,493,"DispSync",676
-6824712533861000,1,27413000,6824712561274000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712561987000,2,1400000,6824712563387000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712562980000,3,66067000,6824712629047000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712563537000,2,61000,6824712563598000,22,"S",120,22,"ksoftirqd/2",25
-6824712563598000,2,28176000,6824712591774000,0,"R",120,0,"swapper",0
-6824712566295000,1,912000,6824712567207000,0,"R",120,0,"swapper",0
-6824712566346000,0,580000,6824712566926000,770,"S",120,493,"Binder:640_2",675
-6824712566926000,0,798000,6824712567724000,0,"R",120,0,"swapper",0
-6824712567207000,1,209000,6824712567416000,773,"S",97,493,"app",678
-6824712567416000,1,25855000,6824712593271000,0,"R",120,0,"swapper",0
-6824712567724000,0,90000,6824712567814000,771,"S",97,493,"DispSync",676
-6824712567814000,0,23951000,6824712591765000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712592918000,0,855000,6824712593773000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712594086000,0,1123000,6824712595209000,0,"R",120,0,"swapper",0
-6824712594094000,1,73000,6824712594167000,15,"S",120,15,"ksoftirqd/1",17
-6824712594167000,1,1145000,6824712595312000,0,"R",120,0,"swapper",0
-6824712594391000,2,543000,6824712594934000,773,"S",97,493,"app",678
-6824712594934000,2,33189000,6824712628123000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712598402000,0,638000,6824712599040000,0,"R",120,0,"swapper",0
-6824712598442000,1,395000,6824712598837000,770,"S",120,493,"Binder:640_2",675
-6824712598837000,1,566000,6824712599403000,0,"R",120,0,"swapper",0
-6824712599040000,0,182000,6824712599222000,773,"S",97,493,"app",678
-6824712599222000,0,27826000,6824712627048000,0,"R",120,0,"swapper",0
-6824712599403000,1,87000,6824712599490000,771,"S",97,493,"DispSync",676
-6824712599490000,1,27821000,6824712627311000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712628610000,2,29797000,6824712658407000,0,"R",120,0,"swapper",0
-6824712628645000,1,4458000,6824712633103000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712632386000,3,20618000,6824712653004000,0,"R",120,0,"swapper",0
-6824712632420000,0,405000,6824712632825000,770,"S",120,493,"Binder:640_2",675
-6824712632825000,0,777000,6824712633602000,0,"R",120,0,"swapper",0
-6824712633103000,1,300000,6824712633403000,773,"S",97,493,"app",678
-6824712633403000,1,24713000,6824712658116000,0,"R",120,0,"swapper",0
-6824712633602000,0,89000,6824712633691000,771,"S",97,493,"DispSync",676
-6824712633691000,0,8761000,6824712642452000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712653004000,3,59000,6824712653063000,469,"S",100,469,"kworker/3:1H",578
-6824712653063000,3,70763000,6824712723826000,0,"R",120,0,"swapper",0
-6824712653362000,0,46000,6824712653408000,458,"S",100,458,"kworker/0:1H",558
-6824712653408000,0,4244000,6824712657652000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712658290000,0,53000,6824712658343000,3,"S",120,3,"ksoftirqd/0",3
-6824712658343000,0,1697000,6824712660040000,0,"R",120,0,"swapper",0
-6824712658407000,2,117000,6824712658524000,771,"S",97,493,"DispSync",676
-6824712658524000,2,2025000,6824712660549000,0,"R",120,0,"swapper",0
-6824712658563000,1,1537000,6824712660100000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712660528000,0,434000,6824712660962000,0,"R",120,0,"swapper",0
-6824712660549000,2,334000,6824712660883000,773,"S",97,493,"app",678
-6824712660883000,2,33798000,6824712694681000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712662806000,1,304000,6824712663110000,770,"S",120,493,"Binder:640_2",675
-6824712662890000,0,402000,6824712663292000,0,"R",120,0,"swapper",0
-6824712663110000,1,497000,6824712663607000,0,"R",120,0,"swapper",0
-6824712663292000,0,190000,6824712663482000,773,"S",97,493,"app",678
-6824712663482000,0,57000,6824712663539000,0,"R",120,0,"swapper",0
-6824712663539000,0,53000,6824712663592000,3,"S",120,3,"ksoftirqd/0",3
-6824712663592000,0,2553000,6824712666145000,0,"R",120,0,"swapper",0
-6824712663607000,1,90000,6824712663697000,771,"S",97,493,"DispSync",676
-6824712663697000,1,25560000,6824712689257000,0,"R",120,0,"swapper",0
-6824712666145000,0,109000,6824712666254000,77,"S",120,77,"smem_native_rpm",87
-6824712666254000,0,23413000,6824712689667000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712689667000,0,99000,6824712689766000,702,"S",120,702,"kworker/u16:7",19422
-6824712689766000,0,1565000,6824712691331000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712692447000,1,1171000,6824712693618000,0,"R",120,0,"swapper",0
-6824712692474000,0,113000,6824712692587000,771,"S",97,493,"DispSync",676
-6824712692587000,0,1292000,6824712693879000,0,"R",120,0,"swapper",0
-6824712693618000,1,43000,6824712693661000,15,"S",120,15,"ksoftirqd/1",17
-6824712693661000,1,535000,6824712694196000,0,"R",120,0,"swapper",0
-6824712693879000,0,175000,6824712694054000,771,"S",97,493,"DispSync",676
-6824712694054000,0,541000,6824712694595000,0,"R",120,0,"swapper",0
-6824712694196000,1,322000,6824712694518000,773,"S",97,493,"app",678
-6824712694518000,1,1868000,6824712696386000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712696386000,1,443000,6824712696829000,770,"S",120,493,"Binder:640_2",675
-6824712696470000,0,141000,6824712696611000,0,"R",120,0,"swapper",0
-6824712696611000,0,78000,6824712696689000,773,"S",97,493,"app",678
-6824712696689000,0,44000,6824712696733000,0,"R",120,0,"swapper",0
-6824712696733000,0,164000,6824712696897000,773,"S",97,493,"app",678
-6824712696829000,1,10308000,6824712707137000,0,"R",120,0,"swapper",0
-6824712696897000,0,6225000,6824712703122000,0,"R",120,0,"swapper",0
-6824712697037000,2,91000,6824712697128000,771,"S",97,493,"DispSync",676
-6824712697128000,2,25635000,6824712722763000,0,"R",120,0,"swapper",0
-6824712703122000,0,62000,6824712703184000,702,"D",120,702,"kworker/u16:7",19422
-6824712703184000,0,133000,6824712703317000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712707137000,1,57000,6824712707194000,15,"S",120,15,"ksoftirqd/1",17
-6824712707194000,1,15412000,6824712722606000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712722606000,1,109000,6824712722715000,13,"S",0,13,"watchdog/1",15
-6824712722715000,1,79000,6824712722794000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712722858000,1,2789000,6824712725647000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712723898000,3,34414000,6824712758312000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712726130000,1,1587000,6824712727717000,0,"R",120,0,"swapper",0
-6824712726382000,4,67000,6824712726449000,34,"S",0,34,"watchdog/4",39
-6824712726449000,4,657913000,6824713384362000,0,"R",120,0,"swapper",0
-6824712726858000,5,46000,6824712726904000,41,"S",0,41,"watchdog/5",47
-6824712726904000,5,48000,6824712726952000,0,"R",120,0,"swapper",0
-6824712726952000,5,38000,6824712726990000,43,"S",120,43,"ksoftirqd/5",49
-6824712726990000,5,678798000,6824713405788000,0,"R",120,0,"swapper",0
-6824712727327000,0,178000,6824712727505000,771,"S",97,493,"DispSync",676
-6824712727505000,0,702000,6824712728207000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712728110000,6,110547000,6824712838657000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712729998000,7,46000,6824712730044000,55,"S",0,55,"watchdog/7",63
-6824712730044000,7,136266000,6824712866310000,0,"R",120,0,"swapper",0
-6824712730330000,1,314000,6824712730644000,770,"S",120,493,"Binder:640_2",675
-6824712730357000,0,484000,6824712730841000,0,"R",120,0,"swapper",0
-6824712730644000,1,567000,6824712731211000,0,"R",120,0,"swapper",0
-6824712730841000,0,182000,6824712731023000,773,"S",97,493,"app",678
-6824712731023000,0,2119000,6824712733142000,0,"R",120,0,"swapper",0
-6824712731211000,1,95000,6824712731306000,771,"S",97,493,"DispSync",676
-6824712731306000,1,27902000,6824712759208000,0,"R",120,0,"swapper",0
-6824712733142000,0,190000,6824712733332000,77,"S",120,77,"smem_native_rpm",87
-6824712733332000,0,25059000,6824712758391000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712758487000,0,574000,6824712759061000,786,"S",111,494,"SDM_EventThread",685
-6824712759061000,0,699000,6824712759760000,0,"R",120,0,"swapper",0
-6824712759208000,1,489000,6824712759697000,777,"S",120,493,"HwBinder:640_1",721
-6824712759697000,1,1610000,6824712761307000,0,"R",120,0,"swapper",0
-6824712759760000,0,131000,6824712759891000,771,"S",97,493,"DispSync",676
-6824712759891000,0,227000,6824712760118000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712760912000,0,182000,6824712761094000,771,"S",97,493,"DispSync",676
-6824712761094000,0,708000,6824712761802000,0,"R",120,0,"swapper",0
-6824712761307000,1,363000,6824712761670000,773,"S",97,493,"app",678
-6824712761670000,1,2291000,6824712763961000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712763961000,1,312000,6824712764273000,770,"S",120,493,"Binder:640_2",675
-6824712763992000,0,457000,6824712764449000,0,"R",120,0,"swapper",0
-6824712764273000,1,539000,6824712764812000,0,"R",120,0,"swapper",0
-6824712764449000,0,182000,6824712764631000,773,"S",97,493,"app",678
-6824712764631000,0,30567000,6824712795198000,0,"R",120,0,"swapper",0
-6824712764812000,1,95000,6824712764907000,771,"S",97,493,"DispSync",676
-6824712764907000,1,30302000,6824712795209000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712796932000,1,4294000,6824712801226000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712797779000,0,87000,6824712797866000,771,"S",97,493,"DispSync",676
-6824712797866000,0,2676000,6824712800542000,0,"R",120,0,"swapper",0
-6824712800521000,3,61041000,6824712861562000,0,"R",120,0,"swapper",0
-6824712800542000,0,397000,6824712800939000,770,"S",120,493,"Binder:640_2",675
-6824712800939000,0,790000,6824712801729000,0,"R",120,0,"swapper",0
-6824712801226000,1,203000,6824712801429000,773,"S",97,493,"app",678
-6824712801429000,1,28175000,6824712829604000,0,"R",120,0,"swapper",0
-6824712801729000,0,88000,6824712801817000,771,"S",97,493,"DispSync",676
-6824712801817000,0,26133000,6824712827950000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712830184000,2,302000,6824712830486000,771,"S",97,493,"DispSync",676
-6824712830375000,1,1386000,6824712831761000,0,"R",120,0,"swapper",0
-6824712830486000,2,1407000,6824712831893000,0,"R",120,0,"swapper",0
-6824712830947000,0,531000,6824712831478000,773,"S",97,493,"app",678
-6824712831478000,0,3444000,6824712834922000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712834889000,1,707000,6824712835596000,0,"R",120,0,"swapper",0
-6824712834922000,0,398000,6824712835320000,770,"S",120,493,"Binder:640_2",675
-6824712835320000,0,775000,6824712836095000,0,"R",120,0,"swapper",0
-6824712835596000,1,201000,6824712835797000,773,"S",97,493,"app",678
-6824712835797000,1,24848000,6824712860645000,0,"R",120,0,"swapper",0
-6824712836095000,0,93000,6824712836188000,771,"S",97,493,"DispSync",676
-6824712836188000,0,23047000,6824712859235000,0,"R",120,0,"swapper",0
-6824712838657000,6,193000,6824712838850000,630,"S",100,630,"kworker/u17:1",1134
-6824712838850000,6,70000,6824712838920000,0,"R",120,0,"swapper",0
-6824712838920000,6,49000,6824712838969000,50,"S",120,50,"ksoftirqd/6",57
-6824712838969000,6,3751000,6824712842720000,0,"R",120,0,"swapper",0
-6824712841394000,2,165000,6824712841559000,22,"S",120,22,"ksoftirqd/2",25
-6824712841559000,2,3497000,6824712845056000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712845056000,2,686000,6824712845742000,2487,"S",120,739,"UsbFfs-worker",20308
-6824712845742000,2,3405000,6824712849147000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712861354000,1,1038000,6824712862392000,0,"R",120,0,"swapper",0
-6824712861562000,3,169000,6824712861731000,771,"S",97,493,"DispSync",676
-6824712861731000,3,1521000,6824712863252000,0,"R",120,0,"swapper",0
-6824712861812000,0,223000,6824712862035000,771,"S",97,493,"DispSync",676
-6824712862035000,0,716000,6824712862751000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712866837000,1,771000,6824712867608000,0,"R",120,0,"swapper",0
-6824712867041000,0,285000,6824712867326000,773,"S",97,493,"app",678
-6824712867326000,0,9974000,6824712877300000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712873102000,7,72000,6824712873174000,145,"S",120,145,"hwrng",215
-6824712873174000,7,5793000,6824712878967000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712890529000,1,61000,6824712890590000,693,"S",120,693,"kworker/1:1",18800
-6824712890590000,1,2884000,6824712893474000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712891573000,0,40000,6824712891613000,5,"S",120,5,"rcu_preempt",7
-6824712891613000,0,803000,6824712892416000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712893474000,1,566000,6824712894040000,777,"S",120,493,"HwBinder:640_1",721
-6824712894040000,1,1068000,6824712895108000,0,"R",120,0,"swapper",0
-6824712894171000,0,111000,6824712894282000,771,"S",97,493,"DispSync",676
-6824712894282000,0,714000,6824712894996000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712895437000,0,1223000,6824712896660000,644,"R",120,644,"ndroid.systemui",1664
-6824712895518000,1,2511000,6824712898029000,0,"R",120,0,"swapper",0
-6824712895858000,3,55000,6824712895913000,771,"S",97,493,"DispSync",676
-6824712895913000,3,20866000,6824712916779000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712898399000,1,53000,6824712898452000,771,"S",97,493,"DispSync",676
-6824712898452000,1,28312000,6824712926764000,0,"R",120,0,"swapper",0
-6824712898460000,0,1923000,6824712900383000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712901640000,7,72000,6824712901712000,145,"S",120,145,"hwrng",215
-6824712901712000,7,80781000,6824712982493000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712916623000,2,1909000,6824712918532000,2733,"I",120,761,"sh",20457
-6824712916779000,3,89000,6824712916868000,25,"S",120,25,"rcuos/2",29
-6824712916868000,3,6871000,6824712923739000,0,"R",120,0,"swapper",0
-6824712917196000,0,38000,6824712917234000,6,"S",120,6,"rcu_sched",8
-6824712917234000,0,3105000,6824712920339000,0,"R",120,0,"swapper",0
-6824712918532000,2,938000,6824712919470000,2734,"I",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,0,"swapper",0
-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,"I",120,762,"sh",20459
-6824712921296000,0,38000,6824712921334000,630,"S",100,630,"kworker/u17:1",1134
-6824712921312000,2,8350000,6824712929662000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712923300000,0,66000,6824712923366000,6,"S",120,6,"rcu_sched",8
-6824712923366000,0,2472000,6824712925838000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712927513000,0,89000,6824712927602000,771,"S",97,493,"DispSync",676
-6824712927602000,0,969000,6824712928571000,0,"R",120,0,"swapper",0
-6824712928571000,0,92000,6824712928663000,771,"S",97,493,"DispSync",676
-6824712928663000,0,909000,6824712929572000,0,"R",120,0,"swapper",0
-6824712928975000,1,295000,6824712929270000,773,"S",97,493,"app",678
-6824712929270000,1,2298000,6824712931568000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712931425000,0,319000,6824712931744000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712931846000,0,29956000,6824712961802000,0,"R",120,0,"swapper",0
-6824712932123000,2,33000,6824712932156000,771,"S",97,493,"DispSync",676
-6824712932156000,2,1529000,6824712933685000,0,"R",120,0,"swapper",0
-6824712933309000,1,55000,6824712933364000,5,"S",120,5,"rcu_preempt",7
-6824712933364000,1,935000,6824712934299000,0,"R",120,0,"swapper",0
-6824712933685000,2,248000,6824712933933000,24,"S",120,24,"rcuop/2",28
-6824712933933000,2,27866000,6824712961799000,0,"R",120,0,"swapper",0
-6824712934299000,1,19000,6824712934318000,5,"S",120,5,"rcu_preempt",7
-6824712934318000,1,7968000,6824712942286000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712963915000,1,403000,6824712964318000,644,"R",120,644,"ndroid.systemui",1664
-6824712964069000,2,11256000,6824712975325000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712965264000,0,98000,6824712965362000,771,"S",97,493,"DispSync",676
-6824712965362000,0,9149000,6824712974511000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824712975325000,2,324000,6824712975649000,773,"S",97,493,"app",678
-6824712975649000,2,11143000,6824712986792000,0,"R",120,0,"swapper",0
-6824712975930000,0,113000,6824712976043000,771,"S",97,493,"DispSync",676
-6824712976043000,0,5748000,6824712981791000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712994664000,0,752000,6824712995416000,0,"R",120,0,"swapper",0
-6824712994894000,3,230000,6824712995124000,771,"S",97,493,"DispSync",676
-6824712995124000,3,1089000,6824712996213000,0,"R",120,0,"swapper",0
-6824712995416000,0,388000,6824712995804000,773,"S",97,493,"app",678
-6824712995804000,0,2854000,6824712998658000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824712999071000,0,62000,6824712999133000,771,"S",97,493,"DispSync",676
-6824712999133000,0,22793000,6824713021926000,0,"R",120,0,"swapper",0
-6824712999139000,2,28073000,6824713027212000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713027467000,3,179000,6824713027646000,771,"S",97,493,"DispSync",676
-6824713027646000,3,1029000,6824713028675000,0,"R",120,0,"swapper",0
-6824713027659000,2,901000,6824713028560000,0,"R",120,0,"swapper",0
-6824713027954000,1,314000,6824713028268000,773,"S",97,493,"app",678
-6824713028268000,1,2352000,6824713030620000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713030620000,1,274000,6824713030894000,770,"S",120,493,"Binder:640_2",675
-6824713030894000,1,696000,6824713031590000,0,"R",120,0,"swapper",0
-6824713031179000,2,137000,6824713031316000,773,"S",97,493,"app",678
-6824713031316000,2,29313000,6824713060629000,0,"R",120,0,"swapper",0
-6824713031590000,1,43000,6824713031633000,771,"S",97,493,"DispSync",676
-6824713031633000,1,2153000,6824713033786000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713060921000,1,103000,6824713061024000,771,"S",97,493,"DispSync",676
-6824713061024000,1,915000,6824713061939000,0,"R",120,0,"swapper",0
-6824713061060000,2,1313000,6824713062373000,0,"R",120,0,"swapper",0
-6824713061939000,1,85000,6824713062024000,771,"S",97,493,"DispSync",676
-6824713062024000,1,1073000,6824713063097000,0,"R",120,0,"swapper",0
-6824713062373000,2,326000,6824713062699000,773,"S",97,493,"app",678
-6824713062699000,2,2701000,6824713065400000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713064767000,3,15375000,6824713080142000,0,"R",120,0,"swapper",0
-6824713064913000,1,237000,6824713065150000,770,"S",120,493,"Binder:640_2",675
-6824713065150000,1,321000,6824713065471000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713065510000,2,1194000,6824713066704000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713066750000,1,18000,6824713066768000,5,"S",120,5,"rcu_preempt",7
-6824713066757000,2,4703000,6824713071460000,0,"R",120,0,"swapper",0
-6824713066768000,1,4421000,6824713071189000,0,"R",120,0,"swapper",0
-6824713071189000,1,109000,6824713071298000,2728,"S",120,739,"shell",20461
-6824713071298000,1,679000,6824713071977000,0,"R",120,0,"swapper",0
-6824713071460000,2,412000,6824713071872000,739,"S",120,739,"adbd",20305
-6824713071872000,2,322000,6824713072194000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713072239000,2,259000,6824713072498000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713072583000,1,374000,6824713072957000,0,"R",120,0,"swapper",0
-6824713072640000,2,21000,6824713072661000,630,"S",100,630,"kworker/u17:1",1134
-6824713072661000,2,249000,6824713072910000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713072990000,2,99000,6824713073089000,739,"S",120,739,"adbd",20305
-6824713073028000,1,235000,6824713073263000,0,"R",120,0,"swapper",0
-6824713073089000,2,196000,6824713073285000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713073402000,2,3701000,6824713077103000,0,"R",120,0,"swapper",0
-6824713073558000,1,15000,6824713073573000,5,"S",120,5,"rcu_preempt",7
-6824713073573000,1,3170000,6824713076743000,0,"R",120,0,"swapper",0
-6824713076743000,1,148000,6824713076891000,702,"D",120,702,"kworker/u16:7",19422
-6824713076891000,1,199000,6824713077090000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713077122000,2,2542000,6824713079664000,0,"R",120,0,"swapper",0
-6824713079437000,1,81000,6824713079518000,2728,"S",120,739,"shell",20461
-6824713079518000,1,417000,6824713079935000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713080018000,1,518000,6824713080536000,0,"R",120,0,"swapper",0
-6824713080142000,3,24000,6824713080166000,5,"S",120,5,"rcu_preempt",7
-6824713080166000,3,419000,6824713080585000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713080536000,1,38000,6824713080574000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713080574000,1,488000,6824713081062000,0,"R",120,0,"swapper",0
-6824713080585000,3,26000,6824713080611000,630,"S",100,630,"kworker/u17:1",1134
-6824713080611000,3,6419000,6824713087030000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713081062000,1,72000,6824713081134000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713081134000,1,6852000,6824713087986000,0,"R",120,0,"swapper",0
-6824713081251000,2,106000,6824713081357000,739,"S",120,739,"adbd",20305
-6824713081357000,2,6049000,6824713087406000,0,"R",120,0,"swapper",0
-6824713087030000,3,62000,6824713087092000,5,"S",120,5,"rcu_preempt",7
-6824713087092000,3,964000,6824713088056000,0,"R",120,0,"swapper",0
-6824713087406000,2,284000,6824713087690000,8,"S",120,8,"rcuop/0",10
-6824713087690000,2,783000,6824713088473000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713088114000,3,404000,6824713088518000,739,"S",120,739,"adbd",20305
-6824713088125000,1,464000,6824713088589000,0,"R",120,0,"swapper",0
-6824713088473000,2,53000,6824713088526000,630,"S",100,630,"kworker/u17:1",1134
-6824713088518000,3,43000,6824713088561000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713088600000,2,1021000,6824713089621000,0,"R",120,0,"swapper",0
-6824713088626000,1,590000,6824713089216000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713088820000,3,17000,6824713088837000,630,"S",100,630,"kworker/u17:1",1134
-6824713088837000,3,135000,6824713088972000,0,"R",120,0,"swapper",0
-6824713088972000,3,44000,6824713089016000,630,"S",100,630,"kworker/u17:1",1134
-6824713089016000,3,47000,6824713089063000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713089216000,1,154000,6824713089370000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713089370000,1,4023000,6824713093393000,0,"R",120,0,"swapper",0
-6824713089621000,2,127000,6824713089748000,739,"S",120,739,"adbd",20305
-6824713089748000,2,4425000,6824713094173000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713093973000,1,507000,6824713094480000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713094549000,2,126000,6824713094675000,777,"S",120,493,"HwBinder:640_1",721
-6824713094579000,1,9000,6824713094588000,0,"R",120,0,"swapper",0
-6824713094588000,1,74000,6824713094662000,771,"S",97,493,"DispSync",676
-6824713094662000,1,934000,6824713095596000,0,"R",120,0,"swapper",0
-6824713094675000,2,1004000,6824713095679000,0,"R",120,0,"swapper",0
-6824713095007000,3,267000,6824713095274000,773,"S",97,493,"app",678
-6824713095274000,3,5029000,6824713100303000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713097848000,2,321000,6824713098169000,770,"S",120,493,"Binder:640_2",675
-6824713098169000,2,724000,6824713098893000,0,"R",120,0,"swapper",0
-6824713098433000,1,171000,6824713098604000,773,"S",97,493,"app",678
-6824713098604000,1,39000,6824713098643000,0,"R",120,0,"swapper",0
-6824713098643000,1,113000,6824713098756000,482,"S",49,482,"sugov:0",605
-6824713098756000,1,488000,6824713099244000,0,"R",120,0,"swapper",0
-6824713098893000,2,25000,6824713098918000,771,"S",97,493,"DispSync",676
-6824713098918000,2,480000,6824713099398000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713099478000,1,65000,6824713099543000,1919,"S",120,667,"Executor-7",14762
-6824713099543000,1,1075000,6824713100618000,0,"R",120,0,"swapper",0
-6824713100303000,3,67000,6824713100370000,630,"S",100,630,"kworker/u17:1",1134
-6824713100370000,3,9000,6824713100379000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713100974000,2,632000,6824713101606000,0,"R",120,0,"swapper",0
-6824713101233000,1,787000,6824713102020000,0,"R",120,0,"swapper",0
-6824713101255000,3,47000,6824713101302000,630,"S",100,630,"kworker/u17:1",1134
-6824713101302000,3,605000,6824713101907000,0,"R",120,0,"swapper",0
-6824713101606000,2,31000,6824713101637000,5,"S",120,5,"rcu_preempt",7
-6824713101637000,2,471000,6824713102108000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713102108000,2,177000,6824713102285000,739,"S",120,739,"adbd",20305
-6824713102197000,1,5230000,6824713107427000,0,"R",120,0,"swapper",0
-6824713102285000,2,4714000,6824713106999000,0,"R",120,0,"swapper",0
-6824713106999000,2,46000,6824713107045000,5,"S",120,5,"rcu_preempt",7
-6824713107045000,2,1111000,6824713108156000,0,"R",120,0,"swapper",0
-6824713107427000,1,382000,6824713107809000,8,"S",120,8,"rcuop/0",10
-6824713107809000,1,1784000,6824713109593000,0,"R",120,0,"swapper",0
-6824713108156000,2,24000,6824713108180000,5,"S",120,5,"rcu_preempt",7
-6824713108180000,2,1871000,6824713110051000,0,"R",120,0,"swapper",0
-6824713109593000,1,155000,6824713109748000,2728,"S",120,739,"shell",20461
-6824713109748000,1,1604000,6824713111352000,0,"R",120,0,"swapper",0
-6824713110051000,2,506000,6824713110557000,739,"S",120,739,"adbd",20305
-6824713110557000,2,728000,6824713111285000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713111285000,2,35000,6824713111320000,630,"S",100,630,"kworker/u17:1",1134
-6824713111320000,2,474000,6824713111794000,0,"R",120,0,"swapper",0
-6824713111352000,1,75000,6824713111427000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713111427000,1,882000,6824713112309000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713112395000,2,142000,6824713112537000,739,"S",120,739,"adbd",20305
-6824713112477000,1,4894000,6824713117371000,0,"R",120,0,"swapper",0
-6824713112537000,2,766000,6824713113303000,0,"R",120,0,"swapper",0
-6824713113303000,2,30000,6824713113333000,5,"S",120,5,"rcu_preempt",7
-6824713113333000,2,3671000,6824713117004000,0,"R",120,0,"swapper",0
-6824713117004000,2,42000,6824713117046000,5,"S",120,5,"rcu_preempt",7
-6824713117046000,2,851000,6824713117897000,0,"R",120,0,"swapper",0
-6824713117371000,1,213000,6824713117584000,8,"S",120,8,"rcuop/0",10
-6824713117584000,1,3602000,6824713121186000,0,"R",120,0,"swapper",0
-6824713117897000,2,22000,6824713117919000,5,"S",120,5,"rcu_preempt",7
-6824713117919000,2,3713000,6824713121632000,0,"R",120,0,"swapper",0
-6824713121186000,1,154000,6824713121340000,2728,"S",120,739,"shell",20461
-6824713121340000,1,1525000,6824713122865000,0,"R",120,0,"swapper",0
-6824713121632000,2,519000,6824713122151000,739,"S",120,739,"adbd",20305
-6824713122151000,2,341000,6824713122492000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713122523000,2,120000,6824713122643000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713122741000,2,16000,6824713122757000,630,"S",100,630,"kworker/u17:1",1134
-6824713122757000,2,136000,6824713122893000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713122960000,1,168000,6824713123128000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713123175000,2,190000,6824713123365000,739,"S",120,739,"adbd",20305
-6824713123280000,1,3559000,6824713126839000,0,"R",120,0,"swapper",0
-6824713123365000,2,3548000,6824713126913000,0,"R",120,0,"swapper",0
-6824713123633000,3,30000,6824713123663000,5,"S",120,5,"rcu_preempt",7
-6824713123663000,3,3867000,6824713127530000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713127530000,3,44000,6824713127574000,77,"S",120,77,"smem_native_rpm",87
-6824713127574000,3,559000,6824713128133000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713128133000,3,26000,6824713128159000,771,"S",97,493,"DispSync",676
-6824713128159000,3,8000,6824713128167000,0,"R",120,0,"swapper",0
-6824713128167000,3,13000,6824713128180000,771,"S",97,493,"DispSync",676
-6824713128180000,3,1804000,6824713129984000,0,"R",120,0,"swapper",0
-6824713128186000,1,2334000,6824713130520000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713130188000,0,29833000,6824713160021000,2722,"R",120,757,"ps",20463
-6824713130421000,2,758000,6824713131179000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713131406000,1,828000,6824713132234000,0,"R",120,0,"swapper",0
-6824713131641000,3,30000,6824713131671000,771,"S",97,493,"DispSync",676
-6824713131671000,3,95000,6824713131766000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713131949000,3,1543000,6824713133492000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713133142000,2,65000,6824713133207000,694,"S",120,694,"kworker/2:0",18823
-6824713133207000,2,70000,6824713133277000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713133545000,3,9733000,6824713143278000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713133683000,1,142000,6824713133825000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713133706000,2,37000,6824713133743000,0,"R",120,0,"swapper",0
-6824713133743000,2,140000,6824713133883000,739,"S",120,739,"adbd",20305
-6824713133825000,1,6827000,6824713140652000,0,"R",120,0,"swapper",0
-6824713133883000,2,3063000,6824713136946000,0,"R",120,0,"swapper",0
-6824713136946000,2,41000,6824713136987000,5,"S",120,5,"rcu_preempt",7
-6824713136987000,2,3274000,6824713140261000,0,"R",120,0,"swapper",0
-6824713140261000,2,37000,6824713140298000,5,"S",120,5,"rcu_preempt",7
-6824713140298000,2,1112000,6824713141410000,0,"R",120,0,"swapper",0
-6824713140652000,1,423000,6824713141075000,8,"S",120,8,"rcuop/0",10
-6824713141075000,1,1022000,6824713142097000,0,"R",120,0,"swapper",0
-6824713141410000,2,18000,6824713141428000,5,"S",120,5,"rcu_preempt",7
-6824713141428000,2,1111000,6824713142539000,0,"R",120,0,"swapper",0
-6824713142097000,1,152000,6824713142249000,2728,"S",120,739,"shell",20461
-6824713142249000,1,2185000,6824713144434000,0,"R",120,0,"swapper",0
-6824713142539000,2,518000,6824713143057000,739,"S",120,739,"adbd",20305
-6824713143057000,2,1528000,6824713144585000,0,"R",120,0,"swapper",0
-6824713143278000,3,57000,6824713143335000,670,"S",100,670,"kworker/u17:2",14944
-6824713143335000,3,606000,6824713143941000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713144434000,1,56000,6824713144490000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713144490000,1,687000,6824713145177000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713144646000,2,26000,6824713144672000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713144908000,3,9292000,6824713154200000,0,"R",120,0,"swapper",0
-6824713145177000,1,151000,6824713145328000,739,"S",120,739,"adbd",20305
-6824713145328000,1,1971000,6824713147299000,0,"R",120,0,"swapper",0
-6824713146916000,2,42000,6824713146958000,5,"S",120,5,"rcu_preempt",7
-6824713146958000,2,950000,6824713147908000,0,"R",120,0,"swapper",0
-6824713147299000,1,265000,6824713147564000,8,"S",120,8,"rcuop/0",10
-6824713147564000,1,6404000,6824713153968000,0,"R",120,0,"swapper",0
-6824713147908000,2,20000,6824713147928000,5,"S",120,5,"rcu_preempt",7
-6824713147928000,2,5718000,6824713153646000,0,"R",120,0,"swapper",0
-6824713153646000,2,49000,6824713153695000,5,"S",120,5,"rcu_preempt",7
-6824713153695000,2,724000,6824713154419000,0,"R",120,0,"swapper",0
-6824713153968000,1,154000,6824713154122000,2728,"S",120,739,"shell",20461
-6824713154122000,1,591000,6824713154713000,0,"R",120,0,"swapper",0
-6824713154200000,3,176000,6824713154376000,8,"S",120,8,"rcuop/0",10
-6824713154376000,3,873000,6824713155249000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713154983000,2,683000,6824713155666000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713155750000,3,177000,6824713155927000,0,"R",120,0,"swapper",0
-6824713155817000,1,92000,6824713155909000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713155943000,3,6823000,6824713162766000,0,"R",120,0,"swapper",0
-6824713155946000,1,615000,6824713156561000,0,"R",120,0,"swapper",0
-6824713156108000,2,34000,6824713156142000,630,"S",100,630,"kworker/u17:1",1134
-6824713156142000,2,27000,6824713156169000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713156751000,2,4509000,6824713161260000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713161260000,2,459000,6824713161719000,777,"S",120,493,"HwBinder:640_1",721
-6824713161719000,2,304000,6824713162023000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713162426000,1,1725000,6824713164151000,644,"S",120,644,"ndroid.systemui",1664
-6824713162470000,2,1183000,6824713163653000,0,"R",120,0,"swapper",0
-6824713162766000,3,32000,6824713162798000,771,"S",97,493,"DispSync",676
-6824713162798000,3,917000,6824713163715000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713164151000,1,689000,6824713164840000,0,"R",120,0,"swapper",0
-6824713164352000,3,251000,6824713164603000,770,"S",120,493,"Binder:640_2",675
-6824713164404000,2,820000,6824713165224000,0,"R",120,0,"swapper",0
-6824713164603000,3,1967000,6824713166570000,0,"R",120,0,"swapper",0
-6824713164840000,1,120000,6824713164960000,773,"S",97,493,"app",678
-6824713164960000,1,408000,6824713165368000,0,"R",120,0,"swapper",0
-6824713165224000,2,31000,6824713165255000,771,"S",97,493,"DispSync",676
-6824713165255000,2,581000,6824713165836000,0,"R",120,0,"swapper",0
-6824713165368000,1,186000,6824713165554000,2728,"S",120,739,"shell",20461
-6824713165554000,1,1573000,6824713167127000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713167269000,1,51000,6824713167320000,630,"S",100,630,"kworker/u17:1",1134
-6824713167320000,1,35000,6824713167355000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713167572000,1,42000,6824713167614000,0,"R",120,0,"swapper",0
-6824713167614000,1,71000,6824713167685000,630,"S",100,630,"kworker/u17:1",1134
-6824713167660000,3,456000,6824713168116000,0,"R",120,0,"swapper",0
-6824713167685000,1,45000,6824713167730000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713168188000,2,118000,6824713168306000,739,"S",120,739,"adbd",20305
-6824713168261000,3,1720000,6824713169981000,0,"R",120,0,"swapper",0
-6824713168306000,2,8737000,6824713177043000,0,"R",120,0,"swapper",0
-6824713169981000,3,63000,6824713170044000,5,"S",120,5,"rcu_preempt",7
-6824713170044000,3,912000,6824713170956000,0,"R",120,0,"swapper",0
-6824713170393000,1,203000,6824713170596000,8,"S",120,8,"rcuop/0",10
-6824713170596000,1,7765000,6824713178361000,0,"R",120,0,"swapper",0
-6824713170956000,3,31000,6824713170987000,5,"S",120,5,"rcu_preempt",7
-6824713170987000,3,6024000,6824713177011000,0,"R",120,0,"swapper",0
-6824713177011000,3,31000,6824713177042000,5,"S",120,5,"rcu_preempt",7
-6824713177042000,3,653000,6824713177695000,0,"R",120,0,"swapper",0
-6824713177043000,2,352000,6824713177395000,702,"D",120,702,"kworker/u16:7",19422
-6824713177395000,2,498000,6824713177893000,0,"R",120,0,"swapper",0
-6824713177695000,3,144000,6824713177839000,77,"S",120,77,"smem_native_rpm",87
-6824713177839000,3,5798000,6824713183637000,0,"R",120,0,"swapper",0
-6824713177893000,2,35000,6824713177928000,702,"S",120,702,"kworker/u16:7",19422
-6824713177928000,2,920000,6824713178848000,0,"R",120,0,"swapper",0
-6824713178361000,1,204000,6824713178565000,2728,"S",120,739,"shell",20461
-6824713178565000,1,1277000,6824713179842000,0,"R",120,0,"swapper",0
-6824713178848000,2,801000,6824713179649000,739,"S",120,739,"adbd",20305
-6824713179649000,2,622000,6824713180271000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713180271000,2,74000,6824713180345000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713180345000,2,438000,6824713180783000,0,"R",120,0,"swapper",0
-6824713180570000,1,46000,6824713180616000,630,"S",100,630,"kworker/u17:1",1134
-6824713180616000,1,116000,6824713180732000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713180825000,1,17000,6824713180842000,630,"S",100,630,"kworker/u17:1",1134
-6824713180842000,1,170000,6824713181012000,0,"R",120,0,"swapper",0
-6824713180854000,2,1030000,6824713181884000,0,"R",120,0,"swapper",0
-6824713181012000,1,44000,6824713181056000,630,"S",100,630,"kworker/u17:1",1134
-6824713181056000,1,406000,6824713181462000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713182138000,1,1920000,6824713184058000,0,"R",120,0,"swapper",0
-6824713183637000,3,58000,6824713183695000,5,"S",120,5,"rcu_preempt",7
-6824713183695000,3,1423000,6824713185118000,0,"R",120,0,"swapper",0
-6824713184058000,1,700000,6824713184758000,8,"S",120,8,"rcuop/0",10
-6824713184758000,1,3775000,6824713188533000,0,"R",120,0,"swapper",0
-6824713185118000,3,22000,6824713185140000,5,"S",120,5,"rcu_preempt",7
-6824713185140000,3,5295000,6824713190435000,0,"R",120,0,"swapper",0
-6824713188533000,1,169000,6824713188702000,2728,"S",120,739,"shell",20461
-6824713188702000,1,1106000,6824713189808000,0,"R",120,0,"swapper",0
-6824713189029000,2,560000,6824713189589000,739,"S",120,739,"adbd",20305
-6824713189589000,2,668000,6824713190257000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713190257000,2,67000,6824713190324000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713190324000,2,156000,6824713190480000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713190567000,1,41000,6824713190608000,630,"S",100,630,"kworker/u17:1",1134
-6824713190608000,1,445000,6824713191053000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713191204000,1,40000,6824713191244000,630,"S",100,630,"kworker/u17:1",1134
-6824713191212000,2,58000,6824713191270000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713191418000,3,4124000,6824713195542000,0,"R",120,0,"swapper",0
-6824713191519000,1,2312000,6824713193831000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713195229000,1,889000,6824713196118000,0,"R",120,0,"swapper",0
-6824713195542000,3,272000,6824713195814000,773,"S",97,493,"app",678
-6824713195814000,3,811000,6824713196625000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713196797000,0,30115000,6824713226912000,2722,"R+",120,757,"ps",20463
-6824713198160000,1,730000,6824713198890000,0,"R",120,0,"swapper",0
-6824713198323000,3,309000,6824713198632000,770,"S",120,493,"Binder:640_2",675
-6824713198632000,3,5040000,6824713203672000,0,"R",120,0,"swapper",0
-6824713198890000,1,161000,6824713199051000,773,"S",97,493,"app",678
-6824713199051000,1,29000,6824713199080000,0,"R",120,0,"swapper",0
-6824713199080000,1,118000,6824713199198000,482,"S",49,482,"sugov:0",605
-6824713199198000,1,5504000,6824713204702000,0,"R",120,0,"swapper",0
-6824713199334000,2,29000,6824713199363000,771,"S",97,493,"DispSync",676
-6824713199363000,2,4705000,6824713204068000,0,"R",120,0,"swapper",0
-6824713203672000,3,74000,6824713203746000,5,"S",120,5,"rcu_preempt",7
-6824713203746000,3,1079000,6824713204825000,0,"R",120,0,"swapper",0
-6824713204068000,2,436000,6824713204504000,8,"S",120,8,"rcuop/0",10
-6824713204504000,2,743000,6824713205247000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713204917000,1,1067000,6824713205984000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713206279000,1,1131000,6824713207410000,0,"R",120,0,"swapper",0
-6824713206507000,3,43000,6824713206550000,630,"S",100,630,"kworker/u17:1",1134
-6824713206550000,3,436000,6824713206986000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713207410000,1,204000,6824713207614000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713207614000,1,16701000,6824713224315000,0,"R",120,0,"swapper",0
-6824713207853000,2,179000,6824713208032000,739,"S",120,739,"adbd",20305
-6824713208032000,2,2366000,6824713210398000,0,"R",120,0,"swapper",0
-6824713209971000,3,55000,6824713210026000,5,"S",120,5,"rcu_preempt",7
-6824713210026000,3,1318000,6824713211344000,0,"R",120,0,"swapper",0
-6824713210398000,2,635000,6824713211033000,8,"S",120,8,"rcuop/0",10
-6824713211033000,2,6391000,6824713217424000,0,"R",120,0,"swapper",0
-6824713211344000,3,26000,6824713211370000,5,"S",120,5,"rcu_preempt",7
-6824713211370000,3,5656000,6824713217026000,0,"R",120,0,"swapper",0
-6824713217026000,3,51000,6824713217077000,5,"S",120,5,"rcu_preempt",7
-6824713217077000,3,770000,6824713217847000,0,"R",120,0,"swapper",0
-6824713217424000,2,75000,6824713217499000,8,"S",120,8,"rcuop/0",10
-6824713217499000,2,6882000,6824713224381000,0,"R",120,0,"swapper",0
-6824713217847000,3,19000,6824713217866000,5,"S",120,5,"rcu_preempt",7
-6824713217866000,3,6087000,6824713223953000,0,"R",120,0,"swapper",0
-6824713223953000,3,87000,6824713224040000,5,"S",120,5,"rcu_preempt",7
-6824713224040000,3,879000,6824713224919000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713224639000,2,1469000,6824713226108000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713225851000,3,755000,6824713226606000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713226606000,3,144000,6824713226750000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713226750000,3,199000,6824713226949000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713227035000,3,61000,6824713227096000,630,"S",100,630,"kworker/u17:1",1134
-6824713227065000,2,78000,6824713227143000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713227289000,1,566000,6824713227855000,786,"S",111,494,"SDM_EventThread",685
-6824713227305000,2,440000,6824713227745000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713227855000,1,548000,6824713228403000,0,"R",120,0,"swapper",0
-6824713228188000,2,681000,6824713228869000,0,"R",120,0,"swapper",0
-6824713228403000,1,135000,6824713228538000,771,"S",97,493,"DispSync",676
-6824713228538000,1,565000,6824713229103000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713229467000,3,23000,6824713229490000,771,"S",97,493,"DispSync",676
-6824713229490000,3,1513000,6824713231003000,0,"R",120,0,"swapper",0
-6824713230310000,2,45000,6824713230355000,5,"S",120,5,"rcu_preempt",7
-6824713230355000,2,1542000,6824713231897000,0,"R",120,0,"swapper",0
-6824713230856000,1,642000,6824713231498000,0,"R",120,0,"swapper",0
-6824713231003000,3,258000,6824713231261000,770,"S",120,493,"Binder:640_2",675
-6824713231261000,3,5466000,6824713236727000,0,"R",120,0,"swapper",0
-6824713231498000,1,138000,6824713231636000,773,"S",97,493,"app",678
-6824713231636000,1,6415000,6824713238051000,0,"R",120,0,"swapper",0
-6824713231897000,2,28000,6824713231925000,771,"S",97,493,"DispSync",676
-6824713231925000,2,4745000,6824713236670000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713236986000,2,17000,6824713237003000,5,"S",120,5,"rcu_preempt",7
-6824713236994000,3,1550000,6824713238544000,0,"R",120,0,"swapper",0
-6824713237003000,2,3473000,6824713240476000,0,"R",120,0,"swapper",0
-6824713238051000,1,210000,6824713238261000,2728,"S",120,739,"shell",20461
-6824713238261000,1,7678000,6824713245939000,0,"R",120,0,"swapper",0
-6824713238544000,3,824000,6824713239368000,739,"S",120,739,"adbd",20305
-6824713239368000,3,631000,6824713239999000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713240476000,2,102000,6824713240578000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713240578000,2,560000,6824713241138000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713241197000,3,40000,6824713241237000,630,"S",100,630,"kworker/u17:1",1134
-6824713241211000,2,447000,6824713241658000,0,"R",120,0,"swapper",0
-6824713241237000,3,43000,6824713241280000,686,"S",120,686,"kworker/3:1",17791
-6824713241280000,3,763000,6824713242043000,0,"R",120,0,"swapper",0
-6824713241658000,2,183000,6824713241841000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713241841000,2,1454000,6824713243295000,0,"R",120,0,"swapper",0
-6824713242043000,3,215000,6824713242258000,739,"S",120,739,"adbd",20305
-6824713242258000,3,5163000,6824713247421000,0,"R",120,0,"swapper",0
-6824713243295000,2,43000,6824713243338000,5,"S",120,5,"rcu_preempt",7
-6824713243338000,2,3652000,6824713246990000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713246990000,2,51000,6824713247041000,5,"S",120,5,"rcu_preempt",7
-6824713247041000,2,1235000,6824713248276000,0,"R",120,0,"swapper",0
-6824713247421000,3,487000,6824713247908000,8,"S",120,8,"rcuop/0",10
-6824713247908000,3,4863000,6824713252771000,0,"R",120,0,"swapper",0
-6824713248276000,2,25000,6824713248301000,5,"S",120,5,"rcu_preempt",7
-6824713248301000,2,4882000,6824713253183000,0,"R",120,0,"swapper",0
-6824713250362000,6,413000,6824713250775000,2728,"S",120,739,"shell",20461
-6824713250775000,6,72000,6824713250847000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713251509000,0,58000,6824713251567000,52,"S",120,52,"rcuop/6",60
-6824713251567000,0,7123000,6824713258690000,0,"R",120,0,"swapper",0
-6824713252050000,7,3110000,6824713255160000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,"I",120,757,"ps",20463
-6824713253823000,2,1690000,6824713255513000,0,"R",120,0,"swapper",0
-6824713254121000,3,94000,6824713254215000,630,"S",100,630,"kworker/u17:1",1134
-6824713254215000,3,534000,6824713254749000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713255075000,3,5496000,6824713260571000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713258690000,0,164000,6824713258854000,739,"S",120,739,"adbd",20305
-6824713258854000,0,1935000,6824713260789000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713261084000,3,994000,6824713262078000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713261539000,1,1177000,6824713262716000,2729,"R+",120,758,"ps",20464
-6824713261690000,2,748000,6824713262438000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713262397000,3,11856000,6824713274253000,0,"R",120,0,"swapper",0
-6824713262438000,2,46000,6824713262484000,771,"S",97,493,"DispSync",676
-6824713262484000,2,2645000,6824713265129000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713265888000,0,23236000,6824713289124000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713271412000,7,52000,6824713271464000,6,"S",120,6,"rcu_sched",8
-6824713271464000,7,1149000,6824713272613000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713287161000,2,80000,6824713287241000,5,"S",120,5,"rcu_preempt",7
-6824713287241000,2,805000,6824713288046000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713289124000,0,238000,6824713289362000,1694,"D",100,660,"thermal-engine",2490
-6824713289362000,0,803000,6824713290165000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713293609000,2,45000,6824713293654000,5,"S",120,5,"rcu_preempt",7
-6824713293654000,2,1390000,6824713295044000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713295127000,0,113000,6824713295240000,777,"S",120,493,"HwBinder:640_1",721
-6824713295230000,2,1013000,6824713296243000,0,"R",120,0,"swapper",0
-6824713295240000,0,922000,6824713296162000,0,"R",120,0,"swapper",0
-6824713295553000,3,326000,6824713295879000,773,"S",97,493,"app",678
-6824713295879000,3,4297000,6824713300176000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713298120000,0,672000,6824713298792000,0,"R",120,0,"swapper",0
-6824713298257000,2,260000,6824713298517000,770,"S",120,493,"Binder:640_2",675
-6824713298517000,2,708000,6824713299225000,0,"R",120,0,"swapper",0
-6824713298792000,0,153000,6824713298945000,773,"S",97,493,"app",678
-6824713298945000,0,25615000,6824713324560000,0,"R",120,0,"swapper",0
-6824713299225000,2,37000,6824713299262000,771,"S",97,493,"DispSync",676
-6824713299262000,2,1065000,6824713300327000,0,"R",120,0,"swapper",0
-6824713300176000,3,48000,6824713300224000,8,"S",120,8,"rcuop/0",10
-6824713300224000,3,7418000,6824713307642000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713307718000,1,9664000,6824713317382000,2729,"R+",120,758,"ps",20464
-6824713307726000,2,5944000,6824713313670000,0,"R",120,0,"swapper",0
-6824713313670000,2,31000,6824713313701000,5,"S",120,5,"rcu_preempt",7
-6824713313701000,2,3234000,6824713316935000,0,"R",120,0,"swapper",0
-6824713316935000,2,46000,6824713316981000,5,"S",120,5,"rcu_preempt",7
-6824713316981000,2,773000,6824713317754000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713324048000,3,59000,6824713324107000,8,"S",120,8,"rcuop/0",10
-6824713324107000,3,3572000,6824713327679000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713324769000,0,2944000,6824713327713000,0,"R",120,0,"swapper",0
-6824713326940000,2,357000,6824713327297000,702,"D",120,702,"kworker/u16:7",19422
-6824713327297000,2,154000,6824713327451000,0,"R",120,0,"swapper",0
-6824713327451000,2,31000,6824713327482000,702,"S",120,702,"kworker/u16:7",19422
-6824713327482000,2,276000,6824713327758000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713327758000,2,602000,6824713328360000,786,"S",111,494,"SDM_EventThread",685
-6824713327805000,0,570000,6824713328375000,0,"R",120,0,"swapper",0
-6824713327807000,1,48000,6824713327855000,14,"S",0,14,"migration/1",16
-6824713327855000,1,1280000,6824713329135000,0,"R",120,0,"swapper",0
-6824713328360000,2,884000,6824713329244000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713329337000,3,722000,6824713330059000,0,"R",120,0,"swapper",0
-6824713329488000,2,470000,6824713329958000,0,"R",120,0,"swapper",0
-6824713329623000,6,113000,6824713329736000,0,"R",120,0,"swapper",0
-6824713329642000,0,49000,6824713329691000,11,"S",0,11,"migration/0",13
-6824713329691000,0,2040000,6824713331731000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713331658000,2,60000,6824713331718000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713332435000,2,4516000,6824713336951000,0,"R",120,0,"swapper",0
-6824713332613000,0,24000,6824713332637000,771,"S",97,493,"DispSync",676
-6824713332637000,0,5094000,6824713337731000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713337341000,3,53000,6824713337394000,8,"S",120,8,"rcuop/0",10
-6824713337394000,3,14884000,6824713352278000,0,"R",120,0,"swapper",0
-6824713337731000,0,167000,6824713337898000,17,"S",120,17,"rcuop/1",20
-6824713337898000,0,14895000,6824713352793000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713361801000,0,57000,6824713361858000,670,"S",100,670,"kworker/u17:2",14944
-6824713361858000,0,46000,6824713361904000,0,"R",120,0,"swapper",0
-6824713361904000,0,39000,6824713361943000,670,"S",100,670,"kworker/u17:2",14944
-6824713361943000,0,43000,6824713361986000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713364450000,3,65000,6824713364515000,771,"S",97,493,"DispSync",676
-6824713364515000,3,13500000,6824713378015000,0,"R",120,0,"swapper",0
-6824713366417000,1,380000,6824713366797000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713366940000,1,28138000,6824713395078000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713377045000,2,741000,6824713377786000,702,"S",120,702,"kworker/u16:7",19422
-6824713377786000,2,2753000,6824713380539000,0,"R",120,0,"swapper",0
-6824713378015000,3,111000,6824713378126000,77,"S",120,77,"smem_native_rpm",87
-6824713378126000,3,5664000,6824713383790000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713383864000,2,90000,6824713383954000,0,"R",120,0,"swapper",0
-6824713383954000,2,184000,6824713384138000,2487,"R+",120,739,"UsbFfs-worker",20308
-6824713383993000,0,6047000,6824713390040000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713390627000,2,119000,6824713390746000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713390746000,2,516000,6824713391262000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713391262000,2,94000,6824713391356000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713391356000,2,737000,6824713392093000,0,"R",120,0,"swapper",0
-6824713391491000,0,76000,6824713391567000,670,"S",100,670,"kworker/u17:2",14944
-6824713391567000,0,314000,6824713391881000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713393791000,0,72000,6824713393863000,670,"S",100,670,"kworker/u17:2",14944
-6824713393833000,2,136000,6824713393969000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713394095000,0,67000,6824713394162000,670,"S",100,670,"kworker/u17:2",14944
-6824713394136000,2,347000,6824713394483000,0,"R",120,0,"swapper",0
-6824713394162000,0,179000,6824713394341000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713394670000,0,199000,6824713394869000,771,"S",97,493,"DispSync",676
-6824713394869000,0,625000,6824713395494000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713395484000,3,8157000,6824713403641000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713397312000,0,423000,6824713397735000,0,"R",120,0,"swapper",0
-6824713397319000,1,268000,6824713397587000,770,"S",120,493,"Binder:640_2",675
-6824713397587000,1,483000,6824713398070000,0,"R",120,0,"swapper",0
-6824713397735000,0,162000,6824713397897000,773,"S",97,493,"app",678
-6824713397897000,0,2323000,6824713400220000,0,"R",120,0,"swapper",0
-6824713398070000,1,74000,6824713398144000,771,"S",97,493,"DispSync",676
-6824713398144000,1,3426000,6824713401570000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713402068000,0,1411000,6824713403479000,0,"R",120,0,"swapper",0
-6824713402488000,7,51000,6824713402539000,2728,"S",120,739,"shell",20461
-6824713402539000,7,4230000,6824713406769000,0,"R",120,0,"swapper",0
-6824713402761000,4,165000,6824713402926000,739,"S",120,739,"adbd",20305
-6824713402926000,4,350000,6824713403276000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713403737000,3,885000,6824713404622000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713404681000,3,582000,6824713405263000,0,"R",120,0,"swapper",0
-6824713404689000,1,62000,6824713404751000,5,"S",120,5,"rcu_preempt",7
-6824713404751000,1,4130000,6824713408881000,0,"R",120,0,"swapper",0
-6824713405022000,2,120000,6824713405142000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713405142000,2,330000,6824713405472000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713405717000,2,2451000,6824713408168000,0,"R",120,0,"swapper",0
-6824713405788000,5,62000,6824713405850000,739,"S",120,739,"adbd",20305
-6824713405850000,5,1152000,6824713407002000,0,"R",120,0,"swapper",0
-6824713406769000,7,10000,6824713406779000,38,"S",120,38,"rcuop/4",44
-6824713406779000,7,26544000,6824713433323000,0,"R",120,0,"swapper",0
-6824713407002000,5,29000,6824713407031000,2728,"S",120,739,"shell",20461
-6824713407031000,5,7986000,6824713415017000,0,"R",120,0,"swapper",0
-6824713407229000,6,96000,6824713407325000,739,"S",120,739,"adbd",20305
-6824713407325000,6,1876000,6824713409201000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713408168000,2,97000,6824713408265000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713408265000,2,326000,6824713408591000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713408708000,0,44000,6824713408752000,670,"S",100,670,"kworker/u17:2",14944
-6824713408752000,0,7418000,6824713416170000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713409142000,1,888000,6824713410030000,0,"R",120,0,"swapper",0
-6824713409201000,6,60000,6824713409261000,739,"S",120,739,"adbd",20305
-6824713409261000,6,6003000,6824713415264000,0,"R",120,0,"swapper",0
-6824713410030000,1,92000,6824713410122000,5,"S",120,5,"rcu_preempt",7
-6824713410122000,1,6146000,6824713416268000,0,"R",120,0,"swapper",0
-6824713410365000,2,1409000,6824713411774000,52,"S",120,52,"rcuop/6",60
-6824713411774000,2,3953000,6824713415727000,0,"R",120,0,"swapper",0
-6824713415017000,5,41000,6824713415058000,2728,"S",120,739,"shell",20461
-6824713415058000,5,871000,6824713415929000,0,"R",120,0,"swapper",0
-6824713415264000,6,114000,6824713415378000,739,"S",120,739,"adbd",20305
-6824713415378000,6,2157000,6824713417535000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713416080000,2,579000,6824713416659000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713416403000,3,2160000,6824713418563000,0,"R",120,0,"swapper",0
-6824713416403000,1,272000,6824713416675000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713416743000,2,74000,6824713416817000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713417253000,1,234000,6824713417487000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713417487000,1,1008000,6824713418495000,0,"R",120,0,"swapper",0
-6824713417535000,6,118000,6824713417653000,739,"S",120,739,"adbd",20305
-6824713417653000,6,1707000,6824713419360000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713418804000,2,62000,6824713418866000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713419057000,1,251000,6824713419308000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713419308000,1,1797000,6824713421105000,0,"R",120,0,"swapper",0
-6824713419360000,6,50000,6824713419410000,739,"S",120,739,"adbd",20305
-6824713419410000,6,798000,6824713420208000,0,"R",120,0,"swapper",0
-6824713419965000,5,29000,6824713419994000,2728,"S",120,739,"shell",20461
-6824713419994000,5,3214000,6824713423208000,0,"R",120,0,"swapper",0
-6824713420208000,6,90000,6824713420298000,739,"S",120,739,"adbd",20305
-6824713420298000,6,1655000,6824713421953000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713421234000,1,424000,6824713421658000,0,"R",120,0,"swapper",0
-6824713421345000,0,93000,6824713421438000,670,"S",100,670,"kworker/u17:2",14944
-6824713421438000,0,49000,6824713421487000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713421882000,1,1476000,6824713423358000,0,"R",120,0,"swapper",0
-6824713421953000,6,52000,6824713422005000,739,"S",120,739,"adbd",20305
-6824713422005000,6,1245000,6824713423250000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713423331000,6,90000,6824713423421000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713423511000,6,2065000,6824713425576000,0,"R",120,0,"swapper",0
-6824713423553000,1,47000,6824713423600000,5,"S",120,5,"rcu_preempt",7
-6824713423600000,1,800000,6824713424400000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713424400000,1,98000,6824713424498000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713424498000,1,773000,6824713425271000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713425531000,1,3451000,6824713428982000,0,"R",120,0,"swapper",0
-6824713425576000,6,50000,6824713425626000,739,"S",120,739,"adbd",20305
-6824713425626000,6,2459000,6824713428085000,0,"R",120,0,"swapper",0
-6824713426874000,2,103000,6824713426977000,702,"S",120,702,"kworker/u16:7",19422
-6824713426977000,2,1675000,6824713428652000,0,"R",120,0,"swapper",0
-6824713427836000,5,29000,6824713427865000,2728,"S",120,739,"shell",20461
-6824713427865000,5,2265000,6824713430130000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713428242000,0,898000,6824713429140000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713429140000,0,196000,6824713429336000,771,"S",97,493,"DispSync",676
-6824713429146000,3,734000,6824713429880000,0,"R",120,0,"swapper",0
-6824713429304000,1,93000,6824713429397000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713429336000,0,735000,6824713430071000,0,"R",120,0,"swapper",0
-6824713429397000,1,470000,6824713429867000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713430006000,2,61000,6824713430067000,5,"S",120,5,"rcu_preempt",7
-6824713430060000,1,492000,6824713430552000,0,"R",120,0,"swapper",0
-6824713430067000,2,113000,6824713430180000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713430180000,2,112000,6824713430292000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713430226000,3,1706000,6824713431932000,0,"R",120,0,"swapper",0
-6824713430292000,2,469000,6824713430761000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713430983000,2,607000,6824713431590000,0,"R",120,0,"swapper",0
-6824713431054000,6,118000,6824713431172000,739,"S",120,739,"adbd",20305
-6824713431172000,6,1852000,6824713433024000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713431854000,2,46000,6824713431900000,773,"S",97,493,"app",678
-6824713431861000,0,492000,6824713432353000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713432059000,2,655000,6824713432714000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713432353000,0,196000,6824713432549000,773,"S",97,493,"app",678
-6824713432549000,0,28791000,6824713461340000,0,"R",120,0,"swapper",0
-6824713432581000,1,110000,6824713432691000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713432691000,1,29017000,6824713461708000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713432880000,2,3801000,6824713436681000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713433323000,7,68000,6824713433391000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713433391000,7,2508000,6824713435899000,0,"R",120,0,"swapper",0
-6824713433547000,5,47000,6824713433594000,739,"S",120,739,"adbd",20305
-6824713433594000,5,1241000,6824713434835000,0,"R",120,0,"swapper",0
-6824713434835000,5,30000,6824713434865000,2728,"S",120,739,"shell",20461
-6824713434865000,5,2740000,6824713437605000,0,"R",120,0,"swapper",0
-6824713435062000,6,101000,6824713435163000,739,"S",120,739,"adbd",20305
-6824713435163000,6,471000,6824713435634000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713435899000,7,15000,6824713435914000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713435914000,7,346000,6824713436260000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713436260000,7,20000,6824713436280000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713436280000,7,302000,6824713436582000,0,"R",120,0,"swapper",0
-6824713436309000,6,29000,6824713436338000,630,"S",100,630,"kworker/u17:1",1134
-6824713436338000,6,35000,6824713436373000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713436653000,6,83000,6824713436736000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713436952000,6,678000,6824713437630000,0,"R",120,0,"swapper",0
-6824713436992000,2,48000,6824713437040000,5,"S",120,5,"rcu_preempt",7
-6824713437040000,2,6321000,6824713443361000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713437712000,6,15000,6824713437727000,0,"R",120,0,"swapper",0
-6824713437727000,6,14000,6824713437741000,630,"S",100,630,"kworker/u17:1",1134
-6824713437741000,6,40000,6824713437781000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713437848000,6,25000,6824713437873000,630,"S",100,630,"kworker/u17:1",1134
-6824713437873000,6,136000,6824713438009000,0,"R",120,0,"swapper",0
-6824713437982000,7,13000,6824713437995000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713437995000,7,43000,6824713438038000,0,"R",120,0,"swapper",0
-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,6,283000,6824713438321000,0,"R",120,0,"swapper",0
-6824713438038000,7,7000,6824713438045000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713438045000,7,470000,6824713438515000,0,"R",120,0,"swapper",0
-6824713438321000,6,20000,6824713438341000,630,"S",100,630,"kworker/u17:1",1134
-6824713438341000,6,34000,6824713438375000,0,"R",120,0,"swapper",0
-6824713438375000,6,29000,6824713438404000,630,"S",100,630,"kworker/u17:1",1134
-6824713438404000,6,71000,6824713438475000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713438528000,6,43000,6824713438571000,739,"S",120,739,"adbd",20305
-6824713438549000,7,3192000,6824713441741000,0,"R",120,0,"swapper",0
-6824713438571000,6,2294000,6824713440865000,0,"R",120,0,"swapper",0
-6824713440637000,5,30000,6824713440667000,2728,"S",120,739,"shell",20461
-6824713440667000,5,2936000,6824713443603000,0,"R",120,0,"swapper",0
-6824713440865000,6,84000,6824713440949000,739,"S",120,739,"adbd",20305
-6824713440949000,6,533000,6824713441482000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713441741000,7,12000,6824713441753000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713441753000,7,349000,6824713442102000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713442102000,7,15000,6824713442117000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713442117000,7,369000,6824713442486000,0,"R",120,0,"swapper",0
-6824713442148000,6,16000,6824713442164000,630,"S",100,630,"kworker/u17:1",1134
-6824713442164000,6,35000,6824713442199000,0,"R",120,0,"swapper",0
-6824713442199000,6,26000,6824713442225000,630,"S",100,630,"kworker/u17:1",1134
-6824713442225000,6,51000,6824713442276000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713442549000,6,1704000,6824713444253000,0,"R",120,0,"swapper",0
-6824713443361000,2,92000,6824713443453000,5,"S",120,5,"rcu_preempt",7
-6824713443453000,2,362000,6824713443815000,0,"R",120,0,"swapper",0
-6824713443603000,5,161000,6824713443764000,38,"S",120,38,"rcuop/4",44
-6824713443764000,5,254000,6824713444018000,0,"R",120,0,"swapper",0
-6824713443815000,2,49000,6824713443864000,5,"S",120,5,"rcu_preempt",7
-6824713443864000,2,6152000,6824713450016000,0,"R",120,0,"swapper",0
-6824713444018000,5,29000,6824713444047000,2728,"S",120,739,"shell",20461
-6824713444047000,5,6341000,6824713450388000,0,"R",120,0,"swapper",0
-6824713444253000,6,82000,6824713444335000,739,"S",120,739,"adbd",20305
-6824713444335000,6,323000,6824713444658000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713444885000,7,14000,6824713444899000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713444899000,7,439000,6824713445338000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713445395000,6,2679000,6824713448074000,0,"R",120,0,"swapper",0
-6824713447845000,7,24000,6824713447869000,2728,"S",120,739,"shell",20461
-6824713447869000,7,782000,6824713448651000,0,"R",120,0,"swapper",0
-6824713448074000,6,85000,6824713448159000,739,"S",120,739,"adbd",20305
-6824713448159000,6,279000,6824713448438000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713448651000,7,12000,6824713448663000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713448663000,7,456000,6824713449119000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713449119000,7,15000,6824713449134000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713449134000,7,353000,6824713449487000,0,"R",120,0,"swapper",0
-6824713449168000,6,27000,6824713449195000,630,"S",100,630,"kworker/u17:1",1134
-6824713449195000,6,48000,6824713449243000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713449487000,7,38000,6824713449525000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713449525000,7,2642000,6824713452167000,0,"R",120,0,"swapper",0
-6824713449676000,6,51000,6824713449727000,739,"S",120,739,"adbd",20305
-6824713449727000,6,349000,6824713450076000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713450163000,6,2199000,6824713452362000,0,"R",120,0,"swapper",0
-6824713450388000,5,13000,6824713450401000,5,"S",120,5,"rcu_preempt",7
-6824713450401000,5,6481000,6824713456882000,0,"R",120,0,"swapper",0
-6824713452167000,7,26000,6824713452193000,2728,"S",120,739,"shell",20461
-6824713452193000,7,989000,6824713453182000,0,"R",120,0,"swapper",0
-6824713452362000,6,87000,6824713452449000,739,"S",120,739,"adbd",20305
-6824713452449000,6,485000,6824713452934000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713453182000,7,12000,6824713453194000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713453194000,7,648000,6824713453842000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713453842000,7,15000,6824713453857000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713453857000,7,551000,6824713454408000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713454471000,6,1697000,6824713456168000,0,"R",120,0,"swapper",0
-6824713455943000,7,21000,6824713455964000,2728,"S",120,739,"shell",20461
-6824713455964000,7,1116000,6824713457080000,0,"R",120,0,"swapper",0
-6824713456168000,6,83000,6824713456251000,739,"S",120,739,"adbd",20305
-6824713456251000,6,535000,6824713456786000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713456882000,5,15000,6824713456897000,5,"S",120,5,"rcu_preempt",7
-6824713456897000,5,644000,6824713457541000,0,"R",120,0,"swapper",0
-6824713457080000,7,12000,6824713457092000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713457092000,7,408000,6824713457500000,0,"R",120,0,"swapper",0
-6824713457244000,6,70000,6824713457314000,38,"S",120,38,"rcuop/4",44
-6824713457314000,6,585000,6824713457899000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713457556000,7,13000,6824713457569000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713457569000,7,270000,6824713457839000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713457945000,6,5819000,6824713463764000,0,"R",120,0,"swapper",0
-6824713459905000,4,14000,6824713459919000,716,"S",120,716,"kworker/4:1",19875
-6824713459919000,4,6161000,6824713466080000,2729,"I",120,758,"ps",20464
-6824713460102000,7,30000,6824713460132000,2728,"S",120,739,"shell",20461
-6824713460132000,7,539000,6824713460671000,0,"R",120,0,"swapper",0
-6824713460298000,5,97000,6824713460395000,739,"S",120,739,"adbd",20305
-6824713460395000,5,1447000,6824713461842000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713461440000,2,373000,6824713461813000,786,"S",111,494,"SDM_EventThread",685
-6824713461449000,0,460000,6824713461909000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713461708000,1,421000,6824713462129000,777,"S",120,493,"HwBinder:640_1",721
-6824713461813000,2,364000,6824713462177000,0,"R",120,0,"swapper",0
-6824713461842000,5,49000,6824713461891000,739,"S",120,739,"adbd",20305
-6824713461891000,5,1341000,6824713463232000,0,"R",120,0,"swapper",0
-6824713461909000,0,68000,6824713461977000,771,"S",97,493,"DispSync",676
-6824713461977000,0,38000,6824713462015000,0,"R",120,0,"swapper",0
-6824713462015000,0,162000,6824713462177000,771,"S",97,493,"DispSync",676
-6824713462129000,1,262000,6824713462391000,0,"R",120,0,"swapper",0
-6824713462177000,0,142000,6824713462319000,0,"R",120,0,"swapper",0
-6824713462177000,2,255000,6824713462432000,773,"S",97,493,"app",678
-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,0,"swapper",0
-6824713462466000,1,2550000,6824713465016000,0,"R",120,0,"swapper",0
-6824713463232000,5,9000,6824713463241000,5,"S",120,5,"rcu_preempt",7
-6824713463241000,5,334000,6824713463575000,0,"R",120,0,"swapper",0
-6824713463575000,5,14000,6824713463589000,5,"S",120,5,"rcu_preempt",7
-6824713463589000,5,243000,6824713463832000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713463837000,5,1034000,6824713464871000,0,"R",120,0,"swapper",0
-6824713464109000,0,488000,6824713464597000,0,"R",120,0,"swapper",0
-6824713464139000,2,5608000,6824713469747000,0,"R",120,0,"swapper",0
-6824713464149000,3,282000,6824713464431000,770,"S",120,493,"Binder:640_2",675
-6824713464431000,3,3478000,6824713467909000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713464769000,0,2190000,6824713466959000,0,"R",120,0,"swapper",0
-6824713464871000,5,85000,6824713464956000,739,"S",120,739,"adbd",20305
-6824713464956000,5,693000,6824713465649000,0,"R",120,0,"swapper",0
-6824713465016000,1,91000,6824713465107000,771,"S",97,493,"DispSync",676
-6824713465107000,1,1435000,6824713466542000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713465553000,7,29000,6824713465582000,630,"S",100,630,"kworker/u17:1",1134
-6824713465582000,7,689000,6824713466271000,0,"R",120,0,"swapper",0
-6824713465649000,5,20000,6824713465669000,2728,"S",120,739,"shell",20461
-6824713465669000,5,418000,6824713466087000,0,"R",120,0,"swapper",0
-6824713466071000,6,16000,6824713466087000,39,"S",120,39,"rcuos/4",45
-6824713466080000,4,812000,6824713466892000,0,"R",120,0,"swapper",0
-6824713466087000,6,4893000,6824713470980000,0,"R",120,0,"swapper",0
-6824713466087000,5,8000,6824713466095000,6,"S",120,6,"rcu_sched",8
-6824713466095000,5,2929000,6824713469024000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713466542000,1,3584000,6824713470126000,2721,"I",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,0,"swapper",0
-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,0,"swapper",0
-6824713467020000,0,1748000,6824713468768000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713467909000,3,104000,6824713468013000,77,"S",120,77,"smem_native_rpm",87
-6824713468013000,3,1027000,6824713469040000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713468839000,0,3202000,6824713472041000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713469089000,4,153000,6824713469242000,0,"R",120,0,"swapper",0
-6824713469111000,3,1771000,6824713470882000,0,"R",120,0,"swapper",0
-6824713469242000,4,14000,6824713469256000,483,"S",49,483,"sugov:4",606
-6824713469256000,4,2174000,6824713471430000,0,"R",120,0,"swapper",0
-6824713469747000,2,68000,6824713469815000,9,"S",120,9,"rcuos/0",11
-6824713469815000,2,281000,6824713470096000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713470096000,2,3770000,6824713473866000,2720,"I",120,755,"sh",20460
-6824713470126000,1,2368000,6824713472494000,0,"R",120,0,"swapper",0
-6824713470702000,5,43000,6824713470745000,5,"S",120,5,"rcu_preempt",7
-6824713470745000,5,381000,6824713471126000,0,"R",120,0,"swapper",0
-6824713470882000,3,62000,6824713470944000,24,"S",120,24,"rcuop/2",28
-6824713470944000,3,15328000,6824713486272000,0,"R",120,0,"swapper",0
-6824713470980000,6,109000,6824713471089000,38,"S",120,38,"rcuop/4",44
-6824713471089000,6,2971000,6824713474060000,0,"R",120,0,"swapper",0
-6824713471126000,5,89000,6824713471215000,2728,"S",120,739,"shell",20461
-6824713471215000,5,687000,6824713471902000,0,"R",120,0,"swapper",0
-6824713471430000,4,246000,6824713471676000,739,"S",120,739,"adbd",20305
-6824713471676000,4,2529000,6824713474205000,0,"R",120,0,"swapper",0
-6824713471902000,5,68000,6824713471970000,2728,"S",120,739,"shell",20461
-6824713471970000,5,263000,6824713472233000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713472291000,0,508000,6824713472799000,0,"R",120,0,"swapper",0
-6824713472494000,1,96000,6824713472590000,670,"S",100,670,"kworker/u17:2",14944
-6824713472590000,1,47000,6824713472637000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713472842000,1,2946000,6824713475788000,0,"R",120,0,"swapper",0
-6824713472860000,0,44000,6824713472904000,0,"R",120,0,"swapper",0
-6824713472904000,0,75000,6824713472979000,670,"S",100,670,"kworker/u17:2",14944
-6824713472979000,0,453000,6824713473432000,0,"R",120,0,"swapper",0
-6824713473109000,5,8000,6824713473117000,25,"S",120,25,"rcuos/2",29
-6824713473117000,5,861000,6824713473978000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713473866000,2,283000,6824713474149000,0,"R",120,0,"swapper",0
-6824713473978000,5,75000,6824713474053000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713474053000,5,975000,6824713475028000,0,"R",120,0,"swapper",0
-6824713474060000,6,244000,6824713474304000,2728,"I",120,739,"shell",20461
-6824713474149000,2,49000,6824713474198000,52,"S",120,52,"rcuop/6",60
-6824713474198000,2,596000,6824713474794000,0,"R",120,0,"swapper",0
-6824713474205000,4,149000,6824713474354000,739,"S",120,739,"adbd",20305
-6824713474304000,6,2488000,6824713476792000,0,"R",120,0,"swapper",0
-6824713474354000,4,1066000,6824713475420000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713474875000,2,189000,6824713475064000,0,"R",120,0,"swapper",0
-6824713475028000,5,13000,6824713475041000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713475041000,5,348000,6824713475389000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713475437000,5,75000,6824713475512000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713475568000,0,10190000,6824713485758000,0,"R",120,0,"swapper",0
-6824713475569000,2,10187000,6824713485756000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713475877000,1,621000,6824713476498000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713476852000,1,9339000,6824713486191000,0,"R",120,0,"swapper",0
-6824713477028000,5,42000,6824713477070000,2487,"S",120,739,"UsbFfs-worker",20308
-6824713477070000,5,7334000,6824713484404000,0,"R",120,0,"swapper",0
-6824713477142000,7,19000,6824713477161000,39,"S",120,39,"rcuos/4",45
-6824713477161000,7,61280000,6824713538441000,0,"R",120,0,"swapper",0
-6824713477264000,4,40000,6824713477304000,739,"S",120,739,"adbd",20305
-6824713477304000,4,31231000,6824713508535000,0,"R",120,0,"swapper",0
-6824713484404000,5,44000,6824713484448000,5,"S",120,5,"rcu_preempt",7
-6824713484448000,5,365000,6824713484813000,0,"R",120,0,"swapper",0
-6824713484499000,6,23000,6824713484522000,6,"S",120,6,"rcu_sched",8
-6824713484522000,6,6802000,6824713491324000,0,"R",120,0,"swapper",0
-6824713484813000,5,36000,6824713484849000,25,"S",120,25,"rcuos/2",29
-6824713484849000,5,6226000,6824713491075000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713485915000,0,9244000,6824713495159000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713486366000,3,6074000,6824713492440000,0,"R",120,0,"swapper",0
-6824713491075000,5,58000,6824713491133000,5,"S",120,5,"rcu_preempt",7
-6824713491133000,5,6463000,6824713497596000,0,"R",120,0,"swapper",0
-6824713491324000,6,54000,6824713491378000,38,"S",120,38,"rcuop/4",44
-6824713491378000,6,47434000,6824713538812000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713492656000,2,2845000,6824713495501000,0,"R",120,0,"swapper",0
-6824713495159000,0,143000,6824713495302000,743,"S",120,743,"kworker/0:5",20371
-6824713495302000,0,61000,6824713495363000,0,"R",120,0,"swapper",0
-6824713495363000,0,62000,6824713495425000,3,"S",120,3,"ksoftirqd/0",3
-6824713495425000,0,586000,6824713496011000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713496275000,0,505000,6824713496780000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713496881000,0,83000,6824713496964000,0,"R",120,0,"swapper",0
-6824713496964000,0,65000,6824713497029000,771,"S",97,493,"DispSync",676
-6824713496992000,2,10168000,6824713507160000,0,"R",120,0,"swapper",0
-6824713497029000,0,2730000,6824713499759000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713499282000,3,328000,6824713499610000,770,"S",120,493,"Binder:640_2",675
-6824713499302000,1,572000,6824713499874000,0,"R",120,0,"swapper",0
-6824713499610000,3,7250000,6824713506860000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713500028000,1,7524000,6824713507552000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713507151000,0,1456000,6824713508607000,0,"R",120,0,"swapper",0
-6824713507160000,2,99000,6824713507259000,22,"S",120,22,"ksoftirqd/2",25
-6824713507259000,2,10658000,6824713517917000,0,"R",120,0,"swapper",0
-6824713507552000,1,436000,6824713507988000,17,"S",120,17,"rcuop/1",20
-6824713507988000,1,16424000,6824713524412000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713508785000,4,35595000,6824713544380000,0,"R",120,0,"swapper",0
-6824713512798000,0,237000,6824713513035000,5,"S",120,5,"rcu_preempt",7
-6824713513035000,0,4264000,6824713517299000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713518137000,2,83000,6824713518220000,45,"S",120,45,"rcuop/5",52
-6824713518220000,2,10550000,6824713528770000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713524412000,1,87000,6824713524499000,17,"S",120,17,"rcuop/1",20
-6824713524499000,1,4727000,6824713529226000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713530415000,2,1002000,6824713531417000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713534377000,1,510000,6824713534887000,0,"R",120,0,"swapper",0
-6824713534532000,0,188000,6824713534720000,773,"S",97,493,"app",678
-6824713534720000,0,2286000,6824713537006000,0,"R",120,0,"swapper",0
-6824713534887000,1,85000,6824713534972000,771,"S",97,493,"DispSync",676
-6824713534972000,1,29038000,6824713564010000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713538441000,7,113000,6824713538554000,39,"S",120,39,"rcuos/4",45
-6824713538554000,7,5437000,6824713543991000,0,"R",120,0,"swapper",0
-6824713538812000,6,55000,6824713538867000,6,"S",120,6,"rcu_sched",8
-6824713538867000,6,4782000,6824713543649000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713543991000,7,134000,6824713544125000,39,"S",120,39,"rcuos/4",45
-6824713544125000,7,6499000,6824713550624000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713544507000,6,5784000,6824713550291000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713550291000,6,80000,6824713550371000,6,"S",120,6,"rcu_sched",8
-6824713550371000,6,24532000,6824713574903000,0,"R",120,0,"swapper",0
-6824713550624000,7,88000,6824713550712000,39,"S",120,39,"rcuos/4",45
-6824713550712000,7,23645000,6824713574357000,0,"R",120,0,"swapper",0
-6824713550971000,4,48000,6824713551019000,46,"S",120,46,"rcuos/5",53
-6824713551019000,4,14066000,6824713565085000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713564595000,3,3222000,6824713567817000,644,"S",120,644,"ndroid.systemui",1664
-6824713564620000,0,1978000,6824713566598000,0,"R",120,0,"swapper",0
-6824713564760000,2,90000,6824713564850000,771,"S",97,493,"DispSync",676
-6824713564850000,2,3091000,6824713567941000,0,"R",120,0,"swapper",0
-6824713565085000,4,514000,6824713565599000,943,"D",100,563,"FileWatcherThre",908
-6824713565599000,4,22373000,6824713587972000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713567081000,1,530000,6824713567611000,943,"S",100,563,"FileWatcherThre",908
-6824713567611000,1,1518000,6824713569129000,0,"R",120,0,"swapper",0
-6824713567817000,3,13943000,6824713581760000,0,"R",120,0,"swapper",0
-6824713567836000,0,68000,6824713567904000,8,"S",120,8,"rcuop/0",10
-6824713567904000,0,700000,6824713568604000,0,"R",120,0,"swapper",0
-6824713567941000,2,390000,6824713568331000,770,"S",120,493,"Binder:640_2",675
-6824713568331000,2,4718000,6824713573049000,0,"R",120,0,"swapper",0
-6824713568604000,0,218000,6824713568822000,773,"S",97,493,"app",678
-6824713568822000,0,11756000,6824713580578000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713573049000,2,113000,6824713573162000,145,"S",120,145,"hwrng",215
-6824713573162000,2,2555000,6824713575717000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713574903000,6,48000,6824713574951000,6,"S",120,6,"rcu_sched",8
-6824713574951000,6,5495000,6824713580446000,0,"R",120,0,"swapper",0
-6824713575717000,2,111000,6824713575828000,145,"S",120,145,"hwrng",215
-6824713575828000,2,22002000,6824713597830000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713581961000,1,15601000,6824713597562000,0,"R",120,0,"swapper",0
-6824713582275000,0,89000,6824713582364000,702,"S",120,702,"kworker/u16:7",19422
-6824713582364000,0,14442000,6824713596806000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713587449000,7,83000,6824713587532000,39,"S",120,39,"rcuos/4",45
-6824713587532000,7,6610000,6824713594142000,0,"R",120,0,"swapper",0
-6824713587972000,4,51000,6824713588023000,46,"S",120,46,"rcuos/5",53
-6824713588023000,4,6549000,6824713594572000,0,"R",120,0,"swapper",0
-6824713588081000,6,24000,6824713588105000,6,"S",120,6,"rcu_sched",8
-6824713588105000,6,5555000,6824713593660000,0,"R",120,0,"swapper",0
-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,-1,6824713593701999,0,"[NULL]",120,0,"swapper",0
-6824713594142000,7,54000,6824713594196000,39,"S",120,39,"rcuos/4",45
-6824713594196000,7,-1,6824713594195999,0,"[NULL]",120,0,"swapper",0
-6824713594572000,4,31000,6824713594603000,46,"S",120,46,"rcuos/5",53
-6824713594603000,4,-1,6824713594602999,0,"[NULL]",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713598635000,2,932000,6824713599567000,0,"R",120,0,"swapper",0
-6824713598845000,1,556000,6824713599401000,777,"S",120,493,"HwBinder:640_1",721
-6824713599401000,1,2173000,6824713601574000,0,"R",120,0,"swapper",0
-6824713599567000,2,90000,6824713599657000,771,"S",97,493,"DispSync",676
-6824713599657000,2,23565000,6824713623222000,0,"R",120,0,"swapper",0
-6824713601561000,0,678000,6824713602239000,0,"R",120,0,"swapper",0
-6824713601574000,1,395000,6824713601969000,770,"S",120,493,"Binder:640_2",675
-6824713601969000,1,395000,6824713602364000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-6824713602448000,0,26982000,6824713629430000,0,"R",120,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-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,0,"swapper",0
-6824713631536000,3,-1,6824713631535999,0,"[NULL]",120,0,"swapper",0
-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,0,"swapper",0
-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,-1,6824713633755999,2738,"[NULL]",120,764,"atrace",20467
-6824713633887000,0,-1,6824713633886999,0,"[NULL]",120,0,"swapper",0
-6824713634629000,1,131000,6824713634760000,773,"S",97,493,"app",678
-6824713634760000,1,-1,6824713634759999,0,"[NULL]",120,0,"swapper",0
-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,-1,6824713634972999,0,"[NULL]",120,0,"swapper",0
diff --git a/test/trace_processor/parsing/systrace_html_test.sql b/test/trace_processor/parsing/systrace_html_test.sql
deleted file mode 100644
index e94b82a..0000000
--- a/test/trace_processor/parsing/systrace_html_test.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 ts, cpu, dur, ts_end, utid, end_state, priority, upid, name, tid
-from sched
-join thread using(utid)
-order by ts;
diff --git a/test/trace_processor/parsing/thread_counter_and_track_test.sql b/test/trace_processor/parsing/thread_counter_and_track_test.sql
deleted file mode 100644
index 24f3d81..0000000
--- a/test/trace_processor/parsing/thread_counter_and_track_test.sql
+++ /dev/null
@@ -1,5 +0,0 @@
-select ts, t.name, value, tid
-from counter c
-join thread_counter_track t on c.track_id = t.id
-join thread using (utid)
-order by ts;
\ No newline at end of file
diff --git a/test/trace_processor/parsing/thread_time_in_thread_slice.out b/test/trace_processor/parsing/thread_time_in_thread_slice.out
deleted file mode 100644
index 1be6304..0000000
--- a/test/trace_processor/parsing/thread_time_in_thread_slice.out
+++ /dev/null
@@ -1,8 +0,0 @@
-"name","thread_ts","thread_dur"
-"SenderB",1000,5000
-"Blergh","[NULL]","[NULL]"
-"SenderA",3005000,7000
-"OtherSlice",3204000,100000
-"SomeSlice",3335000,340000
-"SomeOtherSlice",3335000,996000
-"SomeOtherSliceInstant","[NULL]","[NULL]"
diff --git a/test/trace_processor/parsing/thread_time_in_thread_slice_test.sql b/test/trace_processor/parsing/thread_time_in_thread_slice_test.sql
deleted file mode 100644
index 207376b..0000000
--- a/test/trace_processor/parsing/thread_time_in_thread_slice_test.sql
+++ /dev/null
@@ -1,3 +0,0 @@
-SELECT
-  name, thread_ts, thread_dur
-FROM thread_slice;
diff --git a/test/trace_processor/parsing/trace_size_test.sql b/test/trace_processor/parsing/trace_size_test.sql
deleted file mode 100644
index 01704a2..0000000
--- a/test/trace_processor/parsing/trace_size_test.sql
+++ /dev/null
@@ -1,16 +0,0 @@
---
--- Copyright 2020 The Android Open Source Project
---
--- Licensed under the Apache License, Version 2.0 (the "License");
--- you may not use this file except in compliance with the License.
--- You may obtain a copy of the License at
---
---     https://www.apache.org/licenses/LICENSE-2.0
---
--- Unless required by applicable law or agreed to in writing, software
--- distributed under the License is distributed on an "AS IS" BASIS,
--- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--- See the License for the specific language governing permissions and
--- limitations under the License.
---
-select int_value from metadata where name = 'trace_size_bytes';
diff --git a/test/trace_processor/parsing/trigger_packet_trace.textproto b/test/trace_processor/parsing/trigger_packet_trace.textproto
deleted file mode 100644
index 11148ab..0000000
--- a/test/trace_processor/parsing/trigger_packet_trace.textproto
+++ /dev/null
@@ -1,16 +0,0 @@
-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/parsing/triggers_packets_test.sql b/test/trace_processor/parsing/triggers_packets_test.sql
deleted file mode 100644
index 89d5cdb..0000000
--- a/test/trace_processor/parsing/triggers_packets_test.sql
+++ /dev/null
@@ -1,45 +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
-  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/parsing/triggers_packets_trigger_packet_trace.out b/test/trace_processor/parsing/triggers_packets_trigger_packet_trace.out
deleted file mode 100644
index aafd335..0000000
--- a/test/trace_processor/parsing/triggers_packets_trigger_packet_trace.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"ts","name","string_value","int_value"
-101000002,"test1","producer1",3
-101000004,"test2","producer2",4
diff --git a/test/trace_processor/parsing/ts_desc_filter_android_sched_and_ps.out b/test/trace_processor/parsing/ts_desc_filter_android_sched_and_ps.out
deleted file mode 100644
index 4002595..0000000
--- a/test/trace_processor/parsing/ts_desc_filter_android_sched_and_ps.out
+++ /dev/null
@@ -1,11 +0,0 @@
-"ts"
-81492536383477
-81491101817952
-81491101296858
-81491101029618
-81491099541806
-81491099514618
-81491099495504
-81491099477014
-81491098894566
-81491096076181
diff --git a/test/trace_processor/parsing/ts_desc_filter_test.sql b/test/trace_processor/parsing/ts_desc_filter_test.sql
deleted file mode 100644
index 6f49a98..0000000
--- a/test/trace_processor/parsing/ts_desc_filter_test.sql
+++ /dev/null
@@ -1,21 +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 ts
-from sched
-inner join thread using(utid)
-where tid = 23850
-order by ts desc
-limit 10
diff --git a/test/trace_processor/parsing/very_long_sched_android_trace_quality.out b/test/trace_processor/parsing/very_long_sched_android_trace_quality.out
deleted file mode 100644
index 3c0537c..0000000
--- a/test/trace_processor/parsing/very_long_sched_android_trace_quality.out
+++ /dev/null
@@ -1,5 +0,0 @@
-android_trace_quality {
-  failures {
-    name: "sched_slice_too_long"
-  }
-}
\ No newline at end of file
diff --git a/test/trace_processor/performance/cpu_frequency_limits.out b/test/trace_processor/performance/cpu_frequency_limits.out
deleted file mode 100644
index 1c4348f..0000000
--- a/test/trace_processor/performance/cpu_frequency_limits.out
+++ /dev/null
@@ -1,13 +0,0 @@
-"ts","value","cpu"
-90000000,2800000.000000,"Cpu 6 Max"
-90000000,500000.000000,"Cpu 6 Min"
-100000000,1700000.000000,"Cpu 6 Max"
-100000000,500000.000000,"Cpu 6 Min"
-110000000,2800000.000000,"Cpu 6 Max"
-110000000,1400000.000000,"Cpu 6 Min"
-120000000,1500000.000000,"Cpu 6 Max"
-120000000,500000.000000,"Cpu 6 Min"
-120000000,1400000.000000,"Cpu 4 Max"
-120000000,600000.000000,"Cpu 4 Min"
-130000000,2200000.000000,"Cpu 4 Max"
-130000000,800000.000000,"Cpu 4 Min"
diff --git a/test/trace_processor/performance/cpu_frequency_limits_test.sql b/test/trace_processor/performance/cpu_frequency_limits_test.sql
deleted file mode 100644
index 06e1852..0000000
--- a/test/trace_processor/performance/cpu_frequency_limits_test.sql
+++ /dev/null
@@ -1,12 +0,0 @@
-SELECT
-  ts,
-  value, 
-  REPLACE(name, " Freq Limit", "") AS cpu
-FROM
-  counter AS c
-  LEFT JOIN
-  counter_track AS t
-  ON c.track_id = t.id
-WHERE
-  name GLOB "* Freq Limit"
-ORDER BY ts;
diff --git a/test/trace_processor/performance/index b/test/trace_processor/performance/index
deleted file mode 100644
index b641c5c..0000000
--- a/test/trace_processor/performance/index
+++ /dev/null
@@ -1,6 +0,0 @@
-# IRQ max runtime and count over 1ms
-irq_runtime_metric.textproto android_irq_runtime irq_runtime_metric.out
-# CPU frequency maximum & minimum limits change
-cpu_frequency_limits.textproto cpu_frequency_limits_test.sql cpu_frequency_limits.out
-# frame_timeline_metric collects App_Deadline_Missed metrics
-frame_timeline_metric.py android_frame_timeline_metric frame_timeline_metric.out
diff --git a/test/trace_processor/power/index b/test/trace_processor/power/index
deleted file mode 100644
index 96687df..0000000
--- a/test/trace_processor/power/index
+++ /dev/null
@@ -1,12 +0,0 @@
-# Power rails
-../../data/power_rails.pb power_rails_test.sql power_rails_power_rails.out
-power_rails_custom_clock.textproto power_rails_event_test.sql power_rails_event_power_rails_custom_clock.out
-power_rails.textproto power_rails_timestamp_sort_test.sql power_rails_timestamp_sort.out
-power_rails_well_known.textproto power_rails_test.sql power_rails_well_known_power_rails.out
-
-# Dynamic Voltage and Frequency Scaling
-
-dvfs_metric.textproto android_dvfs dvfs_metric.out
-wakesource.textproto wakesource_test.sql wakesource_wakesource.out
-suspend_resume.textproto suspend_resume_test.sql suspend_resume.out
-suspend_period.textproto android_batt suspend_period.out
diff --git a/test/trace_processor/power/power_rails_event_power_rails_custom_clock.out b/test/trace_processor/power/power_rails_event_power_rails_custom_clock.out
deleted file mode 100644
index 4c222eb..0000000
--- a/test/trace_processor/power/power_rails_event_power_rails_custom_clock.out
+++ /dev/null
@@ -1,5 +0,0 @@
-"ts","value"
-104000000,333.000000
-106000000,666.000000
-106000000,999.000000
-109000000,0.000000
diff --git a/test/trace_processor/power/power_rails_event_test.sql b/test/trace_processor/power/power_rails_event_test.sql
deleted file mode 100644
index ca04ce2..0000000
--- a/test/trace_processor/power/power_rails_event_test.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 ts, value
-from counters
-where name GLOB "power.*"
-limit 20
diff --git a/test/trace_processor/power/power_rails_power_rails.out b/test/trace_processor/power/power_rails_power_rails.out
deleted file mode 100644
index d83b04e..0000000
--- a/test/trace_processor/power/power_rails_power_rails.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"name","AVG(value)","COUNT(*)"
-"power.PPVAR_VPH_PWR_ABH_uws",7390700.360656,61
-"power.PPVAR_VPH_PWR_OLED_uws",202362991.655738,61
diff --git a/test/trace_processor/power/power_rails_test.sql b/test/trace_processor/power/power_rails_test.sql
deleted file mode 100644
index 31af227..0000000
--- a/test/trace_processor/power/power_rails_test.sql
+++ /dev/null
@@ -1,20 +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 name, AVG(value), COUNT(*)
-from counters
-where name GLOB "power.*"
-group by name
-limit 20
\ No newline at end of file
diff --git a/test/trace_processor/power/power_rails_timestamp_sort.out b/test/trace_processor/power/power_rails_timestamp_sort.out
deleted file mode 100644
index ad69e8e..0000000
--- a/test/trace_processor/power/power_rails_timestamp_sort.out
+++ /dev/null
@@ -1,6 +0,0 @@
-"ts","value","name"
-3000000,333.000000,"power.test_rail_uws"
-3000000,0.000000,"power.test_rail_uws"
-3000004,1000.000000,"Testing"
-3000005,999.000000,"power.test_rail2_uws"
-5000000,666.000000,"power.test_rail_uws"
diff --git a/test/trace_processor/power/power_rails_timestamp_sort_test.sql b/test/trace_processor/power/power_rails_timestamp_sort_test.sql
deleted file mode 100644
index aeb452e..0000000
--- a/test/trace_processor/power/power_rails_timestamp_sort_test.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 ts, value, t.name as name
-from counter c inner join counter_track t on t.id = c.track_id
-order by ts
-limit 20
diff --git a/test/trace_processor/power/power_rails_well_known.textproto b/test/trace_processor/power/power_rails_well_known.textproto
deleted file mode 100644
index 5129011..0000000
--- a/test/trace_processor/power/power_rails_well_known.textproto
+++ /dev/null
@@ -1,45 +0,0 @@
-packet {
-  power_rails {
-    rail_descriptor {
-      index: 4
-      rail_name: "S3M_VDD_CPUCL1"
-      subsys_name: "cpu"
-      sampling_rate: 1023
-    }
-  }
-}
-packet {
-  timestamp: 3000003
-  power_rails {
-    energy_data {
-      index: 4
-      timestamp_ms: 3
-      energy: 333
-    }
-  }
-}
-packet {
-  timestamp: 3000005
-  power_rails {
-    rail_descriptor {
-      index: 3
-      rail_name: "S2S_VDD_G3D"
-      subsys_name: "gpu"
-      sampling_rate: 1022
-    }
-    energy_data {
-      index: 4
-      timestamp_ms: 5
-      energy: 666
-    }
-    energy_data {
-      index: 3
-      energy: 999
-    }
-    energy_data {
-      index: 4
-      timestamp_ms: 3
-      energy: 0
-    }
-  }
-}
\ No newline at end of file
diff --git a/test/trace_processor/power/power_rails_well_known_power_rails.out b/test/trace_processor/power/power_rails_well_known_power_rails.out
deleted file mode 100644
index d3c19d6..0000000
--- a/test/trace_processor/power/power_rails_well_known_power_rails.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"name","AVG(value)","COUNT(*)"
-"power.rails.cpu.mid",333.000000,3
-"power.rails.gpu",999.000000,1
diff --git a/test/trace_processor/power/suspend_period.out b/test/trace_processor/power/suspend_period.out
deleted file mode 100644
index b38775e..0000000
--- a/test/trace_processor/power/suspend_period.out
+++ /dev/null
@@ -1,9 +0,0 @@
-android_batt {
-  battery_aggregates {
-    sleep_ns: 10000
-  }
-  suspend_period {
-    timestamp_ns: 30000
-    duration_ns: 10000
-  }
-}
diff --git a/test/trace_processor/power/suspend_period.textproto b/test/trace_processor/power/suspend_period.textproto
deleted file mode 100644
index 0a118ed..0000000
--- a/test/trace_processor/power/suspend_period.textproto
+++ /dev/null
@@ -1,56 +0,0 @@
-packet {
-  ftrace_events {
-    cpu: 0
-    event {
-      timestamp: 20000
-      pid: 0
-      suspend_resume {
-        action: "syscore_suspend"
-        val: 0
-        start: 1
-      }
-    }
-  }
-}
-packet {
-  ftrace_events {
-    cpu: 0
-    event {
-      timestamp: 29999
-      pid: 0
-      suspend_resume {
-        action: "syscore_suspend"
-        val: 0
-        start: 0
-      }
-    }
-  }
-}
-packet {
-  ftrace_events {
-    cpu: 0
-    event {
-      timestamp: 30000
-      pid: 0
-      suspend_resume {
-        action: "syscore_resume"
-        val: 0
-        start: 1
-      }
-    }
-  }
-}
-packet {
-  ftrace_events {
-    cpu: 0
-    event {
-      timestamp: 40000
-      pid: 0
-      suspend_resume {
-        action: "syscore_resume"
-        val: 0
-        start: 0
-      }
-    }
-  }
-}
diff --git a/test/trace_processor/power/suspend_resume.out b/test/trace_processor/power/suspend_resume.out
deleted file mode 100644
index ca21b06..0000000
--- a/test/trace_processor/power/suspend_resume.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"ts","dur","action"
-10000,10000,"suspend_enter(3)"
-30000,10000,"CPU(0)"
diff --git a/test/trace_processor/power/suspend_resume.textproto b/test/trace_processor/power/suspend_resume.textproto
deleted file mode 100644
index ad40430..0000000
--- a/test/trace_processor/power/suspend_resume.textproto
+++ /dev/null
@@ -1,56 +0,0 @@
-packet {
-  ftrace_events {
-    cpu: 0
-    event {
-      timestamp: 10000
-      pid: 0
-      suspend_resume {
-        action: "suspend_enter"
-        val: 3
-        start: 1
-      }
-    }
-  }
-}
-packet {
-  ftrace_events {
-    cpu: 0
-    event {
-      timestamp: 20000
-      pid: 0
-      suspend_resume {
-        action: "suspend_enter"
-        val: 3
-        start: 0
-      }
-    }
-  }
-}
-packet {
-  ftrace_events {
-    cpu: 0
-    event {
-      timestamp: 30000
-      pid: 0
-      suspend_resume {
-        action: "CPU"
-        val: 0
-        start: 1
-      }
-    }
-  }
-}
-packet {
-  ftrace_events {
-    cpu: 0
-    event {
-      timestamp: 40000
-      pid: 0
-      suspend_resume {
-        action: "CPU"
-        val: 0
-        start: 0
-      }
-    }
-  }
-}
diff --git a/test/trace_processor/power/suspend_resume_test.sql b/test/trace_processor/power/suspend_resume_test.sql
deleted file mode 100644
index 30f4542..0000000
--- a/test/trace_processor/power/suspend_resume_test.sql
+++ /dev/null
@@ -1,12 +0,0 @@
-SELECT
-  s.ts,
-  s.dur,
-  s.name AS action
-FROM
-  slice AS s
-  JOIN
-  track AS t
-  ON s.track_id = t.id
-WHERE
-  t.name = 'Suspend/Resume Latency'
-ORDER BY s.ts;
diff --git a/test/trace_processor/power/wakesource_test.sql b/test/trace_processor/power/wakesource_test.sql
deleted file mode 100644
index 8497e87..0000000
--- a/test/trace_processor/power/wakesource_test.sql
+++ /dev/null
@@ -1,5 +0,0 @@
-SELECT ts, dur, slice.name
-FROM slice
-JOIN track on slice.track_id = track.id
-WHERE track.name GLOB 'Wakelock*'
-ORDER BY ts
diff --git a/test/trace_processor/power/wakesource_wakesource.out b/test/trace_processor/power/wakesource_wakesource.out
deleted file mode 100644
index 179e4f5..0000000
--- a/test/trace_processor/power/wakesource_wakesource.out
+++ /dev/null
@@ -1,5 +0,0 @@
-"ts","dur","name"
-34298714043271,7872467,"Wakelock(s2mpw02-power-keys)"
-34298721846504,42732654,"Wakelock(event0)"
-34298721915739,16,"Wakelock(s2mpw02-power-keys)"
-34298764569658,14538,"Wakelock(eventpoll)"
diff --git a/test/trace_processor/process_tracking/index b/test/trace_processor/process_tracking/index
deleted file mode 100644
index 60edcfb..0000000
--- a/test/trace_processor/process_tracking/index
+++ /dev/null
@@ -1,24 +0,0 @@
-# Tests for the core process and thread tracking logic.
-
-# Smoke tests
-synth_process_tracking.py ../common/process_tracking_test.sql process_tracking.out
-
-# Short lived threads/processes
-process_tracking_short_lived_1.py ../common/process_tracking_test.sql process_tracking_process_tracking_short_lived_1.out
-process_tracking_short_lived_2.py ../common/process_tracking_test.sql process_tracking_process_tracking_short_lived_2.out
-
-# Process uid handling
-synth_process_tracking.py process_tracking_uid_test.sql process_tracking_uid.out
-
-# Tracking across execs
-process_tracking_exec.py ../common/process_tracking_test.sql process_tracking_process_tracking_exec.out
-
-# Tracking parent threads
-process_parent_pid_tracking_1.py process_parent_pid_test.sql process_parent_pid_process_parent_pid_tracking_1.out
-process_parent_pid_tracking_2.py process_parent_pid_test.sql process_parent_pid_process_parent_pid_tracking_2.out
-
-# Tracking thread reuse
-reused_thread_print.py ../common/process_tracking_test.sql process_tracking_reused_thread_print.out
-
-# TODO(lalitm): move this out of this folder.
-sde_tracing_mark_write.textproto slice_with_pid_test.sql slice_with_pid_sde_tracing_mark_write.out
diff --git a/test/trace_processor/process_tracking/process_parent_pid_process_parent_pid_tracking_1.out b/test/trace_processor/process_tracking/process_parent_pid_process_parent_pid_tracking_1.out
deleted file mode 100644
index 31f525e..0000000
--- a/test/trace_processor/process_tracking/process_parent_pid_process_parent_pid_tracking_1.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"child_pid","parent_pid"
-10,0
-20,10
diff --git a/test/trace_processor/process_tracking/process_parent_pid_process_parent_pid_tracking_2.out b/test/trace_processor/process_tracking/process_parent_pid_process_parent_pid_tracking_2.out
deleted file mode 100644
index 31f525e..0000000
--- a/test/trace_processor/process_tracking/process_parent_pid_process_parent_pid_tracking_2.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"child_pid","parent_pid"
-10,0
-20,10
diff --git a/test/trace_processor/process_tracking/process_parent_pid_test.sql b/test/trace_processor/process_tracking/process_parent_pid_test.sql
deleted file mode 100644
index a836c92..0000000
--- a/test/trace_processor/process_tracking/process_parent_pid_test.sql
+++ /dev/null
@@ -1,22 +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
-  child.pid as child_pid,
-  parent.pid as parent_pid
-FROM process as child
-INNER JOIN process as parent
-ON child.parent_upid = parent.upid
-ORDER BY child_pid
diff --git a/test/trace_processor/process_tracking/process_tracking.out b/test/trace_processor/process_tracking/process_tracking.out
deleted file mode 100644
index ab7ff90..0000000
--- a/test/trace_processor/process_tracking/process_tracking.out
+++ /dev/null
@@ -1,14 +0,0 @@
-"tid","pid","pname","tname"
-10,10,"process1","p1-t0"
-11,"[NULL]","[NULL]","p1-t1"
-12,10,"process1","p1-t2"
-20,20,"process_2","p2-t0"
-21,20,"process_2","p2-t1"
-22,20,"process_2","p2-t2"
-30,30,"process_3","p3-t0"
-31,30,"process_3","p3-t1"
-31,40,"process_4","p4-t1"
-32,30,"process_3","p3-t2"
-33,30,"process_3","p3-t3"
-34,30,"process_3","p3-t4"
-40,40,"process_4","p4-t0"
diff --git a/test/trace_processor/process_tracking/process_tracking_process_tracking_exec.out b/test/trace_processor/process_tracking/process_tracking_process_tracking_exec.out
deleted file mode 100644
index 6890b54..0000000
--- a/test/trace_processor/process_tracking/process_tracking_process_tracking_exec.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"tid","pid","pname","tname"
-10,10,"parent","parent"
-11,11,"true_process_name","true_name"
diff --git a/test/trace_processor/process_tracking/process_tracking_process_tracking_short_lived_1.out b/test/trace_processor/process_tracking/process_tracking_process_tracking_short_lived_1.out
deleted file mode 100644
index 3719aaa..0000000
--- a/test/trace_processor/process_tracking/process_tracking_process_tracking_short_lived_1.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"tid","pid","pname","tname"
-10,10,"parent","parent"
-11,11,"child","child"
diff --git a/test/trace_processor/process_tracking/process_tracking_process_tracking_short_lived_2.out b/test/trace_processor/process_tracking/process_tracking_process_tracking_short_lived_2.out
deleted file mode 100644
index 6f128de..0000000
--- a/test/trace_processor/process_tracking/process_tracking_process_tracking_short_lived_2.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"tid","pid","pname","tname"
-10,10,"parent","parent"
-11,11,"true_name","true_name"
diff --git a/test/trace_processor/process_tracking/process_tracking_reused_thread_print.out b/test/trace_processor/process_tracking/process_tracking_reused_thread_print.out
deleted file mode 100644
index 2aca01a..0000000
--- a/test/trace_processor/process_tracking/process_tracking_reused_thread_print.out
+++ /dev/null
@@ -1,4 +0,0 @@
-"tid","pid","pname","tname"
-10,10,"parent","[NULL]"
-11,11,"short_lived","[NULL]"
-11,10,"parent","true_name"
diff --git a/test/trace_processor/process_tracking/process_tracking_uid.out b/test/trace_processor/process_tracking/process_tracking_uid.out
deleted file mode 100644
index ae90e8d..0000000
--- a/test/trace_processor/process_tracking/process_tracking_uid.out
+++ /dev/null
@@ -1,6 +0,0 @@
-"pid","uid"
-0,"[NULL]"
-10,1001
-20,1002
-30,"[NULL]"
-40,"[NULL]"
diff --git a/test/trace_processor/process_tracking/process_tracking_uid_test.sql b/test/trace_processor/process_tracking/process_tracking_uid_test.sql
deleted file mode 100644
index fcc590b..0000000
--- a/test/trace_processor/process_tracking/process_tracking_uid_test.sql
+++ /dev/null
@@ -1,18 +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 pid, uid
-from process
-order by pid;
diff --git a/test/trace_processor/process_tracking/sde_tracing_mark_write.textproto b/test/trace_processor/process_tracking/sde_tracing_mark_write.textproto
deleted file mode 100644
index 4a9728b..0000000
--- a/test/trace_processor/process_tracking/sde_tracing_mark_write.textproto
+++ /dev/null
@@ -1,23 +0,0 @@
-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/process_tracking/slice_with_pid_sde_tracing_mark_write.out b/test/trace_processor/process_tracking/slice_with_pid_sde_tracing_mark_write.out
deleted file mode 100644
index 9f7759e..0000000
--- a/test/trace_processor/process_tracking/slice_with_pid_sde_tracing_mark_write.out
+++ /dev/null
@@ -1,2 +0,0 @@
-"name","dur","tid","pid"
-"test_event",1,403,403
diff --git a/test/trace_processor/process_tracking/slice_with_pid_test.sql b/test/trace_processor/process_tracking/slice_with_pid_test.sql
deleted file mode 100644
index c0ea0d2..0000000
--- a/test/trace_processor/process_tracking/slice_with_pid_test.sql
+++ /dev/null
@@ -1,5 +0,0 @@
-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/profiling/callstack_sampling_flamegraph_multi_process.out b/test/trace_processor/profiling/callstack_sampling_flamegraph_multi_process.out
deleted file mode 100644
index a0b68b3..0000000
--- a/test/trace_processor/profiling/callstack_sampling_flamegraph_multi_process.out
+++ /dev/null
@@ -1,4 +0,0 @@
-"count","description"
-658,"BothProcesses"
-483,"FirstProcess"
-175,"SecondProcess"
diff --git a/test/trace_processor/profiling/callstack_sampling_flamegraph_multi_process_test.sql b/test/trace_processor/profiling/callstack_sampling_flamegraph_multi_process_test.sql
deleted file mode 100644
index 4dff21d..0000000
--- a/test/trace_processor/profiling/callstack_sampling_flamegraph_multi_process_test.sql
+++ /dev/null
@@ -1,26 +0,0 @@
-select count(*) as count, 'BothProcesses' as description
-from experimental_flamegraph
-where
-  upid_group = (
-    select group_concat(distinct upid)
-    from perf_sample join thread t using (utid) join process p using (upid)
-  )
-  and profile_type = 'perf'
-  and ts <= 7689491063351
-  and size > 0
-union all
-select count(*) as count, 'FirstProcess' as description
-from experimental_flamegraph
-  join process using (upid)
-where pid = 1728
-  and profile_type = 'perf'
-  and ts <= 7689491063351
-  and size > 0
-union all
-select count(*) as count, 'SecondProcess' as description
-from experimental_flamegraph
-  join process using (upid)
-where pid = 703
-  and profile_type = 'perf'
-  and ts <= 7689491063351
-  and size > 0;
diff --git a/test/trace_processor/profiling/callstack_sampling_flamegraph_test.sql b/test/trace_processor/profiling/callstack_sampling_flamegraph_test.sql
deleted file mode 100644
index 1ed2717..0000000
--- a/test/trace_processor/profiling/callstack_sampling_flamegraph_test.sql
+++ /dev/null
@@ -1,7 +0,0 @@
-select ef.*
-from experimental_flamegraph ef
-  join process using (upid)
-where pid = 1728
-  and profile_type = 'perf'
-  and ts <= 7689491063351
-limit 10;
diff --git a/test/trace_processor/profiling/heap_graph_duplicate.textproto b/test/trace_processor/profiling/heap_graph_duplicate.textproto
deleted file mode 100644
index 23194ef..0000000
--- a/test/trace_processor/profiling/heap_graph_duplicate.textproto
+++ /dev/null
@@ -1,45 +0,0 @@
-packet {
-  process_tree {
-    processes {
-      pid: 2
-      ppid: 1
-      cmdline: "system_server"
-      uid: 1000
-    }
-  }
-}
-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
-    types {
-      id: 1
-      class_name: "FactoryProducerDelegateImplActor"
-      location_id: 1
-    }
-    roots {
-      root_type: ROOT_JAVA_FRAME
-      object_ids: 0x01
-      object_ids: 0x01
-    }
-    objects {
-      id: 0x01
-      type_id: 1
-      self_size: 64
-    }
-    continued: false
-    index: 0
-  }
-}
diff --git a/test/trace_processor/profiling/heap_graph_flamegraph_focused_test.sql b/test/trace_processor/profiling/heap_graph_flamegraph_focused_test.sql
deleted file mode 100644
index e6b514c..0000000
--- a/test/trace_processor/profiling/heap_graph_flamegraph_focused_test.sql
+++ /dev/null
@@ -1,15 +0,0 @@
-SELECT
-  id,
-  depth,
-  name,
-  count,
-  cumulative_count,
-  size,
-  cumulative_size,
-  parent_id
-FROM experimental_flamegraph
-where upid = (select max(upid) from heap_graph_object)
-  and profile_type = 'graph'
-  and ts = (select max(graph_sample_ts) from heap_graph_object)
-  and focus_str = 'left'
-LIMIT 10
diff --git a/test/trace_processor/profiling/heap_graph_flamegraph_matches_objects.out b/test/trace_processor/profiling/heap_graph_flamegraph_matches_objects.out
deleted file mode 100644
index 031cd76..0000000
--- a/test/trace_processor/profiling/heap_graph_flamegraph_matches_objects.out
+++ /dev/null
@@ -1,2 +0,0 @@
-"upid","ts","total_objects_size","total_flamegraph_size"
-1,10,3000000036,3000000036
diff --git a/test/trace_processor/profiling/heap_graph_flamegraph_matches_objects_test.sql b/test/trace_processor/profiling/heap_graph_flamegraph_matches_objects_test.sql
deleted file mode 100644
index 59674af..0000000
--- a/test/trace_processor/profiling/heap_graph_flamegraph_matches_objects_test.sql
+++ /dev/null
@@ -1,23 +0,0 @@
--- For each heap graph dump (upid, ts), builds a flamegraph and outputs:
--- * total_objects_size: the sum of the size (native + java) of all the
---   reachable objects
--- * total_flamegraph_size: the sum of the cumulative size of the roots in the
---   flamegraph
--- If the flamegraph has been built correctly, the numbers should match.
-SELECT
-  obj.upid AS upid,
-  obj.graph_sample_ts AS ts,
-  SUM(obj.self_size + obj.native_size) AS total_objects_size,
-  (
-    SELECT SUM(cumulative_size)
-    FROM experimental_flamegraph
-    WHERE experimental_flamegraph.upid = obj.upid
-      AND experimental_flamegraph.ts = obj.graph_sample_ts
-      AND profile_type = 'graph'
-      AND depth = 0 -- only the roots
-  ) AS total_flamegraph_size
-FROM
-  heap_graph_object AS obj
-WHERE
-  obj.reachable != 0
-GROUP BY obj.upid, obj.graph_sample_ts
diff --git a/test/trace_processor/profiling/heap_graph_flamegraph_test.sql b/test/trace_processor/profiling/heap_graph_flamegraph_test.sql
deleted file mode 100644
index 34692ef..0000000
--- a/test/trace_processor/profiling/heap_graph_flamegraph_test.sql
+++ /dev/null
@@ -1,15 +0,0 @@
-SELECT
-  id,
-  depth,
-  name,
-  map_name,
-  count,
-  cumulative_count,
-  size,
-  cumulative_size,
-  parent_id
-FROM experimental_flamegraph
-where upid = (select max(upid) from heap_graph_object)
-  and profile_type = 'graph'
-  and ts = (select max(graph_sample_ts) from heap_graph_object)
-LIMIT 10
diff --git a/test/trace_processor/profiling/heap_graph_native_size.out b/test/trace_processor/profiling/heap_graph_native_size.out
deleted file mode 100644
index c0d6b61..0000000
--- a/test/trace_processor/profiling/heap_graph_native_size.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"type_name","native_size"
-"android.graphics.Bitmap",123456
-"android.os.BinderProxy",0
diff --git a/test/trace_processor/profiling/heap_graph_native_size_test.sql b/test/trace_processor/profiling/heap_graph_native_size_test.sql
deleted file mode 100644
index fcef1f6..0000000
--- a/test/trace_processor/profiling/heap_graph_native_size_test.sql
+++ /dev/null
@@ -1,19 +0,0 @@
---
--- Copyright 2021 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 c.name as type_name,
-       o.native_size
-from heap_graph_object o join heap_graph_class c on o.type_id = c.id
-where o.root_type = "ROOT_JAVA_FRAME"
diff --git a/test/trace_processor/profiling/heap_graph_object_test.sql b/test/trace_processor/profiling/heap_graph_object_test.sql
deleted file mode 100644
index b7ad1b1..0000000
--- a/test/trace_processor/profiling/heap_graph_object_test.sql
+++ /dev/null
@@ -1,26 +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 o.id,
-       o.type,
-       o.upid,
-       o.graph_sample_ts,
-       o.self_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/profiling/heap_graph_reference_test.sql b/test/trace_processor/profiling/heap_graph_reference_test.sql
deleted file mode 100644
index f3b6894..0000000
--- a/test/trace_processor/profiling/heap_graph_reference_test.sql
+++ /dev/null
@@ -1,16 +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 * from heap_graph_reference
diff --git a/test/trace_processor/profiling/heap_graph_superclass.out b/test/trace_processor/profiling/heap_graph_superclass.out
deleted file mode 100644
index 9f31019..0000000
--- a/test/trace_processor/profiling/heap_graph_superclass.out
+++ /dev/null
@@ -1,7 +0,0 @@
-"id","superclass_id","name","superclass_name","location"
-0,"[NULL]","java.lang.Class<java.lang.Object>","[NULL]","l1"
-1,"[NULL]","java.lang.Class<MySuperClass>","[NULL]","l1"
-2,"[NULL]","java.lang.Class<MyChildClass>","[NULL]","l2"
-3,"[NULL]","java.lang.Object","[NULL]","l1"
-4,3,"MySuperClass","java.lang.Object","l1"
-5,4,"MyChildClass","MySuperClass","l2"
diff --git a/test/trace_processor/profiling/heap_graph_superclass_test.sql b/test/trace_processor/profiling/heap_graph_superclass_test.sql
deleted file mode 100644
index 6526d28..0000000
--- a/test/trace_processor/profiling/heap_graph_superclass_test.sql
+++ /dev/null
@@ -1,2 +0,0 @@
-SELECT c.id, c.superclass_id, c.name, s.name superclass_name, c.location
-FROM heap_graph_class c LEFT JOIN heap_graph_class s ON c.superclass_id = s.id;
\ No newline at end of file
diff --git a/test/trace_processor/profiling/heap_profile_deobfuscate.out b/test/trace_processor/profiling/heap_profile_deobfuscate.out
deleted file mode 100644
index 4381edc..0000000
--- a/test/trace_processor/profiling/heap_profile_deobfuscate.out
+++ /dev/null
@@ -1,2 +0,0 @@
-"deobfuscated_name","mapping","rel_pc"
-"Bar.function1",0,4096
diff --git a/test/trace_processor/profiling/heap_profile_dump_max.out b/test/trace_processor/profiling/heap_profile_dump_max.out
deleted file mode 100644
index 1ef5076..0000000
--- a/test/trace_processor/profiling/heap_profile_dump_max.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"id","type","ts","upid","heap_name","callsite_id","count","size"
-0,"heap_profile_allocation",-10,2,"malloc",2,6,1000
-1,"heap_profile_allocation",-10,2,"malloc",3,1,90
diff --git a/test/trace_processor/profiling/heap_profile_dump_max_legacy.out b/test/trace_processor/profiling/heap_profile_dump_max_legacy.out
deleted file mode 100644
index a5e6319..0000000
--- a/test/trace_processor/profiling/heap_profile_dump_max_legacy.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"id","type","ts","upid","heap_name","callsite_id","count","size"
-0,"heap_profile_allocation",-10,2,"malloc",2,0,1000
-1,"heap_profile_allocation",-10,2,"malloc",3,0,90
diff --git a/test/trace_processor/profiling/heap_profile_flamegraph_test.sql b/test/trace_processor/profiling/heap_profile_flamegraph_test.sql
deleted file mode 100644
index e1feb37..0000000
--- a/test/trace_processor/profiling/heap_profile_flamegraph_test.sql
+++ /dev/null
@@ -1,5 +0,0 @@
-select * from experimental_flamegraph
-where ts = 605908369259172
-    and upid = 1
-    and profile_type = 'native'
-limit 10;
diff --git a/test/trace_processor/profiling/heap_profile_frames_test.sql b/test/trace_processor/profiling/heap_profile_frames_test.sql
deleted file mode 100644
index b59602e..0000000
--- a/test/trace_processor/profiling/heap_profile_frames_test.sql
+++ /dev/null
@@ -1,16 +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 name, mapping, rel_pc FROM stack_profile_frame ORDER BY name;
diff --git a/test/trace_processor/profiling/heap_profile_jit.out b/test/trace_processor/profiling/heap_profile_jit.out
deleted file mode 100644
index 1235280..0000000
--- a/test/trace_processor/profiling/heap_profile_jit.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"name","mapping","rel_pc"
-"java_frame_1",0,4096
-"java_frame_2",0,4096
diff --git a/test/trace_processor/profiling/heap_profile_tracker_new_stack.out b/test/trace_processor/profiling/heap_profile_tracker_new_stack.out
deleted file mode 100644
index 9306208..0000000
--- a/test/trace_processor/profiling/heap_profile_tracker_new_stack.out
+++ /dev/null
@@ -1,5 +0,0 @@
-"id","type","ts","upid","heap_name","callsite_id","count","size"
-0,"heap_profile_allocation",0,0,"malloc",0,1,1
-1,"heap_profile_allocation",0,0,"malloc",0,-1,-1
-2,"heap_profile_allocation",1,0,"malloc",0,1,1
-3,"heap_profile_allocation",1,0,"malloc",0,-1,-1
diff --git a/test/trace_processor/profiling/heap_profile_tracker_new_stack_test.sql b/test/trace_processor/profiling/heap_profile_tracker_new_stack_test.sql
deleted file mode 100644
index efed7da..0000000
--- a/test/trace_processor/profiling/heap_profile_tracker_new_stack_test.sql
+++ /dev/null
@@ -1 +0,0 @@
-select * from heap_profile_allocation;
diff --git a/test/trace_processor/profiling/heap_profile_tracker_twoheaps.out b/test/trace_processor/profiling/heap_profile_tracker_twoheaps.out
deleted file mode 100644
index cc78466..0000000
--- a/test/trace_processor/profiling/heap_profile_tracker_twoheaps.out
+++ /dev/null
@@ -1,5 +0,0 @@
-"id","type","ts","upid","heap_name","callsite_id","count","size"
-0,"heap_profile_allocation",0,0,"malloc",0,1,1
-1,"heap_profile_allocation",0,0,"malloc",0,-1,-1
-2,"heap_profile_allocation",0,0,"custom",0,1,1
-3,"heap_profile_allocation",0,0,"custom",0,-1,-1
diff --git a/test/trace_processor/profiling/heap_profile_tracker_twoheaps_test.sql b/test/trace_processor/profiling/heap_profile_tracker_twoheaps_test.sql
deleted file mode 100644
index efed7da..0000000
--- a/test/trace_processor/profiling/heap_profile_tracker_twoheaps_test.sql
+++ /dev/null
@@ -1 +0,0 @@
-select * from heap_profile_allocation;
diff --git a/test/trace_processor/profiling/index b/test/trace_processor/profiling/index
deleted file mode 100644
index 733a29c..0000000
--- a/test/trace_processor/profiling/index
+++ /dev/null
@@ -1,64 +0,0 @@
-# Contains heap profiling, perf profiling and heap graph tests.
-
-heap_profile_jit.textproto heap_profile_frames_test.sql heap_profile_jit.out
-heap_profile_deobfuscate.textproto heap_profile_deobfuscate_test.sql heap_profile_deobfuscate.out
-heap_profile_deobfuscate_memfd.textproto heap_profile_deobfuscate_test.sql heap_profile_deobfuscate.out
-heap_profile_dump_max_legacy.textproto heap_profile_tracker_new_stack_test.sql heap_profile_dump_max_legacy.out
-heap_profile_dump_max.textproto heap_profile_tracker_new_stack_test.sql heap_profile_dump_max.out
-
-
-profiler_smaps.textproto profiler_smaps_test.sql profiler_smaps.out
-profiler_smaps.textproto profiler_smaps profiler_smaps_metric.out
-
-heap_graph_baseapk.textproto heap_graph_flamegraph_test.sql heap_graph_flamegraph.out
-heap_graph_baseapk.textproto heap_graph_object_test.sql heap_graph_object.out
-heap_graph_baseapk.textproto heap_graph_reference_test.sql heap_graph_reference.out
-heap_graph_deobfuscate_pkg.textproto heap_graph_object_test.sql heap_graph_object.out
-
-heap_graph_duplicate.textproto heap_graph_flamegraph_test.sql heap_graph_duplicate_flamegraph.out
-
-heap_graph.textproto heap_graph_flamegraph_test.sql heap_graph_flamegraph.out
-heap_graph.textproto heap_graph_object_test.sql heap_graph_object.out
-heap_graph.textproto heap_graph_reference_test.sql heap_graph_reference.out
-heap_graph_two_locations.textproto heap_graph_object_test.sql heap_graph_two_locations.out
-heap_graph_legacy.textproto heap_graph_object_test.sql heap_graph_object.out
-heap_graph_legacy.textproto heap_graph_reference_test.sql heap_graph_reference.out
-heap_graph_interleaved.textproto heap_graph_object_test.sql heap_graph_interleaved_object.out
-heap_graph_interleaved.textproto heap_graph_reference_test.sql heap_graph_interleaved_reference.out
-../../data/system-server-heap-graph-new.pftrace heap_graph_flamegraph_test.sql heap_graph_flamegraph_system-server-heap-graph.out
-../../data/system-server-native-profile heap_profile_flamegraph_test.sql heap_profile_flamegraph_system-server-native-profile.out
-heap_profile_tracker_new_stack.textproto heap_profile_tracker_new_stack_test.sql heap_profile_tracker_new_stack.out
-heap_profile_tracker_twoheaps.textproto heap_profile_tracker_twoheaps_test.sql heap_profile_tracker_twoheaps.out
-heap_graph_branching.textproto heap_graph_flamegraph_focused_test.sql heap_graph_flamegraph_focused.out
-heap_graph_superclass.textproto heap_graph_superclass_test.sql heap_graph_superclass.out
-heap_graph_native_size.textproto heap_graph_native_size_test.sql heap_graph_native_size.out
-# Regression test for b/222297079: when cumulative size in a flamegraph
-# overflows a signed 32-bit integer.
-heap_graph_huge_size.textproto heap_graph_flamegraph_matches_objects_test.sql heap_graph_flamegraph_matches_objects.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_test.sql heap_graph_flamegraph.out
-
-stack_profile_tracker_empty_callstack.textproto stack_profile_tracker_empty_callstack_test.sql stack_profile_tracker_empty_callstack.out
-
-# Metrics
-heap_profile_no_symbols.textproto unsymbolized_frames unsymbolized_frames.out
-simpleperf_event.py android_simpleperf simpleperf_event.out
-
-heap_graph.textproto java_heap_stats java_heap_stats.out
-heap_graph_closest_proc.textproto java_heap_stats heap_stats_closest_proc.out
-heap_graph.textproto java_heap_histogram java_heap_histogram.out
-
-# perf_sample table (traced_perf) with android R and S trace inputs.
-../../data/perf_sample.pb perf_sample_test.sql perf_sample_rvc.out
-../../data/perf_sample_sc.pb perf_sample_test.sql perf_sample_sc.out
-
-# this uses llvm-symbolizer to test the offline symbolization built into
-# trace_processor_shell.
-../../data/heapprofd_standalone_client_example-trace stack_profile_symbols_test.sql stack_profile_symbols.out
-../../data/callstack_sampling.pftrace callstack_sampling_flamegraph_test.sql callstack_sampling_flamegraph.out
-../../data/callstack_sampling.pftrace callstack_sampling_flamegraph_multi_process_test.sql callstack_sampling_flamegraph_multi_process.out
-
-heap_profile_data_local_tmp.textproto no_build_id.sql no_build_id.out
diff --git a/test/trace_processor/profiling/java_heap_stats.out b/test/trace_processor/profiling/java_heap_stats.out
deleted file mode 100644
index 6a1237f..0000000
--- a/test/trace_processor/profiling/java_heap_stats.out
+++ /dev/null
@@ -1,29 +0,0 @@
-java_heap_stats {
-  instance_stats {
-    upid: 2
-    process {
-      name: "system_server"
-      uid: 1000
-    }
-    samples {
-      ts: 10
-      heap_size: 1760
-      heap_native_size: 0
-      reachable_heap_size: 352
-      reachable_heap_native_size: 0
-      obj_count: 6
-      reachable_obj_count: 3
-      anon_rss_and_swap_size: 4096000
-      roots {
-        root_type: "ROOT_JAVA_FRAME"
-        type_name: "DeobfuscatedA[]"
-        obj_count: 1
-      }
-      roots {
-        root_type: "ROOT_JAVA_FRAME"
-        type_name: "FactoryProducerDelegateImplActor"
-        obj_count: 1
-      }
-    }
-  }
-}
diff --git a/test/trace_processor/profiling/no_build_id.out b/test/trace_processor/profiling/no_build_id.out
deleted file mode 100644
index ba2fd3f..0000000
--- a/test/trace_processor/profiling/no_build_id.out
+++ /dev/null
@@ -1,2 +0,0 @@
-"value"
-1
diff --git a/test/trace_processor/profiling/no_build_id.sql b/test/trace_processor/profiling/no_build_id.sql
deleted file mode 100644
index 46514db..0000000
--- a/test/trace_processor/profiling/no_build_id.sql
+++ /dev/null
@@ -1 +0,0 @@
-SELECT value FROM stats WHERE name = 'symbolization_tmp_build_id_not_found'
diff --git a/test/trace_processor/profiling/perf_sample_test.sql b/test/trace_processor/profiling/perf_sample_test.sql
deleted file mode 100644
index 5c0f1b4..0000000
--- a/test/trace_processor/profiling/perf_sample_test.sql
+++ /dev/null
@@ -1,31 +0,0 @@
---
--- 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 ps.ts, ps.cpu, ps.cpu_mode, ps.unwind_error, ps.perf_session_id,
-       pct.name cntr_name, pct.is_timebase,
-       thread.tid,
-       spf.name
-from experimental_annotated_callstack eac
-join perf_sample ps
-  on (eac.start_id == ps.callsite_id)
-join perf_counter_track pct
-  using(perf_session_id, cpu)
-join thread
-  using(utid)
-join stack_profile_frame spf
-  on (eac.frame_id == spf.id)
-order by ps.ts asc, eac.depth asc
-
diff --git a/test/trace_processor/profiling/profiler_smaps.out b/test/trace_processor/profiling/profiler_smaps.out
deleted file mode 100644
index cd0111e..0000000
--- a/test/trace_processor/profiling/profiler_smaps.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"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/profiling/profiler_smaps.textproto b/test/trace_processor/profiling/profiler_smaps.textproto
deleted file mode 100644
index 94d51a5..0000000
--- a/test/trace_processor/profiling/profiler_smaps.textproto
+++ /dev/null
@@ -1,35 +0,0 @@
-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/profiling/profiler_smaps_metric.out b/test/trace_processor/profiling/profiler_smaps_metric.out
deleted file mode 100644
index 1c83553..0000000
--- a/test/trace_processor/profiling/profiler_smaps_metric.out
+++ /dev/null
@@ -1,20 +0,0 @@
-profiler_smaps {
-  instance {
-    process {
-      name: "system_server"
-      uid: 1000
-    }
-    mappings {
-      path: "[anon: libc_malloc]"
-      size_kb: 30
-      private_dirty_kb: 10
-      swap_kb: 10
-    }
-    mappings {
-      path: "/system/lib64/libc.so"
-      size_kb: 20
-      private_dirty_kb: 4
-      swap_kb: 4
-    }
-  }
-}
diff --git a/test/trace_processor/profiling/profiler_smaps_test.sql b/test/trace_processor/profiling/profiler_smaps_test.sql
deleted file mode 100644
index 53e5fd2..0000000
--- a/test/trace_processor/profiling/profiler_smaps_test.sql
+++ /dev/null
@@ -1,2 +0,0 @@
-select id, type, upid, ts, path, size_kb, private_dirty_kb, swap_kb
-from profiler_smaps;
diff --git a/test/trace_processor/profiling/stack_profile_symbols.out b/test/trace_processor/profiling/stack_profile_symbols.out
deleted file mode 100644
index 64e7397..0000000
--- a/test/trace_processor/profiling/stack_profile_symbols.out
+++ /dev/null
@@ -1,5 +0,0 @@
-"name","source_file","line_number"
-"[NULL]","[NULL]",0
-"_start","??",0
-"(anonymous namespace)::OtherFn(unsigned int, unsigned long)","/builds/master/experiment/external/perfetto/out/linux_clang_release/../../src/profiling/memory/heapprofd_standalone_client_example.cc",24
-"main","/builds/master/experiment/external/perfetto/out/linux_clang_release/../../src/profiling/memory/heapprofd_standalone_client_example.cc",32
diff --git a/test/trace_processor/profiling/stack_profile_symbols_test.sql b/test/trace_processor/profiling/stack_profile_symbols_test.sql
deleted file mode 100644
index e57f84d..0000000
--- a/test/trace_processor/profiling/stack_profile_symbols_test.sql
+++ /dev/null
@@ -1 +0,0 @@
-select name, source_file, line_number from stack_profile_symbol
diff --git a/test/trace_processor/profiling/stack_profile_tracker_empty_callstack.out b/test/trace_processor/profiling/stack_profile_tracker_empty_callstack.out
deleted file mode 100644
index eb69505..0000000
--- a/test/trace_processor/profiling/stack_profile_tracker_empty_callstack.out
+++ /dev/null
@@ -1,2 +0,0 @@
-"count"
-0
diff --git a/test/trace_processor/profiling/stack_profile_tracker_empty_callstack.textproto b/test/trace_processor/profiling/stack_profile_tracker_empty_callstack.textproto
deleted file mode 100644
index c0b6dfe..0000000
--- a/test/trace_processor/profiling/stack_profile_tracker_empty_callstack.textproto
+++ /dev/null
@@ -1,45 +0,0 @@
-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/profiling/stack_profile_tracker_empty_callstack_test.sql b/test/trace_processor/profiling/stack_profile_tracker_empty_callstack_test.sql
deleted file mode 100644
index c61df0d..0000000
--- a/test/trace_processor/profiling/stack_profile_tracker_empty_callstack_test.sql
+++ /dev/null
@@ -1 +0,0 @@
-select count(1) as count from heap_profile_allocation;
diff --git a/test/trace_processor/profiling/unsymbolized_frames.out b/test/trace_processor/profiling/unsymbolized_frames.out
deleted file mode 100644
index e4474ac..0000000
--- a/test/trace_processor/profiling/unsymbolized_frames.out
+++ /dev/null
@@ -1,26 +0,0 @@
-unsymbolized_frames {
-  frames {
-    module: "/liblib.so"
-    build_id: "6275696c642d6964"
-    address: 4096
-    google_lookup_id: "6275696c642d6964"
-  }
-  frames {
-    module: "/liblib.so"
-    build_id: "6275696c642d6964"
-    address: 8192
-    google_lookup_id: "6275696c642d6964"
-  }
-  frames {
-    module: "/libmonochrome_64.so"
-    build_id: "7f0715c286f8b16c10e4ad349cda3b9b56c7a773"
-    address: 4096
-    google_lookup_id: "c215077ff8866cb110e4ad349cda3b9b0"
-  }
-  frames {
-    module: "/libmonochrome_64.so"
-    build_id: "7f0715c286f8b16c10e4ad349cda3b9b56c7a773"
-    address: 8192
-    google_lookup_id: "c215077ff8866cb110e4ad349cda3b9b0"
-  }
-}
diff --git a/test/trace_processor/scheduler/index b/test/trace_processor/scheduler/index
deleted file mode 100644
index 8bf1dba..0000000
--- a/test/trace_processor/scheduler/index
+++ /dev/null
@@ -1,2 +0,0 @@
-# Scheduler
-sched_cpu_util_cfs.textproto sched_cpu_util_cfs_test.sql sched_cpu_util_cfs.out
diff --git a/test/trace_processor/scheduler/sched_cpu_util_cfs.out b/test/trace_processor/scheduler/sched_cpu_util_cfs.out
deleted file mode 100644
index f59380d..0000000
--- a/test/trace_processor/scheduler/sched_cpu_util_cfs.out
+++ /dev/null
@@ -1,13 +0,0 @@
-"name","ts","value"
-"Cpu 6 Util",10000,1.000000
-"Cpu 6 Cap",10000,1004.000000
-"Cpu 6 Nr Running",10000,0.000000
-"Cpu 7 Util",11000,1.000000
-"Cpu 7 Cap",11000,1007.000000
-"Cpu 7 Nr Running",11000,0.000000
-"Cpu 4 Util",12000,43.000000
-"Cpu 4 Cap",12000,760.000000
-"Cpu 4 Nr Running",12000,0.000000
-"Cpu 5 Util",13000,125.000000
-"Cpu 5 Cap",13000,757.000000
-"Cpu 5 Nr Running",13000,1.000000
diff --git a/test/trace_processor/scheduler/sched_cpu_util_cfs_test.sql b/test/trace_processor/scheduler/sched_cpu_util_cfs_test.sql
deleted file mode 100644
index c1175fe..0000000
--- a/test/trace_processor/scheduler/sched_cpu_util_cfs_test.sql
+++ /dev/null
@@ -1,12 +0,0 @@
-SELECT
-  t.name,
-  c.ts,
-  c.value
-FROM
-  counter AS c
-  LEFT JOIN
-  counter_track AS t
-  ON c.track_id = t.id
-WHERE
-  name GLOB "Cpu ? Cap" OR name GLOB "Cpu ? Util" OR name GLOB "Cpu ? Nr Running"
-ORDER BY ts;
diff --git a/test/trace_processor/smoke/android_sched_and_ps_smoke.out b/test/trace_processor/smoke/android_sched_and_ps_smoke.out
deleted file mode 100644
index cd948f0..0000000
--- a/test/trace_processor/smoke/android_sched_and_ps_smoke.out
+++ /dev/null
@@ -1,11 +0,0 @@
-"ts","cpu","dur","end_state","priority","tid"
-81473010031230,2,78021,"S",120,26204
-81473010109251,2,12500,"R",120,0
-81473010121751,2,58021,"S",120,26205
-81473010179772,2,24114,"R",120,0
-81473010203886,2,30834,"S",120,26206
-81473010234720,2,43802,"R",120,0
-81473010278522,2,29948,"S",120,26207
-81473010308470,2,44322,"R",120,0
-81473010341386,1,158854,"S",116,23912
-81473010352792,2,32917,"S",120,26208
diff --git a/test/trace_processor/smoke/compressed_smoke.out b/test/trace_processor/smoke/compressed_smoke.out
deleted file mode 100644
index 422aaec..0000000
--- a/test/trace_processor/smoke/compressed_smoke.out
+++ /dev/null
@@ -1,11 +0,0 @@
-"ts","cpu","dur","end_state","priority","tid"
-170601497673450,2,53646,"DK",120,6790
-170601497691210,7,22917,"R",120,0
-170601497714127,7,29167,"D",120,6732
-170601497727096,2,55156,"S",120,62
-170601497743294,7,862656,"R",120,0
-170601497766106,3,13594,"S",120,8
-170601497779700,3,31094,"D",120,6790
-170601497782252,2,875313,"R",120,0
-170601497810794,3,824635,"R",120,0
-170601498605950,7,158333,"D",120,6732
diff --git a/test/trace_processor/smoke/index b/test/trace_processor/smoke/index
deleted file mode 100644
index 3a99661..0000000
--- a/test/trace_processor/smoke/index
+++ /dev/null
@@ -1,23 +0,0 @@
-# Contains smoke tests which test the most fundamentally important
-# features of trace processor
-#
-# Note: new tests here should only be added by the Perfetto team.
-
-# JSON trace parsing
-../../data/sfgate.json ../common/smoke_test.sql sfgate_smoke.out
-../../data/sfgate.json ../common/smoke_slices_test.sql sfgate_smoke_slices.out
-
-# Sched events
-../../data/android_sched_and_ps.pb ../common/smoke_test.sql android_sched_and_ps_smoke.out
-
-# Compresesed traces
-../../data/compressed.pb ../common/smoke_test.sql compressed_smoke.out
-
-# Sched events from sythetic trace
-../common/synth_1.py ../common/smoke_test.sql synth_1_smoke.out
-
-# Compute CPU time metric testing several core tables.
-../../data/example_android_trace_30s.pb thread_cpu_time_test.sql thread_cpu_time_example_android_trace_30s.out
-
-# Compute power proxy metric
-../../data/cpu_counters.pb proxy_power_test.sql proxy_power.out
diff --git a/test/trace_processor/smoke/proxy_power_test.sql b/test/trace_processor/smoke/proxy_power_test.sql
deleted file mode 100644
index e975273..0000000
--- a/test/trace_processor/smoke/proxy_power_test.sql
+++ /dev/null
@@ -1,35 +0,0 @@
---
--- 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/android_proxy_power.sql') AS suppress_query_output;
-
--- The test trace doesn't contain metadata necessary to determine the device
--- name, so we create a table with the name directly.
-DROP VIEW device;
-
-CREATE TABLE device (name STRING);
-
-INSERT INTO device VALUES ('walleye');
-
--- Select the top 10 threads by power usage.
-SELECT
-  tid,
-  SUM(dur * COALESCE(power_ma, 0) / 1e9) AS power_mas
-FROM power_per_thread
-JOIN thread USING (utid)
-GROUP BY utid
-ORDER BY power_mas DESC
-LIMIT 10;
diff --git a/test/trace_processor/smoke/sfgate_smoke.out b/test/trace_processor/smoke/sfgate_smoke.out
deleted file mode 100644
index 580e3d9..0000000
--- a/test/trace_processor/smoke/sfgate_smoke.out
+++ /dev/null
@@ -1 +0,0 @@
-"ts","cpu","dur","end_state","priority","tid"
diff --git a/test/trace_processor/smoke/sfgate_smoke_slices.out b/test/trace_processor/smoke/sfgate_smoke_slices.out
deleted file mode 100644
index d4e1c0f..0000000
--- a/test/trace_processor/smoke/sfgate_smoke_slices.out
+++ /dev/null
@@ -1,11 +0,0 @@
-"type","depth","count"
-"thread_track",0,16888
-"thread_track",1,19447
-"thread_track",2,5816
-"thread_track",3,829
-"thread_track",4,191
-"thread_track",5,94
-"thread_track",6,57
-"thread_track",7,19
-"thread_track",8,14
-"thread_track",9,2
diff --git a/test/trace_processor/smoke/synth_1_smoke.out b/test/trace_processor/smoke/synth_1_smoke.out
deleted file mode 100644
index 45bb0b3..0000000
--- a/test/trace_processor/smoke/synth_1_smoke.out
+++ /dev/null
@@ -1,9 +0,0 @@
-"ts","cpu","dur","end_state","priority","tid"
-1,0,99,"R",0,3
-50,1,70,"R",0,1
-100,0,15,"R",0,2
-115,0,-1,"[NULL]",0,3
-120,1,50,"R",0,2
-170,1,80,"R",0,0
-250,1,140,"R",0,2
-390,1,-1,"[NULL]",0,4
diff --git a/test/trace_processor/smoke/thread_cpu_time_test.sql b/test/trace_processor/smoke/thread_cpu_time_test.sql
deleted file mode 100644
index 93f101c..0000000
--- a/test/trace_processor/smoke/thread_cpu_time_test.sql
+++ /dev/null
@@ -1,32 +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
-  tid,
-  pid,
-  thread.name as threadName,
-  process.name as processName,
-  total_dur as totalDur
-from
-  thread
-  left join process using(upid)
-  left join
-    (select upid, sum(dur) as total_dur
-      from sched join thread using(utid)
-      where dur != -1
-      group by upid
-    ) using(upid)
-where utid != 0
-order by total_dur desc, pid, tid
diff --git a/test/trace_processor/span_join/index b/test/trace_processor/span_join/index
deleted file mode 100644
index 6c3a714..0000000
--- a/test/trace_processor/span_join/index
+++ /dev/null
@@ -1,34 +0,0 @@
-# Contains tests on the behaviour of the SPAN_JOIN operator table.
-
-# Smoke tests
-../common/synth_1.py span_join_unordered_cols_test.sql span_join_unordered_cols_synth_1.out
-../common/synth_1.py span_join_unordered_cols_reverse_test.sql span_join_unordered_cols_synth_1.out
-../../data/android_sched_and_ps.pb span_join_zero_negative_dur_test.sql span_join_zero_negative_dur.out
-
-# Regression tests
-../../data/android_sched_and_ps.pb slice_span_join_b118665515_test.sql android_sched_and_ps_slice_span_join_b118665515.out
-../../data/android_sched_and_ps.pb span_join_unpartitioned_empty_test.sql span_join_unpartitioned_empty.out
-
-# Outer join
-../common/synth_1.py span_outer_join_test.sql span_outer_join.out
-
-../common/synth_1.py span_outer_join_empty_test.sql span_outer_join_empty.out
-../common/synth_1.py span_outer_join_unpartitioned_empty_test.sql span_outer_join_unpartitioned_empty.out
-../common/synth_1.py span_outer_join_unpartitioned_left_empty_test.sql span_outer_join_unpartitioned_left_empty.out
-../common/synth_1.py span_outer_join_unpartitioned_right_empty_test.sql span_outer_join_unpartitioned_right_empty.out
-
-../common/synth_1.py span_outer_join_mixed_test.sql span_outer_join_mixed.out
-../common/synth_1.py span_outer_join_mixed_empty_test.sql span_outer_join_mixed_empty.out
-../common/synth_1.py span_outer_join_mixed_left_empty_test.sql span_outer_join_mixed_left_empty.out
-../common/synth_1.py span_outer_join_mixed_left_empty_rev_test.sql span_outer_join_mixed_left_empty_rev.out
-../common/synth_1.py span_outer_join_mixed_right_empty_test.sql span_outer_join_mixed_right_empty.out
-../common/synth_1.py span_outer_join_mixed_right_empty_rev_test.sql span_outer_join_mixed_right_empty_rev.out
-../common/synth_1.py span_outer_join_mixed_test.sql span_outer_join_mixed.out
-
-# Left join
-../common/synth_1.py span_left_join_test.sql span_left_join.out
-../common/synth_1.py span_left_join_unpartitioned_test.sql span_left_join_unpartitioned.out
-../common/synth_1.py span_left_join_left_unpartitioned_test.sql span_left_join_left_unpartitioned.out
-../common/synth_1.py span_left_join_left_partitioned_test.sql span_left_join_left_partitioned.out
-../common/synth_1.py span_left_join_empty_right_test.sql span_left_join_empty_right.out
-../common/synth_1.py span_left_join_unordered_test.sql span_left_join_unordered_android_sched_and_ps.out
diff --git a/test/trace_processor/span_join/slice_span_join_b118665515_test.sql b/test/trace_processor/span_join/slice_span_join_b118665515_test.sql
deleted file mode 100644
index 34bacf0..0000000
--- a/test/trace_processor/span_join/slice_span_join_b118665515_test.sql
+++ /dev/null
@@ -1,22 +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.
---
-create virtual table window_8 using window;
-
-create virtual table span_8 using span_join(sched PARTITIONED cpu, window_8);
-
-update window_8 set window_start=81473010031230, window_dur=19684693341, quantum=10000000 where rowid = 0;
-
-select quantum_ts as bucket, sum(dur)/cast(10000000 as float) as utilization from span_8 where cpu = 7 and utid != 0 group by quantum_ts;
diff --git a/test/trace_processor/span_join/span_join_unordered_cols_reverse_test.sql b/test/trace_processor/span_join/span_join_unordered_cols_reverse_test.sql
deleted file mode 100644
index e533e0d..0000000
--- a/test/trace_processor/span_join/span_join_unordered_cols_reverse_test.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.
---
-create table t1(
-  a1 STRING,
-  a2 BIG INT,
-  dur BIG INT,
-  a3 BIG INT,
-  ts BIG INT PRIMARY KEY
-) without rowid;
-
-INSERT INTO t1(a1, a2, dur, a3, ts)
-VALUES
-("A", 1, 10, 100, 0),
-("B", 2, 90, 101, 10),
-("C", 3, 1, 102, 100);
-
-create table t2(
-  b1 STRING,
-  ts BIG INT,
-  b2 BIG INT,
-  part BIG INT,
-  dur BIG INT,
-  b3 BIG INT,
-  PRIMARY KEY (part, ts)
-) without rowid;
-
-INSERT INTO t2(b1, ts, b2, part, dur, b3)
-VALUES
-("A", 10, 10, 0, 90, 100),
-("B", 100, 90, 0, 10, 200),
-("C", 110, 1, 0, 5, 300),
-("A", 5, 10, 1, 45, 100),
-("B", 50, 90, 1, 40, 200),
-("C", 90, 1, 1, 100, 300);
-
-create virtual table sp using span_join(t2 PARTITIONED part, t1);
-
-select ts,dur,part,b1,b2,b3,a1,a2,a3 from sp;
diff --git a/test/trace_processor/span_join/span_join_unordered_cols_synth_1.out b/test/trace_processor/span_join/span_join_unordered_cols_synth_1.out
deleted file mode 100644
index b711b69..0000000
--- a/test/trace_processor/span_join/span_join_unordered_cols_synth_1.out
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-"ts","dur","part","b1","b2","b3","a1","a2","a3"
-10,90,0,"A",10,100,"B",2,101
-100,1,0,"B",90,200,"C",3,102
-5,5,1,"A",10,100,"A",1,100
-10,40,1,"A",10,100,"B",2,101
-50,40,1,"B",90,200,"B",2,101
-90,10,1,"C",1,300,"B",2,101
-100,1,1,"C",1,300,"C",3,102
diff --git a/test/trace_processor/span_join/span_join_unordered_cols_test.sql b/test/trace_processor/span_join/span_join_unordered_cols_test.sql
deleted file mode 100644
index 5cc1645..0000000
--- a/test/trace_processor/span_join/span_join_unordered_cols_test.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.
---
-create table t1(
-  a1 STRING,
-  a2 BIG INT,
-  dur BIG INT,
-  a3 BIG INT,
-  ts BIG INT PRIMARY KEY
-) without rowid;
-
-INSERT INTO t1(a1, a2, dur, a3, ts)
-VALUES
-("A", 1, 10, 100, 0),
-("B", 2, 90, 101, 10),
-("C", 3, 1, 102, 100);
-
-create table t2(
-  b1 STRING,
-  ts BIG INT,
-  b2 BIG INT,
-  part BIG INT,
-  dur BIG INT,
-  b3 BIG INT,
-  PRIMARY KEY (part, ts)
-) without rowid;
-
-INSERT INTO t2(b1, ts, b2, part, dur, b3)
-VALUES
-("A", 10, 10, 0, 90, 100),
-("B", 100, 90, 0, 10, 200),
-("C", 110, 1, 0, 5, 300),
-("A", 5, 10, 1, 45, 100),
-("B", 50, 90, 1, 40, 200),
-("C", 90, 1, 1, 100, 300);
-
-create virtual table sp using span_join(t1, t2 PARTITIONED part);
-
-select ts,dur,part,b1,b2,b3,a1,a2,a3 from sp;
diff --git a/test/trace_processor/span_join/span_join_unpartitioned_empty.out b/test/trace_processor/span_join/span_join_unpartitioned_empty.out
deleted file mode 100644
index 882336b..0000000
--- a/test/trace_processor/span_join/span_join_unpartitioned_empty.out
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-"ts","dur"
diff --git a/test/trace_processor/span_join/span_join_unpartitioned_empty_test.sql b/test/trace_processor/span_join/span_join_unpartitioned_empty_test.sql
deleted file mode 100644
index 40ca9f0..0000000
--- a/test/trace_processor/span_join/span_join_unpartitioned_empty_test.sql
+++ /dev/null
@@ -1,38 +0,0 @@
---
--- 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.
---
-
--- Model that the first table is empty and the second is has some data.
-create table t1(
-  ts BIG INT,
-  dur BIG INT,
-  PRIMARY KEY (ts, dur)
-) without rowid;
-
-create table t2(
-  ts BIG INT,
-  dur BIG INT,
-  PRIMARY KEY (ts, dur)
-) without rowid;
-
-INSERT INTO t2(ts, dur)
-VALUES
-(1, 2),
-(5, 0),
-(1, 1);
-
-create virtual table sp using span_join(t1, t2);
-
-select ts, dur from sp;
diff --git a/test/trace_processor/span_join/span_join_zero_negative_dur.out b/test/trace_processor/span_join/span_join_zero_negative_dur.out
deleted file mode 100644
index 390a1e1..0000000
--- a/test/trace_processor/span_join/span_join_zero_negative_dur.out
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-"ts","dur","part"
-1,0,0
-1,2,0
-5,-1,0
-5,-1,0
-1,1,1
-2,0,1
diff --git a/test/trace_processor/span_join/span_join_zero_negative_dur_test.sql b/test/trace_processor/span_join/span_join_zero_negative_dur_test.sql
deleted file mode 100644
index f3cece8..0000000
--- a/test/trace_processor/span_join/span_join_zero_negative_dur_test.sql
+++ /dev/null
@@ -1,44 +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.
---
-create table t1(
-  ts BIG INT,
-  dur BIG INT,
-  part BIG INT,
-  PRIMARY KEY (part, ts, dur)
-) without rowid;
-
-INSERT INTO t1(ts, dur, part)
-VALUES
-(1, 0, 0),
-(5, -1, 0),
-(2, 0, 1);
-
-create table t2(
-  ts BIG INT,
-  dur BIG INT,
-  part BIG INT,
-  PRIMARY KEY (part, ts, dur)
-) without rowid;
-
-INSERT INTO t2(ts, dur, part)
-VALUES
-(1, 2, 0),
-(5, 0, 0),
-(1, 1, 1);
-
-create virtual table sp using span_outer_join(t1 PARTITIONED part, t2 PARTITIONED part);
-
-select ts,dur,part from sp;
diff --git a/test/trace_processor/span_join/span_left_join_empty_right.out b/test/trace_processor/span_join/span_left_join_empty_right.out
deleted file mode 100644
index de527a7..0000000
--- a/test/trace_processor/span_join/span_left_join_empty_right.out
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-"ts","dur","part"
-500,500,100
diff --git a/test/trace_processor/span_join/span_left_join_empty_right_test.sql b/test/trace_processor/span_join/span_left_join_empty_right_test.sql
deleted file mode 100644
index 3502780..0000000
--- a/test/trace_processor/span_join/span_left_join_empty_right_test.sql
+++ /dev/null
@@ -1,37 +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.
---
-create table t1(
-  ts BIG INT,
-  dur BIG INT,
-  part BIG INT,
-  PRIMARY KEY (part, ts)
-) without rowid;
-
-create table t2(
-  ts BIG INT,
-  dur BIG INT,
-  part BIG INT,
-  PRIMARY KEY (part, ts)
-) without rowid;
-
-INSERT INTO t1(ts, dur, part)
-VALUES
-(500, 500, 100);
-
-create virtual table sp using span_left_join(t1 PARTITIONED part,
-                                             t2 PARTITIONED part);
-
-select * from sp;
diff --git a/test/trace_processor/span_join/span_left_join_left_partitioned_test.sql b/test/trace_processor/span_join/span_left_join_left_partitioned_test.sql
deleted file mode 100644
index e5a6319..0000000
--- a/test/trace_processor/span_join/span_left_join_left_partitioned_test.sql
+++ /dev/null
@@ -1,49 +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.
---
-create table t1(
-  ts BIG INT,
-  dur BIG INT,
-  part BIG INT,
-  a BIG INT,
-  PRIMARY KEY (part, ts)
-) without rowid;
-
-create table t2(
-  ts BIG INT,
-  dur BIG INT,
-  b BIG INT,
-  PRIMARY KEY (ts)
-) without rowid;
-
--- Then insert some rows into t1 in part 1, 3, 4 and 5.
-INSERT INTO t1(ts, dur, part, a)
-VALUES
-(100, 400, 1, 111),
-(500, 150, 1, 111),
-(500, 50, 3, 222),
-(500, 100, 4, 333),
-(0, 1000, 5, 444);
-
--- Then insert some rows into t2.
-INSERT INTO t2(ts, dur, b)
-VALUES
-(50, 200, 111),
-(300, 100, 222),
-(400, 250, 333);
-
-create virtual table sp using span_left_join(t1 PARTITIONED part, t2);
-
-select * from sp;
diff --git a/test/trace_processor/span_join/span_left_join_left_unpartitioned_test.sql b/test/trace_processor/span_join/span_left_join_left_unpartitioned_test.sql
deleted file mode 100644
index f7228a3..0000000
--- a/test/trace_processor/span_join/span_left_join_left_unpartitioned_test.sql
+++ /dev/null
@@ -1,75 +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.
---
-create table t1(
-  ts BIG INT,
-  dur BIG INT,
-  a BIG INT,
-  PRIMARY KEY (ts)
-) without rowid;
-
-create table t2(
-  ts BIG INT,
-  dur BIG INT,
-  b BIG INT,
-  part BIG INT,
-  PRIMARY KEY (part, ts)
-) without rowid;
-
--- Insert some rows into t2 which are in part 0 and 1 but before t1's rows.
-INSERT INTO t2(ts, dur, part, b)
-VALUES
-(0, 25, 0, 111),
-(50, 50, 0, 222),
-(0, 40, 1, 333);
-
--- Then insert some rows into t1 in part 1, 3, 4 and 5.
-INSERT INTO t1(ts, dur, a)
-VALUES
-(100, 400, 111),
-(500, 50, 222),
-(600, 100, 333),
-(900, 100, 444);
-
--- Insert a row into t2 which should be split up by t1's first row.
-INSERT INTO t2(ts, dur, part, b) VALUES (50, 200, 1, 444);
-
--- Insert a row into t2 should should be completely covered by t1's first row.
-INSERT INTO t2(ts, dur, part, b) VALUES (300, 100, 1, 555);
-
--- Insert a row into t2 which should span between t1's first and second rows.
-INSERT INTO t2(ts, dur, part, b) VALUES (400, 250, 1, 666);
-
--- Insert a row into t2 in partition 2.
-INSERT INTO t2(ts, dur, part, b) VALUES (100, 1000, 2, 777);
-
--- Insert a row into t2 before t1's first row in partition 4.
-INSERT INTO t2(ts, dur, part, b) VALUES (50, 50, 4, 888);
-
--- Insert a row into t2 which perfectly matches the second row in partition 4.
-INSERT INTO t2(ts, dur, part, b) VALUES (500, 50, 4, 999);
-
--- Insert a row into t2 which intersects the first row of partition 5.
-INSERT INTO t2(ts, dur, part, b) VALUES (50, 75, 5, 1111);
-
--- Insert a row into t2 which intersects the third row of partition 5.
-INSERT INTO t2(ts, dur, part, b) VALUES (525, 75, 5, 2222);
-
--- Insert a row into t2 which misses everything in partition 6.
-INSERT INTO t2(ts, dur, part, b) VALUES (0, 100, 6, 2222);
-
-create virtual table sp using span_left_join(t1, t2 PARTITIONED part);
-
-select * from sp;
diff --git a/test/trace_processor/span_join/span_left_join_test.sql b/test/trace_processor/span_join/span_left_join_test.sql
deleted file mode 100644
index 7e8f23e..0000000
--- a/test/trace_processor/span_join/span_left_join_test.sql
+++ /dev/null
@@ -1,78 +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.
---
-create table t1(
-  ts BIG INT,
-  dur BIG INT,
-  part BIG INT,
-  a BIG INT,
-  PRIMARY KEY (part, ts)
-) without rowid;
-
-create table t2(
-  ts BIG INT,
-  dur BIG INT,
-  part BIG INT,
-  b BIG INT,
-  PRIMARY KEY (part, ts)
-) without rowid;
-
--- Insert some rows into t2 which are in part 0 and 1 but before t1's rows.
-INSERT INTO t2(ts, dur, part, b)
-VALUES
-(0, 100, 0, 111),
-(100, 200, 0, 222),
-(0, 50, 1, 333);
-
--- Then insert some rows into t1 in part 1, 3, 4 and 5.
-INSERT INTO t1(ts, dur, part, a)
-VALUES
-(100, 400, 1, 111),
-(500, 50, 1, 222),
-(600, 100, 1, 333),
-(500, 100, 3, 444),
-(100, 100, 4, 555),
-(200, 50, 4, 666),
-(250, 50, 4, 777),
-(100, 100, 5, 888);
-
--- Insert a row into t2 which should be split up by t1's first row.
-INSERT INTO t2(ts, dur, part, b) VALUES (50, 200, 1, 444);
-
--- Insert a row into t2 should should be completely covered by t1's first row.
-INSERT INTO t2(ts, dur, part, b) VALUES (300, 100, 1, 555);
-
--- Insert a row into t2 which should span between t1's first and second rows.
-INSERT INTO t2(ts, dur, part, b) VALUES (400, 250, 1, 666);
-
--- Insert a row into t2 in partition 2.
-INSERT INTO t2(ts, dur, part, b) VALUES (100, 1000, 2, 777);
-
--- Insert a row into t2 before t1's first row in partition 4.
-INSERT INTO t2(ts, dur, part, b) VALUES (50, 50, 4, 888);
-
--- Insert a row into t2 which perfectly matches the second row in partition 4.
-INSERT INTO t2(ts, dur, part, b) VALUES (200, 50, 4, 999);
-
--- Insert a row into t2 which intersects the first row of partition 5.
-INSERT INTO t2(ts, dur, part, b) VALUES (125, 50, 5, 1111);
-
--- Insert a row into t2 which intersects the first row of partition 5.
-INSERT INTO t2(ts, dur, part, b) VALUES (190, 20, 5, 2222);
-
-create virtual table sp using span_left_join(t1 PARTITIONED part,
-                                             t2 PARTITIONED part);
-
-select * from sp;
diff --git a/test/trace_processor/span_join/span_left_join_unordered_android_sched_and_ps.out b/test/trace_processor/span_join/span_left_join_unordered_android_sched_and_ps.out
deleted file mode 100644
index d2bd151..0000000
--- a/test/trace_processor/span_join/span_left_join_unordered_android_sched_and_ps.out
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-"ts","dur","part"
-500,100,10
diff --git a/test/trace_processor/span_join/span_left_join_unordered_test.sql b/test/trace_processor/span_join/span_left_join_unordered_test.sql
deleted file mode 100644
index 713365d..0000000
--- a/test/trace_processor/span_join/span_left_join_unordered_test.sql
+++ /dev/null
@@ -1,26 +0,0 @@
-create table t1(
-  ts BIG INT,
-  dur BIG INT,
-  part BIG INT,
-  PRIMARY KEY (part, ts)
-) without rowid;
-
-create table t2(
-  ts BIG INT,
-  dur BIG INT,
-  part BIG INT,
-  PRIMARY KEY (part, ts)
-) without rowid;
-
--- Insert a single row into t1.
-INSERT INTO t1(ts, dur, part)
-VALUES (500, 100, 10);
-
--- Insert a single row into t2.
-INSERT INTO t2(ts, dur, part)
-VALUES (500, 100, 5);
-
-create virtual table sp using span_left_join(t1 PARTITIONED part,
-                                             t2 PARTITIONED part);
-
-select * from sp;
diff --git a/test/trace_processor/span_join/span_left_join_unpartitioned_test.sql b/test/trace_processor/span_join/span_left_join_unpartitioned_test.sql
deleted file mode 100644
index a0045e8..0000000
--- a/test/trace_processor/span_join/span_left_join_unpartitioned_test.sql
+++ /dev/null
@@ -1,49 +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.
---
-create table t1(
-  ts BIG INT,
-  dur BIG INT,
-  a BIG INT,
-  PRIMARY KEY (ts)
-) without rowid;
-
-create table t2(
-  ts BIG INT,
-  dur BIG INT,
-  b BIG INT,
-  PRIMARY KEY (ts)
-) without rowid;
-
--- Then insert some rows into t1 in part 1, 3, 4 and 5.
-INSERT INTO t1(ts, dur, a)
-VALUES
-(100, 400, 111),
-(500, 50, 222),
-(600, 100, 333),
-(900, 100, 444);
-
--- Insert a row into t2 which should be split up by t1's first row.
-INSERT INTO t2(ts, dur, b) VALUES (50, 200, 111);
-
--- Insert a row into t2 should should be completely covered by t1's first row.
-INSERT INTO t2(ts, dur, b) VALUES (300, 100, 222);
-
--- Insert a row into t2 which should span between t1's first and second rows.
-INSERT INTO t2(ts, dur, b) VALUES (400, 250, 333);
-
-create virtual table sp using span_left_join(t1, t2);
-
-select * from sp;
diff --git a/test/trace_processor/span_join/span_outer_join_empty.out b/test/trace_processor/span_join/span_outer_join_empty.out
deleted file mode 100644
index a64e00b..0000000
--- a/test/trace_processor/span_join/span_outer_join_empty.out
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-"ts","dur","part"
-500,100,10
diff --git a/test/trace_processor/span_join/span_outer_join_empty_test.sql b/test/trace_processor/span_join/span_outer_join_empty_test.sql
deleted file mode 100644
index c6db65c..0000000
--- a/test/trace_processor/span_join/span_outer_join_empty_test.sql
+++ /dev/null
@@ -1,39 +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.
---
-create table t1(
-  ts BIG INT,
-  dur BIG INT,
-  part BIG INT,
-  PRIMARY KEY (part, ts)
-) without rowid;
-
-create table t2(
-  ts BIG INT,
-  dur BIG INT,
-  part BIG INT,
-  PRIMARY KEY (part, ts)
-) without rowid;
-
--- Insert a single row into t1.
-INSERT INTO t1(ts, dur, part)
-VALUES (500, 100, 10);
-
--- t2 is empty.
-
-create virtual table sp using span_outer_join(t1 PARTITIONED part,
-                                             t2 PARTITIONED part);
-
-select * from sp;
diff --git a/test/trace_processor/span_join/span_outer_join_mixed_empty.out b/test/trace_processor/span_join/span_outer_join_mixed_empty.out
deleted file mode 100644
index 29a8f1b..0000000
--- a/test/trace_processor/span_join/span_outer_join_mixed_empty.out
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-"ts","dur","part"
diff --git a/test/trace_processor/span_join/span_outer_join_mixed_empty_test.sql b/test/trace_processor/span_join/span_outer_join_mixed_empty_test.sql
deleted file mode 100644
index f5dc5ba..0000000
--- a/test/trace_processor/span_join/span_outer_join_mixed_empty_test.sql
+++ /dev/null
@@ -1,33 +0,0 @@
---
--- Copyright 2021 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 table t1(
-  ts BIG INT,
-  dur BIG INT,
-  part BIG INT,
-  PRIMARY KEY (part, ts)
-) without rowid;
-
-create table t2(
-  ts BIG INT,
-  dur BIG INT,
-  PRIMARY KEY (ts)
-) without rowid;
-
--- t1 and t2 are empty.
-
-create virtual table sp using span_outer_join(t1 PARTITIONED part, t2);
-
-select * from sp;
diff --git a/test/trace_processor/span_join/span_outer_join_mixed_left_empty.out b/test/trace_processor/span_join/span_outer_join_mixed_left_empty.out
deleted file mode 100644
index 75e52f0..0000000
--- a/test/trace_processor/span_join/span_outer_join_mixed_left_empty.out
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-"ts","dur","part"
diff --git a/test/trace_processor/span_join/span_outer_join_mixed_left_empty_rev.out b/test/trace_processor/span_join/span_outer_join_mixed_left_empty_rev.out
deleted file mode 100644
index f43c5e2..0000000
--- a/test/trace_processor/span_join/span_outer_join_mixed_left_empty_rev.out
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-"ts","dur","part"
-100,400,0
-100,50,1
-600,100,1
diff --git a/test/trace_processor/span_join/span_outer_join_mixed_left_empty_rev_test.sql b/test/trace_processor/span_join/span_outer_join_mixed_left_empty_rev_test.sql
deleted file mode 100644
index 5926fb3..0000000
--- a/test/trace_processor/span_join/span_outer_join_mixed_left_empty_rev_test.sql
+++ /dev/null
@@ -1,37 +0,0 @@
---
--- Copyright 2021 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 table t1(
-  ts BIG INT,
-  dur BIG INT,
-  part BIG INT,
-  PRIMARY KEY (part, ts)
-) without rowid;
-
-create table t2(
-  ts BIG INT,
-  dur BIG INT,
-  PRIMARY KEY (ts)
-) without rowid;
-
-INSERT INTO t1(ts, dur, part)
-VALUES
-(100, 400, 0),
-(100, 50, 1),
-(600, 100, 1);
-
-create virtual table sp using span_outer_join(t2, t1 PARTITIONED part);
-
-select * from sp;
diff --git a/test/trace_processor/span_join/span_outer_join_mixed_left_empty_test.sql b/test/trace_processor/span_join/span_outer_join_mixed_left_empty_test.sql
deleted file mode 100644
index 2eeae27..0000000
--- a/test/trace_processor/span_join/span_outer_join_mixed_left_empty_test.sql
+++ /dev/null
@@ -1,37 +0,0 @@
---
--- Copyright 2021 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 table t1(
-  ts BIG INT,
-  dur BIG INT,
-  part BIG INT,
-  PRIMARY KEY (part, ts)
-) without rowid;
-
-create table t2(
-  ts BIG INT,
-  dur BIG INT,
-  PRIMARY KEY (ts)
-) without rowid;
-
-INSERT INTO t2(ts, dur)
-VALUES
-(100, 400),
-(500, 50),
-(600, 100);
-
-create virtual table sp using span_outer_join(t1 PARTITIONED part, t2);
-
-select * from sp;
diff --git a/test/trace_processor/span_join/span_outer_join_mixed_right_empty.out b/test/trace_processor/span_join/span_outer_join_mixed_right_empty.out
deleted file mode 100644
index 31a5fbb..0000000
--- a/test/trace_processor/span_join/span_outer_join_mixed_right_empty.out
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-"ts","dur","part","b"
-100,400,0,"[NULL]"
-100,50,1,"[NULL]"
-600,100,1,"[NULL]"
diff --git a/test/trace_processor/span_join/span_outer_join_mixed_right_empty_rev.out b/test/trace_processor/span_join/span_outer_join_mixed_right_empty_rev.out
deleted file mode 100644
index 724e3b4..0000000
--- a/test/trace_processor/span_join/span_outer_join_mixed_right_empty_rev.out
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-"ts","dur","part","b"
diff --git a/test/trace_processor/span_join/span_outer_join_mixed_right_empty_rev_test.sql b/test/trace_processor/span_join/span_outer_join_mixed_right_empty_rev_test.sql
deleted file mode 100644
index f325cc4..0000000
--- a/test/trace_processor/span_join/span_outer_join_mixed_right_empty_rev_test.sql
+++ /dev/null
@@ -1,38 +0,0 @@
---
--- Copyright 2021 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 table t1(
-  ts BIG INT,
-  dur BIG INT,
-  part BIG INT,
-  PRIMARY KEY (part, ts)
-) without rowid;
-
-create table t2(
-  ts BIG INT,
-  dur BIG INT,
-  b BIG INT,
-  PRIMARY KEY (ts)
-) without rowid;
-
-INSERT INTO t2(ts, dur)
-VALUES
-(100, 400),
-(500, 50),
-(600, 100);
-
-create virtual table sp using span_outer_join(t2, t1 PARTITIONED part);
-
-select * from sp;
diff --git a/test/trace_processor/span_join/span_outer_join_mixed_right_empty_test.sql b/test/trace_processor/span_join/span_outer_join_mixed_right_empty_test.sql
deleted file mode 100644
index 3001d28..0000000
--- a/test/trace_processor/span_join/span_outer_join_mixed_right_empty_test.sql
+++ /dev/null
@@ -1,38 +0,0 @@
---
--- Copyright 2021 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 table t1(
-  ts BIG INT,
-  dur BIG INT,
-  part BIG INT,
-  PRIMARY KEY (part, ts)
-) without rowid;
-
-create table t2(
-  ts BIG INT,
-  dur BIG INT,
-  b BIG INT,
-  PRIMARY KEY (ts)
-) without rowid;
-
-INSERT INTO t1(ts, dur, part)
-VALUES
-(100, 400, 0),
-(100, 50, 1),
-(600, 100, 1);
-
-create virtual table sp using span_outer_join(t1 PARTITIONED part, t2);
-
-select * from sp;
diff --git a/test/trace_processor/span_join/span_outer_join_mixed_test.sql b/test/trace_processor/span_join/span_outer_join_mixed_test.sql
deleted file mode 100644
index b05b18e..0000000
--- a/test/trace_processor/span_join/span_outer_join_mixed_test.sql
+++ /dev/null
@@ -1,49 +0,0 @@
---
--- Copyright 2021 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 table t1(
-  ts BIG INT,
-  dur BIG INT,
-  part BIG INT,
-  a BIG INT,
-  PRIMARY KEY (part, ts)
-) without rowid;
-
-create table t2(
-  ts BIG INT,
-  dur BIG INT,
-  b BIG INT,
-  PRIMARY KEY (ts)
-) without rowid;
-
--- Add some rows to t1.
-INSERT INTO t1(ts, dur, part, a)
-VALUES
-(100, 400, 1, 10),
-(500, 100, 1, 11),
-(500, 50, 2, 12),
-(600, 100, 3, 13);
-
--- Add some rows to t2.
-INSERT INTO t2(ts, dur, b)
-VALUES
-(50, 100, 14),
-(550, 50, 15),
-(600, 50, 16),
-(900, 500, 17);
-
-create virtual table sp using span_outer_join(t1 PARTITIONED part, t2);
-
-select * from sp;
diff --git a/test/trace_processor/span_join/span_outer_join_test.sql b/test/trace_processor/span_join/span_outer_join_test.sql
deleted file mode 100644
index 294e863..0000000
--- a/test/trace_processor/span_join/span_outer_join_test.sql
+++ /dev/null
@@ -1,78 +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.
---
-create table t1(
-  ts BIG INT,
-  dur BIG INT,
-  part BIG INT,
-  a BIG INT,
-  PRIMARY KEY (part, ts)
-) without rowid;
-
-create table t2(
-  ts BIG INT,
-  dur BIG INT,
-  part BIG INT,
-  b BIG INT,
-  PRIMARY KEY (part, ts)
-) without rowid;
-
--- Insert some rows into t2 which are in part 0 and 1 but before t1's rows.
-INSERT INTO t2(ts, dur, part, b)
-VALUES
-(0, 100, 0, 111),
-(100, 200, 0, 222),
-(0, 50, 1, 333);
-
--- Then insert some rows into t1 in part 1, 3, 4 and 5.
-INSERT INTO t1(ts, dur, part, a)
-VALUES
-(100, 400, 1, 111),
-(500, 50, 1, 222),
-(600, 100, 1, 333),
-(500, 100, 3, 444),
-(100, 100, 4, 555),
-(200, 50, 4, 666),
-(250, 50, 4, 777),
-(100, 100, 5, 888);
-
--- Insert a row into t2 which should be split up by t1's first row.
-INSERT INTO t2(ts, dur, part, b) VALUES (50, 200, 1, 444);
-
--- Insert a row into t2 should should be completely covered by t1's first row.
-INSERT INTO t2(ts, dur, part, b) VALUES (300, 100, 1, 555);
-
--- Insert a row into t2 which should span between t1's first and second rows.
-INSERT INTO t2(ts, dur, part, b) VALUES (400, 250, 1, 666);
-
--- Insert a row into t2 in partition 2.
-INSERT INTO t2(ts, dur, part, b) VALUES (100, 1000, 2, 777);
-
--- Insert a row into t2 before t1's first row in partition 4.
-INSERT INTO t2(ts, dur, part, b) VALUES (50, 50, 4, 888);
-
--- Insert a row into t2 which perfectly matches the second row in partition 4.
-INSERT INTO t2(ts, dur, part, b) VALUES (200, 50, 4, 999);
-
--- Insert a row into t2 which intersects the first row of partition 5.
-INSERT INTO t2(ts, dur, part, b) VALUES (125, 50, 5, 1111);
-
--- Insert a row into t2 which intersects the first row of partition 5.
-INSERT INTO t2(ts, dur, part, b) VALUES (190, 20, 5, 2222);
-
-create virtual table sp using span_outer_join(t1 PARTITIONED part,
-                                              t2 PARTITIONED part);
-
-select * from sp;
diff --git a/test/trace_processor/span_join/span_outer_join_unpartitioned_empty.out b/test/trace_processor/span_join/span_outer_join_unpartitioned_empty.out
deleted file mode 100644
index d02fdd7..0000000
--- a/test/trace_processor/span_join/span_outer_join_unpartitioned_empty.out
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-"ts","dur"
diff --git a/test/trace_processor/span_join/span_outer_join_unpartitioned_empty_test.sql b/test/trace_processor/span_join/span_outer_join_unpartitioned_empty_test.sql
deleted file mode 100644
index 8155974..0000000
--- a/test/trace_processor/span_join/span_outer_join_unpartitioned_empty_test.sql
+++ /dev/null
@@ -1,32 +0,0 @@
---
--- Copyright 2021 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 table t1(
-  ts BIG INT,
-  dur BIG INT,
-  PRIMARY KEY (ts)
-) without rowid;
-
-create table t2(
-  ts BIG INT,
-  dur BIG INT,
-  PRIMARY KEY (ts)
-) without rowid;
-
--- t1 and t2 are empty.
-
-create virtual table sp using span_outer_join(t1, t2);
-
-select * from sp;
diff --git a/test/trace_processor/span_join/span_outer_join_unpartitioned_left_empty.out b/test/trace_processor/span_join/span_outer_join_unpartitioned_left_empty.out
deleted file mode 100644
index 4ea997f..0000000
--- a/test/trace_processor/span_join/span_outer_join_unpartitioned_left_empty.out
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-"ts","dur"
-100,400
-500,50
-600,100
diff --git a/test/trace_processor/span_join/span_outer_join_unpartitioned_left_empty_test.sql b/test/trace_processor/span_join/span_outer_join_unpartitioned_left_empty_test.sql
deleted file mode 100644
index ab41a28..0000000
--- a/test/trace_processor/span_join/span_outer_join_unpartitioned_left_empty_test.sql
+++ /dev/null
@@ -1,37 +0,0 @@
---
--- Copyright 2021 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 table t1(
-  ts BIG INT,
-  dur BIG INT,
-  PRIMARY KEY (ts)
-) without rowid;
-
-create table t2(
-  ts BIG INT,
-  dur BIG INT,
-  PRIMARY KEY (ts)
-) without rowid;
-
--- Add some rows to t2.
-INSERT INTO t2(ts, dur)
-VALUES
-(100, 400),
-(500, 50),
-(600, 100);
-
-create virtual table sp using span_outer_join(t1, t2);
-
-select * from sp;
diff --git a/test/trace_processor/span_join/span_outer_join_unpartitioned_right_empty.out b/test/trace_processor/span_join/span_outer_join_unpartitioned_right_empty.out
deleted file mode 100644
index 4ea997f..0000000
--- a/test/trace_processor/span_join/span_outer_join_unpartitioned_right_empty.out
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-"ts","dur"
-100,400
-500,50
-600,100
diff --git a/test/trace_processor/span_join/span_outer_join_unpartitioned_right_empty_test.sql b/test/trace_processor/span_join/span_outer_join_unpartitioned_right_empty_test.sql
deleted file mode 100644
index 1e3d959..0000000
--- a/test/trace_processor/span_join/span_outer_join_unpartitioned_right_empty_test.sql
+++ /dev/null
@@ -1,37 +0,0 @@
---
--- Copyright 2021 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 table t1(
-  ts BIG INT,
-  dur BIG INT,
-  PRIMARY KEY (ts)
-) without rowid;
-
-create table t2(
-  ts BIG INT,
-  dur BIG INT,
-  PRIMARY KEY (ts)
-) without rowid;
-
--- Add some rows to t1.
-INSERT INTO t1(ts, dur)
-VALUES
-(100, 400),
-(500, 50),
-(600, 100);
-
-create virtual table sp using span_outer_join(t1, t2);
-
-select * from sp;
diff --git a/test/trace_processor/span_join/span_outer_join_unpartitioned_test.sql b/test/trace_processor/span_join/span_outer_join_unpartitioned_test.sql
deleted file mode 100644
index ce4fa79..0000000
--- a/test/trace_processor/span_join/span_outer_join_unpartitioned_test.sql
+++ /dev/null
@@ -1,47 +0,0 @@
---
--- Copyright 2021 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 table t1(
-  ts BIG INT,
-  dur BIG INT,
-  a BIG INT,
-  PRIMARY KEY (ts)
-) without rowid;
-
-create table t2(
-  ts BIG INT,
-  dur BIG INT,
-  b BIG INT,
-  PRIMARY KEY (ts)
-) without rowid;
-
--- Add some rows to t1.
-INSERT INTO t1(ts, dur, a)
-VALUES
-(100, 400, 1),
-(500, 50, 2),
-(600, 100, 3);
-
--- Add some rows to t2.
-INSERT INTO t2(ts, dur, b)
-VALUES
-(50, 100, 4),
-(550, 50, 5),
-(600, 50, 6),
-(900, 500, 7);
-
-create virtual table sp using span_outer_join(t1, t2);
-
-select * from sp;
diff --git a/test/trace_processor/startup/android_batt_counters.out b/test/trace_processor/startup/android_batt_counters.out
deleted file mode 100644
index 82c7a2e..0000000
--- a/test/trace_processor/startup/android_batt_counters.out
+++ /dev/null
@@ -1,29 +0,0 @@
-android_batt{
-   battery_counters{
-      timestamp_ns: 20
-      charge_counter_uah: 52
-      capacity_percent: 0.2
-      current_ua: 10
-      current_avg_ua: 12
-   }
-   battery_counters {
-      timestamp_ns: 52
-      charge_counter_uah: 32
-      capacity_percent: 0.8
-      current_ua: 8
-      current_avg_ua: 93
-   }
-   battery_counters {
-      timestamp_ns: 80
-      charge_counter_uah: 15
-      capacity_percent: 0.5
-      current_ua: 9
-      current_avg_ua: 5
-   }
-   battery_counters {
-      timestamp_ns: 92
-      charge_counter_uah: 21
-      capacity_percent: 0.3
-      current_avg_ua: 25
-   }
-}
\ No newline at end of file
diff --git a/test/trace_processor/startup/android_startup.out b/test/trace_processor/startup/android_startup.out
deleted file mode 100644
index 4282ff4..0000000
--- a/test/trace_processor/startup/android_startup.out
+++ /dev/null
@@ -1,63 +0,0 @@
-android_startup {
-  startup {
-    startup_id: 2
-    package_name: "com.google.android.calendar"
-    process_name: "com.google.android.calendar"
-    zygote_new_process: false
-    to_first_frame {
-      dur_ns: 108
-      main_thread_by_task_state {
-        running_dur_ns: 10
-        runnable_dur_ns: 80
-        uninterruptible_sleep_dur_ns: 0
-        interruptible_sleep_dur_ns: 10
-      }
-      other_processes_spawned_count: 1
-      time_activity_manager {
-        dur_ns: 8
-        dur_ms: 8e-06
-      }
-      time_activity_start {
-        dur_ns: 2
-        dur_ms: 2e-06
-      }
-      time_activity_resume {
-        dur_ns: 1
-        dur_ms: 1e-06
-      }
-      dur_ms: 0.000108
-    }
-    activity_hosting_process_count: 1
-    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
-      }
-    }
-    report_fully_drawn {
-      dur_ns: 198
-      dur_ms: 0.000198
-    }
-    event_timestamps {
-      intent_received: 102
-      first_frame: 210
-    }
-    system_state {
-      dex2oat_running: false
-      installd_running: false
-      broadcast_dispatched_count: 0
-      broadcast_received_count: 0
-      most_active_non_launch_processes: "init"
-      most_active_non_launch_processes: "com.google.android.calendar"
-    }
-    startup_type: "warm"
-  }
-}
diff --git a/test/trace_processor/startup/android_startup_attribution.out b/test/trace_processor/startup/android_startup_attribution.out
deleted file mode 100644
index dfc3fe2..0000000
--- a/test/trace_processor/startup/android_startup_attribution.out
+++ /dev/null
@@ -1,95 +0,0 @@
-android_startup {
-  startup {
-    startup_id: 1
-    package_name: "com.some.app"
-    process_name: "com.some.app"
-    zygote_new_process: false
-    to_first_frame {
-      dur_ns: 999999900
-      main_thread_by_task_state {
-        running_dur_ns: 0
-        runnable_dur_ns: 0
-        uninterruptible_sleep_dur_ns: 0
-        interruptible_sleep_dur_ns: 0
-      }
-      other_processes_spawned_count: 0
-      time_activity_manager {
-        dur_ns: 2
-        dur_ms: 2e-06
-      }
-      time_activity_resume {
-        dur_ns: 5
-        dur_ms: 5e-06
-      }
-      dur_ms: 999.9999
-      time_dex_open {
-        dur_ns: 20
-        dur_ms: 2e-05
-      }
-      time_verify_class {
-        dur_ns: 40
-        dur_ms: 4e-05
-      }
-      jit_compiled_methods: 2
-      time_jit_thread_pool_on_cpu {
-        dur_ns: 20
-        dur_ms: 2e-05
-      }
-      time_gc_total {
-        dur_ns: 130
-        dur_ms: 0.00013
-      }
-      time_gc_on_cpu {
-        dur_ns: 50
-        dur_ms: 5e-05
-      }
-    }
-    activity_hosting_process_count: 1
-    process {
-      name: "com.some.app"
-      uid: 10001
-      package {
-        package_name: "com.some.app"
-        apk_version_code: 123
-        debuggable: false
-      }
-      packages_for_uid {
-        package_name: "com.some.app"
-        apk_version_code: 123
-        debuggable: false
-      }
-    }
-    event_timestamps {
-      intent_received: 100
-      first_frame: 1000000000
-    }
-    long_binder_transactions {
-      duration {
-        dur_ns: 100000000
-        dur_ms: 100.0
-      }
-      thread: "Binder"
-      destination_process: "system_server"
-      flags: "0x00 No Flags Set"
-      code: "0x00 Java Layer Dependent"
-    }
-    long_binder_transactions {
-      duration {
-        dur_ns: 200000000
-        dur_ms: 200.0
-      }
-      thread: "fonts"
-      destination_thread: "Binder"
-      destination_process: "com.some.app"
-      flags: "0x00 No Flags Set"
-      code: "0x00 Java Layer Dependent"
-    }
-    system_state {
-      dex2oat_running: false
-      installd_running: false
-      broadcast_dispatched_count: 0
-      broadcast_received_count: 0
-    }
-    startup_type: "hot"
-  }
-}
diff --git a/test/trace_processor/startup/android_startup_attribution.py b/test/trace_processor/startup/android_startup_attribution.py
deleted file mode 100644
index a12601b..0000000
--- a/test/trace_processor/startup/android_startup_attribution.py
+++ /dev/null
@@ -1,181 +0,0 @@
-#!/usr/bin/env python3
-# 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.
-
-from os import sys, path
-
-import synth_common
-
-APP_PID = 3
-APP_TID = APP_PID
-SECOND_APP_TID = 3
-JIT_TID = 4
-GC_TID = 5
-GC2_TID = 6
-BINDER_TID = 7
-FONTS_TID = 8
-SYSTEM_SERVER_PID = 2
-SYSTEM_SERVER_TID = 2
-LAUNCH_START_TS = 100
-LAUNCH_END_TS = 10**9
-
-trace = synth_common.create_trace()
-trace.add_packet()
-trace.add_process(1, 0, 'init')
-trace.add_process(SYSTEM_SERVER_PID, 1, 'system_server')
-trace.add_process(APP_PID, 1, 'com.some.app', uid=10001)
-trace.add_thread(tid=SECOND_APP_TID, tgid=APP_PID, cmdline='second_thread')
-trace.add_thread(
-    tid=JIT_TID,
-    tgid=APP_PID,
-    cmdline='Jit thread pool',
-    name='Jit thread pool')
-trace.add_thread(
-    tid=GC_TID, tgid=APP_PID, cmdline='HeapTaskDaemon', name='HeapTaskDaemon')
-trace.add_thread(
-    tid=GC2_TID, tgid=APP_PID, cmdline='HeapTaskDaemon', name='HeapTaskDaemon')
-trace.add_thread(tid=BINDER_TID, tgid=APP_PID, cmdline='Binder', name='Binder')
-trace.add_thread(tid=FONTS_TID, tgid=APP_PID, cmdline='fonts', name='fonts')
-
-trace.add_package_list(ts=99, name='com.some.app', uid=10001, version_code=123)
-
-trace.add_ftrace_packet(cpu=0)
-# Start intent.
-trace.add_atrace_begin(
-    ts=LAUNCH_START_TS,
-    pid=SYSTEM_SERVER_PID,
-    tid=SYSTEM_SERVER_TID,
-    buf='MetricsLogger:launchObserverNotifyIntentStarted')
-trace.add_atrace_end(
-    ts=LAUNCH_START_TS + 1, tid=SYSTEM_SERVER_TID, pid=SYSTEM_SERVER_PID)
-
-# System server launching the app.
-trace.add_atrace_async_begin(
-    ts=LAUNCH_START_TS + 2,
-    pid=SYSTEM_SERVER_PID,
-    tid=SYSTEM_SERVER_TID,
-    buf='launching: com.some.app')
-
-# Emulate a hot start (and therefore that we only see activityResume).
-trace.add_atrace_begin(ts=125, tid=APP_TID, pid=APP_PID, buf='activityResume')
-trace.add_atrace_end(ts=130, tid=APP_TID, pid=APP_PID)
-
-# OpenDex slices within the startup.
-trace.add_atrace_begin(
-    ts=150, pid=APP_PID, tid=APP_TID, buf='OpenDexFilesFromOat(something)')
-trace.add_atrace_end(ts=165, pid=APP_PID, tid=APP_TID)
-
-trace.add_atrace_begin(
-    ts=170, pid=APP_PID, tid=APP_TID, buf='OpenDexFilesFromOat(something else)')
-trace.add_atrace_end(ts=175, pid=APP_PID, tid=APP_TID)
-
-# OpenDex slice outside the startup.
-trace.add_atrace_begin(
-    ts=5, pid=APP_PID, tid=APP_TID, buf='OpenDexFilesFromOat(nothing)')
-trace.add_atrace_end(ts=35, pid=APP_PID, tid=APP_TID)
-
-trace.add_atrace_async_end(
-    ts=LAUNCH_END_TS,
-    tid=SYSTEM_SERVER_TID,
-    pid=SYSTEM_SERVER_PID,
-    buf='launching: com.some.app')
-
-# VerifyClass slices within the startup.
-trace.add_atrace_begin(ts=250, pid=APP_PID, tid=APP_TID, buf='VerifyClass vr')
-trace.add_atrace_end(ts=265, pid=APP_PID, tid=APP_TID)
-
-trace.add_atrace_begin(ts=270, pid=APP_PID, tid=APP_TID, buf='VerifyClass dl')
-trace.add_atrace_end(ts=275, pid=APP_PID, tid=APP_TID)
-
-# VerifyClass slice outside the startup.
-trace.add_atrace_begin(ts=55, pid=APP_PID, tid=APP_TID, buf='VerifyClass xf')
-trace.add_atrace_end(ts=65, pid=APP_PID, tid=APP_TID)
-
-# VerifyClass slice on a different thread, overlapping with the other slices.
-trace.add_atrace_begin(
-    ts=260, pid=APP_PID, tid=SECOND_APP_TID, buf='VerifyClass vp')
-trace.add_atrace_end(ts=280, pid=APP_PID, tid=SECOND_APP_TID)
-
-# JIT compilation slices
-trace.add_atrace_begin(
-    ts=150, pid=APP_PID, tid=JIT_TID, buf='JIT compiling someting')
-trace.add_atrace_end(ts=160, pid=APP_PID, tid=JIT_TID)
-
-trace.add_sched(ts=155, prev_pid=0, next_pid=JIT_TID)
-trace.add_sched(ts=165, prev_pid=JIT_TID, next_pid=0)
-
-trace.add_atrace_begin(
-    ts=170, pid=APP_PID, tid=JIT_TID, buf='JIT compiling something else')
-trace.add_atrace_end(ts=190, pid=APP_PID, tid=JIT_TID)
-
-trace.add_sched(ts=170, prev_pid=0, next_pid=JIT_TID)
-trace.add_sched(ts=175, prev_pid=JIT_TID, next_pid=0, prev_state='R')
-trace.add_sched(ts=185, prev_pid=0, next_pid=JIT_TID)
-trace.add_sched(ts=190, prev_pid=JIT_TID, next_pid=0)
-
-# JIT slice, but not on JIT thread.
-trace.add_atrace_begin(
-    ts=200, pid=APP_PID, tid=SECOND_APP_TID, buf='JIT compiling nothing')
-trace.add_atrace_end(ts=210, pid=APP_PID, tid=SECOND_APP_TID)
-
-# Slice on JIT thread, but name doesn't match
-trace.add_atrace_begin(
-    ts=200, pid=APP_PID, tid=JIT_TID, buf='JIT compiled something')
-trace.add_atrace_end(ts=210, pid=APP_PID, tid=JIT_TID)
-
-# GC slices.
-trace.add_atrace_begin(
-    ts=300, pid=APP_PID, tid=GC_TID, buf='Background concurrent copying GC')
-trace.add_atrace_end(ts=330, pid=APP_PID, tid=GC_TID)
-
-trace.add_atrace_begin(
-    ts=340, pid=APP_PID, tid=GC_TID, buf='CollectorTransition mark sweep GC')
-trace.add_atrace_end(ts=390, pid=APP_PID, tid=GC_TID)
-
-trace.add_atrace_begin(ts=320, pid=APP_PID, tid=GC2_TID, buf='semispace GC')
-trace.add_atrace_end(ts=370, pid=APP_PID, tid=GC2_TID)
-
-# Start running copying slice on the first thread
-trace.add_sched(ts=310, prev_pid=0, next_pid=GC_TID)
-# Switch to the second thread to run semispace slice
-trace.add_sched(ts=325, prev_pid=GC_TID, next_pid=GC2_TID)
-# Switch back to the first thread to run mark sweep slice
-trace.add_sched(ts=350, prev_pid=GC2_TID, next_pid=GC_TID)
-# Finish running for GC.
-trace.add_sched(ts=360, prev_pid=GC_TID, next_pid=0)
-
-# Long binder transactions.
-trace.add_binder_transaction(1, 10**8, 2 * (10**8), BINDER_TID, APP_PID, 2,
-                             10**8 + 1, 2 * (10**8) - 1, SYSTEM_SERVER_TID,
-                             SYSTEM_SERVER_PID)
-
-trace.add_binder_transaction(3, 3 * (10**8), 5 * (10**8), FONTS_TID, APP_PID, 4,
-                             3 * (10**8) + 1, 5 * (10**8) - 1, BINDER_TID,
-                             APP_PID)
-
-# A short binder transaction.
-trace.add_binder_transaction(5, 10**7, 5 * (10**7), BINDER_TID, APP_TID, 6,
-                             10**7 + 1, 5 * (10**7) - 1, SYSTEM_SERVER_TID,
-                             SYSTEM_SERVER_PID)
-
-# Intent successful.
-trace.add_atrace_begin(
-    ts=LAUNCH_END_TS + 1,
-    pid=SYSTEM_SERVER_PID,
-    tid=SYSTEM_SERVER_TID,
-    buf='MetricsLogger:launchObserverNotifyActivityLaunchFinished')
-trace.add_atrace_end(
-    ts=LAUNCH_END_TS + 2, tid=SYSTEM_SERVER_TID, pid=SYSTEM_SERVER_PID)
-
-sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/startup/android_startup_breakdown.out b/test/trace_processor/startup/android_startup_breakdown.out
deleted file mode 100644
index 92ff7e0..0000000
--- a/test/trace_processor/startup/android_startup_breakdown.out
+++ /dev/null
@@ -1,103 +0,0 @@
-android_startup {
-  startup {
-    startup_id: 1
-    package_name: "com.google.android.calendar"
-    process_name: "com.google.android.calendar"
-    zygote_new_process: true
-    to_first_frame {
-      dur_ns: 108000000000
-      main_thread_by_task_state {
-        running_dur_ns: 25000000000
-        runnable_dur_ns: 5000000000
-        uninterruptible_sleep_dur_ns: 0
-        interruptible_sleep_dur_ns: 0
-      }
-      other_processes_spawned_count: 0
-      time_activity_manager {
-        dur_ns: 8000000000
-        dur_ms: 8000.0
-      }
-      time_bind_application {
-        dur_ns: 9000000000
-        dur_ms: 9000.0
-      }
-      time_activity_start {
-        dur_ns: 1000000000
-        dur_ms: 1000.0
-      }
-      time_activity_resume {
-        dur_ns: 1000000000
-        dur_ms: 1000.0
-      }
-      time_before_start_process {
-        dur_ns: 18000000000
-        dur_ms: 18000.0
-      }
-      time_during_start_process {
-        dur_ns: 35000000000
-        dur_ms: 35000.0
-      }
-      dur_ms: 108000.0
-      to_bind_application {
-        dur_ns: 83000000000
-        dur_ms: 83000.0
-      }
-      time_inflate {
-        dur_ns: 3000000000
-        dur_ms: 3000.0
-      }
-      time_get_resources {
-        dur_ns: 1000000000
-        dur_ms: 1000.0
-      }
-      mcycles_by_core_type {
-        unknown: 103
-      }
-    }
-    activity_hosting_process_count: 1
-    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
-      }
-    }
-    activities {
-      name: "com.google.android.calendar.MainActivity"
-      method: "performCreate"
-      ts_method_start: 188000000000
-    }
-    optimization_status {
-      odex_status: "up-to-date"
-      compilation_filter: "speed"
-      compilation_reason: "prebuilt"
-      location: "/system/framework/oat/arm/com.android.location.provider.odex"
-    }
-    optimization_status {
-      odex_status: "io-error-no-oat"
-      compilation_filter: "run-from-apk"
-      compilation_reason: "unknown"
-      location: "error"
-    }
-    event_timestamps {
-      intent_received: 102000000000
-      first_frame: 210000000000
-    }
-    system_state {
-      dex2oat_running: false
-      installd_running: false
-      broadcast_dispatched_count: 0
-      broadcast_received_count: 0
-      most_active_non_launch_processes: "init"
-      most_active_non_launch_processes: "system_server"
-    }
-    startup_type: "cold"
-  }
-}
diff --git a/test/trace_processor/startup/android_startup_breakdown.py b/test/trace_processor/startup/android_startup_breakdown.py
deleted file mode 100644
index 7ea4256..0000000
--- a/test/trace_processor/startup/android_startup_breakdown.py
+++ /dev/null
@@ -1,120 +0,0 @@
-#!/usr/bin/env python3
-# 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.
-
-from os import sys, path
-
-import synth_common
-
-
-def to_s(ts):
-  return ts * 1000 * 1000 * 1000
-
-
-trace = synth_common.create_trace()
-trace.add_packet()
-trace.add_process(1, 0, 'init')
-trace.add_process(2, 1, 'system_server')
-trace.add_process(3, 1, 'com.google.android.calendar', uid=10001)
-
-trace.add_package_list(
-    ts=100, name='com.google.android.calendar', uid=10001, version_code=123)
-
-trace.add_ftrace_packet(cpu=0)
-
-# Start intent for a successful launch of calendar
-trace.add_atrace_begin(
-    ts=to_s(102),
-    tid=2,
-    pid=2,
-    buf='MetricsLogger:launchObserverNotifyIntentStarted')
-trace.add_atrace_end(ts=to_s(103), tid=2, pid=2)
-
-trace.add_atrace_async_begin(
-    ts=to_s(110), tid=2, pid=2, buf='launching: com.google.android.calendar')
-
-trace.add_atrace_begin(
-    ts=to_s(120), tid=2, pid=2, buf='Start proc: com.google.android.calendar')
-trace.add_atrace_end(ts=to_s(155), tid=2, pid=2)
-
-# Unrelated process binding, ignored
-trace.add_atrace_begin(ts=to_s(125), tid=1, pid=1, buf='bindApplication')
-trace.add_atrace_end(ts=to_s(195), tid=1, pid=1)
-
-trace.add_atrace_begin(ts=to_s(185), tid=3, pid=3, buf='bindApplication')
-trace.add_atrace_begin(
-    ts=to_s(188),
-    tid=3,
-    pid=3,
-    buf='performCreate:com.google.android.calendar.MainActivity')
-trace.add_atrace_begin(ts=to_s(188), tid=3, pid=3, buf='inflate')
-trace.add_atrace_end(ts=to_s(189), tid=3, pid=3)
-trace.add_atrace_begin(
-    ts=to_s(188), tid=3, pid=3, buf='ResourcesManager#getResources')
-trace.add_atrace_end(ts=to_s(189), tid=3, pid=3)
-trace.add_atrace_begin(ts=to_s(190), tid=3, pid=3, buf='inflate')
-trace.add_atrace_end(ts=to_s(192), tid=3, pid=3)
-trace.add_atrace_end(ts=to_s(192), tid=3, pid=3)
-trace.add_atrace_begin(
-    ts=193,
-    tid=3,
-    pid=3,
-    buf='performResume:com.google.android.calendar.MainActivity')
-trace.add_atrace_end(ts=to_s(194), tid=3, pid=3)
-trace.add_atrace_end(ts=to_s(195), tid=3, pid=3)
-
-trace.add_atrace_begin(ts=to_s(195), tid=3, pid=3, buf='activityStart')
-trace.add_atrace_end(ts=to_s(196), tid=3, pid=3)
-
-trace.add_atrace_begin(ts=to_s(196), tid=3, pid=3, buf='activityResume')
-trace.add_atrace_end(ts=to_s(197), tid=3, pid=3)
-
-trace.add_atrace_begin(
-    ts=to_s(200),
-    tid=3,
-    pid=3,
-    buf='location=error status=io-error-no-oat ' \
-        'filter=run-from-apk reason=unknown')
-trace.add_atrace_end(ts=to_s(202), tid=3, pid=3)
-trace.add_atrace_begin(
-    ts=to_s(204),
-    tid=3,
-    pid=3,
-    buf='location=/system/framework/oat/arm/com.android.location.provider' \
-        '.odex status=up-to-date filter=speed reason=prebuilt')
-trace.add_atrace_end(ts=to_s(205), tid=3, pid=3)
-
-trace.add_atrace_async_end(
-    ts=to_s(210), tid=2, pid=2, buf='launching: com.google.android.calendar')
-trace.add_atrace_begin(
-    ts=to_s(211),
-    tid=2,
-    pid=2,
-    buf='MetricsLogger:launchObserverNotifyActivityLaunchFinished')
-trace.add_atrace_end(ts=to_s(212), tid=2, pid=2)
-
-# Add the scheduling data to match the timestamps of events above but with
-# some idle time inbetween to make the computation more realisitic.
-trace.add_cpufreq(ts=to_s(50), freq=1000, cpu=0)
-trace.add_sched(ts=to_s(100), prev_pid=0, next_pid=2)
-trace.add_sched(ts=to_s(115), prev_pid=2, next_pid=0)
-trace.add_sched(ts=to_s(120), prev_pid=0, next_pid=2)
-trace.add_sched(ts=to_s(125), prev_pid=2, next_pid=1)
-trace.add_sched(ts=to_s(150), prev_pid=1, next_pid=2)
-trace.add_sched(ts=to_s(160), prev_pid=2, next_pid=1)
-trace.add_sched(ts=to_s(180), prev_pid=1, next_pid=3)
-trace.add_sched(ts=to_s(205), prev_pid=3, next_pid=2)
-trace.add_sched(ts=to_s(220), prev_pid=2, next_pid=0)
-
-sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/startup/android_startup_broadcast.out b/test/trace_processor/startup/android_startup_broadcast.out
deleted file mode 100644
index f5862b7..0000000
--- a/test/trace_processor/startup/android_startup_broadcast.out
+++ /dev/null
@@ -1,29 +0,0 @@
-android_startup {
-  startup {
-    startup_id: 1
-    package_name: "com.google.android.calendar"
-    zygote_new_process: false
-    to_first_frame {
-      dur_ns: 100
-      main_thread_by_task_state {
-        running_dur_ns: 0
-        runnable_dur_ns: 0
-        uninterruptible_sleep_dur_ns: 0
-        interruptible_sleep_dur_ns: 0
-      }
-      other_processes_spawned_count: 0
-      dur_ms: 0.0001
-    }
-    activity_hosting_process_count: 0
-    event_timestamps {
-      intent_received: 100
-      first_frame: 200
-    }
-    system_state {
-      dex2oat_running: false
-      installd_running: false
-      broadcast_dispatched_count: 1
-      broadcast_received_count: 1
-    }
-  }
-}
diff --git a/test/trace_processor/startup/android_startup_installd_dex2oat.out b/test/trace_processor/startup/android_startup_installd_dex2oat.out
deleted file mode 100644
index be2c479..0000000
--- a/test/trace_processor/startup/android_startup_installd_dex2oat.out
+++ /dev/null
@@ -1,118 +0,0 @@
-android_startup {
-  startup {
-    startup_id: 1
-    package_name: "com.google.android.calendar"
-    zygote_new_process: false
-    to_first_frame {
-      dur_ns: 100
-      main_thread_by_task_state {
-        running_dur_ns: 0
-        runnable_dur_ns: 0
-        uninterruptible_sleep_dur_ns: 0
-        interruptible_sleep_dur_ns: 0
-      }
-      other_processes_spawned_count: 0
-      dur_ms: 0.0001
-    }
-    activity_hosting_process_count: 0
-    event_timestamps {
-      intent_received: 100
-      first_frame: 200
-    }
-    system_state {
-      dex2oat_running: false
-      installd_running: false
-      broadcast_dispatched_count: 0
-      broadcast_received_count: 0
-    }
-  }
-  startup {
-    startup_id: 2
-    package_name: "com.google.android.calculator"
-    zygote_new_process: false
-    to_first_frame {
-      dur_ns: 100
-      main_thread_by_task_state {
-        running_dur_ns: 0
-        runnable_dur_ns: 0
-        uninterruptible_sleep_dur_ns: 0
-        interruptible_sleep_dur_ns: 0
-      }
-      other_processes_spawned_count: 0
-      dur_ms: 0.0001
-    }
-    activity_hosting_process_count: 0
-    event_timestamps {
-      intent_received: 300
-      first_frame: 400
-    }
-    system_state {
-      dex2oat_running: true
-      installd_running: false
-      broadcast_dispatched_count: 0
-      broadcast_received_count: 0
-      most_active_non_launch_processes: "dex2oat64"
-    }
-    slow_start_reason: "dex2oat running during launch"
-  }
-  startup {
-    startup_id: 3
-    package_name: "com.google.android.deskclock"
-    zygote_new_process: false
-    to_first_frame {
-      dur_ns: 100
-      main_thread_by_task_state {
-        running_dur_ns: 0
-        runnable_dur_ns: 0
-        uninterruptible_sleep_dur_ns: 0
-        interruptible_sleep_dur_ns: 0
-      }
-      other_processes_spawned_count: 0
-      dur_ms: 0.0001
-    }
-    activity_hosting_process_count: 0
-    event_timestamps {
-      intent_received: 500
-      first_frame: 600
-    }
-    system_state {
-      dex2oat_running: false
-      installd_running: true
-      broadcast_dispatched_count: 0
-      broadcast_received_count: 0
-      most_active_non_launch_processes: "installd"
-    }
-    slow_start_reason: "installd running during launch"
-  }
-  startup {
-    startup_id: 4
-    package_name: "com.google.android.gm"
-    zygote_new_process: false
-    to_first_frame {
-      dur_ns: 100
-      main_thread_by_task_state {
-        running_dur_ns: 0
-        runnable_dur_ns: 0
-        uninterruptible_sleep_dur_ns: 0
-        interruptible_sleep_dur_ns: 0
-      }
-      other_processes_spawned_count: 0
-      dur_ms: 0.0001
-    }
-    activity_hosting_process_count: 0
-    event_timestamps {
-      intent_received: 700
-      first_frame: 800
-    }
-    system_state {
-      dex2oat_running: true
-      installd_running: true
-      broadcast_dispatched_count: 0
-      broadcast_received_count: 0
-      most_active_non_launch_processes: "dex2oat64"
-      most_active_non_launch_processes: "installd"
-    }
-    slow_start_reason: "dex2oat running during launch"
-    slow_start_reason: "installd running during launch"
-  }
-}
diff --git a/test/trace_processor/startup/android_startup_lock_contention.out b/test/trace_processor/startup/android_startup_lock_contention.out
deleted file mode 100644
index 2e63c73..0000000
--- a/test/trace_processor/startup/android_startup_lock_contention.out
+++ /dev/null
@@ -1,69 +0,0 @@
-android_startup {
-  startup {
-    startup_id: 1
-    package_name: "com.google.android.calendar"
-    process_name: "com.google.android.calendar"
-    zygote_new_process: false
-    to_first_frame {
-      dur_ns: 100
-      main_thread_by_task_state {
-        running_dur_ns: 0
-        runnable_dur_ns: 0
-        uninterruptible_sleep_dur_ns: 0
-        interruptible_sleep_dur_ns: 0
-      }
-      other_processes_spawned_count: 0
-      time_bind_application {
-        dur_ns: 3
-        dur_ms: 3e-06
-      }
-      time_activity_start {
-        dur_ns: 1
-        dur_ms: 1e-06
-      }
-      time_activity_resume {
-        dur_ns: 1
-        dur_ms: 1e-06
-      }
-      dur_ms: 0.0001
-      to_bind_application {
-        dur_ns: 2
-        dur_ms: 2e-06
-      }
-      time_lock_contention_thread_main {
-        dur_ns: 20
-        dur_ms: 2e-05
-      }
-      time_monitor_contention_thread_main {
-        dur_ns: 10
-        dur_ms: 1e-05
-      }
-    }
-    activity_hosting_process_count: 1
-    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
-      }
-    }
-    event_timestamps {
-      intent_received: 110
-      first_frame: 210
-    }
-    system_state {
-      dex2oat_running: false
-      installd_running: false
-      broadcast_dispatched_count: 0
-      broadcast_received_count: 0
-    }
-    startup_type: "cold"
-  }
-}
diff --git a/test/trace_processor/startup/android_startup_minsdk33.out b/test/trace_processor/startup/android_startup_minsdk33.out
deleted file mode 100644
index d83b189..0000000
--- a/test/trace_processor/startup/android_startup_minsdk33.out
+++ /dev/null
@@ -1,29 +0,0 @@
-android_startup {
-  startup {
-    startup_id: 1
-    package_name: "com.google.android.calendar"
-    zygote_new_process: false
-    to_first_frame {
-      dur_ns: 100
-      main_thread_by_task_state {
-        running_dur_ns: 0
-        runnable_dur_ns: 0
-        uninterruptible_sleep_dur_ns: 0
-        interruptible_sleep_dur_ns: 0
-      }
-      other_processes_spawned_count: 0
-      dur_ms: 0.0001
-    }
-    activity_hosting_process_count: 0
-    event_timestamps {
-      intent_received: 110
-      first_frame: 210
-    }
-    system_state {
-      dex2oat_running: false
-      installd_running: false
-      broadcast_dispatched_count: 0
-      broadcast_received_count: 0
-    }
-  }
-}
diff --git a/test/trace_processor/startup/android_startup_minsdk33.py b/test/trace_processor/startup/android_startup_minsdk33.py
deleted file mode 100644
index dddc503..0000000
--- a/test/trace_processor/startup/android_startup_minsdk33.py
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env python3
-# 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.
-
-from os import sys, path
-
-import synth_common
-
-trace = synth_common.create_trace()
-trace.add_packet()
-trace.add_process(1, 0, 'init')
-trace.add_process(2, 1, 'system_server')
-trace.add_process(3, 1, 'com.google.android.calendar', 10001)
-
-trace.add_package_list(
-    ts=1, name='com.google.android.calendar', uid=10001, version_code=123)
-
-trace.add_ftrace_packet(cpu=0)
-trace.add_atrace_async_begin(ts=110, tid=2, pid=2, buf='launchingActivity#1')
-trace.add_atrace_async_end(ts=210, tid=2, pid=2, buf='launchingActivity#1')
-trace.add_atrace_instant(
-    ts=211,
-    tid=2,
-    pid=2,
-    buf='launchingActivity#1:completed:com.google.android.calendar')
-
-sys.stdout.buffer.write(trace.trace.SerializeToString())
diff --git a/test/trace_processor/startup/android_startup_process_track.out b/test/trace_processor/startup/android_startup_process_track.out
deleted file mode 100644
index 9e0ff4c..0000000
--- a/test/trace_processor/startup/android_startup_process_track.out
+++ /dev/null
@@ -1,128 +0,0 @@
-android_startup {
-  startup {
-    startup_id: 1
-    package_name: "com.google.android.calendar"
-    process_name: "com.google.android.calendar:debug"
-    zygote_new_process: false
-    to_first_frame {
-      dur_ns: 7
-      main_thread_by_task_state {
-        running_dur_ns: 0
-        runnable_dur_ns: 0
-        uninterruptible_sleep_dur_ns: 0
-        interruptible_sleep_dur_ns: 0
-      }
-      other_processes_spawned_count: 0
-      time_activity_manager {
-        dur_ns: 2
-        dur_ms: 2e-06
-      }
-      time_bind_application {
-        dur_ns: 1
-        dur_ms: 1e-06
-      }
-      time_activity_start {
-        dur_ns: 1
-        dur_ms: 1e-06
-      }
-      time_activity_resume {
-        dur_ns: 1
-        dur_ms: 1e-06
-      }
-      dur_ms: 7e-06
-      to_bind_application {
-        dur_ns: 3
-        dur_ms: 3e-06
-      }
-    }
-    activity_hosting_process_count: 1
-    process {
-      name: "com.google.android.calendar:debug"
-      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
-      }
-    }
-    event_timestamps {
-      intent_received: 100
-      first_frame: 107
-    }
-    system_state {
-      dex2oat_running: false
-      installd_running: false
-      broadcast_dispatched_count: 0
-      broadcast_received_count: 0
-    }
-    startup_type: "cold"
-  }
-  startup {
-    startup_id: 2
-    package_name: "com.google.android.calendar"
-    process_name: "com.google.android.calendar"
-    zygote_new_process: false
-    to_first_frame {
-      dur_ns: 7
-      main_thread_by_task_state {
-        running_dur_ns: 0
-        runnable_dur_ns: 0
-        uninterruptible_sleep_dur_ns: 0
-        interruptible_sleep_dur_ns: 0
-      }
-      other_processes_spawned_count: 0
-      time_activity_manager {
-        dur_ns: 2
-        dur_ms: 2e-06
-      }
-      time_bind_application {
-        dur_ns: 1
-        dur_ms: 1e-06
-      }
-      time_activity_start {
-        dur_ns: 1
-        dur_ms: 1e-06
-      }
-      time_activity_resume {
-        dur_ns: 1
-        dur_ms: 1e-06
-      }
-      dur_ms: 7e-06
-      to_bind_application {
-        dur_ns: 3
-        dur_ms: 3e-06
-      }
-    }
-    activity_hosting_process_count: 1
-    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
-      }
-    }
-    event_timestamps {
-      intent_received: 200
-      first_frame: 207
-    }
-    system_state {
-      dex2oat_running: false
-      installd_running: false
-      broadcast_dispatched_count: 0
-      broadcast_received_count: 0
-    }
-    startup_type: "cold"
-  }
-}
diff --git a/test/trace_processor/startup/index b/test/trace_processor/startup/index
deleted file mode 100644
index 3e22773..0000000
--- a/test/trace_processor/startup/index
+++ /dev/null
@@ -1,22 +0,0 @@
-# Contains tests related to the startup of Android apps.
-
-# Startup metric tests.
-android_startup.py android_startup android_startup.out
-android_startup_minsdk33.py android_startup android_startup_minsdk33.out
-android_startup_breakdown.py android_startup android_startup_breakdown.out
-android_startup_process_track.py android_startup android_startup_process_track.out
-android_startup_attribution.py android_startup android_startup_attribution.out
-
-# Test lock contention is correctly attributed.
-android_startup_lock_contention.py android_startup android_startup_lock_contention.out
-
-# Test that dex2oat/installd running in parallel are flagged.
-android_startup_installd_dex2oat.py android_startup android_startup_installd_dex2oat.out
-
-# Test that broadcasts are correctly counted.
-android_startup_broadcast.py android_startup android_startup_broadcast.out
-
-# Other metrics associated with startup.
-android_startup_battery.py android_batt android_batt_counters.out
-android_startup_cpu.py android_cpu android_startup_cpu.out
-android_startup_powrails.py android_powrails android_startup_powrails.out
diff --git a/test/trace_processor/tables/android_sched_and_ps_b119301023.out b/test/trace_processor/tables/android_sched_and_ps_b119301023.out
deleted file mode 100644
index 99ee147..0000000
--- a/test/trace_processor/tables/android_sched_and_ps_b119301023.out
+++ /dev/null
@@ -1,11 +0,0 @@
-"ts"
-81473010031230
-81473010109251
-81473010121751
-81473010179772
-81473010203886
-81473010234720
-81473010278522
-81473010308470
-81473010341386
-81473010352792
diff --git a/test/trace_processor/tables/android_sched_and_ps_b119496959.out b/test/trace_processor/tables/android_sched_and_ps_b119496959.out
deleted file mode 100644
index 480ada8..0000000
--- a/test/trace_processor/tables/android_sched_and_ps_b119496959.out
+++ /dev/null
@@ -1,11 +0,0 @@
-"ts","cpu"
-81473797824982,3
-81473797942847,3
-81473798135399,0
-81473798786857,2
-81473798875451,3
-81473799019930,2
-81473799079982,0
-81473800089357,3
-81473800144461,3
-81473800441805,3
diff --git a/test/trace_processor/tables/android_sched_and_ps_smoke_window.out b/test/trace_processor/tables/android_sched_and_ps_smoke_window.out
deleted file mode 100644
index 3c4149c..0000000
--- a/test/trace_processor/tables/android_sched_and_ps_smoke_window.out
+++ /dev/null
@@ -1,2 +0,0 @@
-"ts","dur","quantum_ts"
-0,9223372036854775807,0
diff --git a/test/trace_processor/tables/android_task_names.out b/test/trace_processor/tables/android_task_names.out
deleted file mode 100644
index 2a931e3..0000000
--- a/test/trace_processor/tables/android_task_names.out
+++ /dev/null
@@ -1,13 +0,0 @@
-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/trace_processor/tables/b119301023_test.sql b/test/trace_processor/tables/b119301023_test.sql
deleted file mode 100644
index c5efec1..0000000
--- a/test/trace_processor/tables/b119301023_test.sql
+++ /dev/null
@@ -1,18 +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 ts from sched
-where ts > 0.1 + 1e9
-limit 10;
diff --git a/test/trace_processor/tables/b119496959_test.sql b/test/trace_processor/tables/b119496959_test.sql
deleted file mode 100644
index 207ca4f..0000000
--- a/test/trace_processor/tables/b119496959_test.sql
+++ /dev/null
@@ -1,16 +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 ts, cpu from sched where ts >= 81473797418963 limit 10
diff --git a/test/trace_processor/tables/b120278869_neg_ts_end_test.sql b/test/trace_processor/tables/b120278869_neg_ts_end_test.sql
deleted file mode 100644
index bee5180..0000000
--- a/test/trace_processor/tables/b120278869_neg_ts_end_test.sql
+++ /dev/null
@@ -1,16 +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 count(*) from counters where -1 < ts
diff --git a/test/trace_processor/tables/counter_dur_example_android_trace_30s.out b/test/trace_processor/tables/counter_dur_example_android_trace_30s.out
deleted file mode 100644
index 9208779..0000000
--- a/test/trace_processor/tables/counter_dur_example_android_trace_30s.out
+++ /dev/null
@@ -1,11 +0,0 @@
-"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/tables/counter_dur_test.sql b/test/trace_processor/tables/counter_dur_test.sql
deleted file mode 100644
index 5bf2366..0000000
--- a/test/trace_processor/tables/counter_dur_test.sql
+++ /dev/null
@@ -1 +0,0 @@
-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/tables/counters_group_by_freq_counters_group_by_freq.out b/test/trace_processor/tables/counters_group_by_freq_counters_group_by_freq.out
deleted file mode 100644
index bf3ddf4..0000000
--- a/test/trace_processor/tables/counters_group_by_freq_counters_group_by_freq.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"value","dur_sum"
-4000.000000,2
-3000.000000,1
diff --git a/test/trace_processor/tables/counters_group_by_freq_test.sql b/test/trace_processor/tables/counters_group_by_freq_test.sql
deleted file mode 100644
index 2bd1706..0000000
--- a/test/trace_processor/tables/counters_group_by_freq_test.sql
+++ /dev/null
@@ -1,27 +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
-  value,
-  sum(dur) as dur_sum
-FROM (
-  SELECT value,
-  lead(ts) OVER (PARTITION BY name, track_id ORDER BY ts) - ts AS dur
-  FROM counter
-  INNER JOIN counter_track ON counter.track_id = counter_track.id
-)
-WHERE value > 0
-GROUP BY value
-ORDER BY dur_sum desc
diff --git a/test/trace_processor/tables/counters_where_cpu_counters_where_cpu.out b/test/trace_processor/tables/counters_where_cpu_counters_where_cpu.out
deleted file mode 100644
index ac7857f..0000000
--- a/test/trace_processor/tables/counters_where_cpu_counters_where_cpu.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"ts","dur","value"
-1000,1,3000.000000
-1001,0,4000.000000
diff --git a/test/trace_processor/tables/counters_where_cpu_test.sql b/test/trace_processor/tables/counters_where_cpu_test.sql
deleted file mode 100644
index 14fea10..0000000
--- a/test/trace_processor/tables/counters_where_cpu_test.sql
+++ /dev/null
@@ -1,22 +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
-  ts,
-  lead(ts, 1, ts) OVER (PARTITION BY name ORDER BY ts) - ts AS dur,
-  value
-FROM counter c
-INNER JOIN cpu_counter_track t ON t.id = c.track_id
-WHERE cpu = 1;
diff --git a/test/trace_processor/tables/filter_counter_test.sql b/test/trace_processor/tables/filter_counter_test.sql
deleted file mode 100644
index e234b77..0000000
--- a/test/trace_processor/tables/filter_counter_test.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 COUNT(*)
-FROM counter
-WHERE
-  track_id = 0;
diff --git a/test/trace_processor/tables/filter_row_vector_test.sql b/test/trace_processor/tables/filter_row_vector_test.sql
deleted file mode 100644
index 855d40c..0000000
--- a/test/trace_processor/tables/filter_row_vector_test.sql
+++ /dev/null
@@ -1,29 +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 ts
-FROM counter
-WHERE
-  ts > 72563651549 AND
-  track_id = (
-    SELECT t.id
-    FROM process_counter_track t
-    JOIN process p USING (upid)
-    WHERE
-      t.name = 'Heap size (KB)'
-      AND p.pid = 1204
-  ) AND
-  value != 17952.000000
-LIMIT 20
diff --git a/test/trace_processor/tables/filter_sched_test.sql b/test/trace_processor/tables/filter_sched_test.sql
deleted file mode 100644
index 07ff56c..0000000
--- a/test/trace_processor/tables/filter_sched_test.sql
+++ /dev/null
@@ -1,22 +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 ts, cpu, dur from sched
-where
-  cpu = 1 and
-  dur > 50 and
-  dur <= 100 and
-  ts >= 100 and
-  ts <= 400;
diff --git a/test/trace_processor/tables/ftrace_setup_errors.out b/test/trace_processor/tables/ftrace_setup_errors.out
deleted file mode 100644
index 5309e54..0000000
--- a/test/trace_processor/tables/ftrace_setup_errors.out
+++ /dev/null
@@ -1,7 +0,0 @@
-"value"
-3
-"Ftrace event unknown: foo/bar
-Ftrace event unknown: sched/foobar
-Atrace failures: error: unknown tracing category "bar"
-error enabling tracing category "bar"
-"
diff --git a/test/trace_processor/tables/ftrace_setup_errors_test.sql b/test/trace_processor/tables/ftrace_setup_errors_test.sql
deleted file mode 100644
index cc1b3c0..0000000
--- a/test/trace_processor/tables/ftrace_setup_errors_test.sql
+++ /dev/null
@@ -1,3 +0,0 @@
-SELECT value FROM stats WHERE name = 'ftrace_setup_errors'
-UNION ALL
-SELECT str_value FROM metadata WHERE name = 'ftrace_setup_errors'
diff --git a/test/trace_processor/tables/index b/test/trace_processor/tables/index
deleted file mode 100644
index fbeb166..0000000
--- a/test/trace_processor/tables/index
+++ /dev/null
@@ -1,38 +0,0 @@
-# Contains tests for the handling of tables by trace processor. The focus of tests here
-# is to check that trace processor is correctly returning and handling constraints on
-# the really important tables in trace processor.
-#
-# Note: It's generally *not* advisable to add tests here. Check the guidance provided by
-# http://perfetto/dev/docs/analysis/trace-processor#diff-tests for choosing which folder
-# to add a new test to.
-
-# Window table
-../../data/android_sched_and_ps.pb smoke_window_test.sql android_sched_and_ps_smoke_window.out
-
-# Sched table
-../common/synth_1.py filter_sched_test.sql synth_1_filter_sched.out
-../../data/android_sched_and_ps.pb b119496959_test.sql android_sched_and_ps_b119496959.out
-../../data/android_sched_and_ps.pb b119301023_test.sql android_sched_and_ps_b119301023.out
-
-# Counters table
-../common/synth_1.py filter_counter_test.sql synth_1_filter_counter.out
-../../data/memory_counters.pb b120278869_neg_ts_end_test.sql memory_counters_b120278869_neg_ts_end.out
-counters_where_cpu.py counters_where_cpu_test.sql counters_where_cpu_counters_where_cpu.out
-counters_group_by_freq.py counters_group_by_freq_test.sql counters_group_by_freq_counters_group_by_freq.out
-../../data/example_android_trace_30s.pb filter_row_vector_test.sql filter_row_vector_example_android_trace_30s.out
-../../data/example_android_trace_30s.pb counter_dur_test.sql counter_dur_example_android_trace_30s.out
-
-# Null printing
-../common/synth_1.py nulls_test.sql nulls.out
-
-# Thread table
-thread_main_thread.textproto thread_main_thread_test.sql thread_main_thread.out
-
-# Json output
-../../data/memory_counters.pb trace_metadata trace_metadata.json.out
-
-# Processes as a metric
-process_uids.textproto android_task_names android_task_names.out
-
-# Ftrace stats imports in metadata and stats tables
-../../data/ftrace_error_stats.pftrace ftrace_setup_errors_test.sql ftrace_setup_errors.out
diff --git a/test/trace_processor/tables/memory_counters_b120278869_neg_ts_end.out b/test/trace_processor/tables/memory_counters_b120278869_neg_ts_end.out
deleted file mode 100644
index 99cb66b..0000000
--- a/test/trace_processor/tables/memory_counters_b120278869_neg_ts_end.out
+++ /dev/null
@@ -1,2 +0,0 @@
-"count(*)"
-98688
diff --git a/test/trace_processor/tables/nulls_test.sql b/test/trace_processor/tables/nulls_test.sql
deleted file mode 100644
index cccbc47..0000000
--- a/test/trace_processor/tables/nulls_test.sql
+++ /dev/null
@@ -1,42 +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.
---
-CREATE TABLE null_test (
-  primary_key INTEGER PRIMARY KEY,
-  int_nulls INTEGER,
-  string_nulls STRING,
-  double_nulls DOUBLE,
-  start_int_nulls INTEGER,
-  start_string_nulls STRING,
-  start_double_nulls DOUBLE,
-  all_nulls INTEGER
-);
-
-INSERT INTO null_test(
-  int_nulls,
-  string_nulls,
-  double_nulls,
-  start_int_nulls,
-  start_string_nulls,
-  start_double_nulls
-)
-VALUES
-(1,     "test",   2.0,  NULL, NULL,   NULL),
-(2,     NULL,     NULL, NULL, "test", NULL),
-(1,     "other",  NULL, NULL, NULL,   NULL),
-(4,     NULL,     NULL, NULL, NULL,   1.0),
-(NULL,  "test",   1.0,  1,    NULL,   NULL);
-
-SELECT * from null_test;
diff --git a/test/trace_processor/tables/process_uids.textproto b/test/trace_processor/tables/process_uids.textproto
deleted file mode 100644
index bb6547c..0000000
--- a/test/trace_processor/tables/process_uids.textproto
+++ /dev/null
@@ -1,24 +0,0 @@
-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/trace_processor/tables/smoke_window_test.sql b/test/trace_processor/tables/smoke_window_test.sql
deleted file mode 100644
index 7fe479f..0000000
--- a/test/trace_processor/tables/smoke_window_test.sql
+++ /dev/null
@@ -1,16 +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 * from window;
diff --git a/test/trace_processor/tables/synth_1_filter_counter.out b/test/trace_processor/tables/synth_1_filter_counter.out
deleted file mode 100644
index 08a8b2f..0000000
--- a/test/trace_processor/tables/synth_1_filter_counter.out
+++ /dev/null
@@ -1,2 +0,0 @@
-"COUNT(*)"
-2
diff --git a/test/trace_processor/tables/synth_1_filter_sched.out b/test/trace_processor/tables/synth_1_filter_sched.out
deleted file mode 100644
index cdd6230..0000000
--- a/test/trace_processor/tables/synth_1_filter_sched.out
+++ /dev/null
@@ -1,2 +0,0 @@
-"ts","cpu","dur"
-170,1,80
diff --git a/test/trace_processor/tables/thread_main_thread.out b/test/trace_processor/tables/thread_main_thread.out
deleted file mode 100644
index 9589528..0000000
--- a/test/trace_processor/tables/thread_main_thread.out
+++ /dev/null
@@ -1,6 +0,0 @@
-"tid","is_main_thread"
-5,1
-7,0
-11,1
-12,0
-99,"[NULL]"
diff --git a/test/trace_processor/tables/thread_main_thread.textproto b/test/trace_processor/tables/thread_main_thread.textproto
deleted file mode 100644
index 9d2e627..0000000
--- a/test/trace_processor/tables/thread_main_thread.textproto
+++ /dev/null
@@ -1,47 +0,0 @@
-packet {
-  timestamp: 1
-  process_tree {
-    processes {
-      pid: 5
-      ppid: 1
-      cmdline: "com.google.pid5"
-    }
-    threads {
-      tid: 5
-      tgid: 5
-    }
-    threads {
-      tid: 7
-      tgid: 5
-      name: "tid7"
-    }
-    processes {
-      pid: 11
-      ppid: 1
-      cmdline: "com.google.pid11"
-    }
-    threads {
-      tid: 11
-      tgid: 11
-      name: "tid11"
-    }
-    threads {
-      tid: 12
-      tgid: 11
-      name: "tid12"
-    }
-  }
-}
-packet {
-  timestamp: 2
-  ftrace_events {
-    cpu: 0
-    event {
-      timestamp: 2
-      pid: 99
-      lowmemory_kill {
-        pid: 99
-      }
-    }
-  }
-}
diff --git a/test/trace_processor/tables/thread_main_thread_test.sql b/test/trace_processor/tables/thread_main_thread_test.sql
deleted file mode 100644
index a6aab0a..0000000
--- a/test/trace_processor/tables/thread_main_thread_test.sql
+++ /dev/null
@@ -1,6 +0,0 @@
-SELECT
-  tid,
-  is_main_thread
-FROM thread
-WHERE tid IN (5, 7, 11, 12, 99)
-ORDER BY tid;
diff --git a/test/trace_processor/track_event/experimental_slice_layout_depth.out b/test/trace_processor/track_event/experimental_slice_layout_depth.out
deleted file mode 100644
index 00f83ee..0000000
--- a/test/trace_processor/track_event/experimental_slice_layout_depth.out
+++ /dev/null
@@ -1,4 +0,0 @@
-"layout_depth"
-0
-0
-0
diff --git a/test/trace_processor/track_event/experimental_slice_layout_depth_test.sql b/test/trace_processor/track_event/experimental_slice_layout_depth_test.sql
deleted file mode 100644
index b13dbe6..0000000
--- a/test/trace_processor/track_event/experimental_slice_layout_depth_test.sql
+++ /dev/null
@@ -1,14 +0,0 @@
--- 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 layout_depth from experimental_slice_layout
-where filter_track_ids = (select group_concat(track_id, ',') from slice);
\ No newline at end of file
diff --git a/test/trace_processor/track_event/flow_events_proto_v1.out b/test/trace_processor/track_event/flow_events_proto_v1.out
deleted file mode 100644
index b9283edc..0000000
--- a/test/trace_processor/track_event/flow_events_proto_v1.out
+++ /dev/null
@@ -1,4 +0,0 @@
-"slice_out","slice_in"
-"FlowBeginSlice","FlowEndSlice_1"
-"FlowEndSlice_1","FlowStepSlice"
-"FlowStepSlice","FlowEndSlice_2"
diff --git a/test/trace_processor/track_event/flow_events_proto_v2.out b/test/trace_processor/track_event/flow_events_proto_v2.out
deleted file mode 100644
index a8e6334..0000000
--- a/test/trace_processor/track_event/flow_events_proto_v2.out
+++ /dev/null
@@ -1,4 +0,0 @@
-"slice_out","slice_in"
-"FlowBeginSlice","FlowEndSlice_1"
-"FlowBeginSlice","FlowStepSlice"
-"FlowStepSlice","FlowEndSlice_2"
diff --git a/test/trace_processor/track_event/flow_events_test.sql b/test/trace_processor/track_event/flow_events_test.sql
deleted file mode 100644
index 11ef1a9..0000000
--- a/test/trace_processor/track_event/flow_events_test.sql
+++ /dev/null
@@ -1,18 +0,0 @@
---
--- 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 t1.name as slice_out, t2.name as slice_in from flow t
-join slice t1 on t.slice_out == t1.slice_id
-join slice t2 on t.slice_in == t2.slice_id;
diff --git a/test/trace_processor/track_event/flow_events_track_event.out b/test/trace_processor/track_event/flow_events_track_event.out
deleted file mode 100644
index d44237e..0000000
--- a/test/trace_processor/track_event/flow_events_track_event.out
+++ /dev/null
@@ -1,8 +0,0 @@
-"slice_out","slice_in"
-"FlowSlice1Start","FlowSlice1End"
-"FlowSlice1Start2Start","FlowSlice1End"
-"FlowSlice1Start2Start","FlowSlice2End"
-"FlowSlice3Begin","FlowSlice3End4Begin"
-"FlowSlice3End4Begin","FlowSlice4Step"
-"FlowSlice4Step","FlowSlice4Step2_FlowIdOnAsyncEndEvent"
-"FlowSlice4Step2_FlowIdOnAsyncEndEvent","FlowSlice4End"
diff --git a/test/trace_processor/track_event/index b/test/trace_processor/track_event/index
deleted file mode 100644
index 24b6703..0000000
--- a/test/trace_processor/track_event/index
+++ /dev/null
@@ -1,47 +0,0 @@
-# Contains tests on the parsing and ingestion of TrackEvent packets.
-
-# Same tid handling
-track_event_same_tids.textproto ../common/process_tracking_test.sql track_event_same_tids_threads.out
-track_event_same_tids.textproto track_event_slices_test.sql track_event_same_tids_slices.out
-
-# Typed args
-track_event_typed_args.textproto track_event_slices_test.sql track_event_typed_args_slices.out
-track_event_typed_args.textproto track_event_args_test.sql track_event_typed_args_args.out
-
-# Track handling
-track_event_tracks.textproto track_event_slices_test.sql track_event_tracks_slices.out
-track_event_tracks.textproto track_event_processes_test.sql track_event_tracks_processes.out
-track_event_tracks.textproto track_event_tracks_test.sql track_event_tracks.out
-
-# Instant events
-track_event_instant.textproto track_event_slices_test.sql track_event_instant_slices.out
-
-# Legacy async events
-legacy_async_event.textproto track_event_slice_with_args_test.sql legacy_async_event.out
-
-# Legacy atrace
-track_event_with_atrace.textproto track_event_slices_test.sql track_event_with_atrace.out
-
-# Debug annotations
-track_event_merged_debug_annotations.textproto track_event_args_test.sql track_event_merged_debug_annotations_args.out
-
-# Counters
-track_event_counters.textproto track_event_slices_test.sql track_event_counters_slices.out
-track_event_counters.textproto track_event_counters_test.sql track_event_counters_counters.out
-
-# Clock handling
-track_event_monotonic_trace_clock.textproto track_event_slices_test.sql track_event_monotonic_trace_clock_slices.out
-
-# HistogramName interning
-track_event_chrome_histogram_sample.textproto track_event_args_test.sql track_event_chrome_histogram_sample_args.out
-
-# Flow events importing from proto
-flow_events_track_event.textproto flow_events_test.sql flow_events_track_event.out
-flow_events_proto_v2.textproto flow_events_test.sql flow_events_proto_v2.out
-flow_events_proto_v1.textproto flow_events_test.sql flow_events_proto_v1.out
-
-# Async slices starting and ending at the same time
-experimental_slice_layout_depth.py experimental_slice_layout_depth_test.sql experimental_slice_layout_depth.out
-
-# Descriptor merging regression test (bug: b/197203390)
-../../data/trace_with_descriptor.pftrace merging_regression_test.sql merging_regression.out
diff --git a/test/trace_processor/track_event/merging_regression.out b/test/trace_processor/track_event/merging_regression.out
deleted file mode 100644
index 5158c9d..0000000
--- a/test/trace_processor/track_event/merging_regression.out
+++ /dev/null
@@ -1,11 +0,0 @@
-"ts"
-605361018360000
-605361018360000
-605361028265000
-605361028265000
-605361028361000
-605361028878000
-605361033445000
-605361033445000
-605361034257000
-605361035040000
diff --git a/test/trace_processor/track_event/merging_regression_test.sql b/test/trace_processor/track_event/merging_regression_test.sql
deleted file mode 100644
index 270e269..0000000
--- a/test/trace_processor/track_event/merging_regression_test.sql
+++ /dev/null
@@ -1,19 +0,0 @@
---
--- Copyright 2021 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.
---
-
--- Actual query in this file does not matter: we only want to ensure that
--- parsing of the trace succeeds.
-select ts from slice order by ts limit 10;
diff --git a/test/trace_processor/track_event/track_event_args_test.sql b/test/trace_processor/track_event/track_event_args_test.sql
deleted file mode 100644
index d1d4cc3..0000000
--- a/test/trace_processor/track_event/track_event_args_test.sql
+++ /dev/null
@@ -1,16 +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 flat_key, key, int_value, string_value from args order by arg_set_id, key asc;
\ No newline at end of file
diff --git a/test/trace_processor/track_event/track_event_chrome_histogram_sample_args.out b/test/trace_processor/track_event/track_event_chrome_histogram_sample_args.out
deleted file mode 100644
index c2265c9..0000000
--- a/test/trace_processor/track_event/track_event_chrome_histogram_sample_args.out
+++ /dev/null
@@ -1,24 +0,0 @@
-"flat_key","key","int_value","string_value"
-"is_root_in_scope","is_root_in_scope",1,"[NULL]"
-"source","source","[NULL]","descriptor"
-"source_id","source_id",0,"[NULL]"
-"chrome_histogram_sample.name","chrome_histogram_sample.name","[NULL]","Compositing.Display.DrawToSwapUs"
-"chrome_histogram_sample.name_hash","chrome_histogram_sample.name_hash",10,"[NULL]"
-"chrome_histogram_sample.name_iid","chrome_histogram_sample.name_iid",1,"[NULL]"
-"chrome_histogram_sample.sample","chrome_histogram_sample.sample",100,"[NULL]"
-"chrome_histogram_sample.name","chrome_histogram_sample.name","[NULL]","CompositorLatency.TotalLatency"
-"chrome_histogram_sample.name_hash","chrome_histogram_sample.name_hash",20,"[NULL]"
-"chrome_histogram_sample.name_iid","chrome_histogram_sample.name_iid",2,"[NULL]"
-"chrome_histogram_sample.sample","chrome_histogram_sample.sample",200,"[NULL]"
-"chrome_histogram_sample.name","chrome_histogram_sample.name","[NULL]","Graphics.Smoothness.Checkerboarding.MainThreadAnimation"
-"chrome_histogram_sample.name_hash","chrome_histogram_sample.name_hash",30,"[NULL]"
-"chrome_histogram_sample.name_iid","chrome_histogram_sample.name_iid",3,"[NULL]"
-"chrome_histogram_sample.sample","chrome_histogram_sample.sample",300,"[NULL]"
-"chrome_histogram_sample.name_hash","chrome_histogram_sample.name_hash",40,"[NULL]"
-"chrome_histogram_sample.name_iid","chrome_histogram_sample.name_iid",4,"[NULL]"
-"chrome_histogram_sample.sample","chrome_histogram_sample.sample",400,"[NULL]"
-"chrome_histogram_sample.name_hash","chrome_histogram_sample.name_hash",50,"[NULL]"
-"chrome_histogram_sample.sample","chrome_histogram_sample.sample",500,"[NULL]"
-"chrome_histogram_sample.name","chrome_histogram_sample.name","[NULL]","Memory.GPU.PeakMemoryUsage.PageLoad"
-"chrome_histogram_sample.name_hash","chrome_histogram_sample.name_hash",60,"[NULL]"
-"chrome_histogram_sample.sample","chrome_histogram_sample.sample",600,"[NULL]"
diff --git a/test/trace_processor/track_event/track_event_counters_slices.out b/test/trace_processor/track_event/track_event_counters_slices.out
deleted file mode 100644
index cf188ef..0000000
--- a/test/trace_processor/track_event/track_event_counters_slices.out
+++ /dev/null
@@ -1,8 +0,0 @@
-"track","process","thread","thread_process","ts","dur","category","name"
-"[NULL]","[NULL]","t1","Browser",1000,100,"cat","event1_on_t1"
-"[NULL]","[NULL]","t1","Browser",2000,200,"cat","event2_on_t1"
-"[NULL]","[NULL]","t1","Browser",2000,200,"cat","event3_on_t1"
-"[NULL]","[NULL]","t1","Browser",4000,0,"cat","event4_on_t1"
-"[NULL]","[NULL]","t4","Browser",4000,100,"cat","event1_on_t3"
-"[NULL]","[NULL]","t1","Browser",4300,0,"cat","float_counter_on_t1"
-"[NULL]","[NULL]","t1","Browser",4500,0,"cat","float_counter_on_t1"
diff --git a/test/trace_processor/track_event/track_event_counters_test.sql b/test/trace_processor/track_event/track_event_counters_test.sql
deleted file mode 100644
index ffa5d57..0000000
--- a/test/trace_processor/track_event/track_event_counters_test.sql
+++ /dev/null
@@ -1,31 +0,0 @@
---
--- 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.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/track_event_instant.textproto b/test/trace_processor/track_event/track_event_instant.textproto
deleted file mode 100644
index 48046a9..0000000
--- a/test/trace_processor/track_event/track_event_instant.textproto
+++ /dev/null
@@ -1,50 +0,0 @@
-# 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
-    }
-  }
-}
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 1000
-  track_event {
-    categories: "cat"
-    name: "instant_on_t1"
-    type: 3
-  }
-}
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 2000
-  track_event {
-    categories: "cat"
-    name: "legacy_instant_on_t1"
-    legacy_event {
-      phase: 73               # 'I'
-    }
-  }
-}
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 3000
-  track_event {
-    categories: "cat"
-    name: "legacy_mark_on_t1"
-    legacy_event {
-      phase: 82               # 'R'
-    }
-  }
-}
diff --git a/test/trace_processor/track_event/track_event_instant_slices.out b/test/trace_processor/track_event/track_event_instant_slices.out
deleted file mode 100644
index 9237f4d..0000000
--- a/test/trace_processor/track_event/track_event_instant_slices.out
+++ /dev/null
@@ -1,4 +0,0 @@
-"track","process","thread","thread_process","ts","dur","category","name"
-"[NULL]","[NULL]","t1","[NULL]",1000,0,"cat","instant_on_t1"
-"[NULL]","[NULL]","t1","[NULL]",2000,0,"cat","legacy_instant_on_t1"
-"[NULL]","[NULL]","t1","[NULL]",3000,0,"cat","legacy_mark_on_t1"
diff --git a/test/trace_processor/track_event/track_event_merged_debug_annotations_args.out b/test/trace_processor/track_event/track_event_merged_debug_annotations_args.out
deleted file mode 100644
index 8cecb1d..0000000
--- a/test/trace_processor/track_event/track_event_merged_debug_annotations_args.out
+++ /dev/null
@@ -1,27 +0,0 @@
-"flat_key","key","int_value","string_value"
-"is_root_in_scope","is_root_in_scope",1,"[NULL]"
-"source","source","[NULL]","descriptor"
-"source_id","source_id",1,"[NULL]"
-"source","source","[NULL]","chrome"
-"source_id","source_id",1234,"[NULL]"
-"source_id_is_process_scoped","source_id_is_process_scoped",0,"[NULL]"
-"source_scope","source_scope","[NULL]","cat"
-"debug.debug1.key1","debug.debug1.key1",10,"[NULL]"
-"debug.debug1.key2","debug.debug1.key2[0]",20,"[NULL]"
-"debug.debug1.key2","debug.debug1.key2[1]",21,"[NULL]"
-"debug.debug1.key2","debug.debug1.key2[2]",22,"[NULL]"
-"debug.debug1.key2","debug.debug1.key2[3]",23,"[NULL]"
-"debug.debug1.key3","debug.debug1.key3",30,"[NULL]"
-"debug.debug2.key1","debug.debug2.key1",10,"[NULL]"
-"debug.debug2.key2","debug.debug2.key2[0]",20,"[NULL]"
-"debug.debug2.key2","debug.debug2.key2[1]",21,"[NULL]"
-"debug.debug2.key2","debug.debug2.key2[2]",22,"[NULL]"
-"debug.debug2.key2","debug.debug2.key2[3]",23,"[NULL]"
-"debug.debug2.key3.key31","debug.debug2.key3.key31",31,"[NULL]"
-"debug.debug2.key3.key32","debug.debug2.key3.key32",32,"[NULL]"
-"debug.debug2.key4","debug.debug2.key4",40,"[NULL]"
-"debug.debug3","debug.debug3",32,"[NULL]"
-"debug.debug4.key1","debug.debug4.key1",10,"[NULL]"
-"debug.debug4.key2","debug.debug4.key2[0]",20,"[NULL]"
-"debug.debug4.key2","debug.debug4.key2[1]",21,"[NULL]"
-"legacy_event.passthrough_utid","legacy_event.passthrough_utid",1,"[NULL]"
diff --git a/test/trace_processor/track_event/track_event_monotonic_trace_clock.textproto b/test/trace_processor/track_event/track_event_monotonic_trace_clock.textproto
deleted file mode 100644
index fa1b543..0000000
--- a/test/trace_processor/track_event/track_event_monotonic_trace_clock.textproto
+++ /dev/null
@@ -1,39 +0,0 @@
-# Define MONOTONIC as trace clock and expect event timestamps to be imported
-# in MONOTONIC / converted to MONOTONIC if necessary.
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 0
-  clock_snapshot {
-    primary_trace_clock: 3  # BUILTIN_CLOCK_MONOTONIC
-    clocks {
-      clock_id: 3  # BUILTIN_CLOCK_MONOTONIC
-      timestamp: 1000
-    }
-    clocks {
-      clock_id: 6  # BUILTIN_CLOCK_BOOTTIME
-      timestamp: 11000
-    }
-  }
-}
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 1000
-  timestamp_clock_id: 3  # BUILTIN_CLOCK_MONOTONIC
-  track_event {
-    track_uuid: 1
-    categories: "cat"
-    name: "name1"
-    type: 3
-  }
-}
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 12000
-  timestamp_clock_id: 6  # BUILTIN_CLOCK_BOOTTIME
-  track_event {
-    track_uuid: 1
-    categories: "cat"
-    name: "name2"
-    type: 3
-  }
-}
\ No newline at end of file
diff --git a/test/trace_processor/track_event/track_event_monotonic_trace_clock_slices.out b/test/trace_processor/track_event/track_event_monotonic_trace_clock_slices.out
deleted file mode 100644
index 55c67b8..0000000
--- a/test/trace_processor/track_event/track_event_monotonic_trace_clock_slices.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"track","process","thread","thread_process","ts","dur","category","name"
-"name1","[NULL]","[NULL]","[NULL]",1000,0,"cat","name1"
-"name1","[NULL]","[NULL]","[NULL]",2000,0,"cat","name2"
diff --git a/test/trace_processor/track_event/track_event_processes_test.sql b/test/trace_processor/track_event/track_event_processes_test.sql
deleted file mode 100644
index 316e927..0000000
--- a/test/trace_processor/track_event/track_event_processes_test.sql
+++ /dev/null
@@ -1,20 +0,0 @@
---
--- 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
-  id,
-  name,
-  extract_arg(arg_set_id, "chrome.host_app_package_name") as host_app
-from process;
diff --git a/test/trace_processor/track_event/track_event_same_tids.textproto b/test/trace_processor/track_event/track_event_same_tids.textproto
deleted file mode 100644
index d8d7ca2..0000000
--- a/test/trace_processor/track_event/track_event_same_tids.textproto
+++ /dev/null
@@ -1,45 +0,0 @@
-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: 1
-  timestamp: 0
-  track_descriptor {
-    uuid: 2
-    thread {
-      pid: 10
-      tid: 1
-      thread_name: "t2"
-    }
-  }
-}
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 1000
-  track_event {
-    track_uuid: 1
-    categories: "cat"
-    name: "name1"
-    type: 3
-  }
-}
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 2000
-  track_event {
-    track_uuid: 2
-    categories: "cat"
-    name: "name2"
-    type: 3
-  }
-}
diff --git a/test/trace_processor/track_event/track_event_same_tids_slices.out b/test/trace_processor/track_event/track_event_same_tids_slices.out
deleted file mode 100644
index 0812854..0000000
--- a/test/trace_processor/track_event/track_event_same_tids_slices.out
+++ /dev/null
@@ -1,3 +0,0 @@
-"track","process","thread","thread_process","ts","dur","category","name"
-"[NULL]","[NULL]","t1","[NULL]",1000,0,"cat","name1"
-"[NULL]","[NULL]","t2","[NULL]",2000,0,"cat","name2"
diff --git a/test/trace_processor/track_event/track_event_same_tids_threads.out b/test/trace_processor/track_event/track_event_same_tids_threads.out
deleted file mode 100644
index b4ce83a..0000000
--- a/test/trace_processor/track_event/track_event_same_tids_threads.out
+++ /dev/null
@@ -1,5 +0,0 @@
-"tid","pid","pname","tname"
-1,5,"[NULL]","t1"
-1,10,"[NULL]","t2"
-5,5,"[NULL]","[NULL]"
-10,10,"[NULL]","[NULL]"
diff --git a/test/trace_processor/track_event/track_event_slice_with_args_test.sql b/test/trace_processor/track_event/track_event_slice_with_args_test.sql
deleted file mode 100644
index 8cf8501..0000000
--- a/test/trace_processor/track_event/track_event_slice_with_args_test.sql
+++ /dev/null
@@ -1,36 +0,0 @@
---
--- Copyright 2021 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,
-  process.name as process,
-  thread.name as thread,
-  thread_process.name as thread_process,
-  slice.ts,
-  slice.dur,
-  slice.category,
-  slice.name,
-  args.key,
-  args.string_value,
-  args.int_value
-from slice
-left join track on slice.track_id = track.id
-left join process_track on slice.track_id = process_track.id
-left join process on process_track.upid = process.upid
-left join thread_track on slice.track_id = thread_track.id
-left join thread on thread_track.utid = thread.utid
-left join process thread_process on thread.upid = thread_process.upid
-left join args on slice.arg_set_id = args.arg_set_id
-order by ts asc;
diff --git a/test/trace_processor/track_event/track_event_slices_test.sql b/test/trace_processor/track_event/track_event_slices_test.sql
deleted file mode 100644
index 8308b16..0000000
--- a/test/trace_processor/track_event/track_event_slices_test.sql
+++ /dev/null
@@ -1,32 +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
-  track.name as track,
-  process.name as process,
-  thread.name as thread,
-  thread_process.name as thread_process,
-  slice.ts,
-  slice.dur,
-  slice.category,
-  slice.name
-from slice
-left join track on slice.track_id = track.id
-left join process_track on slice.track_id = process_track.id
-left join process on process_track.upid = process.upid
-left join thread_track on slice.track_id = thread_track.id
-left join thread on thread_track.utid = thread.utid
-left join process thread_process on thread.upid = thread_process.upid
-order by ts asc;
diff --git a/test/trace_processor/track_event/track_event_tracks.out b/test/trace_processor/track_event/track_event_tracks.out
deleted file mode 100644
index e0dae6e..0000000
--- a/test/trace_processor/track_event/track_event_tracks.out
+++ /dev/null
@@ -1,14 +0,0 @@
-"name","parent_name"
-"Default Track","[NULL]"
-"async","process=p1"
-"async2","process=p1"
-"async3","thread=t2"
-"event_and_track_async3","process=p1"
-"process=p1","[NULL]"
-"process=p2","[NULL]"
-"process=p2","[NULL]"
-"thread=t1","process=p1"
-"thread=t2","process=p1"
-"thread=t3","process=p1"
-"thread=t4","process=p2"
-"tid=1","[NULL]"
diff --git a/test/trace_processor/track_event/track_event_tracks.textproto b/test/trace_processor/track_event/track_event_tracks.textproto
deleted file mode 100644
index eca8181..0000000
--- a/test/trace_processor/track_event/track_event_tracks.textproto
+++ /dev/null
@@ -1,330 +0,0 @@
-# Sequence 1 defaults to track for "t1".
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 0
-  incremental_state_cleared: true
-  track_descriptor {
-    uuid: 1
-    parent_uuid: 10
-    thread {
-      pid: 5
-      tid: 1
-      thread_name: "t1"
-    }
-  }
-  trace_packet_defaults {
-    track_event_defaults {
-      track_uuid: 1
-    }
-  }
-}
-# Sequence 2 defaults to track for "t2".
-packet {
-  trusted_packet_sequence_id: 2
-  timestamp: 0
-  incremental_state_cleared: true
-  track_descriptor {
-    uuid: 2
-    parent_uuid: 10
-    thread {
-      pid: 5
-      tid: 2
-      thread_name: "t2"
-    }
-  }
-  trace_packet_defaults {
-    track_event_defaults {
-      track_uuid: 2
-    }
-  }
-}
-# Both thread tracks are nested underneath this process track.
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 0
-  track_descriptor {
-    uuid: 10
-    process {
-      pid: 5
-      process_name: "p1"
-    }
-    chrome_process {
-      host_app_package_name: "host_app"
-    }
-  }
-}
-# And we have an async track underneath the process too.
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 0
-  track_descriptor {
-    uuid: 11
-    parent_uuid: 10
-    name: "async"
-  }
-}
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 100
-  track_descriptor {
-    uuid: 12
-    parent_uuid: 10
-    name: "async2"
-  }
-}
-packet {
-  trusted_packet_sequence_id: 2
-  timestamp: 200
-  track_descriptor {
-    uuid: 12
-    parent_uuid: 10
-    name: "async2"
-  }
-}
-
-# Threads also can have child async tracks.
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 200
-  track_descriptor {
-    uuid: 14
-    parent_uuid: 2
-    name: "async3"
-  }
-}
-
-# Should appear on default track "t1".
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 1000
-  track_event {
-    categories: "cat"
-    name: "event1_on_t1"
-    type: 3
-  }
-}
-# Should appear on default track "t2".
-packet {
-  trusted_packet_sequence_id: 2
-  timestamp: 2000
-  track_event {
-    categories: "cat"
-    name: "event1_on_t2"
-    type: 3
-  }
-}
-# Should appear on overridden track "t2".
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 3000
-  track_event {
-    track_uuid: 2
-    categories: "cat"
-    name: "event2_on_t2"
-    type: 3
-  }
-}
-# Should appear on process track.
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 4000
-  track_event {
-    track_uuid: 10
-    categories: "cat"
-    name: "event1_on_p1"
-    type: 3
-  }
-}
-# Should appear on async track.
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 5000
-  track_event {
-    track_uuid: 11
-    categories: "cat"
-    name: "event1_on_async"
-    type: 3
-  }
-}
-# Event for the "async2" track starting on one thread and ending on another.
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 5100
-  track_event {
-    track_uuid: 12
-    categories: "cat"
-    name: "event1_on_async2"
-    type: 1
-  }
-}
-packet {
-  trusted_packet_sequence_id: 2
-  timestamp: 5200
-  track_event {
-    track_uuid: 12
-    categories: "cat"
-    name: "event1_on_async2"
-    type: 2
-  }
-}
-
-# If we later see another track descriptor for tid 1, but with a different uuid,
-# we should detect tid reuse and start a new thread.
-packet {
-  trusted_packet_sequence_id: 3
-  timestamp: 10000
-  incremental_state_cleared: true
-  track_descriptor {
-    uuid: 3
-    parent_uuid: 10
-    thread {
-      pid: 5
-      tid: 1
-      thread_name: "t3"
-    }
-  }
-}
-# Should appear on t3.
-packet {
-  trusted_packet_sequence_id: 3
-  timestamp: 11000
-  track_event {
-    track_uuid: 3
-    categories: "cat"
-    name: "event1_on_t3"
-    type: 3
-  }
-}
-
-# If we later see another track descriptor for pid 5, but with a different uuid,
-# we should detect pid reuse and start a new process.
-packet {
-  trusted_packet_sequence_id: 4
-  timestamp: 20000
-  incremental_state_cleared: true
-  track_descriptor {
-    uuid: 20
-    process {
-      pid: 5
-      process_name: "p2"
-    }
-  }
-}
-# Should appear on p2.
-packet {
-  trusted_packet_sequence_id: 4
-  timestamp: 21000
-  track_event {
-    track_uuid: 20
-    categories: "cat"
-    name: "event1_on_p2"
-    type: 3
-  }
-}
-# Another thread t4 in the new process.
-packet {
-  trusted_packet_sequence_id: 4
-  timestamp: 22000
-  incremental_state_cleared: true
-  track_descriptor {
-    uuid: 21
-    parent_uuid: 20
-    thread {
-      pid: 5
-      tid: 4
-      thread_name: "t4"
-    }
-  }
-}
-# Should appear on t4.
-packet {
-  trusted_packet_sequence_id: 4
-  timestamp: 22000
-  track_event {
-    track_uuid: 21
-    categories: "cat"
-    name: "event1_on_t4"
-    type: 3
-  }
-}
-
-# Another packet for a thread track in the old process, badly sorted.
-packet {
-  trusted_packet_sequence_id: 2
-  timestamp: 6000
-  track_event {
-    track_uuid: 1
-    categories: "cat"
-    name: "event3_on_t1"
-    type: 3
-  }
-}
-
-# Override the track to the default descriptor track for an event with a
-# TrackEvent type. Should appear on the default descriptor track instead of
-# "t1".
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 30000
-  track_event {
-    track_uuid: 0
-    categories: "cat"
-    name: "event1_on_t1"
-    type: 3
-  }
-}
-
-# But a legacy event without TrackEvent type falls back to legacy tracks (based
-# on ThreadDescriptor / async IDs / legacy instant scopes). This instant event
-# should appear on the process track "p2".
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 31000
-  track_event {
-    track_uuid: 0
-    categories: "cat"
-    name: "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
-    }
-  }
-}
-
-# Track descriptor without name and process/thread association derives its
-# name from the first event on the track.
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 40000
-  track_descriptor {
-    uuid: 13
-    parent_uuid: 10
-  }
-}
-
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 40000
-  track_event {
-    track_uuid: 13
-    categories: "cat"
-    name: "event_and_track_async3"
-    type: 3
-  }
-}
diff --git a/test/trace_processor/track_event/track_event_tracks_processes.out b/test/trace_processor/track_event/track_event_tracks_processes.out
deleted file mode 100644
index cdf3653..0000000
--- a/test/trace_processor/track_event/track_event_tracks_processes.out
+++ /dev/null
@@ -1,4 +0,0 @@
-"id","name","host_app"
-0,"[NULL]","[NULL]"
-1,"p1","host_app"
-2,"p2","[NULL]"
diff --git a/test/trace_processor/track_event/track_event_tracks_test.sql b/test/trace_processor/track_event/track_event_tracks_test.sql
deleted file mode 100644
index 7f8155b..0000000
--- a/test/trace_processor/track_event/track_event_tracks_test.sql
+++ /dev/null
@@ -1,22 +0,0 @@
-with track_with_name as (
-  select
-    COALESCE(
-      t1.name, 
-      'thread=' || thread.name,
-      'process=' || process.name,
-      'tid=' || thread.tid,
-      'pid=' || process.pid
-    ) as full_name,
-    *
-  from track t1
-  left join thread_track t2 using (id)
-  left join thread using (utid)
-  left join process_track t3 using (id)
-  left join process on t3.upid=process.id
-  order by id
-)
-select t1.full_name as name, t2.full_name as parent_name
-from track_with_name t1
-left join track_with_name t2 on t1.parent_id=t2.id
-order by 1, 2;
-
diff --git a/test/trace_processor/track_event/track_event_typed_args_args.out b/test/trace_processor/track_event/track_event_typed_args_args.out
deleted file mode 100644
index fae18d7..0000000
--- a/test/trace_processor/track_event/track_event_typed_args_args.out
+++ /dev/null
@@ -1,27 +0,0 @@
-"flat_key","key","int_value","string_value"
-"is_root_in_scope","is_root_in_scope",1,"[NULL]"
-"source","source","[NULL]","descriptor"
-"source_id","source_id",1,"[NULL]"
-"chrome_user_event.action","chrome_user_event.action","[NULL]","NewTab"
-"chrome_legacy_ipc.message_class","chrome_legacy_ipc.message_class","[NULL]","CLASS_AUTOMATION"
-"chrome_legacy_ipc.message_line","chrome_legacy_ipc.message_line",10,"[NULL]"
-"chrome_keyed_service.name","chrome_keyed_service.name","[NULL]","MediaRouter"
-"chrome_latency_info.component_info.component_type","chrome_latency_info.component_info[0].component_type","[NULL]","COMPONENT_INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL"
-"chrome_latency_info.component_info.time_us","chrome_latency_info.component_info[0].time_us",1201,"[NULL]"
-"chrome_latency_info.component_info.time_us","chrome_latency_info.component_info[1].time_us",928310,"[NULL]"
-"chrome_latency_info.is_coalesced","chrome_latency_info.is_coalesced",1,"[NULL]"
-"chrome_latency_info.trace_id","chrome_latency_info.trace_id",7,"[NULL]"
-"int_extension_for_testing","int_extension_for_testing[0]",42,"[NULL]"
-"int_extension_for_testing","int_extension_for_testing[1]",1337,"[NULL]"
-"nested_message_extension_for_testing.arg1","nested_message_extension_for_testing.arg1","[NULL]","value"
-"nested_message_extension_for_testing.arg2.key","nested_message_extension_for_testing.arg2.key","[NULL]","value"
-"nested_message_extension_for_testing.child_field_for_testing","nested_message_extension_for_testing.child_field_for_testing","[NULL]","nesting test"
-"string_extension_for_testing","string_extension_for_testing","[NULL]","an extension string!"
-"chrome_app_state","chrome_app_state","[NULL]","APP_STATE_FOREGROUND"
-"chrome_memory_pressure_notification.file_name","chrome_memory_pressure_notification.file_name","[NULL]","another_source.cc"
-"chrome_memory_pressure_notification.function_name","chrome_memory_pressure_notification.function_name","[NULL]","AnotherSourceFunction"
-"chrome_memory_pressure_notification.line_number","chrome_memory_pressure_notification.line_number",1337,"[NULL]"
-"source.file_name","source.file_name","[NULL]","source.cc"
-"source.function_name","source.function_name","[NULL]","SourceFunction"
-"source.line_number","source.line_number",0,"[NULL]"
-"source_location_iid","source_location_iid",1,"[NULL]"
diff --git a/test/trace_processor/track_event/track_event_typed_args_slices.out b/test/trace_processor/track_event/track_event_typed_args_slices.out
deleted file mode 100644
index 1c10497..0000000
--- a/test/trace_processor/track_event/track_event_typed_args_slices.out
+++ /dev/null
@@ -1,7 +0,0 @@
-"track","process","thread","thread_process","ts","dur","category","name"
-"[NULL]","[NULL]","t1","[NULL]",1000,0,"cat","name1"
-"[NULL]","[NULL]","t1","[NULL]",2000,0,"cat","name2"
-"[NULL]","[NULL]","t1","[NULL]",3000,0,"cat","name3"
-"[NULL]","[NULL]","t1","[NULL]",4000,0,"cat","name4"
-"[NULL]","[NULL]","t1","[NULL]",6000,0,"cat","name5"
-"[NULL]","[NULL]","t1","[NULL]",7000,0,"cat","name6"
diff --git a/test/trace_processor/track_event/track_event_with_atrace.out b/test/trace_processor/track_event/track_event_with_atrace.out
deleted file mode 100644
index cb5f333..0000000
--- a/test/trace_processor/track_event/track_event_with_atrace.out
+++ /dev/null
@@ -1,4 +0,0 @@
-"track","process","thread","thread_process","ts","dur","category","name"
-"[NULL]","[NULL]","t1","[NULL]",10000,1000,"cat","event1"
-"[NULL]","[NULL]","t1","[NULL]",20000,8000,"cat","event2"
-"[NULL]","[NULL]","t1","[NULL]",21000,7000,"[NULL]","atrace"
diff --git a/test/trace_processor/translation/chrome_args_test.sql b/test/trace_processor/translation/chrome_args_test.sql
deleted file mode 100644
index 63c2492..0000000
--- a/test/trace_processor/translation/chrome_args_test.sql
+++ /dev/null
@@ -1,16 +0,0 @@
---
--- Copyright 2022 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 flat_key, key, int_value, string_value from args order by arg_set_id, key asc;
diff --git a/test/trace_processor/translation/chrome_histogram.out b/test/trace_processor/translation/chrome_histogram.out
deleted file mode 100644
index 484b008..0000000
--- a/test/trace_processor/translation/chrome_histogram.out
+++ /dev/null
@@ -1,10 +0,0 @@
-"flat_key","key","int_value","string_value"
-"is_root_in_scope","is_root_in_scope",1,"[NULL]"
-"source","source","[NULL]","descriptor"
-"source_id","source_id",12345,"[NULL]"
-"chrome_histogram_sample.name","chrome_histogram_sample.name","[NULL]","histogram_name1"
-"chrome_histogram_sample.name_hash","chrome_histogram_sample.name_hash",10,"[NULL]"
-"chrome_histogram_sample.sample","chrome_histogram_sample.sample",100,"[NULL]"
-"chrome_histogram_sample.name","chrome_histogram_sample.name","[NULL]","histogram_name2"
-"chrome_histogram_sample.name_hash","chrome_histogram_sample.name_hash",20,"[NULL]"
-"chrome_histogram_sample.name_hash","chrome_histogram_sample.name_hash",30,"[NULL]"
diff --git a/test/trace_processor/translation/chrome_performance_mark.out b/test/trace_processor/translation/chrome_performance_mark.out
deleted file mode 100644
index ae86237..0000000
--- a/test/trace_processor/translation/chrome_performance_mark.out
+++ /dev/null
@@ -1,8 +0,0 @@
-"flat_key","key","int_value","string_value"
-"is_root_in_scope","is_root_in_scope",1,"[NULL]"
-"source","source","[NULL]","descriptor"
-"source_id","source_id",12345,"[NULL]"
-"chrome_hashed_performance_mark.mark","chrome_hashed_performance_mark.mark","[NULL]","mark2"
-"chrome_hashed_performance_mark.mark_hash","chrome_hashed_performance_mark.mark_hash",20,"[NULL]"
-"chrome_hashed_performance_mark.site","chrome_hashed_performance_mark.site","[NULL]","site1"
-"chrome_hashed_performance_mark.site_hash","chrome_hashed_performance_mark.site_hash",10,"[NULL]"
diff --git a/test/trace_processor/translation/chrome_performance_mark.textproto b/test/trace_processor/translation/chrome_performance_mark.textproto
deleted file mode 100644
index b3ff668..0000000
--- a/test/trace_processor/translation/chrome_performance_mark.textproto
+++ /dev/null
@@ -1,50 +0,0 @@
-# Chrome performance mark hashes translation rules
-packet {
-  translation_table {
-    chrome_performance_mark {
-      site_hash_to_name { key: 10 value: "site1" }
-      mark_hash_to_name { key: 20 value: "mark2" }
-    }
-  }
-}
-# Track for slice begin/end events.
-packet {
-  timestamp: 0
-  trusted_packet_sequence_id: 1
-  track_descriptor {
-    uuid: 12345
-    thread {
-      pid: 123
-      tid: 345
-    }
-    parent_uuid: 0
-    chrome_thread {
-      thread_type: THREAD_POOL_FG_WORKER
-    }
-  }
-}
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 1
-  track_event {
-    categories: "cat1"
-    track_uuid: 12345
-    type: 1
-    name: "slice1"
-    [perfetto.protos.ChromeTrackEvent.chrome_hashed_performance_mark] {
-      site_hash: 10
-      mark_hash: 20
-    }
-  }
-}
-# Slice end
-packet {
-  trusted_packet_sequence_id: 1
-  timestamp: 6000
-  track_event {
-    track_uuid: 12345
-    categories: "cat1"
-    name: "slice1"
-    type: 2
-  }
-}
diff --git a/test/trace_processor/translation/chrome_user_event.out b/test/trace_processor/translation/chrome_user_event.out
deleted file mode 100644
index 6532ef8..0000000
--- a/test/trace_processor/translation/chrome_user_event.out
+++ /dev/null
@@ -1,9 +0,0 @@
-"flat_key","key","int_value","string_value"
-"is_root_in_scope","is_root_in_scope",1,"[NULL]"
-"source","source","[NULL]","descriptor"
-"source_id","source_id",12345,"[NULL]"
-"chrome_user_event.action","chrome_user_event.action","[NULL]","action1"
-"chrome_user_event.action_hash","chrome_user_event.action_hash",10,"[NULL]"
-"chrome_user_event.action","chrome_user_event.action","[NULL]","action2"
-"chrome_user_event.action_hash","chrome_user_event.action_hash",20,"[NULL]"
-"chrome_user_event.action_hash","chrome_user_event.action_hash",30,"[NULL]"
diff --git a/test/trace_processor/translation/index b/test/trace_processor/translation/index
deleted file mode 100644
index 7b03b51..0000000
--- a/test/trace_processor/translation/index
+++ /dev/null
@@ -1,7 +0,0 @@
-chrome_histogram.textproto chrome_args_test.sql chrome_histogram.out
-chrome_user_event.textproto chrome_args_test.sql chrome_user_event.out
-chrome_performance_mark.textproto chrome_args_test.sql chrome_performance_mark.out
-slice_name.textproto slice_name_test.sql slice_name.out
-slice_name_negative_timestamp.textproto slice_name_test.sql slice_name.out
-native_symbol_arg.textproto chrome_args_test.sql native_symbol_arg.out
-native_symbol_arg_incomplete.textproto chrome_args_test.sql native_symbol_arg.out
\ No newline at end of file
diff --git a/test/trace_processor/translation/native_symbol_arg.out b/test/trace_processor/translation/native_symbol_arg.out
deleted file mode 100644
index 5f0377b..0000000
--- a/test/trace_processor/translation/native_symbol_arg.out
+++ /dev/null
@@ -1,12 +0,0 @@
-"flat_key","key","int_value","string_value"
-"is_root_in_scope","is_root_in_scope",1,"[NULL]"
-"source","source","[NULL]","descriptor"
-"source_id","source_id",12345,"[NULL]"
-"chrome_mojo_event_info.mojo_interface_tag","chrome_mojo_event_info.mojo_interface_tag","[NULL]","storage.mojom.Directory"
-"chrome_mojo_event_info.mojo_method_name","chrome_mojo_event_info.mojo_method_name","[NULL]","Directory::OpenFile"
-"chrome_mojo_event_info.mojo_interface_tag","chrome_mojo_event_info.mojo_interface_tag","[NULL]","storage.mojom.File"
-"chrome_mojo_event_info.mojo_method_name","chrome_mojo_event_info.mojo_method_name","[NULL]","File::Close"
-"chrome_mojo_event_info.mojo_interface_tag","chrome_mojo_event_info.mojo_interface_tag","[NULL]","Class::Method"
-"chrome_mojo_event_info.mojo_method_name","chrome_mojo_event_info.mojo_method_name","[NULL]","Class::Method"
-"chrome_mojo_event_info.mojo_interface_tag","chrome_mojo_event_info.mojo_interface_tag","[NULL]","Func"
-"chrome_mojo_event_info.mojo_method_name","chrome_mojo_event_info.mojo_method_name","[NULL]","Func"
diff --git a/test/trace_processor/translation/slice_name.out b/test/trace_processor/translation/slice_name.out
deleted file mode 100644
index 32dedec..0000000
--- a/test/trace_processor/translation/slice_name.out
+++ /dev/null
@@ -1,5 +0,0 @@
-"name"
-"mapped_name1"
-"mapped_name2"
-"raw_name3"
-"slice_begin"
diff --git a/test/trace_processor/translation/slice_name_test.sql b/test/trace_processor/translation/slice_name_test.sql
deleted file mode 100644
index a3011a8..0000000
--- a/test/trace_processor/translation/slice_name_test.sql
+++ /dev/null
@@ -1 +0,0 @@
-SELECT name FROM slice ORDER BY name;
diff --git a/test/traced_integrationtest.cc b/test/traced_integrationtest.cc
index de71c27..426b713 100644
--- a/test/traced_integrationtest.cc
+++ b/test/traced_integrationtest.cc
@@ -71,7 +71,7 @@
   ds_config->set_name("android.perfetto.FakeProducer");
   ds_config->set_target_buffer(0);
 
-  static constexpr size_t kNumPackets = 11;
+  static constexpr size_t kNumPackets = 12;
   static constexpr uint32_t kRandomSeed = 42;
   static constexpr uint32_t kMsgSize = 1024;
   ds_config->mutable_for_testing()->set_seed(kRandomSeed);
diff --git a/test/vts/Android.bp b/test/vts/Android.bp
new file mode 100644
index 0000000..f65011e
--- /dev/null
+++ b/test/vts/Android.bp
@@ -0,0 +1,32 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "external_perfetto_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["external_perfetto_license"],
+}
+
+cc_test {
+  name: "VtsPerfettoTestCases",
+  srcs: [
+    "vendor_perfetto_atrace_test.cc",
+  ],
+  static_libs: [
+    "libgmock",
+    "perfetto_vts_deps",
+  ],
+  whole_static_libs: [
+    "perfetto_gtest_logcat_printer",
+  ],
+  shared_libs: [
+    "libandroid",
+    "liblog",
+  ],
+  test_suites: [
+    "vts",
+  ],
+  defaults: [
+    "perfetto_defaults",
+  ],
+}
diff --git a/test/vts/AndroidTest.xml b/test/vts/AndroidTest.xml
new file mode 100644
index 0000000..575c065
--- /dev/null
+++ b/test/vts/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<!-- The Android.mk file that defines these target lives in /external/perfetto/cts -->
+<configuration description="Config for VTS Perfetto test cases">
+    <option name="test-suite-tag" value="vts" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="VtsPerfettoTestCases->/data/local/tmp/VtsPerfettoTestCases" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="VtsPerfettoTestCases" />
+        <option name="runtime-hint" value="0m5s" />
+        <!-- test-timeout unit is ms -->
+        <option name="native-test-timeout" value="10000" />
+    </test>
+</configuration>
diff --git a/test/vts/BUILD.gn b/test/vts/BUILD.gn
new file mode 100644
index 0000000..48c03e9
--- /dev/null
+++ b/test/vts/BUILD.gn
@@ -0,0 +1,30 @@
+# Copyright (C) 2022 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(is_android)
+
+static_library("perfetto_vts_deps") {
+  complete_static_lib = true
+  testonly = true
+  deps = [
+    "../../gn:default_deps",
+    "../../gn:gtest_and_gmock",
+    "../../src/base",
+    "../../src/base:test_support",
+    "../../src/traced/probes/ftrace",
+    "../../test:test_helper",
+  ]
+}
diff --git a/test/vts/vendor_perfetto_atrace_test.cc b/test/vts/vendor_perfetto_atrace_test.cc
new file mode 100644
index 0000000..fcda81c
--- /dev/null
+++ b/test/vts/vendor_perfetto_atrace_test.cc
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 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 <string>
+
+#include "perfetto/ext/base/file_utils.h"
+#include "src/traced/probes/ftrace/vendor_tracepoints.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace {
+
+TEST(PerfettoAtraceCategoriesVendorTest, Parse) {
+  if (base::FileExists(vendor_tracepoints::kCategoriesFile)) {
+    std::map<std::string, std::vector<GroupAndName>> categories;
+
+    base::Status status = vendor_tracepoints::DiscoverVendorTracepointsWithFile(
+        vendor_tracepoints::kCategoriesFile, &categories);
+
+    EXPECT_TRUE(status.ok()) << status.message();
+  }
+}
+
+}  // namespace
+}  // namespace perfetto
diff --git a/tools/check_sql_modules.py b/tools/check_sql_modules.py
new file mode 100755
index 0000000..051f1a9
--- /dev/null
+++ b/tools/check_sql_modules.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python3
+# Copyright (C) 2022 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 tool checks that every SQL object created without prefix
+# 'internal_' is documented with proper schema.
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import os
+import sys
+
+ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+sys.path.append(os.path.join(ROOT_DIR))
+FILE_DIR = ROOT_DIR
+
+from python.generators.stdlib_docs.stdlib import *
+
+
+def main():
+
+  errors = []
+  metrics_sources = os.path.join(FILE_DIR, "src", "trace_processor", "stdlib")
+  for root, _, files in os.walk(metrics_sources, topdown=True):
+    for f in files:
+      path = os.path.join(root, f)
+      if path.endswith(".sql"):
+        with open(path) as f:
+          sql = f.read()
+        errors += parse_file_to_dict(path, sql)[1]
+  sys.stderr.write("\n\n".join(errors))
+  return 0 if not errors else 1
+
+
+if __name__ == "__main__":
+  sys.exit(main())
diff --git a/tools/cpu_profile b/tools/cpu_profile
index 46cedbd..df93bba 100755
--- a/tools/cpu_profile
+++ b/tools/cpu_profile
@@ -37,18 +37,18 @@
 
 
 # ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/traceconv.py
-# This file has been generated by: /s/perfetto/tools/roll-prebuilts v28.0
+# This file has been generated by: /usr/local/google/home/lalitm/perfetto/tools/roll-prebuilts v31.0
 TRACECONV_MANIFEST = [{
     'arch':
         'mac-amd64',
     'file_name':
         'traceconv',
     'file_size':
-        7181712,
+        7772872,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-amd64/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/mac-amd64/traceconv',
     'sha256':
-        '6b398ad9539ddf8208536c0412db198d4627daa97efc7e0850f3e7ec0e115510',
+        'a987cfd2e722994a5911032d046ec2d0b912845a83b093226c3fccd5e316ed01',
     'platform':
         'darwin',
     'machine': ['x86_64']
@@ -58,11 +58,11 @@
     'file_name':
         'traceconv',
     'file_size':
-        6025176,
+        6554552,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-arm64/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/mac-arm64/traceconv',
     'sha256':
-        '407e2988e795a593158304061c547093ad74419f826dd03c2a66911b5a29d065',
+        '972610d249990d4b03de50bece4a7adf03ebd6b3cc3c2c10feb7bb6561c1fce1',
     'platform':
         'darwin',
     'machine': ['arm64']
@@ -72,11 +72,11 @@
     'file_name':
         'traceconv',
     'file_size':
-        7668600,
+        8073432,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-amd64/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-amd64/traceconv',
     'sha256':
-        '1bebc0dd7b2b18fd4abeeb5f811d6d4c7f431d212efd5469c7e5d8b18b19e0c7',
+        'e34f6ddd91ad0de62bfbef76303f2a94136470fe5a4f679af2f5b8898548ac13',
     'platform':
         'linux',
     'machine': ['x86_64']
@@ -86,11 +86,11 @@
     'file_name':
         'traceconv',
     'file_size':
-        5827680,
+        6677612,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-arm/traceconv',
     'sha256':
-        'a19780145f965838e334a57a52230bc67b0db207365746360314fbbbe4e1d12f',
+        '55c9cd6e5c5bc65210068a173b0c16ad3323faaba0fccec20247dca5bd3b913d',
     'platform':
         'linux',
     'machine': ['armv6l', 'armv7l', 'armv8l']
@@ -100,11 +100,11 @@
     'file_name':
         'traceconv',
     'file_size':
-        6876384,
+        7543648,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm64/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-arm64/traceconv',
     'sha256':
-        '2a5e5fcf340070ed6a30204c79b7e76787c5f26181bc8377587547f3eb5df685',
+        '2477857470f613410f6151acb06b6b6a067ef8612d619eee86dd2a3b8aac2a3e',
     'platform':
         'linux',
     'machine': ['aarch64']
@@ -114,55 +114,55 @@
     'file_name':
         'traceconv',
     'file_size':
-        4881820,
+        5360020,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-arm/traceconv',
     'sha256':
-        '73827b82d941a9650580fbd48c3d4ff2323eb8d4ff9d3fffd3e0cac1bc853f34'
+        '60cecbd8d9b6357bb89929fa5afa1eba6c1d46d621e62ee558509424d88bd53d'
 }, {
     'arch':
         'android-arm64',
     'file_name':
         'traceconv',
     'file_size':
-        6222592,
+        6769184,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm64/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-arm64/traceconv',
     'sha256':
-        '72d46258645d486f40ee463052b609d1fd7c4cc64f70c0ba2ef811a9924be98e'
+        'e373cbed4502977290abe3e246132ced538fca33881058e6049676ef9c613dd9'
 }, {
     'arch':
         'android-x86',
     'file_name':
         'traceconv',
     'file_size':
-        7089524,
+        7682412,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x86/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-x86/traceconv',
     'sha256':
-        '689d0b48f91624585285b3833362cdcfdf0de1ff5dedcb97bb9851c729b4a15e'
+        'dcaa5d156247433537d59ad33238feb23dc0f97ba64c51581360f25ac0fc16a8'
 }, {
     'arch':
         'android-x64',
     'file_name':
         'traceconv',
     'file_size':
-        7316248,
+        7903832,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x64/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-x64/traceconv',
     'sha256':
-        '785ec3f0da302ed52521febc5ed5e2cef57ae8840ff241037c51b8d94464f6a2'
+        'b73b8d398019b2a349d3748b4fe4096a069c6362697375d3c2668cf79ef1bb38'
 }, {
     'arch':
         'windows-amd64',
     'file_name':
         'traceconv.exe',
     'file_size':
-        6850048,
+        7188992,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/windows-amd64/traceconv.exe',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/windows-amd64/traceconv.exe',
     'sha256':
-        '19cdec3824d369be3bb053b40b3cfe9f62c2e57e71a5e2ee17ca15b6e7463683',
+        '9cbdfcad3b5f2164d0c9e79d4f8b749db693814ae97fba038385d2e6f0111f72',
     'platform':
         'win32',
     'machine': ['amd64']
@@ -225,8 +225,8 @@
   """ Downloads a prebuilt or returns a cached version
 
   The first time this is invoked, it downloads the |url| and caches it into
-  ~/.perfetto/prebuilts/$tool_name. On subsequent invocations it just runs the
-  cached version.
+  ~/.local/share/perfetto/prebuilts/$tool_name. On subsequent invocations it
+  just runs the cached version.
   """
   dir = os.path.join(
       os.path.expanduser('~'), '.local', 'share', 'perfetto', 'prebuilts')
@@ -254,7 +254,7 @@
       raise Exception('Checksum mismatch for %s (actual: %s, expected: %s)' %
                       (url, actual_sha256, sha256))
     os.chmod(tmp_path, 0o755)
-    os.rename(tmp_path, bin_path)
+    os.replace(tmp_path, bin_path)
     with open(sha256_path, 'w') as f:
       f.write(sha256)
   return bin_path
@@ -382,6 +382,11 @@
       metavar="CONFIG",
       default=None)
   parser.add_argument(
+      "--no-annotations",
+      help="Do not suffix the pprof function names with Android ART mode "
+      "annotations such as [jit].",
+      action="store_true")
+  parser.add_argument(
       "--print-config",
       action="store_true",
       help="Print config instead of running. For debugging.")
@@ -657,7 +662,7 @@
   return trace_file
 
 
-def generate_pprof_profiles(traceconv, trace_file):
+def generate_pprof_profiles(traceconv, trace_file, args):
   """Generates pprof profiles from the recorded trace.
 
   Args:
@@ -668,8 +673,9 @@
     The directory where pprof profiles are output.
   """
   try:
-    traceconv_output = subprocess.check_output(
-        [traceconv, 'profile', '--perf', trace_file])
+    conversion_args = [traceconv, 'profile', '--perf'] + (
+        ['--no-annotations'] if args.no_annotations else []) + [trace_file]
+    traceconv_output = subprocess.check_output(conversion_args)
   except Exception as error:
     exit_with_bug_report(
         "Unable to extract profiles from trace: {}".format(error))
@@ -708,8 +714,8 @@
   record_trace(trace_config, profile_target)
   traceconv = get_traceconv()
   trace_file = symbolize_trace(traceconv, profile_target)
-  copy_profiles_to_destination(profile_target,
-                               generate_pprof_profiles(traceconv, trace_file))
+  copy_profiles_to_destination(
+      profile_target, generate_pprof_profiles(traceconv, trace_file, args))
   return 0
 
 
diff --git a/tools/diff_test_trace_processor.py b/tools/diff_test_trace_processor.py
index 41166fc..e505258 100755
--- a/tools/diff_test_trace_processor.py
+++ b/tools/diff_test_trace_processor.py
@@ -18,389 +18,19 @@
 from __future__ import print_function
 
 import argparse
-import concurrent.futures
 import datetime
-import difflib
 import json
 import os
 import re
 import signal
-import subprocess
 import sys
-import tempfile
-
-from google.protobuf import text_format
-
-from proto_utils import create_message_factory, serialize_textproto_trace, serialize_python_trace
 
 ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-ENV = {
-    'PERFETTO_BINARY_PATH': os.path.join(ROOT_DIR, 'test', 'data'),
-}
-if sys.platform.startswith('linux'):
-  ENV['PATH'] = os.path.join(ROOT_DIR, 'buildtools', 'linux64', 'clang', 'bin')
-elif sys.platform.startswith('darwin'):
-  # Sadly, on macOS we need to check out the Android deps to get
-  # llvm symbolizer.
-  ENV['PATH'] = os.path.join(ROOT_DIR, 'buildtools', 'ndk', 'toolchains',
-                             'llvm', 'prebuilt', 'darwin-x86_64', 'bin')
-elif sys.platform.startswith('win32'):
-  ENV['PATH'] = os.path.join(ROOT_DIR, 'buildtools', 'win', 'clang', 'bin')
+sys.path.append(os.path.join(ROOT_DIR))
 
-USE_COLOR_CODES = sys.stderr.isatty()
-
-def red(no_colors):
-  return "\u001b[31m" if USE_COLOR_CODES and not no_colors else ""
-
-
-def green(no_colors):
-  return "\u001b[32m" if USE_COLOR_CODES and not no_colors else ""
-
-
-def yellow(no_colors):
-  return "\u001b[33m" if USE_COLOR_CODES and not no_colors else ""
-
-
-def end_color(no_colors):
-  return "\u001b[0m" if USE_COLOR_CODES and not no_colors else ""
-
-
-class Test(object):
-
-  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, 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)
-
-
-class TestResult(object):
-
-  def __init__(self, test_type, input_name, trace, cmd, expected, actual,
-               stderr, exit_code):
-    self.test_type = test_type
-    self.input_name = input_name
-    self.trace = trace
-    self.cmd = cmd
-    self.expected = expected
-    self.actual = actual
-    self.stderr = stderr
-    self.exit_code = exit_code
-
-
-def create_metrics_message_factory(metrics_descriptor_paths):
-  return create_message_factory(metrics_descriptor_paths,
-                                'perfetto.protos.TraceMetrics')
-
-
-def write_diff(expected, actual):
-  expected_lines = expected.splitlines(True)
-  actual_lines = actual.splitlines(True)
-  diff = difflib.unified_diff(
-      expected_lines, actual_lines, fromfile='expected', tofile='actual')
-  res = ""
-  for line in diff:
-    res += line
-  return res
-
-
-def run_metrics_test(trace_processor_path, gen_trace_path, metric,
-                     expected_path, perf_path, metrics_message_factory):
-  with open(expected_path, 'r') as expected_file:
-    expected = expected_file.read()
-
-  json_output = os.path.basename(expected_path).endswith('.json.out')
-  cmd = [
-      trace_processor_path,
-      '--run-metrics',
-      metric,
-      '--metrics-output=%s' % ('json' if json_output else 'binary'),
-      '--perf-file',
-      perf_path,
-      gen_trace_path,
-  ]
-  tp = subprocess.Popen(
-      cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=ENV)
-  (stdout, stderr) = tp.communicate()
-
-  if json_output:
-    expected_text = expected
-    actual_text = stdout.decode('utf8')
-  else:
-    # Expected will be in text proto format and we'll need to parse it to
-    # a real proto.
-    expected_message = metrics_message_factory()
-    text_format.Merge(expected, expected_message)
-
-    # Actual will be the raw bytes of the proto and we'll need to parse it
-    # into a message.
-    actual_message = metrics_message_factory()
-    actual_message.ParseFromString(stdout)
-
-    # Convert both back to text format.
-    expected_text = text_format.MessageToString(expected_message)
-    actual_text = text_format.MessageToString(actual_message)
-
-  return TestResult('metric', metric, gen_trace_path, cmd, expected_text,
-                    actual_text, stderr.decode('utf8'), tp.returncode)
-
-
-def run_query_test(trace_processor_path, gen_trace_path, query_path,
-                   expected_path, perf_path):
-  with open(expected_path, 'r') as expected_file:
-    expected = expected_file.read()
-
-  cmd = [
-      trace_processor_path,
-      '-q',
-      query_path,
-      '--perf-file',
-      perf_path,
-      gen_trace_path,
-  ]
-  tp = subprocess.Popen(
-      cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=ENV)
-  (stdout, stderr) = tp.communicate()
-  return TestResult('query', query_path, gen_trace_path, cmd, expected,
-                    stdout.decode('utf8'), stderr.decode('utf8'), tp.returncode)
-
-
-def run_test(trace_descriptor_path, extension_descriptor_paths, args, test):
-  """
-  Returns:
-    test_name -> str,
-    passed -> bools,
-    result_str -> str,
-    perf_data -> str
-  """
-  out_path = os.path.dirname(args.trace_processor)
-  if args.metrics_descriptor:
-    metrics_descriptor_paths = [args.metrics_descriptor]
-  else:
-    metrics_protos_path = os.path.join(out_path, 'gen', 'protos', 'perfetto',
-                                       'metrics')
-    metrics_descriptor_paths = [
-        os.path.join(metrics_protos_path, 'metrics.descriptor'),
-        os.path.join(metrics_protos_path, 'chrome',
-                     'all_chrome_metrics.descriptor')
-    ]
-  metrics_message_factory = create_message_factory(
-      metrics_descriptor_paths, 'perfetto.protos.TraceMetrics')
-  result_str = ""
-  red_str = red(args.no_colors)
-  green_str = green(args.no_colors)
-  end_color_str = end_color(args.no_colors)
-  trace_path = test.trace_path
-  expected_path = test.expected_path
-  test_name = f"{os.path.basename(test.query_path_or_metric)}\
-  {os.path.basename(trace_path)}"
-
-  if not os.path.exists(trace_path):
-    result_str += f"Trace file not found {trace_path}\n"
-    return test_name, False, result_str, ""
-  elif not os.path.exists(expected_path):
-    result_str = f"Expected file not found {expected_path}"
-    return test_name, False, result_str, ""
-
-  is_generated_trace = trace_path.endswith('.py') or trace_path.endswith(
-      '.textproto')
-  if trace_path.endswith('.py'):
-    gen_trace_file = tempfile.NamedTemporaryFile(delete=False)
-    serialize_python_trace(trace_descriptor_path, trace_path, gen_trace_file)
-    gen_trace_path = os.path.realpath(gen_trace_file.name)
-  elif trace_path.endswith('.textproto'):
-    gen_trace_file = tempfile.NamedTemporaryFile(delete=False)
-    serialize_textproto_trace(trace_descriptor_path, extension_descriptor_paths,
-                              trace_path, gen_trace_file)
-    gen_trace_path = os.path.realpath(gen_trace_file.name)
-  else:
-    gen_trace_file = None
-    gen_trace_path = trace_path
-
-  # We can't use delete=True here. When using that on Windows, the
-  # resulting file is opened in exclusive mode (in turn that's a subtle
-  # side-effect of the underlying CreateFile(FILE_ATTRIBUTE_TEMPORARY))
-  # and TP fails to open the passed path.
-  tmp_perf_file = tempfile.NamedTemporaryFile(delete=False)
-  result_str += f"{yellow(args.no_colors)}[ RUN      ]{end_color_str} "
-  result_str += f"{test_name}\n"
-
-  tmp_perf_path = tmp_perf_file.name
-  if test.type == 'queries':
-    query_path = test.query_path_or_metric
-
-    if not os.path.exists(test.query_path_or_metric):
-      result_str += f"Query file not found {query_path}"
-      return test_name, False, result_str, ""
-
-    result = run_query_test(args.trace_processor, gen_trace_path, query_path,
-                            expected_path, tmp_perf_path)
-  elif test.type == 'metrics':
-    result = run_metrics_test(args.trace_processor, gen_trace_path,
-                              test.query_path_or_metric, expected_path,
-                              tmp_perf_path, metrics_message_factory)
-  else:
-    assert False
-
-  perf_lines = [line.decode('utf8') for line in tmp_perf_file.readlines()]
-  tmp_perf_file.close()
-  os.remove(tmp_perf_file.name)
-
-  if gen_trace_file:
-    if args.keep_input:
-      result_str += f"Saving generated input trace: {gen_trace_path}\n"
-    else:
-      gen_trace_file.close()
-      os.remove(gen_trace_path)
-
-  def write_cmdlines():
-    res = ""
-    if is_generated_trace:
-      res += 'Command to generate trace:\n'
-      res += 'tools/serialize_test_trace.py '
-      res += '--descriptor {} {} > {}\n'.format(
-          os.path.relpath(trace_descriptor_path, ROOT_DIR),
-          os.path.relpath(trace_path, ROOT_DIR),
-          os.path.relpath(gen_trace_path, ROOT_DIR))
-    res += f"Command line:\n{' '.join(result.cmd)}\n"
-    return res
-
-  expected_content = result.expected.replace('\r\n', '\n')
-  actual_content = result.actual.replace('\r\n', '\n')
-  contents_equal = (expected_content == actual_content)
-  if result.exit_code != 0 or not contents_equal:
-    result_str += result.stderr
-
-    if result.exit_code == 0:
-      result_str += f"Expected did not match actual for trace "
-      result_str += f"{trace_path} and {result.test_type} {result.input_name}\n"
-      result_str += f"Expected file: {expected_path}\n"
-      result_str += write_cmdlines()
-      result_str += write_diff(result.expected, result.actual)
-    else:
-      result_str += write_cmdlines()
-
-    result_str += f"{red_str}[     FAIL ]{end_color_str} {test_name} "
-    result_str += f"{os.path.basename(trace_path)}\n"
-
-    if args.rebase:
-      if result.exit_code == 0:
-        result_str += f"Rebasing {expected_path}\n"
-        with open(expected_path, 'w') as f:
-          f.write(result.actual)
-        rebased += 1
-      else:
-        result_str += f"Rebase failed for {expected_path} as query failed\n"
-
-    return test_name, False, result_str, ""
-  else:
-    assert len(perf_lines) == 1
-    perf_numbers = perf_lines[0].split(',')
-
-    assert len(perf_numbers) == 2
-    perf_result = PerfResult(test.type, trace_path, test.query_path_or_metric,
-                             perf_numbers[0], perf_numbers[1])
-
-    result_str += f"{green_str}[       OK ]{end_color_str} "
-    result_str += f"{os.path.basename(test.query_path_or_metric)} "
-    result_str += f"{os.path.basename(trace_path)} "
-    result_str += f"(ingest: {perf_result.ingest_time_ns / 1000000:.2f} ms "
-    result_str += f"query: {perf_result.real_time_ns / 1000000:.2f} ms)\n"
-  return test_name, True, result_str, perf_result
-
-
-def run_all_tests(trace_descriptor_path, extension_descriptor_paths, args,
-                  tests):
-  perf_data = []
-  test_failure = []
-  rebased = 0
-  with concurrent.futures.ProcessPoolExecutor() as e:
-    fut = [
-        e.submit(run_test, trace_descriptor_path, extension_descriptor_paths,
-                 args, test) for test in tests
-    ]
-    for res in concurrent.futures.as_completed(fut):
-      test_name, test_passed, res_str, perf_result = res.result()
-      sys.stderr.write(res_str)
-      if test_passed:
-        perf_data.append(perf_result)
-        if args.rebase:
-          rebased += 1
-      else:
-        test_failure.append(test_name)
-
-  return test_failure, perf_data, rebased
-
-
-def read_all_tests_from_index(index_path, query_metric_pattern, trace_pattern):
-  index_dir = os.path.dirname(index_path)
-
-  with open(index_path, 'r') as index_file:
-    index_lines = index_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))
-
-    if query_fname_or_metric.endswith('.sql'):
-      test_type = 'queries'
-      query_path_or_metric = os.path.abspath(
-          os.path.join(index_dir, query_fname_or_metric))
-    else:
-      test_type = 'metrics'
-      query_path_or_metric = query_fname_or_metric
-
-    tests.append(
-        Test(test_type, trace_path, query_path_or_metric, expected_path))
-  return tests
-
-
-def read_all_tests(query_metric_pattern, trace_pattern):
-  include_index_dir = os.path.join(ROOT_DIR, 'test', 'trace_processor')
-  include_index = os.path.join(include_index_dir, 'include_index')
-  tests = []
-  with open(include_index, 'r') as include_file:
-    for index_relpath in include_file.readlines():
-      index_path = os.path.join(include_index_dir, index_relpath.strip())
-      tests.extend(
-          read_all_tests_from_index(index_path, query_metric_pattern,
-                                    trace_pattern))
-  return tests
-
-
-def ctrl_c_handler(_num, _frame):
-  # Send a sigkill to the whole process group. Our process group looks like:
-  # - Main python interpreter running the main()
-  #   - N python interpreters coming from ProcessPoolExecutor workers.
-  #     - 1 trace_processor_shell subprocess coming from the subprocess.Popen().
-  # We don't need any graceful termination as the diff tests are stateless and
-  # don't write any file. Just kill them all immediately.
-  os.killpg(os.getpid(), signal.SIGKILL)
+from python.generators.diff_tests.testing import TestType
+from python.generators.diff_tests.utils import ctrl_c_handler
+from python.generators.diff_tests.runner import DiffTestsRunner
 
 
 def main():
@@ -411,15 +41,10 @@
   parser.add_argument('--metrics-descriptor', type=str)
   parser.add_argument('--perf-file', type=str)
   parser.add_argument(
-      '--query-metric-filter',
+      '--name-filter',
       default='.*',
       type=str,
-      help='Filter the name of query files or metrics to test (regex syntax)')
-  parser.add_argument(
-      '--trace-filter',
-      default='.*',
-      type=str,
-      help='Filter the name of trace files to test (regex syntax)')
+      help='Filter the name of the tests to run (regex syntax)')
   parser.add_argument(
       '--keep-input',
       action='store_true',
@@ -434,100 +59,54 @@
       'trace_processor', type=str, help='location of trace processor binary')
   args = parser.parse_args()
 
-  query_metric_pattern = re.compile(args.query_metric_filter)
-  trace_pattern = re.compile(args.trace_filter)
+  test_runner = DiffTestsRunner(args.name_filter, args.trace_processor,
+                                args.trace_descriptor, args.no_colors)
+  sys.stderr.write(f"[==========] Running {len(test_runner.tests)} tests.\n")
 
-  tests = read_all_tests(query_metric_pattern, trace_pattern)
-  sys.stderr.write(f"[==========] Running {len(tests)} tests.\n")
-
-  out_path = os.path.dirname(args.trace_processor)
-  if args.trace_descriptor:
-    trace_descriptor_path = args.trace_descriptor
-  else:
-
-    def find_trace_descriptor(parent):
-      trace_protos_path = os.path.join(parent, 'gen', 'protos', 'perfetto',
-                                       'trace')
-      return os.path.join(trace_protos_path, 'trace.descriptor')
-
-    trace_descriptor_path = find_trace_descriptor(out_path)
-    if not os.path.exists(trace_descriptor_path):
-      trace_descriptor_path = find_trace_descriptor(
-          os.path.join(out_path, 'gcc_like_host'))
-
-  chrome_extensions = os.path.join(out_path, 'gen', 'protos', 'third_party',
-                                   'chromium', 'chrome_track_event.descriptor')
-  test_extensions = os.path.join(out_path, 'gen', 'protos', 'perfetto', 'trace',
-                                 'test_extensions.descriptor')
-
-  test_run_start = datetime.datetime.now()
-  test_failures, perf_data, rebased = run_all_tests(
-      trace_descriptor_path, [chrome_extensions, test_extensions], args, tests)
-  test_run_end = datetime.datetime.now()
-  test_time_ms = int((test_run_end - test_run_start).total_seconds() * 1000)
-
-  sys.stderr.write(
-      f"[==========] {len(tests)} tests ran. ({test_time_ms} ms total)\n")
-  if test_failures:
-    sys.stderr.write(
-        f"{red(args.no_colors)}[  PASSED  ]{end_color(args.no_colors)} "
-        f"{len(tests) - len(test_failures)} tests.\n")
-  else:
-    sys.stderr.write(
-        f"{green(args.no_colors)}[  PASSED  ]{end_color(args.no_colors)} "
-        f"{len(tests)} tests.\n")
+  results = test_runner.run_all_tests(args.metrics_descriptor, args.keep_input,
+                                      args.rebase)
+  sys.stderr.write(results.str(args.no_colors, len(test_runner.tests)))
 
   if args.rebase:
-    sys.stderr.write('\n')
-    sys.stderr.write(f"{rebased} tests rebased.\n")
+    sys.stderr.write(results.rebase_str())
 
-  if len(test_failures) == 0:
-    if args.perf_file:
-      test_dir = os.path.join(ROOT_DIR, 'test')
-      trace_processor_dir = os.path.join(test_dir, 'trace_processor')
-
-      metrics = []
-      sorted_data = sorted(
-          perf_data,
-          key=lambda x: (x.test_type, x.trace_path, x.query_path_or_metric))
-      for perf_args in sorted_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': f"{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': f"{trace_short_path}-{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
-  else:
-    for failure in test_failures:
-      sys.stderr.write(f"[  FAILED  ] {failure}\n")
+  if len(results.test_failures) > 0:
     return 1
 
+  if args.perf_file:
+    test_dir = os.path.join(ROOT_DIR, 'test')
+    trace_processor_dir = os.path.join(test_dir, 'trace_processor')
+
+    metrics = []
+    sorted_data = sorted(
+        results.perf_data, key=lambda x: (x.test.type.name, x.test.name))
+    for perf_args in sorted_data:
+      metrics.append({
+          'metric': 'tp_perf_test_ingest_time',
+          'value': float(perf_args.ingest_time_ns) / 1.0e9,
+          'unit': 's',
+          'tags': {
+              'test_name': perf_args.test.name,
+              'test_type': perf_args.test.type.name,
+          },
+          'labels': {},
+      })
+      metrics.append({
+          'metric': 'perf_test_real_time',
+          'value': float(perf_args.real_time_ns) / 1.0e9,
+          'unit': 's',
+          'tags': {
+              'test_name': perf_args.test.name,
+              'test_type': perf_args.test.type.name,
+          },
+          '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
+
 
 if __name__ == '__main__':
   sys.exit(main())
diff --git a/tools/download_changed_screenshots.py b/tools/download_changed_screenshots.py
index 326b130..539da22 100755
--- a/tools/download_changed_screenshots.py
+++ b/tools/download_changed_screenshots.py
@@ -47,7 +47,8 @@
     output_path = path.join('test', 'data', 'ui-screenshots', screenshot_name)
     print(f'Downloading {url}')
     urllib.request.urlretrieve(url, output_path)
-  print('All done!')
+  print('Done. Now run:')
+  print('./tools/test_data upload')
 
 
 if __name__ == "__main__":
diff --git a/tools/gen_amalgamated b/tools/gen_amalgamated
index 0da3e42..c529155 100755
--- a/tools/gen_amalgamated
+++ b/tools/gen_amalgamated
@@ -496,7 +496,7 @@
     if not os.path.isdir(directory):
       os.makedirs(directory)
 
-  def save(self, output_prefix):
+  def save(self, output_prefix, system_buildtools=False):
     """Save the generated header and source file pair.
 
         Returns a message describing the output with build instructions.
@@ -511,7 +511,7 @@
       include_stmt = '#include "%s"' % os.path.basename(header_file)
       f.write('\n'.join([preamble] + self.source_defines + [include_stmt] +
                         self.source + ['\n']))
-    build_cmd = self.get_build_command(output_prefix)
+    build_cmd = self.get_build_command(output_prefix, system_buildtools)
     return """Amalgamated project written to %s and %s.
 
 Build settings:
@@ -525,12 +525,12 @@
 """ % (header_file, source_file, ' '.join(self.cflags), ' '.join(
         self.ldflags), ' '.join(self.libs), ' '.join(build_cmd))
 
-  def get_build_command(self, output_prefix):
+  def get_build_command(self, output_prefix, system_buildtools=False):
     """Returns an example command line for building the output source."""
     source = self._get_nice_path(output_prefix, '%s.cc')
     library = self._get_nice_path(output_prefix, 'lib%s.so')
 
-    if sys.platform.startswith('linux'):
+    if sys.platform.startswith('linux') and not system_buildtools:
       llvm_script = os.path.join(gn_utils.repo_root(), 'gn', 'standalone',
                                  'toolchain', 'linux_find_llvm.py')
       cxx = subprocess.check_output([llvm_script]).splitlines()[2].decode()
@@ -574,6 +574,11 @@
       help='List all source files that the amalgamated output depends on',
       action='store_true')
   parser.add_argument(
+      '--system_buildtools',
+      help='Use the buildtools (e.g. gn) preinstalled in the system instead '
+      'of the hermetic ones',
+      action='store_true')
+  parser.add_argument(
       'targets',
       nargs=argparse.REMAINDER,
       help='Targets to include in the output (e.g., "//:libperfetto")')
@@ -591,18 +596,22 @@
   if args.check:
     output = os.path.join(tempfile.mkdtemp(), 'perfetto_amalgamated')
 
-  out = gn_utils.prepare_out_directory(args.gn_args, args.out)
+  out = gn_utils.prepare_out_directory(args.gn_args,
+                                       args.out,
+                                       system_buildtools=args.system_buildtools)
   if not args.quiet:
     print('Building project...')
   try:
-    desc = gn_utils.load_build_description(out)
+    desc = gn_utils.load_build_description(out, args.system_buildtools)
 
     # We need to build everything first so that the necessary header
     # dependencies get generated. However if we are just dumping dependency
     # information this can be skipped, allowing cross-platform operation.
     if not args.dump_deps:
-      gn_utils.build_targets(out, targets)
-    source_deps = gn_utils.compute_source_dependencies(out)
+      gn_utils.build_targets(out, targets,
+                             system_buildtools=args.system_buildtools)
+    source_deps = gn_utils.compute_source_dependencies(out,
+                                                       args.system_buildtools)
     project = AmalgamatedProject(
         desc, source_deps, compute_deps_only=args.dump_deps)
 
@@ -617,14 +626,15 @@
       return
 
     project.generate()
-    result = project.save(output)
+    result = project.save(output, args.system_buildtools)
     if not args.quiet:
       print(result)
     if args.build:
       if not args.quiet:
         sys.stdout.write('Building amalgamated project...')
         sys.stdout.flush()
-      subprocess.check_call(project.get_build_command(output))
+      subprocess.check_call(project.get_build_command(output,
+                                                      args.system_buildtools))
       if not args.quiet:
         print('done')
   finally:
diff --git a/tools/gen_amalgamated_sql.py b/tools/gen_amalgamated_sql.py
new file mode 100755
index 0000000..1ac0ab0
--- /dev/null
+++ b/tools/gen_amalgamated_sql.py
@@ -0,0 +1,152 @@
+#!/usr/bin/env python3
+# 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 argparse
+import os
+import sys
+
+# Converts the SQL metrics for trace processor into a C++ header with the SQL
+# as a string constant to allow trace processor to exectue the metrics.
+
+REPLACEMENT_HEADER = '''/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ *******************************************************************************
+ * AUTOGENERATED BY tools/gen_merged_sql_metrics - DO NOT EDIT
+ *******************************************************************************
+ */
+
+ #include <string.h>
+'''
+
+NAMESPACE_BEGIN = '''
+namespace perfetto {{
+namespace trace_processor {{
+namespace {} {{
+'''
+
+NAMESPACE_END = '''
+}}  // namespace {}
+}}  // namespace trace_processor
+}}  // namespace perfetto
+'''
+
+FILE_TO_SQL_STRUCT = '''
+struct FileToSql {
+  const char* path;
+  const char* sql;
+};
+'''
+
+def filename_to_variable(filename):
+  return "k" + "".join([x.capitalize() for x in filename.split("_")])
+
+
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--namespace', required=True)
+  parser.add_argument('--cpp-out', required=True)
+  parser.add_argument('--input-list-file')
+  parser.add_argument('sql_files', nargs='*')
+  args = parser.parse_args()
+
+  if args.input_list_file and args.sql_files:
+    print("Only one of --input-list-file and list of SQL files expected")
+    return 1
+
+  sql_files = []
+  if args.input_list_file:
+    with open(args.input_list_file, 'r') as input_list_file:
+      for line in input_list_file.read().splitlines():
+        sql_files.append(line)
+  else:
+    sql_files = args.sql_files
+
+  # Unfortunately we cannot pass this in as an arg as soong does not provide
+  # us a way to get the path to the Perfetto source directory. This fails on
+  # empty path but it's a price worth paying to have to use gross hacks in
+  # Soong.
+  root_dir = os.path.commonpath(sql_files)
+
+  # Extract the SQL output from each file.
+  sql_outputs = {}
+  for file_name in sql_files:
+    with open(file_name, 'r') as f:
+      relpath = os.path.relpath(file_name, root_dir)
+
+      # We've had bugs (e.g. b/264711057) when Soong's common path logic breaks
+      # and ends up with a bunch of ../ prefixing the path: disallow any ../
+      # as this should never be a valid in our C++ output.
+      assert '../' not in relpath
+
+      sql_outputs[relpath] = "".join(
+          x.lstrip() for x in f.readlines() if not x.lstrip().startswith('--'))
+
+  with open(args.cpp_out, 'w+') as output:
+    output.write(REPLACEMENT_HEADER)
+    output.write(NAMESPACE_BEGIN.format(args.namespace))
+
+    # Create the C++ variable for each SQL file.
+    for path, sql in sql_outputs.items():
+      name = os.path.basename(path)
+      variable = filename_to_variable(os.path.splitext(name)[0])
+      output.write('\nconst char {}[] = '.format(variable))
+      # MSVC doesn't like string literals that are individually longer than 16k.
+      # However it's still fine "if" "we" "concatenate" "many" "of" "them".
+      # This code splits the sql in string literals of ~1000 chars each.
+      line_groups = ['']
+      for line in sql.split('\n'):
+        line_groups[-1] += line + '\n'
+        if len(line_groups[-1]) > 1000:
+          line_groups.append('')
+
+      for line in line_groups:
+        output.write('R"_d3l1m1t3r_({})_d3l1m1t3r_"\n'.format(line))
+      output.write(';\n')
+
+    output.write(FILE_TO_SQL_STRUCT)
+
+    # Create mapping of filename to variable name for each variable.
+    output.write("\nconst FileToSql kFileToSql[] = {")
+    for path in sql_outputs.keys():
+      name = os.path.basename(path)
+      variable = filename_to_variable(os.path.splitext(name)[0])
+
+      # This is for Windows which has \ as a path separator.
+      path = path.replace("\\", "/")
+      output.write('\n  {{"{}", {}}},\n'.format(path, variable))
+    output.write("};\n")
+
+    output.write(NAMESPACE_END.format(args.namespace))
+
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/tools/gen_amalgamated_sql_metrics.py b/tools/gen_amalgamated_sql_metrics.py
deleted file mode 100755
index 16f6aac..0000000
--- a/tools/gen_amalgamated_sql_metrics.py
+++ /dev/null
@@ -1,131 +0,0 @@
-#!/usr/bin/env python3
-# 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 argparse
-import os
-import sys
-
-# Converts the SQL metrics for trace processor into a C++ header with the SQL
-# as a string constant to allow trace processor to exectue the metrics.
-
-REPLACEMENT_HEADER = '''/*
- * 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.
- */
-
-/*
- *******************************************************************************
- * AUTOGENERATED BY tools/gen_merged_sql_metrics - DO NOT EDIT
- *******************************************************************************
- */
-
- #include <string.h>
-'''
-
-NAMESPACE_BEGIN = '''
-namespace perfetto {
-namespace trace_processor {
-namespace metrics {
-namespace sql_metrics {
-'''
-
-FILE_TO_SQL_STRUCT = '''
-struct FileToSql {
-  const char* path;
-  const char* sql;
-};
-'''
-
-NAMESPACE_END = '''
-}  // namespace sql_metrics
-}  // namespace metrics
-}  // namespace trace_processor
-}  // namsepace perfetto
-'''
-
-
-def filename_to_variable(filename):
-  return "k" + "".join([x.capitalize() for x in filename.split("_")])
-
-
-def main():
-  parser = argparse.ArgumentParser()
-  parser.add_argument('--cpp_out', required=True)
-  parser.add_argument('sql_files', nargs='*')
-  args = parser.parse_args()
-
-  root_path = os.path.commonprefix([os.path.abspath(x) for x in args.sql_files])
-
-  # Extract the SQL output from each file.
-  sql_outputs = {}
-  for file_name in args.sql_files:
-    with open(file_name, 'r') as f:
-      relpath = os.path.relpath(file_name, root_path)
-      sql_outputs[relpath] = "".join(
-          x.lstrip() for x in f.readlines() if not x.lstrip().startswith('--'))
-
-  with open(args.cpp_out, 'w+') as output:
-    output.write(REPLACEMENT_HEADER)
-    output.write(NAMESPACE_BEGIN)
-
-    # Create the C++ variable for each SQL file.
-    for path, sql in sql_outputs.items():
-      name = os.path.basename(path)
-      variable = filename_to_variable(os.path.splitext(name)[0])
-      output.write('\nconst char {}[] = '.format(variable))
-      # MSVC doesn't like string literals that are individually longer than 16k.
-      # However it's still fine "if" "we" "concatenate" "many" "of" "them".
-      # This code splits the sql in string literals of ~1000 chars each.
-      line_groups = ['']
-      for line in sql.split('\n'):
-        line_groups[-1] += line + '\n'
-        if len(line_groups[-1]) > 1000:
-          line_groups.append('')
-
-      for line in line_groups:
-        output.write('R"_d3l1m1t3r_({})_d3l1m1t3r_"\n'.format(line))
-      output.write(';\n')
-
-    output.write(FILE_TO_SQL_STRUCT)
-
-    # Create mapping of filename to variable name for each variable.
-    output.write("\nconst FileToSql kFileToSql[] = {")
-    for path in sql_outputs.keys():
-      name = os.path.basename(path)
-      variable = filename_to_variable(os.path.splitext(name)[0])
-
-      # This is for Windows which has \ as a path separator.
-      path = path.replace("\\", "/")
-      output.write('\n  {{"{}", {}}},\n'.format(path, variable))
-    output.write("};\n")
-
-    output.write(NAMESPACE_END)
-
-  return 0
-
-
-if __name__ == '__main__':
-  sys.exit(main())
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index a369ff7..e8a5677 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -26,13 +26,16 @@
 # libraries are also mapped to their Android equivalents -- see |builtin_deps|.
 
 import argparse
-import collections
 import json
 import os
 import re
 import sys
+from typing import Dict
+from typing import List
+from typing import Optional
 
 import gn_utils
+from gn_utils import GnParser
 
 from compat import itervalues
 
@@ -55,6 +58,7 @@
     '//:perfetto_unittests',
     '//protos/perfetto/trace:perfetto_trace_protos',
     '//src/android_internal:libperfetto_android_internal',
+    '//src/base:perfetto_base_default_platform',
     '//src/perfetto_cmd:perfetto',
     '//src/perfetto_cmd:trigger_perfetto',
     '//src/profiling/memory:heapprofd_client',
@@ -69,6 +73,7 @@
     '//test/cts:perfetto_cts_deps',
     '//test/cts:perfetto_cts_jni_deps',
     '//test:perfetto_gtest_logcat_printer',
+    '//test/vts:perfetto_vts_deps',
 ]
 
 # Host targets
@@ -120,7 +125,7 @@
     'android',
     'android.hardware.atrace@1.0',
     'android.hardware.health@2.0',
-    'android.hardware.health-V1-ndk',
+    'android.hardware.health-V2-ndk',
     'android.hardware.power.stats@1.0',
     "android.hardware.power.stats-V1-cpp",
     'base',
@@ -241,9 +246,9 @@
     'libperfetto_client_experimental': [
         ('apex_available', {
             '//apex_available:platform', 'com.android.art',
-            'com.android.art.debug'
+            'com.android.art.debug', 'com.android.tethering'
         }),
-        ('min_sdk_version', 'S'),
+        ('min_sdk_version', '30'),
         ('shared_libs', {'liblog'}),
         ('export_include_dirs', {'include', buildflags_dir}),
     ],
@@ -258,6 +263,10 @@
 }
 
 
+def enable_base_platform(module):
+  module.srcs.add(':perfetto_base_default_platform')
+
+
 def enable_gtest_and_gmock(module):
   module.static_libs.add('libgmock')
   module.static_libs.add('libgtest')
@@ -308,18 +317,22 @@
 def enable_sqlite(module):
   if module.type == 'cc_binary_host':
     module.static_libs.add('libsqlite')
+    module.static_libs.add('sqlite_ext_percentile')
   elif module.host_supported:
     # Copy what the sqlite3 command line tool does.
     module.android.shared_libs.add('libsqlite')
     module.android.shared_libs.add('libicu')
     module.android.shared_libs.add('liblog')
     module.android.shared_libs.add('libutils')
+    module.android.static_libs.add('sqlite_ext_percentile')
     module.host.static_libs.add('libsqlite')
+    module.host.static_libs.add('sqlite_ext_percentile')
   else:
     module.shared_libs.add('libsqlite')
     module.shared_libs.add('libicu')
     module.shared_libs.add('liblog')
     module.shared_libs.add('libutils')
+    module.static_libs.add('sqlite_ext_percentile')
 
 
 def enable_zlib(module):
@@ -349,6 +362,8 @@
         lambda x: None,
     '//gn:protoc':
         lambda x: None,
+    '//gn:base_platform':
+        enable_base_platform,
     '//gn:gtest_and_gmock':
         enable_gtest_and_gmock,
     '//gn:libunwind':
@@ -463,17 +478,19 @@
   """A single module (e.g., cc_binary, cc_test) in a blueprint."""
 
   def __init__(self, mod_type, name, gn_target):
+    assert (gn_target)
     self.type = mod_type
     self.gn_target = gn_target
     self.name = name
     self.srcs = set()
+    self.main: Optional[str] = None
     self.comment = 'GN: ' + gn_utils.label_without_toolchain(gn_target)
     self.shared_libs = set()
     self.static_libs = set()
     self.whole_static_libs = set()
     self.runtime_libs = set()
     self.tools = set()
-    self.cmd = None
+    self.cmd: Optional[str] = None
     self.host_supported = False
     self.vendor_available = False
     self.init_rc = set()
@@ -487,10 +504,10 @@
     self.header_libs = set()
     self.required = set()
     self.user_debug_flag = False
-    self.tool_files = None
+    self.tool_files: Optional[List[str]] = None
     self.android = Target('android')
     self.host = Target('host')
-    self.lto = None
+    self.lto: Optional[bool] = None
     self.stl = None
     self.dist = dict()
     self.strip = dict()
@@ -546,6 +563,7 @@
     self._output_field(output, 'test_config')
     self._output_field(output, 'stubs')
     self._output_field(output, 'proto')
+    self._output_field(output, 'main')
 
     target_out = []
     self._output_field(target_out, 'android')
@@ -600,9 +618,10 @@
   """In-memory representation of an Android.bp file."""
 
   def __init__(self):
-    self.modules = {}
+    self.modules: Dict[str, Module] = {}
+    self.gn_target_to_module: Dict[str, Module] = {}
 
-  def add_module(self, module):
+  def add_module(self, module: Module):
     """Adds a new module to the blueprint, replacing any existing module
         with the same name.
 
@@ -610,13 +629,14 @@
             module: Module instance.
         """
     self.modules[module.name] = module
+    self.gn_target_to_module[module.gn_target] = module
 
   def to_string(self, output):
     for m in sorted(itervalues(self.modules), key=lambda m: m.name):
       m.to_string(output)
 
 
-def label_to_module_name(label):
+def label_to_module_name(label: str):
   """Turn a GN label (e.g., //:perfetto_tests) into a module name."""
   # If the label is explicibly listed in the default target list, don't prefix
   # its name and return just the target name. This is so tools like
@@ -632,12 +652,13 @@
   return module
 
 
-def is_supported_source_file(name):
+def is_supported_source_file(name: str):
   """Returns True if |name| can appear in a 'srcs' list."""
   return os.path.splitext(name)[1] in ['.c', '.cc', '.proto']
 
 
-def create_proto_modules(blueprint, gn, target):
+def create_proto_modules(blueprint: Blueprint, gn: GnParser,
+                         target: GnParser.Target):
   """Generate genrules for a proto GN target.
 
     GN actions are used to dynamically generate files during the build. The
@@ -654,6 +675,11 @@
     """
   assert (target.type == 'proto_library')
 
+  # We don't generate any targets for source_set proto modules because
+  # they will be inlined into other modules if required.
+  if target.proto_plugin == 'source_set':
+    return None
+
   tools = {'aprotoc'}
   cpp_out_dir = '$(genDir)/%s/' % tree_path
   target_module_name = label_to_module_name(target.name)
@@ -666,11 +692,6 @@
   if buildtools_protobuf_src in target.proto_paths:
     cmd += ['--proto_path=%s' % android_protobuf_src]
 
-  # We don't generate any targets for source_set proto modules because
-  # they will be inlined into other modules if required.
-  if target.proto_plugin == 'source_set':
-    return None
-
   # Descriptor targets only generate a single target.
   if target.proto_plugin == 'descriptor':
     out = '{}.bin'.format(target_module_name)
@@ -680,7 +701,7 @@
 
     descriptor_module = Module('genrule', target_module_name, target.name)
     descriptor_module.cmd = ' '.join(cmd)
-    descriptor_module.out = [out]
+    descriptor_module.out.add(out)
     descriptor_module.tools = tools
     blueprint.add_module(descriptor_module)
 
@@ -688,8 +709,8 @@
     # add them to srcs.
     descriptor_module.srcs.update(
         gn_utils.label_to_path(src) for src in target.sources)
-    for dep in target.transitive_proto_deps:
-      current_target = gn.get_target(dep)
+    for dep in target.transitive_proto_deps():
+      current_target = gn.get_target(dep.name)
       descriptor_module.srcs.update(
           gn_utils.label_to_path(src) for src in current_target.sources)
 
@@ -728,18 +749,21 @@
   elif target.proto_plugin == 'protozero':
     suffixes = ['pbzero']
     plugin = create_modules_from_target(blueprint, gn, protozero_plugin)
+    assert (plugin)
     tools.add(plugin.name)
     cmd += ['--plugin=protoc-gen-plugin=$(location %s)' % plugin.name]
     cmd += ['--plugin_out=wrapper_namespace=pbzero:' + cpp_out_dir]
   elif target.proto_plugin == 'cppgen':
     suffixes = ['gen']
     plugin = create_modules_from_target(blueprint, gn, cppgen_plugin)
+    assert (plugin)
     tools.add(plugin.name)
     cmd += ['--plugin=protoc-gen-plugin=$(location %s)' % plugin.name]
     cmd += ['--plugin_out=wrapper_namespace=gen:' + cpp_out_dir]
   elif target.proto_plugin == 'ipc':
     suffixes = ['ipc']
     plugin = create_modules_from_target(blueprint, gn, ipc_plugin)
+    assert (plugin)
     tools.add(plugin.name)
     cmd += ['--plugin=protoc-gen-plugin=$(location %s)' % plugin.name]
     cmd += ['--plugin_out=wrapper_namespace=gen:' + cpp_out_dir]
@@ -762,25 +786,76 @@
   return source_module
 
 
-def create_amalgamated_sql_metrics_module(blueprint, target):
+def create_tp_tables_module(blueprint: Blueprint, gn: GnParser,
+                            target: GnParser.Target):
   bp_module_name = label_to_module_name(target.name)
+  bp_binary_module_name = f'{bp_module_name}_binary'
+  srcs = [gn_utils.label_to_path(src) for src in target.sources]
+
+  binary_module = Module('python_binary_host', bp_binary_module_name,
+                         target.name)
+  for dep in target.non_proto_or_source_set_deps():
+    binary_module.srcs.update([
+        gn_utils.label_to_path(src) for src in gn.get_target(dep.name).sources
+    ])
+  binary_module.srcs.update(srcs)
+  binary_module.srcs.add('tools/gen_tp_table_headers.py')
+  binary_module.main = 'tools/gen_tp_table_headers.py'
+  blueprint.add_module(binary_module)
+
+  module = Module('genrule', bp_module_name, target.name)
+  module.tools = set([
+      bp_binary_module_name,
+  ])
+  module.cmd = ' '.join([
+      f'$(location {bp_binary_module_name})',
+      '--gen-dir=$(genDir)',
+      '--inputs',
+      '$(in)',
+      '--outputs',
+      '$(out)',
+  ])
+  module.out.update(target.outputs)
+  module.genrule_headers.add(module.name)
+  module.srcs.update(srcs)
+  blueprint.add_module(module)
+
+  return module
+
+
+def create_amalgamated_sql_module(blueprint: Blueprint, gn: GnParser,
+                                  target: GnParser.Target):
+  bp_module_name = label_to_module_name(target.name)
+
+  def find_arg(name):
+    for i, arg in enumerate(target.args):
+      if arg.startswith(f'--{name}'):
+        return target.args[i + 1]
+
+  namespace = find_arg('namespace')
+
   module = Module('genrule', bp_module_name, target.name)
   module.tool_files = [
-      'tools/gen_amalgamated_sql_metrics.py',
+      'tools/gen_amalgamated_sql.py',
   ]
   module.cmd = ' '.join([
-      '$(location tools/gen_amalgamated_sql_metrics.py)',
-      '--cpp_out=$(out)',
+      '$(location tools/gen_amalgamated_sql.py)',
+      f'--namespace={namespace}',
+      '--cpp-out=$(out)',
       '$(in)',
   ])
   module.genrule_headers.add(module.name)
   module.out.update(target.outputs)
-  module.srcs.update(gn_utils.label_to_path(src) for src in target.inputs)
+
+  for dep in target.transitive_deps:
+    module.srcs.update(
+        [gn_utils.label_to_path(src) for src in gn.get_target(dep.name).inputs])
   blueprint.add_module(module)
   return module
 
 
-def create_cc_proto_descriptor_module(blueprint, target):
+def create_cc_proto_descriptor_module(blueprint: Blueprint,
+                                      target: GnParser.Target):
   bp_module_name = label_to_module_name(target.name)
   module = Module('genrule', bp_module_name, target.name)
   module.tool_files = [
@@ -792,7 +867,7 @@
   ])
   module.genrule_headers.add(module.name)
   module.srcs.update(
-      ':' + label_to_module_name(dep) for dep in target.proto_deps)
+      ':' + label_to_module_name(dep.name) for dep in target.proto_deps())
   module.srcs.update(
       gn_utils.label_to_path(src)
       for src in target.inputs
@@ -802,7 +877,8 @@
   return module
 
 
-def create_gen_version_module(blueprint, target, bp_module_name):
+def create_gen_version_module(blueprint: Blueprint, target: GnParser.Target,
+                              bp_module_name: str):
   module = Module('genrule', bp_module_name, gn_utils.GEN_VERSION_TARGET)
   script_path = gn_utils.label_to_path(target.script)
   module.genrule_headers.add(bp_module_name)
@@ -817,7 +893,8 @@
   return module
 
 
-def create_proto_group_modules(blueprint, gn, module_name, target_names):
+def create_proto_group_modules(blueprint, gn: GnParser, module_name: str,
+                               target_names):
   # TODO(lalitm): today, we're only adding a Java lite module because that's
   # the only one used in practice. In the future, if we need other target types
   # (e.g. C++, Java full etc.) add them here.
@@ -829,14 +906,14 @@
   for name in target_names:
     target = gn.get_target(name)
     module.srcs.update(gn_utils.label_to_path(src) for src in target.sources)
-    for dep_label in target.transitive_proto_deps:
-      dep = gn.get_target(dep_label)
+    for dep_label in target.transitive_proto_deps():
+      dep = gn.get_target(dep_label.name)
       module.srcs.update(gn_utils.label_to_path(src) for src in dep.sources)
 
   blueprint.add_module(module)
 
 
-def _get_cflags(target):
+def _get_cflags(target: GnParser.Target):
   cflags = {flag for flag in target.cflags if re.match(cflag_allowlist, flag)}
   cflags |= set("-D%s" % define
                 for define in target.defines
@@ -844,7 +921,8 @@
   return cflags
 
 
-def create_modules_from_target(blueprint, gn, gn_target_name):
+def create_modules_from_target(blueprint: Blueprint, gn: GnParser,
+                               gn_target_name: str) -> Optional[Module]:
   """Generate module(s) for a given GN target.
 
     Given a GN target name, generate one or more corresponding modules into a
@@ -855,11 +933,11 @@
         gn: gn_utils.GnParser object.
         gn_target_name: GN target for module generation.
     """
-  bp_module_name = label_to_module_name(gn_target_name)
-  if bp_module_name in blueprint.modules:
-    return blueprint.modules[bp_module_name]
-  target = gn.get_target(gn_target_name)
+  if gn_target_name in blueprint.gn_target_to_module:
+    return blueprint.gn_target_to_module[gn_target_name]
 
+  target = gn.get_target(gn_target_name)
+  bp_module_name = label_to_module_name(gn_target_name)
   name_without_toolchain = gn_utils.label_without_toolchain(target.name)
   if target.type == 'executable':
     if target.toolchain == gn_utils.HOST_TOOLCHAIN:
@@ -884,12 +962,14 @@
     if module is None:
       return None
   elif target.type == 'action':
-    if 'gen_amalgamated_sql_metrics' in target.name:
-      module = create_amalgamated_sql_metrics_module(blueprint, target)
-    elif re.match('.*gen_cc_.*_descriptor$', name_without_toolchain):
+    if target.custom_action_type == 'sql_amalgamation':
+      return create_amalgamated_sql_module(blueprint, gn, target)
+    if target.custom_action_type == 'tp_tables':
+      return create_tp_tables_module(blueprint, gn, target)
+
+    if target.custom_action_type == 'cc_proto_descriptor':
       module = create_cc_proto_descriptor_module(blueprint, target)
-    elif target.type == 'action' and \
-        name_without_toolchain == gn_utils.GEN_VERSION_TARGET:
+    elif name_without_toolchain == gn_utils.GEN_VERSION_TARGET:
       module = create_gen_version_module(blueprint, target, bp_module_name)
     else:
       raise Error('Unhandled action: {}'.format(target.name))
@@ -899,7 +979,7 @@
   blueprint.add_module(module)
   module.host_supported = (name_without_toolchain in target_host_supported)
   module.vendor_available = (name_without_toolchain in target_vendor_available)
-  module.init_rc = target_initrc.get(target.name, [])
+  module.init_rc.update(target_initrc.get(target.name, []))
   module.srcs.update(
       gn_utils.label_to_path(src)
       for src in target.sources
@@ -912,7 +992,7 @@
   if module_is_compiled:
     # Don't try to inject library/source dependencies into genrules or
     # filegroups because they are not compiled in the traditional sense.
-    module.defaults = [defaults_module]
+    module.defaults.update([defaults_module])
     for lib in target.libs:
       # Generally library names should be mangled as 'libXXX', unless they
       # are HAL libraries (e.g., android.hardware.health@2.0) or AIDL c++ / NDK
@@ -946,12 +1026,15 @@
                   (type(add_val), key))
 
   # dep_name is an unmangled GN target name (e.g. //foo:bar(toolchain)).
-  all_deps = target.deps | target.source_set_deps | target.transitive_proto_deps
-  for dep_name in all_deps:
+  all_deps = target.non_proto_or_source_set_deps()
+  all_deps |= target.transitive_source_set_deps()
+  all_deps |= target.transitive_proto_deps()
+  for dep in all_deps:
     # If the dependency refers to a library which we can replace with an
     # Android equivalent, stop recursing and patch the dependency in.
     # Don't recurse into //buildtools, builtin_deps are intercepted at
     # the //gn:xxx level.
+    dep_name = dep.name
     if dep_name.startswith('//buildtools'):
       continue
 
@@ -998,7 +1081,7 @@
   return module
 
 
-def create_blueprint_for_targets(gn, desc, targets):
+def create_blueprint_for_targets(gn: GnParser, targets: List[str]):
   """Generate a blueprint for a list of GN targets."""
   blueprint = Blueprint()
 
@@ -1014,14 +1097,14 @@
       tree_path, tree_path + '/include', tree_path + '/' + buildflags_dir,
       tree_path + '/src/profiling/memory/include'
   }
-  defaults.cflags = [
+  defaults.cflags.update([
       '-Wno-error=return-type',
       '-Wno-sign-compare',
       '-Wno-sign-promo',
       '-Wno-unused-parameter',
       '-fvisibility=hidden',
       '-O2',
-  ]
+  ])
   defaults.user_debug_flag = True
   defaults.lto = True
 
@@ -1065,8 +1148,7 @@
     desc = gn_utils.create_build_description(gn_args)
 
   gn = gn_utils.GnParser(desc)
-  blueprint = create_blueprint_for_targets(gn, desc, args.targets or
-                                           default_targets)
+  blueprint = create_blueprint_for_targets(gn, args.targets or default_targets)
   project_root = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
   tool_name = os.path.relpath(os.path.abspath(__file__), project_root)
 
diff --git a/tools/gen_bazel b/tools/gen_bazel
index 1c64257..10b047d 100755
--- a/tools/gen_bazel
+++ b/tools/gen_bazel
@@ -25,12 +25,16 @@
 
 from __future__ import print_function
 import argparse
-import itertools
 import json
 import os
 import re
 import sys
+from typing import Dict
+from typing import List
+from typing import Optional
+from typing import Union
 
+from gn_utils import GnParser
 import gn_utils
 
 from compat import itervalues, iteritems, basestring
@@ -66,6 +70,7 @@
 # These targets are required by internal build rules but don't need to be
 # exported publicly.
 default_targets = [
+    '//src/base:perfetto_base_default_platform',
     '//src/ipc:perfetto_ipc',
     '//src/ipc/protoc_plugin:ipc_plugin',
     '//src/protozero:protozero',
@@ -98,6 +103,7 @@
 # depends on.
 external_deps = {
     '//gn:default_deps': [],
+    '//gn:base_platform': ['PERFETTO_CONFIG.deps.base_platform'],
     '//gn:jsoncpp': ['PERFETTO_CONFIG.deps.jsoncpp'],
     '//gn:linenoise': ['PERFETTO_CONFIG.deps.linenoise'],
     '//gn:protobuf_full': ['PERFETTO_CONFIG.deps.protobuf_full'],
@@ -111,25 +117,34 @@
     '//gn:zlib': ['PERFETTO_CONFIG.deps.zlib'],
     '//gn:llvm_demangle': ['PERFETTO_CONFIG.deps.llvm_demangle'],
     '//src/trace_processor:demangle': ['PERFETTO_CONFIG.deps.demangle_wrapper'],
-    '//src/trace_processor/metrics/sql:gen_amalgamated_sql_metrics': [[
-        ':cc_amalgamated_sql_metrics'
-    ]],
     gn_utils.GEN_VERSION_TARGET: ['PERFETTO_CONFIG.deps.version_header'],
 }
 
+# These are Python targets which are exposed with public visibility.
+public_python_targets = [
+    '//python:batch_trace_processor',
+    '//python:trace_processor_py',
+]
 
-def gen_amalgamated_sql_metrics(target):
-  label = BazelLabel(get_bazel_label_name(target.name), 'perfetto_genrule')
-  label.srcs += [re.sub('^//', '', x) for x in sorted(target.inputs)]
-  label.outs += target.outputs
-  label.cmd = r'$(location gen_amalgamated_sql_metrics_py) --cpp_out=$@ $(SRCS)'
-  label.exec_tools += [':gen_amalgamated_sql_metrics_py']
-  return [label]
+# These are Python targets which are exposed by default.
+default_python_targets = [
+    '//python:batch_trace_processor',
+    '//python:experimental_slice_breakdown_bin',
+    '//python:trace_processor_table_generator',
+    '//python:trace_processor_py_example',
+]
+
+# Internal equivalents for third-party Python libraries.
+external_python_deps: Dict[str, List[str]] = {
+    '//gn:pandas_py': ['PERFETTO_CONFIG.deps.pandas_py'],
+    '//gn:protobuf_py': ['PERFETTO_CONFIG.deps.protobuf_py'],
+    '//gn:tp_vendor_py': ['PERFETTO_CONFIG.deps.tp_vendor_py'],
+}
 
 
 def gen_version_header(target):
   label = BazelLabel(get_bazel_label_name(target.name), 'perfetto_genrule')
-  label.srcs += [re.sub('^//', '', x) for x in sorted(target.inputs)]
+  label.srcs += [gn_utils.label_to_path(x) for x in sorted(target.inputs)]
   label.outs += target.outputs
   label.cmd = r'$(location gen_version_header_py)'
   label.cmd += r' --cpp_out=$@ --changelog=$(location CHANGELOG)'
@@ -137,21 +152,8 @@
   return [label]
 
 
-def gen_cc_metrics_descriptor(target):
-  label = BazelLabel(
-      get_bazel_label_name(target.name), 'perfetto_cc_proto_descriptor')
-  label.deps += [':' + get_bazel_label_name(x) for x in target.proto_deps]
-  label.deps += [gn_utils.label_to_path(src) for src in target.inputs if "tmp.gn_utils" not in src]
-
-  label.outs += target.outputs
-  return [label]
-
-
 custom_actions = {
-    gn_utils.GEN_VERSION_TARGET:
-        gen_version_header,
-    '//src/trace_processor/metrics/sql:gen_amalgamated_sql_metrics':
-        gen_amalgamated_sql_metrics,
+    gn_utils.GEN_VERSION_TARGET: gen_version_header,
 }
 
 # ------------------------------------------------------------------------------
@@ -159,6 +161,122 @@
 # ------------------------------------------------------------------------------
 
 
+class PythonBuildGenerator:
+  '''Generator of the BUILD file in the python folder.
+
+  This code is split into its own class to avoid polluting
+  the generation of the main build file with Python related
+  content.
+  '''
+
+  def populate_python_deps(self, target: GnParser.Target, label: 'BazelLabel'):
+    '''Populates deps for a GN target into Bazel Python label.'''
+    for dep in sorted(target.non_proto_or_source_set_deps()):
+      if dep.name in external_python_deps:
+        assert (isinstance(external_python_deps[dep.name], list))
+        label.external_deps += external_python_deps[dep.name]
+      else:
+        label.deps += [':' + get_bazel_python_label_name(dep.name)]
+
+  def python_label_to_path(self, gn_name: str):
+    """Converts a Python GN path label into a Bazel path."""
+    return re.sub(r'^python/', '', gn_utils.label_to_path(gn_name))
+
+  def python_data_to_path(self, gn_name: str):
+    """Converts a Python GN data label into a Bazel data label."""
+    return re.sub(r'^\.\.(.*)', r'PERFETTO_CONFIG.root + "\1"', gn_name)
+
+  def gen_python_library(self, target: GnParser.Target):
+    """Creates a Bazel target for a Python library GN target."""
+    label = BazelLabel(
+        get_bazel_python_label_name(target.name), 'perfetto_py_library')
+    label.comment = target.name
+    label.srcs += (self.python_label_to_path(x) for x in target.sources)
+    label.data += (self.python_data_to_path(x) for x in target.data)
+    self.populate_python_deps(target, label)
+    if target.name in public_python_targets:
+      label.visibility = ['//visibility:public']
+    return [label]
+
+  def gen_python_binary(self, target: GnParser.Target):
+    """Creates a Bazel target for a Python binary GN target."""
+    label = BazelLabel(
+        get_bazel_python_label_name(target.name), 'perfetto_py_binary')
+    label.comment = target.name
+    label.srcs += (self.python_label_to_path(x) for x in target.sources)
+    label.data += (self.python_data_to_path(x) for x in target.data)
+    label.main = target.python_main
+    label.python_version = 'PY3'
+    if target.name in public_python_targets:
+      label.visibility = ['//visibility:public']
+
+    self.populate_python_deps(target, label)
+    return [label]
+
+  def gen_target(self, gn_target: GnParser.Target):
+    """Creates a Bazel target for a Python GN target."""
+    assert (gn_target.type == 'action')
+    if gn_target.name in external_python_deps:
+      return []
+    if gn_target.custom_action_type == 'python_library':
+      return self.gen_python_library(gn_target)
+    if gn_target.custom_action_type == 'python_binary':
+      return self.gen_python_binary(gn_target)
+    assert (False)
+
+  def gen_target_str(self, gn_target: GnParser.Target):
+    """Creates a Bazel target string for a Python GN target."""
+    return ''.join(str(x) for x in self.gen_target(gn_target))
+
+  def generate(self, gn_desc):
+    """Creates a Python BUILD file for the GN description."""
+    gn = gn_utils.GnParser(gn_desc)
+    project_root = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
+    tool_name = os.path.relpath(os.path.abspath(__file__), project_root)
+    res = '''
+# Copyright (C) 2022 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 is automatically generated by {}. Do not edit.
+
+load("@perfetto_cfg//:perfetto_cfg.bzl", "PERFETTO_CONFIG")
+load(
+    "@perfetto//bazel:rules.bzl",
+    "perfetto_py_binary",
+    "perfetto_py_library",
+)
+
+licenses(["notice"])
+
+package(default_visibility = [PERFETTO_CONFIG.root + ":__subpackages__"])
+
+'''.format(tool_name).lstrip()
+
+    # Find all the targets in the //python folder.
+    for target_name in default_python_targets:
+      target = gn.get_target(target_name)
+      res += self.gen_target_str(target)
+
+    # Generate all the intermediate targets.
+    for target in sorted(itervalues(gn.all_targets)):
+      if target.name in default_python_targets:
+        continue
+      res += self.gen_target_str(target)
+
+    return res
+
+
 class Error(Exception):
   pass
 
@@ -166,18 +284,24 @@
 class BazelLabel(object):
 
   def __init__(self, name, type):
-    self.comment = None
+    self.comment: Optional[str] = None
     self.name = name
     self.type = type
-    self.visibility = []
+    self.visibility: Union[List[str], str] = []
     self.srcs = []
     self.hdrs = []
+    self.data = []
     self.deps = []
     self.external_deps = []
     self.tools = []
     self.exec_tools = []
     self.outs = []
     self.exports = []
+    self.main = None
+    self.cmd: Optional[str] = None
+    self.python_version: Optional[str] = None
+    self.root_dir: Optional[str] = None
+    self.namespace: Optional[str] = None
 
   def __lt__(self, other):
     if isinstance(other, self.__class__):
@@ -193,8 +317,8 @@
     res += '%s(\n' % self.type
     any_deps = len(self.deps) + len(self.external_deps) > 0
     ORD = [
-        'name', 'srcs', 'hdrs', 'visibility', 'deps', 'outs', 'cmd', 'tools',
-        'exec_tools', 'exports'
+        'name', 'srcs', 'hdrs', 'visibility', 'data', 'deps', 'outs', 'cmd',
+        'tools', 'exec_tools', 'exports', 'main', 'python_version'
     ]
     hasher = lambda x: sum((99,) + tuple(ord(c) for c in x))
     key_sorter = lambda kv: ORD.index(kv[0]) if kv[0] in ORD else hasher(kv[0])
@@ -244,7 +368,7 @@
 PUBLIC_VISIBILITY = 'PERFETTO_CONFIG.public_visibility'
 
 
-def get_bazel_label_name(gn_name):
+def get_bazel_label_name(gn_name: str):
   """Converts a GN target name into a Bazel label name.
 
   If target is in the public target list, returns only the GN target name,
@@ -258,13 +382,19 @@
   return gn_utils.label_to_target_name_with_path(gn_name)
 
 
-def get_bazel_proto_sources_label(target_name):
+def get_bazel_python_label_name(gn_name: str):
+  """Converts a Python GN label into a Bazel label."""
+  name = re.sub(r'^//python:?', '', gn_name)
+  return gn_utils.label_to_target_name_with_path(name)
+
+
+def get_bazel_proto_sources_label(target_name: str):
   """Converts a GN target name into a Bazel proto label name."""
   return re.sub('_(lite|zero|cpp|ipc|source_set|descriptor)$', '',
                 get_bazel_label_name(target_name)) + '_protos'
 
 
-def gen_proto_label(target):
+def gen_proto_label(target: GnParser.Target):
   """ Generates the xx_proto_library label for proto targets."""
   assert (target.type == 'proto_library')
 
@@ -279,8 +409,8 @@
     assert (all(x.endswith('.proto') for x in target.sources))
     sources_label.srcs = sorted([x[2:] for x in target.sources])  # Strip //.
     sources_label.deps = sorted([
-        ':' + get_bazel_proto_sources_label(x)
-        for x in target.transitive_proto_deps
+        ':' + get_bazel_proto_sources_label(x.name)
+        for x in target.transitive_proto_deps()
     ])
 
     # In Bazel, proto_paths are not a supported concept becauase strong
@@ -324,7 +454,8 @@
   # common.gen.{cc,h}.
   if target.proto_plugin in ('cppgen', 'ipc', 'protozero'):
     plugin_label.deps += [
-        ':' + get_bazel_label_name(x) for x in target.transitive_proto_deps
+        ':' + get_bazel_label_name(x.name)
+        for x in target.transitive_proto_deps()
     ]
 
   # Add any dependencies on source_set targets (i.e. targets containing proto
@@ -333,7 +464,8 @@
   # implicit.
   if target.proto_plugin == 'descriptor':
     plugin_label.deps += [
-        ':' + get_bazel_proto_sources_label(x) for x in target.proto_deps
+        ':' + get_bazel_proto_sources_label(x.name)
+        for x in target.proto_deps()
     ]
   else:
     plugin_label.deps += [':' + sources_label_name]
@@ -346,13 +478,13 @@
   return plugin_label
 
 
-def gen_proto_group_target(gn, name, target_names):
+def gen_proto_group_target(gn: GnParser, name: str, target_names: List[str]):
   # Get a recursive list of the proto_library targets rooted here which
   # have src.
   deps_set = set(target_names)
   for target_name in target_names:
     target = gn.get_target(target_name)
-    deps_set.update(target.transitive_proto_deps)
+    deps_set.update(d.name for d in target.transitive_proto_deps())
 
   # First, create a root source set target which references all the child
   # source set targets. We publish this as well as depending on this in all
@@ -389,14 +521,70 @@
   return [sources_label, cc_label, java_label, java_lite_label, py_label]
 
 
-def gen_target(gn_target):
+def gen_cc_proto_descriptor(target: GnParser.Target):
+  label = BazelLabel(
+      get_bazel_label_name(target.name), 'perfetto_cc_proto_descriptor')
+  label.comment = target.name
+  label.deps += [
+      ':' + get_bazel_label_name(x.name) for x in target.proto_deps()
+  ]
+  label.deps += [
+      gn_utils.label_to_path(src)
+      for src in target.inputs
+      if "tmp.gn_utils" not in src
+  ]
+
+  label.outs += target.outputs
+  return [label]
+
+
+def gen_cc_amalgamated_sql(target: GnParser.Target):
+  label = BazelLabel(
+      get_bazel_label_name(target.name), 'perfetto_cc_amalgamated_sql')
+
+  def find_arg(name):
+    for i, arg in enumerate(target.args):
+      if arg.startswith(f'--{name}'):
+        return target.args[i + 1]
+
+  label.comment = target.name
+  label.namespace = find_arg('namespace')
+
+  label.deps += sorted(':' + get_bazel_label_name(x.name)
+                       for x in target.transitive_deps)
+  label.outs += target.outputs
+  return [label]
+
+
+def gen_sql_source_set(target: GnParser.Target):
+  label = BazelLabel(get_bazel_label_name(target.name), 'perfetto_filegroup')
+  label.comment = target.name
+  label.srcs += (gn_utils.label_to_path(x) for x in target.inputs)
+  return [label]
+
+
+def gen_cc_tp_tables(target: GnParser.Target):
+  label = BazelLabel(get_bazel_label_name(target.name), 'perfetto_cc_tp_tables')
+  label.comment = target.name
+  label.srcs += (gn_utils.label_to_path(x) for x in target.sources)
+  label.outs += target.outputs
+  return [label]
+
+
+def gen_target(gn_target: GnParser.Target):
   if gn_target.type == 'proto_library':
     return [gen_proto_label(gn_target)]
   elif gn_target.type == 'action':
     if gn_target.name in custom_actions:
       return custom_actions[gn_target.name](gn_target)
-    elif re.match('.*gen_cc_.*_descriptor$', gn_target.name):
-      return gen_cc_metrics_descriptor(gn_target)
+    if gn_target.custom_action_type == 'sql_amalgamation':
+      return gen_cc_amalgamated_sql(gn_target)
+    if gn_target.custom_action_type == 'sql_source_set':
+      return gen_sql_source_set(gn_target)
+    if gn_target.custom_action_type == 'cc_proto_descriptor':
+      return gen_cc_proto_descriptor(gn_target)
+    if gn_target.custom_action_type == 'tp_tables':
+      return gen_cc_tp_tables(gn_target)
     return []
   elif gn_target.type == 'group':
     return []
@@ -404,11 +592,12 @@
     bazel_type = 'perfetto_cc_binary'
   elif gn_target.type == 'shared_library':
     bazel_type = 'perfetto_cc_binary'
-    vars['linkshared'] = True
   elif gn_target.type == 'static_library':
     bazel_type = 'perfetto_cc_library'
   elif gn_target.type == 'source_set':
     bazel_type = 'perfetto_filegroup'
+  elif gn_target.type == 'generated_file':
+    return []
   else:
     raise Error('target type not supported: %s' % gn_target.type)
 
@@ -425,6 +614,7 @@
                   gn_target.name)
 
   raw_srcs = [x[2:] for x in gn_target.sources]
+  raw_srcs += [x[2:] for x in gn_target.inputs]
   if bazel_type == 'perfetto_cc_library':
     label.srcs += [x for x in raw_srcs if not x.startswith('include')]
     label.hdrs += [x for x in raw_srcs if x.startswith('include')]
@@ -440,23 +630,30 @@
 
   if gn_target.type in gn_utils.LINKER_UNIT_TYPES:
     # |source_sets| contains the transitive set of source_set deps.
-    for trans_dep in gn_target.source_set_deps:
-      name = ':' + get_bazel_label_name(trans_dep)
+    for trans_dep in gn_target.transitive_source_set_deps():
+      name = ':' + get_bazel_label_name(trans_dep.name)
       if name.startswith(
           ':include_perfetto_') and gn_target.type != 'executable':
         label.hdrs += [name]
       else:
         label.srcs += [name]
-    for dep in sorted(gn_target.deps):
-      if dep.startswith('//gn:'):
-        assert (dep in external_deps), dep
-      if dep in external_deps:
-        assert (isinstance(external_deps[dep], list))
-        label.external_deps += external_deps[dep]
+    for dep in sorted(gn_target.non_proto_or_source_set_deps()):
+      dep_name = dep.name
+      if dep_name.startswith('//gn:'):
+        assert (dep_name in external_deps), dep
+
+      # tp_tables produces a filegroup not a cc_lbirary so should end up srcs
+      # not deps.
+      if dep.custom_action_type == 'tp_tables':
+        label.srcs += [':' + get_bazel_label_name(dep_name)]
+      elif dep_name in external_deps:
+        assert (isinstance(external_deps[dep_name], list))
+        label.external_deps += external_deps[dep_name]
       else:
-        label.deps += [':' + get_bazel_label_name(dep)]
+        label.deps += [':' + get_bazel_label_name(dep_name)]
     label.deps += [
-        ':' + get_bazel_label_name(x) for x in gn_target.transitive_proto_deps
+        ':' + get_bazel_label_name(x.name)
+        for x in gn_target.transitive_cpp_proto_deps()
     ]
 
   # All items starting with : need to be sorted to the end of the list.
@@ -500,26 +697,28 @@
 load(
     "@perfetto//bazel:rules.bzl",
     "perfetto_build_config_cc_library",
+    "perfetto_cc_amalgamated_sql",
     "perfetto_cc_binary",
-    "perfetto_filegroup",
-    "perfetto_genrule",
     "perfetto_cc_ipc_library",
     "perfetto_cc_library",
     "perfetto_cc_proto_descriptor",
     "perfetto_cc_proto_library",
     "perfetto_cc_protocpp_library",
     "perfetto_cc_protozero_library",
+    "perfetto_cc_tp_tables",
+    "perfetto_filegroup",
+    "perfetto_genrule",
     "perfetto_go_proto_library",
-    "perfetto_java_proto_library",
     "perfetto_java_lite_proto_library",
-    "perfetto_proto_library",
+    "perfetto_java_proto_library",
     "perfetto_proto_descriptor",
+    "perfetto_proto_library",
     "perfetto_py_binary",
     "perfetto_py_library",
     "perfetto_py_proto_library",
 )
 
-package(default_visibility = ["//visibility:private"])
+package(default_visibility = [PERFETTO_CONFIG.root + ":__subpackages__"])
 
 licenses(["notice"])
 
@@ -621,9 +820,9 @@
       default=os.path.join(gn_utils.repo_root(), 'BUILD'),
   )
   parser.add_argument(
-      '--output-proto',
-      help='Proto BUILD file to create',
-      default=os.path.join(gn_utils.repo_root(), 'protos', 'BUILD'),
+      '--output-python',
+      help='Python BUILD file to create',
+      default=os.path.join(gn_utils.repo_root(), 'python', 'BUILD'),
   )
   parser.add_argument(
       'targets',
@@ -648,6 +847,13 @@
   with open(out_files[-1], 'w') as out_f:
     out_f.write(contents)
 
+  # Generate the python BUILD file.
+  python_gen = PythonBuildGenerator()
+  python_contents = python_gen.generate(desc)
+  out_files.append(args.output_python + '.swp')
+  with open(out_files[-1], 'w') as out_f:
+    out_f.write(python_contents)
+
   # Generate the build flags file.
   out_files.append(os.path.join(buildflags_dir, 'perfetto_build_flags.h.swp'))
   gn_utils.gen_buildflags(gn_args, out_files[-1])
diff --git a/tools/gen_stdlib_docs_json.py b/tools/gen_stdlib_docs_json.py
new file mode 100755
index 0000000..c89b7a7
--- /dev/null
+++ b/tools/gen_stdlib_docs_json.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python3
+# Copyright (C) 2022 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 argparse
+import os
+import sys
+import json
+from collections import defaultdict
+from typing import Dict
+
+ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+sys.path.append(os.path.join(ROOT_DIR))
+
+from python.generators.stdlib_docs.stdlib import *
+
+
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--json-out', required=True)
+  parser.add_argument('--input-list-file')
+  parser.add_argument('sql_files', nargs='*')
+  args = parser.parse_args()
+
+  if args.input_list_file and args.sql_files:
+    print("Only one of --input-list-file and list of SQL files expected")
+    return 1
+
+  sql_files = []
+  if args.input_list_file:
+    with open(args.input_list_file, 'r') as input_list_file:
+      for line in input_list_file.read().splitlines():
+        sql_files.append(line)
+  else:
+    sql_files = args.sql_files
+
+  # Unfortunately we cannot pass this in as an arg as soong does not provide
+  # us a way to get the path to the Perfetto source directory. This fails on
+  # empty path but it's a price worth paying to have to use gross hacks in
+  # Soong.
+  root_dir = os.path.commonpath(sql_files)
+
+  # Extract the SQL output from each file.
+  sql_outputs: Dict[str, str] = {}
+  for file_name in sql_files:
+    with open(file_name, 'r') as f:
+      relpath = os.path.relpath(file_name, root_dir)
+
+      # We've had bugs (e.g. b/264711057) when Soong's common path logic breaks
+      # and ends up with a bunch of ../ prefixing the path: disallow any ../
+      # as this should never be a valid in our C++ output.
+      assert '../' not in relpath
+
+      sql_outputs[relpath] = f.read()
+
+  modules = defaultdict(list)
+  # Add documentation from each file
+  for path, sql in sql_outputs.items():
+    module_name = path.split("/")[0]
+    import_key = path.split(".sql")[0].replace("/", ".")
+
+    docs = parse_file_to_dict(path, sql)[0]
+    if not any(docs.values()):
+      continue
+    file_dict = {'import_key': import_key, **docs}
+    modules[module_name].append(file_dict)
+
+  with open(args.json_out, 'w+') as f:
+    json.dump(modules, f, indent=4)
+
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/tools/gen_tp_table_docs.py b/tools/gen_tp_table_docs.py
new file mode 100755
index 0000000..b144a94
--- /dev/null
+++ b/tools/gen_tp_table_docs.py
@@ -0,0 +1,123 @@
+#!/usr/bin/env python3
+# Copyright (C) 2022 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 argparse
+import json
+import os
+import runpy
+import sys
+from typing import Any
+from typing import Dict
+from typing import List
+from typing import Optional
+from typing import Union
+
+# Allow importing of root-relative modules.
+ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+sys.path.append(os.path.join(ROOT_DIR))
+
+from python.generators.trace_processor_table.public import Column
+from python.generators.trace_processor_table.public import ColumnDoc
+from python.generators.trace_processor_table.public import Table
+import python.generators.trace_processor_table.util as util
+
+
+def gen_json_for_column(table: Table, col: Column,
+                        doc: Union[ColumnDoc, str]) -> Optional[Dict[str, Any]]:
+  """Generates the JSON documentation for a column in a table."""
+
+  # id and type columns should be skipped if the table specifies so.
+  is_skippable_col = col._is_auto_added_id or col._is_auto_added_type
+  if table.tabledoc.skip_id_and_type and is_skippable_col:
+    return None
+
+  # Our default assumption is the documentation for a column is a plain string
+  # so just make the comment for the column equal to that.
+
+  if isinstance(doc, ColumnDoc):
+    comment = doc.doc
+    if doc.joinable:
+      join_table, join_type = doc.joinable.split('.')
+    else:
+      join_table, join_type = None, None
+  elif isinstance(doc, str):
+    comment = doc
+    join_table, join_type = None, None
+  else:
+    raise Exception('Unknown column documentation type')
+
+  parsed_type = util.parse_type(table, col.type)
+  docs_type = parsed_type.cpp_type
+  if docs_type == 'StringPool::Id':
+    docs_type = 'string'
+
+  ref_class_name = None
+  if parsed_type.id_table and not col._is_auto_added_id:
+    id_table_name = util.public_sql_name_for_table(parsed_type.id_table)
+    ref_class_name = parsed_type.id_table.class_name
+
+    # We shouldn't really specify the join tables when it's a simple id join.
+    assert join_table is None
+    assert join_type is None
+
+    join_table = id_table_name
+    join_type = "id"
+
+  return {
+      'name': col.name,
+      'type': docs_type,
+      'comment': comment,
+      'optional': parsed_type.is_optional,
+      'refTableCppName': ref_class_name,
+      'joinTable': join_table,
+      'joinCol': join_type,
+  }
+
+
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--out', required=True)
+  parser.add_argument('inputs', nargs='*')
+  args = parser.parse_args()
+
+  tables: List[Table] = []
+  for in_path in args.inputs:
+    for table in runpy.run_path(in_path)['ALL_TABLES']:
+      tables.append(util.augment_table_with_auto_cols(table))
+
+  table_docs = []
+  for table in tables:
+    doc = table.tabledoc
+    cols = (
+        gen_json_for_column(table, c, doc.columns[c.name])
+        for c in table.columns)
+    table_docs.append({
+        'name': util.public_sql_name_for_table(table),
+        'cppClassName': table.class_name,
+        'defMacro': table.class_name,
+        'comment': doc.doc,
+        'parent': None,
+        'parentDefName': '',
+        'tablegroup': doc.group,
+        'cols': [c for c in cols if c]
+    })
+
+  with open(args.out, 'w') as out:
+    json.dump(table_docs, out, indent=2)
+    out.write('\n')
+
+
+if __name__ == '__main__':
+  exit(main())
diff --git a/tools/gen_tp_table_headers.py b/tools/gen_tp_table_headers.py
new file mode 100755
index 0000000..52ebe35
--- /dev/null
+++ b/tools/gen_tp_table_headers.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python3
+# Copyright (C) 2022 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 argparse
+from dataclasses import dataclass
+import os
+import re
+import runpy
+import sys
+from typing import List
+from typing import Set
+
+# Allow importing of root-relative modules.
+ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+sys.path.append(os.path.join(ROOT_DIR))
+
+#pylint: disable=wrong-import-position
+from python.generators.trace_processor_table.public import Alias
+from python.generators.trace_processor_table.public import Table
+from python.generators.trace_processor_table.serialize import serialize_header
+from python.generators.trace_processor_table.util import find_table_deps
+from python.generators.trace_processor_table.util import augment_table_with_auto_cols
+from python.generators.trace_processor_table.util import topological_sort_tables
+#pylint: enable=wrong-import-position
+
+
+@dataclass
+class Header:
+  """Represents a Python module which will be converted to a header."""
+  out_path: str
+  relout_path: str
+  tables: List[Table]
+
+
+def normalize_table_for_serialization(table: Table) -> Table:
+  """Normalize the table for generating headers.
+
+  Normalizing = taking the table the user define and converting it into
+  the form needed by the seralizer. Speficially this means:
+  1. Adding the 'id' and 'type" columns.
+  2. Removing any alias columns (for now, these are handled in SQL not C++.
+     This may change in the future.
+  """
+  augmented = augment_table_with_auto_cols(table)
+  augmented.columns = [
+      c for c in augmented.columns if not isinstance(c.type, Alias)
+  ]
+  return augmented
+
+
+def main():
+  """Main function."""
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--gen-dir', required=True)
+  parser.add_argument('--inputs', required=True, nargs='*')
+  parser.add_argument('--outputs', required=True, nargs='*')
+  args = parser.parse_args()
+
+  if len(args.inputs) != len(args.outputs):
+    raise Exception('Number of inputs must match number of outputs')
+
+  headers: List[Header] = []
+  for (in_path, out_path) in zip(args.inputs, args.outputs):
+    tables = runpy.run_path(in_path)['ALL_TABLES']
+    relout_path = os.path.relpath(out_path, args.gen_dir)
+    headers.append(Header(out_path, relout_path, tables))
+
+  # Build a mapping from table class name to the output path of the header
+  # which will be generated for it. This is used to include one header into
+  # another for Id dependencies.
+  table_class_name_to_relout = {}
+  for header in headers:
+    for table in header.tables:
+      table_class_name_to_relout[table.class_name] = header.relout_path
+
+  for header in headers:
+    # Topologically sort the tables in this header to ensure that any deps are
+    # defined *before* the table itself.
+    sorted_tables = topological_sort_tables(
+        [normalize_table_for_serialization(table) for table in header.tables])
+
+    # Find all headers depended on by this table. These will be #include-ed when
+    # generating the header file below so ensure we remove ourself.
+    header_relout_deps: Set[str] = set()
+    for table in sorted_tables:
+      header_relout_deps.union(
+          table_class_name_to_relout[c] for c in find_table_deps(table))
+    header_relout_deps.discard(header.relout_path)
+
+    with open(header.out_path, 'w', encoding='utf8') as out:
+      ifdef_guard = re.sub(r'[^a-zA-Z0-9_-]', '_',
+                           header.relout_path).upper() + '_'
+      out.write(
+          serialize_header(ifdef_guard, sorted_tables,
+                           sorted(header_relout_deps)))
+      out.write('\n')
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/tools/gn_utils.py b/tools/gn_utils.py
index 0b9a9d3..7abe160 100644
--- a/tools/gn_utils.py
+++ b/tools/gn_utils.py
@@ -17,6 +17,7 @@
 
 from __future__ import print_function
 import collections
+from compat import iteritems
 import errno
 import filecmp
 import json
@@ -25,7 +26,10 @@
 import shutil
 import subprocess
 import sys
-from compat import iteritems
+from typing import Dict
+from typing import Optional
+from typing import Set
+from typing import Tuple
 
 BUILDFLAGS_TARGET = '//gn:gen_buildflags'
 GEN_VERSION_TARGET = '//src/base:version_gen_h'
@@ -59,13 +63,20 @@
       os.path.realpath(os.path.dirname(__file__)), os.path.pardir)
 
 
-def _tool_path(name):
+def _tool_path(name, system_buildtools=False):
+  # Pass-through to use name if the caller requests to use the system
+  # toolchain.
+  if system_buildtools:
+    return [name]
   wrapper = os.path.abspath(
       os.path.join(repo_root(), 'tools', 'run_buildtools_binary.py'))
   return ['python3', wrapper, name]
 
 
-def prepare_out_directory(gn_args, name, root=repo_root()):
+def prepare_out_directory(gn_args,
+                          name,
+                          root=repo_root(),
+                          system_buildtools=False):
   """Creates the JSON build description by running GN.
 
     Returns (path, desc) where |path| is the location of the output directory
@@ -78,14 +89,16 @@
     if e.errno != errno.EEXIST:
       raise
   _check_command_output(
-      _tool_path('gn') + ['gen', out, '--args=%s' % gn_args], cwd=repo_root())
+      _tool_path('gn', system_buildtools) +
+      ['gen', out, '--args=%s' % gn_args],
+      cwd=repo_root())
   return out
 
 
-def load_build_description(out):
+def load_build_description(out, system_buildtools=False):
   """Creates the JSON build description by running GN."""
   desc = _check_command_output(
-      _tool_path('gn') +
+      _tool_path('gn', system_buildtools) +
       ['desc', out, '--format=json', '--all-toolchains', '//*'],
       cwd=repo_root())
   return json.loads(desc)
@@ -103,7 +116,7 @@
     shutil.rmtree(out)
 
 
-def build_targets(out, targets, quiet=False):
+def build_targets(out, targets, quiet=False, system_buildtools=False):
   """Runs ninja to build a list of GN targets in the given out directory.
 
     Compiling these targets is required so that we can include any generated
@@ -112,14 +125,14 @@
   targets = [t.replace('//', '') for t in targets]
   with open(os.devnull, 'w') as devnull:
     stdout = devnull if quiet else None
-    cmd = _tool_path('ninja') + targets
+    cmd = _tool_path('ninja', system_buildtools) + targets
     subprocess.check_call(cmd, cwd=os.path.abspath(out), stdout=stdout)
 
 
-def compute_source_dependencies(out):
+def compute_source_dependencies(out, system_buildtools=False):
   """For each source file, computes a set of headers it depends on."""
   ninja_deps = _check_command_output(
-      _tool_path('ninja') + ['-t', 'deps'], cwd=out)
+      _tool_path('ninja', system_buildtools) + ['-t', 'deps'], cwd=out)
   deps = {}
   current_source = None
   for line in ninja_deps.split('\n'):
@@ -213,10 +226,10 @@
   traced.exe -> libperfetto(static lib) -> base(file group)
   """
 
-  def __init__(self, gn, target_name):
+  def __init__(self, gn: 'GnParser', target_name: str):
     self.gn = gn
     self.root = gn.get_target(target_name)
-    self.source_sets = collections.defaultdict(set)
+    self.source_sets: Dict[str, Set[str]] = collections.defaultdict(set)
     self.deps_visited = set()
     self.source_set_hdr_only = {}
 
@@ -237,18 +250,17 @@
       raise Exception('%d ODR violations detected. Build generation aborted' %
                       num_violations)
 
-  def _visit(self, target_name, parent_path=''):
+  def _visit(self, target_name: str, parent_path=''):
     target = self.gn.get_target(target_name)
     path = ((parent_path + ' > ') if parent_path else '') + target_name
     if not target:
       raise Exception('Cannot find target %s' % target_name)
-    for ssdep in target.source_set_deps:
+    for ssdep in target.transitive_source_set_deps():
       name_and_path = '%s (via %s)' % (target_name, path)
-      self.source_sets[ssdep].add(name_and_path)
-    deps = set(target.deps).union(
-        target.transitive_proto_deps) - self.deps_visited
-    for dep_name in deps:
-      dep = self.gn.get_target(dep_name)
+      self.source_sets[ssdep.name].add(name_and_path)
+    deps = set(target.non_proto_or_source_set_deps()).union(
+        target.transitive_proto_deps()) - self.deps_visited
+    for dep in deps:
       if dep.type == 'executable':
         continue  # Execs are strong boundaries and don't cause ODR violations.
       # static_library dependencies should reset the path. It doesn't matter if
@@ -258,10 +270,10 @@
       # This is NOT an ODR violation because source.cc is linked from the same
       # static library
       next_parent_path = path if dep.type != 'static_library' else ''
-      self.deps_visited.add(dep_name)
-      self._visit(dep_name, next_parent_path)
+      self.deps_visited.add(dep.name)
+      self._visit(dep.name, next_parent_path)
 
-  def is_header_only(self, source_set_name):
+  def is_header_only(self, source_set_name: str):
     cached = self.source_set_hdr_only.get(source_set_name)
     if cached is not None:
       return cached
@@ -301,7 +313,7 @@
       self.name = name  # e.g. //src/ipc:ipc
 
       VALID_TYPES = ('static_library', 'shared_library', 'executable', 'group',
-                     'action', 'source_set', 'proto_library')
+                     'action', 'source_set', 'proto_library', 'generated_file')
       assert (type in VALID_TYPES)
       self.type = type
       self.testonly = False
@@ -309,7 +321,7 @@
 
       # These are valid only for type == proto_library.
       # This is typically: 'proto', 'protozero', 'ipc'.
-      self.proto_plugin = None
+      self.proto_plugin: Optional[str] = None
       self.proto_paths = set()
       self.proto_exports = set()
 
@@ -319,22 +331,23 @@
       self.public_headers = set()  # 'public'
 
       # These are valid only for type == 'action'
+      self.data = set()
       self.inputs = set()
       self.outputs = set()
       self.script = None
       self.args = []
+      self.custom_action_type = None
+      self.python_main = None
 
       # These variables are propagated up when encountering a dependency
       # on a source_set target.
       self.cflags = set()
       self.defines = set()
-      self.deps = set()
+      self.deps: Set[GnParser.Target] = set()
+      self.transitive_deps: Set[GnParser.Target] = set()
       self.libs = set()
       self.include_dirs = set()
       self.ldflags = set()
-      self.source_set_deps = set()  # Transitive set of source_set deps.
-      self.proto_deps = set()
-      self.transitive_proto_deps = set()
 
       # Deps on //gn:xxx have this flag set to True. These dependencies
       # are special because they pull third_party code from buildtools/.
@@ -343,6 +356,24 @@
       # placeholder target once we hit //gn:protoc or similar.
       self.is_third_party_dep_ = False
 
+    def non_proto_or_source_set_deps(self):
+      return set(d for d in self.deps
+                 if d.type != 'proto_library' and d.type != 'source_set')
+
+    def proto_deps(self):
+      return set(d for d in self.deps if d.type == 'proto_library')
+
+    def transitive_proto_deps(self):
+      return set(d for d in self.transitive_deps if d.type == 'proto_library')
+
+    def transitive_cpp_proto_deps(self):
+      return set(
+          d for d in self.transitive_deps if d.type == 'proto_library' and
+          d.proto_plugin != 'descriptor' and d.proto_plugin != 'source_set')
+
+    def transitive_source_set_deps(self):
+      return set(d for d in self.transitive_deps if d.type == 'source_set')
+
     def __lt__(self, other):
       if isinstance(other, self.__class__):
         return self.name < other.name
@@ -359,9 +390,8 @@
                         sort_keys=True)
 
     def update(self, other):
-      for key in ('cflags', 'defines', 'deps', 'include_dirs', 'ldflags',
-                  'source_set_deps', 'proto_deps', 'transitive_proto_deps',
-                  'libs', 'proto_paths'):
+      for key in ('cflags', 'data', 'defines', 'deps', 'include_dirs',
+                  'ldflags', 'transitive_deps', 'libs', 'proto_paths'):
         self.__dict__[key].update(other.__dict__.get(key, []))
 
   def __init__(self, gn_desc):
@@ -372,7 +402,7 @@
     self.actions = {}
     self.proto_libs = {}
 
-  def get_target(self, gn_target_name):
+  def get_target(self, gn_target_name: str) -> Target:
     """Returns a Target object from the fully qualified GN target name.
 
         It bubbles up variables from source_set dependencies as described in the
@@ -400,7 +430,8 @@
       return target
 
     proto_target_type, proto_desc = self.get_proto_target_type(target)
-    if proto_target_type is not None:
+    if proto_target_type:
+      assert proto_desc
       self.proto_libs[target.name] = target
       target.type = 'proto_library'
       target.proto_plugin = proto_target_type
@@ -411,11 +442,13 @@
     elif target.type == 'source_set':
       self.source_sets[gn_target_name] = target
       target.sources.update(desc.get('sources', []))
+      target.inputs.update(desc.get('inputs', []))
     elif target.type in LINKER_UNIT_TYPES:
       self.linker_units[gn_target_name] = target
       target.sources.update(desc.get('sources', []))
     elif target.type == 'action':
       self.actions[gn_target_name] = target
+      target.data.update(desc.get('metadata', {}).get('perfetto_data', []))
       target.inputs.update(desc.get('inputs', []))
       target.sources.update(desc.get('sources', []))
       outs = [re.sub('^//out/.+?/gen/', '', x) for x in desc['outputs']]
@@ -424,6 +457,12 @@
       # Args are typically relative to the root build dir (../../xxx)
       # because root build dir is typically out/xxx/).
       target.args = [re.sub('^../../', '//', x) for x in desc['args']]
+      action_types = desc.get('metadata',
+                              {}).get('perfetto_action_type_for_generator', [])
+      target.custom_action_type = action_types[0] if len(
+          action_types) > 0 else None
+      python_main = desc.get('metadata', {}).get('perfetto_python_main', [])
+      target.python_main = python_main[0] if python_main else None
 
     # Default for 'public' is //* - all headers in 'sources' are public.
     # TODO(primiano): if a 'public' section is specified (even if empty), then
@@ -442,23 +481,42 @@
     # Recurse in dependencies.
     for dep_name in desc.get('deps', []):
       dep = self.get_target(dep_name)
-      if dep.is_third_party_dep_:
-        target.deps.add(dep_name)
-      elif dep.type == 'proto_library':
-        target.proto_deps.add(dep_name)
-        target.transitive_proto_deps.add(dep_name)
-        target.proto_paths.update(dep.proto_paths)
-        target.transitive_proto_deps.update(dep.transitive_proto_deps)
-      elif dep.type == 'source_set':
-        target.source_set_deps.add(dep_name)
-        target.update(dep)  # Bubble up source set's cflags/ldflags etc.
-      elif dep.type == 'group':
+
+      # generated_file targets only exist for GN builds: we can safely ignore
+      # them.
+      if dep.type == 'generated_file':
+        continue
+
+      # When a proto_library depends on an action, that is always the "_gen"
+      # rule of the action which is "private" to the proto_library rule.
+      # therefore, just ignore it for dep tracking purposes.
+      if dep.type == 'action' and proto_target_type is not None:
+        target_no_toolchain = label_without_toolchain(target.name)
+        dep_no_toolchain = label_without_toolchain(dep.name)
+        assert (dep_no_toolchain == f'{target_no_toolchain}_gen')
+        continue
+
+      # Non-third party groups are only used for bubbling cflags etc so don't
+      # add a dep.
+      if dep.type == 'group' and not dep.is_third_party_dep_:
         target.update(dep)  # Bubble up groups's cflags/ldflags etc.
-      elif dep.type == 'action':
-        if proto_target_type is None:
-          target.deps.add(dep_name)
-      elif dep.type in LINKER_UNIT_TYPES:
-        target.deps.add(dep_name)
+        continue
+
+      # Linker units act as a hard boundary making all their internal deps
+      # opaque to the outside world. For this reason, do not propogate deps
+      # transitively across them.
+      if dep.type in LINKER_UNIT_TYPES:
+        target.deps.add(dep)
+        continue
+
+      if dep.type == 'source_set':
+        target.update(dep)  # Bubble up source set's cflags/ldflags etc.
+      elif dep.type == 'proto_library':
+        target.proto_paths.update(dep.proto_paths)
+
+      target.deps.add(dep)
+      target.transitive_deps.add(dep)
+      target.transitive_deps.update(dep.transitive_deps)
 
     return target
 
@@ -472,7 +530,8 @@
     metadata = proto_desc.get('metadata', {})
     return metadata.get('import_dirs', [])
 
-  def get_proto_target_type(self, target):
+  def get_proto_target_type(self, target: Target
+                           ) -> Tuple[Optional[str], Optional[Dict]]:
     """ Checks if the target is a proto library and return the plugin.
 
         Returns:
diff --git a/tools/heap_profile b/tools/heap_profile
index c952191..910216b 100755
--- a/tools/heap_profile
+++ b/tools/heap_profile
@@ -34,18 +34,18 @@
 
 
 # ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/traceconv.py
-# This file has been generated by: /s/perfetto/tools/roll-prebuilts v28.0
+# This file has been generated by: /usr/local/google/home/lalitm/perfetto/tools/roll-prebuilts v31.0
 TRACECONV_MANIFEST = [{
     'arch':
         'mac-amd64',
     'file_name':
         'traceconv',
     'file_size':
-        7181712,
+        7772872,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-amd64/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/mac-amd64/traceconv',
     'sha256':
-        '6b398ad9539ddf8208536c0412db198d4627daa97efc7e0850f3e7ec0e115510',
+        'a987cfd2e722994a5911032d046ec2d0b912845a83b093226c3fccd5e316ed01',
     'platform':
         'darwin',
     'machine': ['x86_64']
@@ -55,11 +55,11 @@
     'file_name':
         'traceconv',
     'file_size':
-        6025176,
+        6554552,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-arm64/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/mac-arm64/traceconv',
     'sha256':
-        '407e2988e795a593158304061c547093ad74419f826dd03c2a66911b5a29d065',
+        '972610d249990d4b03de50bece4a7adf03ebd6b3cc3c2c10feb7bb6561c1fce1',
     'platform':
         'darwin',
     'machine': ['arm64']
@@ -69,11 +69,11 @@
     'file_name':
         'traceconv',
     'file_size':
-        7668600,
+        8073432,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-amd64/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-amd64/traceconv',
     'sha256':
-        '1bebc0dd7b2b18fd4abeeb5f811d6d4c7f431d212efd5469c7e5d8b18b19e0c7',
+        'e34f6ddd91ad0de62bfbef76303f2a94136470fe5a4f679af2f5b8898548ac13',
     'platform':
         'linux',
     'machine': ['x86_64']
@@ -83,11 +83,11 @@
     'file_name':
         'traceconv',
     'file_size':
-        5827680,
+        6677612,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-arm/traceconv',
     'sha256':
-        'a19780145f965838e334a57a52230bc67b0db207365746360314fbbbe4e1d12f',
+        '55c9cd6e5c5bc65210068a173b0c16ad3323faaba0fccec20247dca5bd3b913d',
     'platform':
         'linux',
     'machine': ['armv6l', 'armv7l', 'armv8l']
@@ -97,11 +97,11 @@
     'file_name':
         'traceconv',
     'file_size':
-        6876384,
+        7543648,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm64/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-arm64/traceconv',
     'sha256':
-        '2a5e5fcf340070ed6a30204c79b7e76787c5f26181bc8377587547f3eb5df685',
+        '2477857470f613410f6151acb06b6b6a067ef8612d619eee86dd2a3b8aac2a3e',
     'platform':
         'linux',
     'machine': ['aarch64']
@@ -111,55 +111,55 @@
     'file_name':
         'traceconv',
     'file_size':
-        4881820,
+        5360020,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-arm/traceconv',
     'sha256':
-        '73827b82d941a9650580fbd48c3d4ff2323eb8d4ff9d3fffd3e0cac1bc853f34'
+        '60cecbd8d9b6357bb89929fa5afa1eba6c1d46d621e62ee558509424d88bd53d'
 }, {
     'arch':
         'android-arm64',
     'file_name':
         'traceconv',
     'file_size':
-        6222592,
+        6769184,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm64/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-arm64/traceconv',
     'sha256':
-        '72d46258645d486f40ee463052b609d1fd7c4cc64f70c0ba2ef811a9924be98e'
+        'e373cbed4502977290abe3e246132ced538fca33881058e6049676ef9c613dd9'
 }, {
     'arch':
         'android-x86',
     'file_name':
         'traceconv',
     'file_size':
-        7089524,
+        7682412,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x86/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-x86/traceconv',
     'sha256':
-        '689d0b48f91624585285b3833362cdcfdf0de1ff5dedcb97bb9851c729b4a15e'
+        'dcaa5d156247433537d59ad33238feb23dc0f97ba64c51581360f25ac0fc16a8'
 }, {
     'arch':
         'android-x64',
     'file_name':
         'traceconv',
     'file_size':
-        7316248,
+        7903832,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x64/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-x64/traceconv',
     'sha256':
-        '785ec3f0da302ed52521febc5ed5e2cef57ae8840ff241037c51b8d94464f6a2'
+        'b73b8d398019b2a349d3748b4fe4096a069c6362697375d3c2668cf79ef1bb38'
 }, {
     'arch':
         'windows-amd64',
     'file_name':
         'traceconv.exe',
     'file_size':
-        6850048,
+        7188992,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/windows-amd64/traceconv.exe',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/windows-amd64/traceconv.exe',
     'sha256':
-        '19cdec3824d369be3bb053b40b3cfe9f62c2e57e71a5e2ee17ca15b6e7463683',
+        '9cbdfcad3b5f2164d0c9e79d4f8b749db693814ae97fba038385d2e6f0111f72',
     'platform':
         'win32',
     'machine': ['amd64']
@@ -222,8 +222,8 @@
   """ Downloads a prebuilt or returns a cached version
 
   The first time this is invoked, it downloads the |url| and caches it into
-  ~/.perfetto/prebuilts/$tool_name. On subsequent invocations it just runs the
-  cached version.
+  ~/.local/share/perfetto/prebuilts/$tool_name. On subsequent invocations it
+  just runs the cached version.
   """
   dir = os.path.join(
       os.path.expanduser('~'), '.local', 'share', 'perfetto', 'prebuilts')
@@ -251,7 +251,7 @@
       raise Exception('Checksum mismatch for %s (actual: %s, expected: %s)' %
                       (url, actual_sha256, sha256))
     os.chmod(tmp_path, 0o755)
-    os.rename(tmp_path, bin_path)
+    os.replace(tmp_path, bin_path)
     with open(sha256_path, 'w') as f:
       f.write(sha256)
   return bin_path
@@ -536,6 +536,11 @@
   parser.add_argument(
       "--traceconv-binary", help="Path to local trace to text. For debugging.")
   parser.add_argument(
+      "--no-annotations",
+      help="Do not suffix the pprof function names with Android ART mode "
+      "annotations such as [jit].",
+      action="store_true")
+  parser.add_argument(
       "--print-config",
       action="store_true",
       help="Print config instead of running. For debugging.")
@@ -652,7 +657,8 @@
                   'perfetto --txt -c - -o ' + profile_device_path + ' -d')
 
   if args.disable_selinux:
-    enforcing = subprocess.check_output(['adb', 'shell', 'getenforce'])
+    enforcing = subprocess.check_output(['adb', 'shell',
+                                         'getenforce']).decode('utf-8').strip()
     atexit.register(
         subprocess.check_call,
         ['adb', 'shell', 'su root setenforce %s' % enforcing])
@@ -795,8 +801,9 @@
             out.write(buf)
     trace_file = os.path.join(profile_target, 'symbolized-trace')
 
-  traceconv_output = subprocess.check_output(
-      [traceconv_binary, 'profile', trace_file])
+  conversion_args = [traceconv_binary, 'profile'] + (
+      ['--no-annotations'] if args.no_annotations else []) + [trace_file]
+  traceconv_output = subprocess.check_output(conversion_args)
   profile_path = None
   for word in traceconv_output.decode('utf-8').split():
     if 'heap_profile-' in word:
diff --git a/tools/install-build-deps b/tools/install-build-deps
index bb58d88..3ad1026 100755
--- a/tools/install-build-deps
+++ b/tools/install-build-deps
@@ -126,13 +126,13 @@
     # tools/clang/scripts/update.py.
     Dependency(
         'buildtools/linux64/clang.tgz',
-        'https://commondatastorage.googleapis.com/chromium-browser-clang/Linux_x64/clang-llvmorg-14-init-3191-g0e03450a-1.tgz',
-        'dd7479d43ce61401e057a5dee8b7e32bc2bd0d0e15d4f46c6858daf9170c9978',
+        'https://commondatastorage.googleapis.com/chromium-browser-clang/Linux_x64/clang-llvmorg-16-init-8697-g60809cd2-1.tgz',
+        '5ae35f85e0d32136795c6b223bf64263d46678dd4a24fea4e9039e58a32670de',
         'linux', 'x64'),
     Dependency(
         'buildtools/win/clang.tgz',
-        'https://commondatastorage.googleapis.com/chromium-browser-clang/Win/clang-llvmorg-14-init-3191-g0e03450a-1.tgz',
-        '4292d191742e7120200c63224f02e1f2f2a7be8b57c0f18edf6ca0955bdd43df',
+        'https://commondatastorage.googleapis.com/chromium-browser-clang/Win/clang-llvmorg-16-init-8697-g60809cd2-1.tgz',
+        '086faec822acba5b9c0308c6a8be34424031027d757efa2b81805aed18ffc521',
         'windows', 'x64'),
 ]
 
@@ -156,15 +156,15 @@
     Dependency(
         'buildtools/libcxx',
         'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libcxx.git',
-        'd9040c75cfea5928c804ab7c235fed06a63f743a', 'all', 'all'),
+        'f8571eaba606bde2eb8cd34b30104ca33e7c207e', 'all', 'all'),
     Dependency(
         'buildtools/libcxxabi',
         'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libcxxabi.git',
-        '196ba1aaa8ac285d94f4ea8d9836390a45360533', 'all', 'all'),
+        '8dd405113a4f3694e910b79785dd7fb7535a888a', 'all', 'all'),
     Dependency(
         'buildtools/libunwind',
         'https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libunwind.git',
-        'd999d54f4bca789543a2eb6c995af2d9b5a1f3ed', 'all', 'all'),
+        'aabcd8753678f1536e15eb6385a948470debdae4', 'all', 'all'),
 
     # Keep in sync with chromium DEPS.
     Dependency(
@@ -193,13 +193,13 @@
     # If updating the version, also update bazel/deps.bzl.
     Dependency(
         'buildtools/sqlite.zip',
-        'https://storage.googleapis.com/perfetto/sqlite-amalgamation-3350400.zip',
-        'f3bf0df69f5de0675196f4644e05d07dbc698d674dc563a12eff17d5b215cdf5',
+        'https://storage.googleapis.com/perfetto/sqlite-amalgamation-3390200.zip',
+        '87775784f8b22d0d0f1d7811870d39feaa7896319c7c20b849a4181c5a50609b',
         'all', 'all'),
     Dependency(
         'buildtools/sqlite_src',
         'https://chromium.googlesource.com/external/github.com/sqlite/sqlite.git',
-        'ee3686eb50c0e3dbb087c9a0976f7e37e1b014ae',  # refs/tags/version-3.32.3.
+        '202b2a7b54ea2dd13a8a5adfd75523abe4dcf17f',  # refs/tags/version-3.39.2.
         'all',
         'all'),
 
@@ -231,7 +231,7 @@
     Dependency(
         'buildtools/android-unwinding',
         'https://android.googlesource.com/platform/system/unwinding.git',
-        '215644709bd7bbda4c05a9db4d55e96be61bcf64', 'all', 'all'),
+        '11cf5564fbff659b5cdba9c3e977d4fc51bab4b3', 'all', 'all'),
     Dependency('buildtools/android-logging',
                'https://android.googlesource.com/platform/system/logging.git',
                '7b36b566c9113fc703d68f76e8f40c0c2432481c', 'all', 'all'),
@@ -247,7 +247,7 @@
                '7851dce6f4ca17f5caa1c93a4e0a45686b1d56c3', 'all', 'all'),
     Dependency('buildtools/bionic',
                'https://android.googlesource.com/platform/bionic.git',
-               '332065d57e734b65f56474d136d22d767e36cbcd', 'all', 'all'),
+               '4b0e16bc72a82a63c699977376a7d6eadca1b206', 'all', 'all'),
 
     # Zlib used both in the tracing binaries, as well as the trace processor and
     # assorted tools.
@@ -534,7 +534,7 @@
       action='store_true',
       help='Node and NPM packages to Build the Web-based UI via ./ui/build')
   parser.add_argument('--check-only')
-  parser.add_argument('--filter', default='')
+  parser.add_argument('--filter', action='append')
   parser.add_argument('--verify', help='Check all URLs', action='store_true')
   parser.add_argument(
       '--no-toolchain', help='Do not download toolchain', action='store_true')
@@ -569,7 +569,7 @@
     matches_arch = dep.target_arch == 'all' or target_arch == dep.target_arch
     if not matches_os or not matches_arch:
       continue
-    if args.filter and args.filter not in dep.target_folder:
+    if args.filter and not any(f in dep.target_folder for f in args.filter):
       continue
     local_path = os.path.join(ROOT_DIR, dep.target_folder)
     if dep.source_url.endswith('.git'):
diff --git a/tools/java_heap_dump b/tools/java_heap_dump
index c35c111..eebf074 100755
--- a/tools/java_heap_dump
+++ b/tools/java_heap_dump
@@ -37,8 +37,8 @@
 
 CFG_IDENT = '      '
 CFG = '''buffers {{
-  size_kb: 100024
-  fill_policy: RING_BUFFER
+  size_kb: {size_kb}
+  fill_policy: DISCARD
 }}
 
 data_sources {{
@@ -55,6 +55,29 @@
 duration_ms: {duration_ms}
 '''
 
+OOM_CFG = '''buffers: {{
+    size_kb: {size_kb}
+    fill_policy: DISCARD
+}}
+
+data_sources: {{
+    config {{
+        name: "android.java_hprof.oom"
+    }}
+}}
+
+data_source_stop_timeout_ms: 100000
+
+trigger_config {{
+    trigger_mode: START_TRACING
+    trigger_timeout_ms: {wait_duration_ms}
+    triggers {{
+      name: "com.android.telemetry.art-outofmemory"
+      stop_delay_ms: 500
+    }}
+}}
+'''
+
 CONTINUOUS_DUMP = """
       continuous_dump_config {{
         dump_phase_ms: 0
@@ -70,6 +93,7 @@
 
 SDK = {
     'S': 31,
+    'UpsideDownCake': 34,
 }
 
 
@@ -85,6 +109,84 @@
        'ro.build.version.codename']).decode('utf-8').strip()
   return codename == release
 
+def convert_size_to_kb(size):
+  if size.endswith("kb"):
+    return int(size[:-2])
+  elif size.endswith("mb"):
+    return int(size[:-2]) * 1024
+  elif size.endswith("gb"):
+    return int(size[:-2]) * 1024 * 1024
+  else:
+    return int(size)
+
+def generate_heap_dump_config(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:
+    return None
+
+  continuous_dump_cfg = ""
+  if args.continuous_dump:
+    continuous_dump_cfg = CONTINUOUS_DUMP.format(
+        dump_interval=args.continuous_dump)
+
+  if args.stop_when_done:
+    duration_ms = 1000
+    data_source_stop_timeout_ms = 100000
+  else:
+    duration_ms = 20000
+    data_source_stop_timeout_ms = 0
+
+  return CFG.format(
+      size_kb=convert_size_to_kb(args.buffer_size),
+      target_cfg=target_cfg,
+      continuous_dump_config=continuous_dump_cfg,
+      duration_ms=duration_ms,
+      data_source_stop_timeout_ms=data_source_stop_timeout_ms)
+
+def generate_oom_config(args):
+  if not release_or_newer('UpsideDownCake'):
+    print("FATAL: OOM mode not supported for this android version",
+        file=sys.stderr)
+    return None
+
+  if args.pid or args.name:
+    print("FATAL: Specifying process not supported in OOM mode",
+        file=sys.stderr)
+    return None
+
+  if args.continuous_dump:
+    print("FATAL: Specifying continuous dump not supported in OOM mode",
+        file=sys.stderr)
+    return None
+
+  if args.dump_smaps:
+    print("FATAL: Dumping smaps not supported in OOM mode",
+        file=sys.stderr)
+    return None
+
+  return OOM_CFG.format(
+      size_kb=convert_size_to_kb(args.buffer_size),
+      wait_duration_ms=args.oom_wait_seconds * 1000)
+
 
 def main(argv):
   parser = argparse.ArgumentParser()
@@ -107,6 +209,12 @@
       "names to profile.",
       metavar="NAMES")
   parser.add_argument(
+      "-b",
+      "--buffer-size",
+      help="Buffer size in memory that store the whole java heap graph. N(kb|mb|gb)",
+      type=str,
+      default="100024kb")
+  parser.add_argument(
       "-c",
       "--continuous-dump",
       help="Dump interval in ms. 0 to disable continuous dump.",
@@ -135,59 +243,34 @@
       action="store_false",
       dest='stop_when_done',
       help="Do not use a new method to stop the profile when the dump is done.")
+  parser.add_argument(
+      "--wait-for-oom",
+      action="store_true",
+      dest='wait_for_oom',
+      help="Starts a tracing session waiting for an OutOfMemoryError to be "
+      "thrown. Available on U.")
+  parser.add_argument(
+      "--oom-wait-seconds",
+      type=int,
+      default=60,
+      help="Seconds to wait for an OutOfMemoryError to be thrown. "
+      "Defaults to 60.")
 
   args = parser.parse_args()
 
   if args.stop_when_done is None:
     args.stop_when_done = release_or_newer('S')
 
-  fail = False
-  if args.pid is None and args.name is None:
-    print("FATAL: Neither PID nor NAME given.", file=sys.stderr)
-    fail = True
+  cfg = None
+  if args.wait_for_oom:
+    cfg = generate_oom_config(args)
+  else:
+    cfg = generate_heap_dump_config(args)
 
-  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:
+  if not cfg:
     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)
-
-  if args.stop_when_done:
-    duration_ms = 1000
-    data_source_stop_timeout_ms = 100000
-  else:
-    duration_ms = 20000
-    data_source_stop_timeout_ms = 0
-
-  cfg = CFG.format(
-      target_cfg=target_cfg,
-      continuous_dump_config=continuous_dump_cfg,
-      duration_ms=duration_ms,
-      data_source_stop_timeout_ms=data_source_stop_timeout_ms)
   if not args.no_versions:
     cfg += PACKAGES_LIST_CFG
 
@@ -195,6 +278,12 @@
     print(cfg)
     return 0
 
+  output_file = args.output
+  if output_file is None:
+    fd, name = tempfile.mkstemp('profile')
+    os.close(fd)
+    output_file = name
+
   user = subprocess.check_output(['adb', 'shell',
                                   'whoami']).strip().decode('utf8')
   perfetto_pid = subprocess.check_output(
@@ -206,9 +295,12 @@
     print("Failed to invoke perfetto: {}".format(perfetto_pid), file=sys.stderr)
     return 1
 
-  print("Dumping Java Heap.")
-  exists = True
+  if args.wait_for_oom:
+    print("Waiting for OutOfMemoryError")
+  else:
+    print("Dumping Java Heap.")
 
+  exists = True
   # Wait for perfetto cmd to return.
   while exists:
     exists = subprocess.call(
diff --git a/tools/proto_utils.py b/tools/proto_utils.py
deleted file mode 100644
index 87a3150..0000000
--- a/tools/proto_utils.py
+++ /dev/null
@@ -1,77 +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.
-
-from __future__ import absolute_import
-
-import os
-import subprocess
-import tempfile
-
-from google.protobuf import descriptor, descriptor_pb2, message_factory
-from google.protobuf import reflection, text_format
-
-
-ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-
-
-def create_message_factory(descriptor_file_paths, proto_type):
-  files = []
-  for file_path in descriptor_file_paths:
-    files.extend(read_descriptor(file_path).file)
-
-  # We use this method rather than working directly with DescriptorPool
-  # because, when the pure-Python protobuf runtime is used, extensions
-  # need to be explicitly registered with the message type. See
-  # https://github.com/protocolbuffers/protobuf/blob/9e09343a49e9e75be576b31ed7402bf8502b080c/python/google/protobuf/message_factory.py#L145
-  return message_factory.GetMessages(files)[proto_type]
-
-
-def read_descriptor(file_name):
-  with open(file_name, 'rb') as f:
-    contents = f.read()
-
-  descriptor = descriptor_pb2.FileDescriptorSet()
-  descriptor.MergeFromString(contents)
-
-  return descriptor
-
-
-def serialize_textproto_trace(trace_descriptor_path, extension_descriptor_paths,
-                              text_proto_path, out_stream):
-  proto = create_message_factory([trace_descriptor_path] +
-                                 extension_descriptor_paths,
-                                 'perfetto.protos.Trace')()
-
-  with open(text_proto_path, 'r') as text_proto_file:
-    text_format.Merge(text_proto_file.read(), proto)
-  out_stream.write(proto.SerializeToString())
-  out_stream.flush()
-
-
-def serialize_python_trace(trace_descriptor_path, python_trace_path,
-                           out_stream):
-  python_cmd = [
-      'python3',
-      python_trace_path,
-      trace_descriptor_path,
-  ]
-
-  # Add the test dir to the PYTHONPATH to allow synth_common to be found.
-  env = os.environ.copy()
-  if 'PYTHONPATH' in env:
-    env['PYTHONPATH'] = "{}:{}".format(
-        os.path.join(ROOT_DIR, 'test'), env['PYTHONPATH'])
-  else:
-    env['PYTHONPATH'] = os.path.join(ROOT_DIR, 'test')
-  subprocess.check_call(python_cmd, env=env, stdout=out_stream)
diff --git a/tools/record_android_trace b/tools/record_android_trace
index c507a2a..9c86659 100755
--- a/tools/record_android_trace
+++ b/tools/record_android_trace
@@ -33,18 +33,18 @@
 
 
 # ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/tracebox.py
-# This file has been generated by: /s/perfetto/tools/roll-prebuilts v28.0
+# This file has been generated by: /usr/local/google/home/lalitm/perfetto/tools/roll-prebuilts v31.0
 TRACEBOX_MANIFEST = [{
     'arch':
         'mac-amd64',
     'file_name':
         'tracebox',
     'file_size':
-        1399080,
+        1431944,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-amd64/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/mac-amd64/tracebox',
     'sha256':
-        '05674f9872a14bf92d4215a180c883ab54c58376d7e08b95e3f83d03efdeba21',
+        '9ba2bd56eac2464060158fc6a972709663102fd79e210036558a79e0450a39a7',
     'platform':
         'darwin',
     'machine': ['x86_64']
@@ -54,11 +54,11 @@
     'file_name':
         'tracebox',
     'file_size':
-        1292504,
+        1325592,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-arm64/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/mac-arm64/tracebox',
     'sha256':
-        '48008e6aeb7680c4f31b1147d5c26a504368a696c793aec1895bb5fb1f597f64',
+        '2bc59074dc169bd3ca0b58fc8e6536b5a8d35f99ef6d44ce467719b888f5f65e',
     'platform':
         'darwin',
     'machine': ['arm64']
@@ -68,11 +68,11 @@
     'file_name':
         'tracebox',
     'file_size':
-        2243632,
+        2140080,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-amd64/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-amd64/tracebox',
     'sha256':
-        '12c87ba1d03e39d4c07792e09f0be9e240677cec2d9bc1638b0a676bc664d724',
+        'ee12c839586f19d024c6de78825b416fcb7d9a6d4fe6e9266e239812ec63e188',
     'platform':
         'linux',
     'machine': ['x86_64']
@@ -82,11 +82,11 @@
     'file_name':
         'tracebox',
     'file_size':
-        1315252,
+        1286400,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-arm/tracebox',
     'sha256':
-        '3d026fe982e90fb5bc410528f210458f9bcafea30d3e1b9728772fd32148ee9a',
+        'e4b76213b71d192a6155f576ccf265b9f2605090eff01444216fa49e3f06c40c',
     'platform':
         'linux',
     'machine': ['armv6l', 'armv7l', 'armv8l']
@@ -96,11 +96,11 @@
     'file_name':
         'tracebox',
     'file_size':
-        2089160,
+        2070760,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm64/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-arm64/tracebox',
     'sha256':
-        '9f524f943a1c12dcb6b273e78d5c46952d4a7528514639cd2537686d5c530c89',
+        '653469e497c7f021c51220b2b3f0a70efce35dcb4ca897bbf09e24a89af92aea',
     'platform':
         'linux',
     'machine': ['aarch64']
@@ -110,44 +110,44 @@
     'file_name':
         'tracebox',
     'file_size':
-        1099732,
+        1157076,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-arm/tracebox',
     'sha256':
-        '53a1a65e2e409a552cd75f003c755b5903075e37e06841b3a514889306fb9616'
+        '6f610486d0ee1d052a21d6c16491a2c373634bbfa5374fed01026eb2767c05f7'
 }, {
     'arch':
         'android-arm64',
     'file_name':
         'tracebox',
     'file_size':
-        1653416,
+        1768104,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm64/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-arm64/tracebox',
     'sha256':
-        'df911aa2e5d80d1fdda5860e1d18dca7ba13c835aada090bb07e8f253562129a'
+        'b30ca03fa0cb1261386b5d6b01161c275bf68026e894bf5d74781abf8f78acef'
 }, {
     'arch':
         'android-x86',
     'file_name':
         'tracebox',
     'file_size':
-        1677228,
+        1755052,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x86/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-x86/tracebox',
     'sha256':
-        '8d1a296a084f488f826dac837530a40f2ca38af76c70a10966e11f9fec91a2f3'
+        '781ed489fe91aad98fc72f9d0faf69c62323feaac8ae0ce813fbfe0274789144'
 }, {
     'arch':
         'android-x64',
     'file_name':
         'tracebox',
     'file_size':
-        1911464,
+        2038440,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x64/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-x64/tracebox',
     'sha256':
-        '8f844f4d8263d0ff904d120b5858a02d7a24efd87f49c3fa4fd8682d534c738f'
+        'e58167092e4ec060efc152ace675175f04af57c564ec2ca38704fa65af8a7b25'
 }]
 
 # ----- Amalgamator: end of python/perfetto/prebuilts/manifests/tracebox.py
@@ -207,8 +207,8 @@
   """ Downloads a prebuilt or returns a cached version
 
   The first time this is invoked, it downloads the |url| and caches it into
-  ~/.perfetto/prebuilts/$tool_name. On subsequent invocations it just runs the
-  cached version.
+  ~/.local/share/perfetto/prebuilts/$tool_name. On subsequent invocations it
+  just runs the cached version.
   """
   dir = os.path.join(
       os.path.expanduser('~'), '.local', 'share', 'perfetto', 'prebuilts')
@@ -236,7 +236,7 @@
       raise Exception('Checksum mismatch for %s (actual: %s, expected: %s)' %
                       (url, actual_sha256, sha256))
     os.chmod(tmp_path, 0o755)
-    os.rename(tmp_path, bin_path)
+    os.replace(tmp_path, bin_path)
     with open(sha256_path, 'w') as f:
       f.write(sha256)
   return bin_path
@@ -320,7 +320,7 @@
 
 # This is not required. It's only used as a fallback if no adb is found on the
 # PATH. It's fine if it doesn't exist so this script can be copied elsewhere.
-HERMETIC_ADB_PATH = repo_dir('/buildtools/android_sdk/platform-tools/adb')
+HERMETIC_ADB_PATH = repo_dir('buildtools/android_sdk/platform-tools/adb')
 
 # Translates the Android ro.product.cpu.abi into the GN's target_cpu.
 ABI_TO_ARCH = {
diff --git a/tools/serialize_test_trace.py b/tools/serialize_test_trace.py
index 49cdc68..6c7fa85 100755
--- a/tools/serialize_test_trace.py
+++ b/tools/serialize_test_trace.py
@@ -21,7 +21,10 @@
 import os
 import sys
 
-from proto_utils import serialize_python_trace, serialize_textproto_trace
+ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+sys.path.append(ROOT_DIR)
+
+from python.generators.diff_tests.utils import serialize_textproto_trace, serialize_python_trace
 
 
 def main():
@@ -55,7 +58,8 @@
   trace_path = args.trace_path
 
   if trace_path.endswith('.py'):
-    serialize_python_trace(trace_descriptor_path, trace_path, sys.stdout.buffer)
+    serialize_python_trace(ROOT_DIR, trace_descriptor_path, trace_path,
+                           sys.stdout.buffer)
   elif trace_path.endswith('.textproto'):
     serialize_textproto_trace(trace_descriptor_path, extension_descriptors,
                               trace_path, sys.stdout.buffer)
diff --git a/tools/build_all_configs.py b/tools/setup_all_configs.py
similarity index 100%
rename from tools/build_all_configs.py
rename to tools/setup_all_configs.py
diff --git a/tools/test_gen_amalgamated.py b/tools/test_gen_amalgamated.py
index aa99bc4..9eb17ad 100755
--- a/tools/test_gen_amalgamated.py
+++ b/tools/test_gen_amalgamated.py
@@ -52,7 +52,7 @@
 
 def check_amalgamated_build():
   args = [
-      '-std=c++11', '-Werror', '-Wall', '-Wextra',
+      '-std=c++17', '-Werror', '-Wall', '-Wextra',
       '-DPERFETTO_AMALGAMATED_SDK_TEST', '-I' + OUT_DIR,
       OUT_DIR + '/perfetto.cc', 'test/client_api_example.cc', '-o',
       OUT_DIR + '/test'
diff --git a/tools/trace_processor b/tools/trace_processor
index 18354b2..c703ea0 100755
--- a/tools/trace_processor
+++ b/tools/trace_processor
@@ -30,18 +30,18 @@
 
 
 # ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/trace_processor_shell.py
-# This file has been generated by: /s/perfetto/tools/roll-prebuilts v28.0
+# This file has been generated by: /usr/local/google/home/lalitm/perfetto/tools/roll-prebuilts v31.0
 TRACE_PROCESSOR_SHELL_MANIFEST = [{
     'arch':
         'mac-amd64',
     'file_name':
         'trace_processor_shell',
     'file_size':
-        7991568,
+        8516960,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-amd64/trace_processor_shell',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/mac-amd64/trace_processor_shell',
     'sha256':
-        '4704eb8c51946ae288b74ad9f4386019a2b146c310c7717aeeaada73d6eef8fc',
+        'e15e409929d3c140ce82f69ad3f0b0634a4d9b765a1a61f03d8ff29d87cb8f7f',
     'platform':
         'darwin',
     'machine': ['x86_64']
@@ -51,11 +51,11 @@
     'file_name':
         'trace_processor_shell',
     'file_size':
-        6757608,
+        7270264,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-arm64/trace_processor_shell',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/mac-arm64/trace_processor_shell',
     'sha256':
-        '2aa7ddebedb6d5cf6300b3dc3e6ea8e294f645f67c2dbd727d497ab4a17d6599',
+        'd500226e1d53c3e8db4f29f44df463139f8fbc81ba2364934f6a69e76d9d065d',
     'platform':
         'darwin',
     'machine': ['arm64']
@@ -65,11 +65,11 @@
     'file_name':
         'trace_processor_shell',
     'file_size':
-        8568248,
+        8944032,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-amd64/trace_processor_shell',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-amd64/trace_processor_shell',
     'sha256':
-        '67deabb7e215cc66d3694c0e0367f441cd48a3456d2dd5877bab6eb07117887f',
+        '5a2303c36e852f044962c502b9e24d1f5ffb468f25c4672cbe8e5dd9c345906c',
     'platform':
         'linux',
     'machine': ['x86_64']
@@ -79,11 +79,11 @@
     'file_name':
         'trace_processor_shell',
     'file_size':
-        6279736,
+        7110964,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm/trace_processor_shell',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-arm/trace_processor_shell',
     'sha256':
-        '172ac15b010b1e9a28e1efb8991e2e9195dc732969b33e04ebd5b5f801a99218',
+        '4646a44ccab1f28c29c045edda338ebbde2880d5431cc151a6003daf8efe8ddb',
     'platform':
         'linux',
     'machine': ['armv6l', 'armv7l', 'armv8l']
@@ -93,11 +93,11 @@
     'file_name':
         'trace_processor_shell',
     'file_size':
-        7703256,
+        8380416,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm64/trace_processor_shell',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-arm64/trace_processor_shell',
     'sha256':
-        'd70d621ca9aedff0b29a24984ff896e7069cadee607ada1ccfca58634e1774a1',
+        '130c60bebbbb338d8e9c250a5fbd526296d4cab49af08680fef5134cb6831130',
     'platform':
         'linux',
     'machine': ['aarch64']
@@ -107,55 +107,55 @@
     'file_name':
         'trace_processor_shell',
     'file_size':
-        5357880,
+        5807408,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm/trace_processor_shell',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-arm/trace_processor_shell',
     'sha256':
-        '75285ac8964e93890b936bfacd2679ee67ae4cd029be74e675253f92746f9904'
+        '5d13bc0d37f3379a6fd0d66d8f98c36ec8bf80c133b7660ae8b7cbcfe41284f4'
 }, {
     'arch':
         'android-arm64',
     'file_name':
         'trace_processor_shell',
     'file_size':
-        6940832,
+        7458752,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm64/trace_processor_shell',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-arm64/trace_processor_shell',
     'sha256':
-        '75118859c9a1b2ed2bcffc664eb98884dab26a9d2ad7788d05d90ef22b24a8d5'
+        '3e038732d0ee77875e6376ddf0f804271c4e5337b76977e939c91a553621587a'
 }, {
     'arch':
         'android-x86',
     'file_name':
         'trace_processor_shell',
     'file_size':
-        7856396,
+        8404228,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x86/trace_processor_shell',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-x86/trace_processor_shell',
     'sha256':
-        'c3d4da7aa12a2eedd59df98b5c8143658f39ebd9a4ba3c8180be33ab3484e824'
+        '1a04a4d888dfa16a5baa36073df50f28ea9f5ed688fb59fe8ef24a9f3341ad77'
 }, {
     'arch':
         'android-x64',
     'file_name':
         'trace_processor_shell',
     'file_size':
-        8194240,
+        8744960,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x64/trace_processor_shell',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-x64/trace_processor_shell',
     'sha256':
-        '105c70a3908bf6997b428ee7f74bf7ee8a48907b860480c87e62a62cf92e1a8a'
+        '45fe2744b60baac964c636304b4b83853405f7f4f7329b2764cf921d3d90a2b1'
 }, {
     'arch':
         'windows-amd64',
     'file_name':
         'trace_processor_shell.exe',
     'file_size':
-        7857152,
+        8152064,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/windows-amd64/trace_processor_shell.exe',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/windows-amd64/trace_processor_shell.exe',
     'sha256':
-        'eda1e38cb7490a99ba31285a977a3a564c53f25eae2b301152b2578de62fcc5d',
+        '6d5e873b65ea68ec5ddf51a02a049f350e9da4f5f78cc7294d0bbf95f7673ea9',
     'platform':
         'win32',
     'machine': ['amd64']
@@ -218,8 +218,8 @@
   """ Downloads a prebuilt or returns a cached version
 
   The first time this is invoked, it downloads the |url| and caches it into
-  ~/.perfetto/prebuilts/$tool_name. On subsequent invocations it just runs the
-  cached version.
+  ~/.local/share/perfetto/prebuilts/$tool_name. On subsequent invocations it
+  just runs the cached version.
   """
   dir = os.path.join(
       os.path.expanduser('~'), '.local', 'share', 'perfetto', 'prebuilts')
@@ -247,7 +247,7 @@
       raise Exception('Checksum mismatch for %s (actual: %s, expected: %s)' %
                       (url, actual_sha256, sha256))
     os.chmod(tmp_path, 0o755)
-    os.rename(tmp_path, bin_path)
+    os.replace(tmp_path, bin_path)
     with open(sha256_path, 'w') as f:
       f.write(sha256)
   return bin_path
diff --git a/tools/tracebox b/tools/tracebox
index 89eebea..60a4cb9 100755
--- a/tools/tracebox
+++ b/tools/tracebox
@@ -30,18 +30,18 @@
 
 
 # ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/tracebox.py
-# This file has been generated by: /s/perfetto/tools/roll-prebuilts v28.0
+# This file has been generated by: /usr/local/google/home/lalitm/perfetto/tools/roll-prebuilts v31.0
 TRACEBOX_MANIFEST = [{
     'arch':
         'mac-amd64',
     'file_name':
         'tracebox',
     'file_size':
-        1399080,
+        1431944,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-amd64/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/mac-amd64/tracebox',
     'sha256':
-        '05674f9872a14bf92d4215a180c883ab54c58376d7e08b95e3f83d03efdeba21',
+        '9ba2bd56eac2464060158fc6a972709663102fd79e210036558a79e0450a39a7',
     'platform':
         'darwin',
     'machine': ['x86_64']
@@ -51,11 +51,11 @@
     'file_name':
         'tracebox',
     'file_size':
-        1292504,
+        1325592,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-arm64/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/mac-arm64/tracebox',
     'sha256':
-        '48008e6aeb7680c4f31b1147d5c26a504368a696c793aec1895bb5fb1f597f64',
+        '2bc59074dc169bd3ca0b58fc8e6536b5a8d35f99ef6d44ce467719b888f5f65e',
     'platform':
         'darwin',
     'machine': ['arm64']
@@ -65,11 +65,11 @@
     'file_name':
         'tracebox',
     'file_size':
-        2243632,
+        2140080,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-amd64/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-amd64/tracebox',
     'sha256':
-        '12c87ba1d03e39d4c07792e09f0be9e240677cec2d9bc1638b0a676bc664d724',
+        'ee12c839586f19d024c6de78825b416fcb7d9a6d4fe6e9266e239812ec63e188',
     'platform':
         'linux',
     'machine': ['x86_64']
@@ -79,11 +79,11 @@
     'file_name':
         'tracebox',
     'file_size':
-        1315252,
+        1286400,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-arm/tracebox',
     'sha256':
-        '3d026fe982e90fb5bc410528f210458f9bcafea30d3e1b9728772fd32148ee9a',
+        'e4b76213b71d192a6155f576ccf265b9f2605090eff01444216fa49e3f06c40c',
     'platform':
         'linux',
     'machine': ['armv6l', 'armv7l', 'armv8l']
@@ -93,11 +93,11 @@
     'file_name':
         'tracebox',
     'file_size':
-        2089160,
+        2070760,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm64/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-arm64/tracebox',
     'sha256':
-        '9f524f943a1c12dcb6b273e78d5c46952d4a7528514639cd2537686d5c530c89',
+        '653469e497c7f021c51220b2b3f0a70efce35dcb4ca897bbf09e24a89af92aea',
     'platform':
         'linux',
     'machine': ['aarch64']
@@ -107,44 +107,44 @@
     'file_name':
         'tracebox',
     'file_size':
-        1099732,
+        1157076,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-arm/tracebox',
     'sha256':
-        '53a1a65e2e409a552cd75f003c755b5903075e37e06841b3a514889306fb9616'
+        '6f610486d0ee1d052a21d6c16491a2c373634bbfa5374fed01026eb2767c05f7'
 }, {
     'arch':
         'android-arm64',
     'file_name':
         'tracebox',
     'file_size':
-        1653416,
+        1768104,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm64/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-arm64/tracebox',
     'sha256':
-        'df911aa2e5d80d1fdda5860e1d18dca7ba13c835aada090bb07e8f253562129a'
+        'b30ca03fa0cb1261386b5d6b01161c275bf68026e894bf5d74781abf8f78acef'
 }, {
     'arch':
         'android-x86',
     'file_name':
         'tracebox',
     'file_size':
-        1677228,
+        1755052,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x86/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-x86/tracebox',
     'sha256':
-        '8d1a296a084f488f826dac837530a40f2ca38af76c70a10966e11f9fec91a2f3'
+        '781ed489fe91aad98fc72f9d0faf69c62323feaac8ae0ce813fbfe0274789144'
 }, {
     'arch':
         'android-x64',
     'file_name':
         'tracebox',
     'file_size':
-        1911464,
+        2038440,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x64/tracebox',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-x64/tracebox',
     'sha256':
-        '8f844f4d8263d0ff904d120b5858a02d7a24efd87f49c3fa4fd8682d534c738f'
+        'e58167092e4ec060efc152ace675175f04af57c564ec2ca38704fa65af8a7b25'
 }]
 
 # ----- Amalgamator: end of python/perfetto/prebuilts/manifests/tracebox.py
@@ -204,8 +204,8 @@
   """ Downloads a prebuilt or returns a cached version
 
   The first time this is invoked, it downloads the |url| and caches it into
-  ~/.perfetto/prebuilts/$tool_name. On subsequent invocations it just runs the
-  cached version.
+  ~/.local/share/perfetto/prebuilts/$tool_name. On subsequent invocations it
+  just runs the cached version.
   """
   dir = os.path.join(
       os.path.expanduser('~'), '.local', 'share', 'perfetto', 'prebuilts')
@@ -233,7 +233,7 @@
       raise Exception('Checksum mismatch for %s (actual: %s, expected: %s)' %
                       (url, actual_sha256, sha256))
     os.chmod(tmp_path, 0o755)
-    os.rename(tmp_path, bin_path)
+    os.replace(tmp_path, bin_path)
     with open(sha256_path, 'w') as f:
       f.write(sha256)
   return bin_path
diff --git a/tools/traceconv b/tools/traceconv
index db6699b..1079ba0 100755
--- a/tools/traceconv
+++ b/tools/traceconv
@@ -30,18 +30,18 @@
 
 
 # ----- Amalgamator: begin of python/perfetto/prebuilts/manifests/traceconv.py
-# This file has been generated by: /s/perfetto/tools/roll-prebuilts v28.0
+# This file has been generated by: /usr/local/google/home/lalitm/perfetto/tools/roll-prebuilts v31.0
 TRACECONV_MANIFEST = [{
     'arch':
         'mac-amd64',
     'file_name':
         'traceconv',
     'file_size':
-        7181712,
+        7772872,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-amd64/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/mac-amd64/traceconv',
     'sha256':
-        '6b398ad9539ddf8208536c0412db198d4627daa97efc7e0850f3e7ec0e115510',
+        'a987cfd2e722994a5911032d046ec2d0b912845a83b093226c3fccd5e316ed01',
     'platform':
         'darwin',
     'machine': ['x86_64']
@@ -51,11 +51,11 @@
     'file_name':
         'traceconv',
     'file_size':
-        6025176,
+        6554552,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/mac-arm64/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/mac-arm64/traceconv',
     'sha256':
-        '407e2988e795a593158304061c547093ad74419f826dd03c2a66911b5a29d065',
+        '972610d249990d4b03de50bece4a7adf03ebd6b3cc3c2c10feb7bb6561c1fce1',
     'platform':
         'darwin',
     'machine': ['arm64']
@@ -65,11 +65,11 @@
     'file_name':
         'traceconv',
     'file_size':
-        7668600,
+        8073432,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-amd64/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-amd64/traceconv',
     'sha256':
-        '1bebc0dd7b2b18fd4abeeb5f811d6d4c7f431d212efd5469c7e5d8b18b19e0c7',
+        'e34f6ddd91ad0de62bfbef76303f2a94136470fe5a4f679af2f5b8898548ac13',
     'platform':
         'linux',
     'machine': ['x86_64']
@@ -79,11 +79,11 @@
     'file_name':
         'traceconv',
     'file_size':
-        5827680,
+        6677612,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-arm/traceconv',
     'sha256':
-        'a19780145f965838e334a57a52230bc67b0db207365746360314fbbbe4e1d12f',
+        '55c9cd6e5c5bc65210068a173b0c16ad3323faaba0fccec20247dca5bd3b913d',
     'platform':
         'linux',
     'machine': ['armv6l', 'armv7l', 'armv8l']
@@ -93,11 +93,11 @@
     'file_name':
         'traceconv',
     'file_size':
-        6876384,
+        7543648,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/linux-arm64/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/linux-arm64/traceconv',
     'sha256':
-        '2a5e5fcf340070ed6a30204c79b7e76787c5f26181bc8377587547f3eb5df685',
+        '2477857470f613410f6151acb06b6b6a067ef8612d619eee86dd2a3b8aac2a3e',
     'platform':
         'linux',
     'machine': ['aarch64']
@@ -107,55 +107,55 @@
     'file_name':
         'traceconv',
     'file_size':
-        4881820,
+        5360020,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-arm/traceconv',
     'sha256':
-        '73827b82d941a9650580fbd48c3d4ff2323eb8d4ff9d3fffd3e0cac1bc853f34'
+        '60cecbd8d9b6357bb89929fa5afa1eba6c1d46d621e62ee558509424d88bd53d'
 }, {
     'arch':
         'android-arm64',
     'file_name':
         'traceconv',
     'file_size':
-        6222592,
+        6769184,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-arm64/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-arm64/traceconv',
     'sha256':
-        '72d46258645d486f40ee463052b609d1fd7c4cc64f70c0ba2ef811a9924be98e'
+        'e373cbed4502977290abe3e246132ced538fca33881058e6049676ef9c613dd9'
 }, {
     'arch':
         'android-x86',
     'file_name':
         'traceconv',
     'file_size':
-        7089524,
+        7682412,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x86/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-x86/traceconv',
     'sha256':
-        '689d0b48f91624585285b3833362cdcfdf0de1ff5dedcb97bb9851c729b4a15e'
+        'dcaa5d156247433537d59ad33238feb23dc0f97ba64c51581360f25ac0fc16a8'
 }, {
     'arch':
         'android-x64',
     'file_name':
         'traceconv',
     'file_size':
-        7316248,
+        7903832,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/android-x64/traceconv',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/android-x64/traceconv',
     'sha256':
-        '785ec3f0da302ed52521febc5ed5e2cef57ae8840ff241037c51b8d94464f6a2'
+        'b73b8d398019b2a349d3748b4fe4096a069c6362697375d3c2668cf79ef1bb38'
 }, {
     'arch':
         'windows-amd64',
     'file_name':
         'traceconv.exe',
     'file_size':
-        6850048,
+        7188992,
     'url':
-        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v28.0/windows-amd64/traceconv.exe',
+        'https://commondatastorage.googleapis.com/perfetto-luci-artifacts/v31.0/windows-amd64/traceconv.exe',
     'sha256':
-        '19cdec3824d369be3bb053b40b3cfe9f62c2e57e71a5e2ee17ca15b6e7463683',
+        '9cbdfcad3b5f2164d0c9e79d4f8b749db693814ae97fba038385d2e6f0111f72',
     'platform':
         'win32',
     'machine': ['amd64']
@@ -218,8 +218,8 @@
   """ Downloads a prebuilt or returns a cached version
 
   The first time this is invoked, it downloads the |url| and caches it into
-  ~/.perfetto/prebuilts/$tool_name. On subsequent invocations it just runs the
-  cached version.
+  ~/.local/share/perfetto/prebuilts/$tool_name. On subsequent invocations it
+  just runs the cached version.
   """
   dir = os.path.join(
       os.path.expanduser('~'), '.local', 'share', 'perfetto', 'prebuilts')
@@ -247,7 +247,7 @@
       raise Exception('Checksum mismatch for %s (actual: %s, expected: %s)' %
                       (url, actual_sha256, sha256))
     os.chmod(tmp_path, 0o755)
-    os.rename(tmp_path, bin_path)
+    os.replace(tmp_path, bin_path)
     with open(sha256_path, 'w') as f:
       f.write(sha256)
   return bin_path
diff --git a/ui/.eslintrc.js b/ui/.eslintrc.js
index 3682c35..b890b89 100644
--- a/ui/.eslintrc.js
+++ b/ui/.eslintrc.js
@@ -47,7 +47,7 @@
     // https://github.com/typescript-eslint/typescript-eslint/issues/2621
     'no-unused-vars': 'off',
     '@typescript-eslint/no-unused-vars':
-        ['error', {'argsIgnorePattern': '^_.*'}],
+        ['error', {'argsIgnorePattern': '^_.*', 'varsIgnorePattern': '^_.*'}],
 
     // new Array() is banned (use [] instead) but new Array<Foo>() is
     // allowed since it can be clearer to put the type by the
@@ -55,6 +55,13 @@
     'no-array-constructor': 'off',
     '@typescript-eslint/no-array-constructor': ['error'],
 
+    // Rest parameters are not equivalent to 'arguments'.
+    // Rest parameters are arrays: https://developer.mozilla.org/en-US/docs/Web/
+    // JavaScript/Reference/Functions/rest_parameters
+    // 'arguments' are objects: https://developer.mozilla.org/en-US/docs/Web/
+    // JavaScript/Reference/Functions/arguments
+    'prefer-rest-params': 'off',
+
     // We have a lot normal functions which are capitalised.
     // TODO(hjd): Switch these to be lowercase and remove capIsNew.
     // There are also some properties like: foo.factory these should
diff --git a/ui/PRESUBMIT.py b/ui/PRESUBMIT.py
index fe427dc..26cf3bc 100644
--- a/ui/PRESUBMIT.py
+++ b/ui/PRESUBMIT.py
@@ -18,6 +18,9 @@
 from os.path import relpath
 
 
+USE_PYTHON3 = True
+
+
 def RunAndReportIfLong(func, *args, **kargs):
   start = time.time()
   results = func(*args, **kargs)
diff --git a/ui/build.js b/ui/build.js
index 096d35f..73b2aac 100644
--- a/ui/build.js
+++ b/ui/build.js
@@ -88,6 +88,7 @@
   wasmModules: ['trace_processor', 'traceconv'],
   crossOriginIsolation: false,
   testFilter: '',
+  noOverrideGnArgs: false,
 
   // The fields below will be changed by main() after cmdline parsing.
   // Directory structure:
@@ -152,6 +153,7 @@
   parser.addArgument(
       ['--test-filter', '-f'],
       {help: 'filter Jest tests by regex, e.g. \'chrome_render\''});
+  parser.addArgument(['--no-override-gn-args'], {action: 'storeTrue'});
 
   const args = parser.parseArgs();
   const clean = !args.no_build;
@@ -170,6 +172,7 @@
   cfg.verbose = !!args.verbose;
   cfg.debug = !!args.debug;
   cfg.startHttpServer = args.serve;
+  cfg.noOverrideGnArgs = !!args.no_override_gn_args;
   if (args.serve_host) {
     cfg.httpServerListenHost = args.serve_host;
   }
@@ -253,7 +256,7 @@
   const tStart = Date.now();
   while (!isDistComplete()) {
     const secs = Math.ceil((Date.now() - tStart) / 1000);
-    process.stdout.write(`Waiting for first build to complete... ${secs} s\r`);
+    process.stdout.write(`\t\tWaiting for first build to complete... ${secs} s\r`);
     await new Promise((r) => setTimeout(r, 500));
   }
   if (cfg.watch) console.log('\nFirst build completed!');
@@ -335,15 +338,21 @@
   const dstJs = pjoin(cfg.outGenDir, 'protos.js');
   const dstTs = pjoin(cfg.outGenDir, 'protos.d.ts');
   const inputs = [
-    'protos/perfetto/trace_processor/trace_processor.proto',
     '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',
+    'protos/perfetto/trace/perfetto/perfetto_metatrace.proto',
+    'protos/perfetto/trace/trace.proto',
+    'protos/perfetto/trace/trace_packet.proto',
+    'protos/perfetto/trace_processor/trace_processor.proto',
   ];
+  // Can't put --no-comments here - The comments are load bearing for
+  // the pbts invocation which follows.
   const pbjsArgs = [
+    '--no-beautify',
     '--force-number',
     '-t',
     'static-module',
@@ -355,7 +364,7 @@
     dstJs,
   ].concat(inputs);
   addTask(execNode, ['pbjs', pbjsArgs]);
-  const pbtsArgs = ['-p', ROOT_DIR, '-o', dstTs, dstJs];
+  const pbtsArgs = ['--no-comments', '-p', ROOT_DIR, '-o', dstTs, dstJs];
   addTask(execNode, ['pbts', pbtsArgs]);
 }
 
@@ -407,8 +416,10 @@
 // out/tsc/, so the typescript compiler and the bundler can pick them up.
 function buildWasm(skipWasmBuild) {
   if (!skipWasmBuild) {
-    const gnArgs = ['gen', `--args=is_debug=${cfg.debug}`, cfg.outDir];
-    addTask(exec, [pjoin(ROOT_DIR, 'tools/gn'), gnArgs]);
+    if (!cfg.noOverrideGnArgs) {
+      const gnArgs = ['gen', `--args=is_debug=${cfg.debug}`, cfg.outDir];
+      addTask(exec, [pjoin(ROOT_DIR, 'tools/gn'), gnArgs]);
+    }
 
     const ninjaArgs = ['-C', cfg.outDir];
     ninjaArgs.push(...cfg.wasmModules.map((x) => `${x}_wasm`));
diff --git a/ui/config/integrationtest_env.js b/ui/config/integrationtest_env.js
index b7509ee..830025e 100644
--- a/ui/config/integrationtest_env.js
+++ b/ui/config/integrationtest_env.js
@@ -42,7 +42,7 @@
                                   // determinism.
       ],
 
-      // This is so screenshot in --interactive and headles mode match. The
+      // This is so screenshot in --interactive and headless mode match. The
       // scrollbars are never part of the screenshot, but without this cmdline
       // switch, in headless mode we don't get any blank space (as if it was
       // overflow:hidden) and that changes the layout of the page.
@@ -62,4 +62,4 @@
   runScript(script) {
     return super.runScript(script);
   }
-}
+};
diff --git a/ui/config/integrationtest_setup.js b/ui/config/integrationtest_setup.js
index e4cd7e8..7dc1873 100644
--- a/ui/config/integrationtest_setup.js
+++ b/ui/config/integrationtest_setup.js
@@ -14,7 +14,7 @@
 
 const path = require('path');
 const http = require('http');
-const child_process = require('child_process');
+const childProcess = require('child_process');
 
 module.exports = async function() {
   // Start the local HTTP server.
@@ -24,34 +24,33 @@
     path.join(ROOT_DIR, 'ui', 'build.js'),
     '--serve',
     '--no-build',
-    '--out=.'
+    '--out=.',
   ];
   const spwOpts = {stdio: ['ignore', 'inherit', 'inherit']};
-  const srvProc = child_process.spawn(node, args, spwOpts);
+  const srvProc = childProcess.spawn(node, args, spwOpts);
   global.__DEV_SERVER__ = srvProc;
 
   // Wait for the HTTP server to be ready.
   let attempts = 10;
   for (; attempts > 0; attempts--) {
-    await new Promise(r => setTimeout(r, 1000));
+    await new Promise((r) => setTimeout(r, 1000));
     try {
       await new Promise((resolve, reject) => {
         const req = http.request('http://127.0.0.1:10000/frontend_bundle.js');
         req.end();
-        req.on('error', err => reject(err));
+        req.on('error', (err) => reject(err));
         req.on('finish', () => resolve());
       });
       break;
     } catch (err) {
       console.error('Waiting for HTTP server to come up', err.message);
-      continue;
     }
   }
-  if (attempts == 0) {
+  if (attempts === 0) {
     throw new Error('HTTP server didn\'t come up');
   }
   if (srvProc.exitCode !== null) {
     throw new Error(
         `The dev server unexpectedly exited, code=${srvProc.exitCode}`);
   }
-}
+};
diff --git a/ui/config/integrationtest_teardown.js b/ui/config/integrationtest_teardown.js
index 6d152dd..4cfbc84 100644
--- a/ui/config/integrationtest_teardown.js
+++ b/ui/config/integrationtest_teardown.js
@@ -19,6 +19,6 @@
   for (;;) {
     if (proc.exitCode !== null || proc.killed) break;
     console.log('Waiting for dev server termination');
-    await new Promise(r => setTimeout(r, 1000));
+    await new Promise((r) => setTimeout(r, 1000));
   }
-}
+};
diff --git a/ui/config/jest.integrationtest.config.js b/ui/config/jest.integrationtest.config.js
index 079abaf..9f58c3d 100644
--- a/ui/config/jest.integrationtest.config.js
+++ b/ui/config/jest.integrationtest.config.js
@@ -18,4 +18,4 @@
   globalSetup: __dirname + '/integrationtest_setup.js',
   globalTeardown: __dirname + '/integrationtest_teardown.js',
   testEnvironment: __dirname + '/integrationtest_env.js',
-}
+};
diff --git a/ui/config/jest.unittest.config.js b/ui/config/jest.unittest.config.js
index fc1bd35..e8efad3 100644
--- a/ui/config/jest.unittest.config.js
+++ b/ui/config/jest.unittest.config.js
@@ -15,5 +15,5 @@
 module.exports = {
   transform: {},
   testRegex: '_(unittest|jsdomtest)[.]js$',
-  testEnvironment: 'jsdom'
-}
+  testEnvironment: 'jsdom',
+};
diff --git a/ui/config/rollup-serviceworker.config.js b/ui/config/rollup-serviceworker.config.js
index b7aaf21..023dbdd 100644
--- a/ui/config/rollup-serviceworker.config.js
+++ b/ui/config/rollup-serviceworker.config.js
@@ -38,4 +38,4 @@
     commonjs(),
     sourcemaps(),
   ],
-}]
+}];
diff --git a/ui/config/rollup.config.js b/ui/config/rollup.config.js
index 7a28ba4..7372ccf 100644
--- a/ui/config/rollup.config.js
+++ b/ui/config/rollup.config.js
@@ -46,7 +46,7 @@
           'fs',
           'path',
           'crypto',
-        ]
+        ],
       }),
 
       replace({
@@ -59,7 +59,7 @@
           // but |process| is not defined in the browser. Bypass.
           // https://github.com/immerjs/immer/issues/557
           {test: /process\.env\.NODE_ENV/g, replace: '\'production\''},
-        ]
+        ],
       }),
 
       // Translate source maps to point back to the .ts sources.
@@ -74,7 +74,7 @@
 
       // Call the default warning handler for all remaining warnings.
       warn(warning);
-    }
+    },
   };
 }
 
@@ -102,9 +102,8 @@
 
 export default [
   defBundle('frontend', 'dist_version'),
-  defBundle('controller', 'dist_version'),
   defBundle('engine', 'dist_version'),
   defBundle('traceconv', 'dist_version'),
   defBundle('chrome_extension', 'chrome_extension'),
   defServiceWorkerBundle(),
-]
+];
diff --git a/ui/package-lock.json b/ui/package-lock.json
index 73f1448..b515d19 100644
--- a/ui/package-lock.json
+++ b/ui/package-lock.json
@@ -9,7 +9,6 @@
       "version": "1.0.0",
       "license": "Apache-2.0",
       "dependencies": {
-        "@tsundoku/micromodal_types": "^0.0.1",
         "@types/chrome": "0.0.186",
         "@types/color-convert": "^1.9.0",
         "@types/filesystem": "^0.0.32",
@@ -22,11 +21,11 @@
         "color-convert": "^2.0.1",
         "custom_utils": "file:src/base/utils",
         "devtools-protocol": "0.0.847576",
+        "esbuild": "^0.15.12",
         "events": "^3.1.0",
         "hsluv": "^0.1.0",
         "immer": "^9.0.2",
         "jsbn-rsa": "^1.0.4",
-        "micromodal": "^0.4.6",
         "mithril": "^2.2.0",
         "noice-json-rpc": "^1.2.0",
         "pako": "^1.0.11",
@@ -50,6 +49,7 @@
         "node-watch": "^0.7.1",
         "pixelmatch": "^5.2.1",
         "pngjs": "^6.0.0",
+        "prettier": "^2.8.0",
         "puppeteer": "^14.1.0",
         "rollup": "^2.38.5",
         "rollup-plugin-re": "^1.0.7",
@@ -569,6 +569,36 @@
         "node": ">=0.1.95"
       }
     },
+    "node_modules/@esbuild/android-arm": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.12.tgz",
+      "integrity": "sha512-IC7TqIqiyE0MmvAhWkl/8AEzpOtbhRNDo7aph47We1NbE5w2bt/Q+giAhe0YYeVpYnIhGMcuZY92qDK6dQauvA==",
+      "cpu": [
+        "arm"
+      ],
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-loong64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.12.tgz",
+      "integrity": "sha512-tZEowDjvU7O7I04GYvWQOS4yyP9E/7YlsB0jjw1Ycukgr2ycEzKyIk5tms5WnLBymaewc6VmRKnn5IJWgK4eFw==",
+      "cpu": [
+        "loong64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
     "node_modules/@eslint/eslintrc": {
       "version": "1.2.3",
       "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.3.tgz",
@@ -1149,11 +1179,6 @@
         "node": ">= 6"
       }
     },
-    "node_modules/@tsundoku/micromodal_types": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/@tsundoku/micromodal_types/-/micromodal_types-0.0.1.tgz",
-      "integrity": "sha512-9k95tyHczZp/Uwu7SysnekpA2/o/y5gb/jMwqoLuTlJqwIVwnxfpsfmxc/bMfHnct7ESSqmRUJ1qYnUPD9Z7og=="
-    },
     "node_modules/@types/babel__core": {
       "version": "7.1.14",
       "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.14.tgz",
@@ -3224,6 +3249,342 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/esbuild": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.12.tgz",
+      "integrity": "sha512-PcT+/wyDqJQsRVhaE9uX/Oq4XLrFh0ce/bs2TJh4CSaw9xuvI+xFrH2nAYOADbhQjUgAhNWC5LKoUsakm4dxng==",
+      "hasInstallScript": true,
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "@esbuild/android-arm": "0.15.12",
+        "@esbuild/linux-loong64": "0.15.12",
+        "esbuild-android-64": "0.15.12",
+        "esbuild-android-arm64": "0.15.12",
+        "esbuild-darwin-64": "0.15.12",
+        "esbuild-darwin-arm64": "0.15.12",
+        "esbuild-freebsd-64": "0.15.12",
+        "esbuild-freebsd-arm64": "0.15.12",
+        "esbuild-linux-32": "0.15.12",
+        "esbuild-linux-64": "0.15.12",
+        "esbuild-linux-arm": "0.15.12",
+        "esbuild-linux-arm64": "0.15.12",
+        "esbuild-linux-mips64le": "0.15.12",
+        "esbuild-linux-ppc64le": "0.15.12",
+        "esbuild-linux-riscv64": "0.15.12",
+        "esbuild-linux-s390x": "0.15.12",
+        "esbuild-netbsd-64": "0.15.12",
+        "esbuild-openbsd-64": "0.15.12",
+        "esbuild-sunos-64": "0.15.12",
+        "esbuild-windows-32": "0.15.12",
+        "esbuild-windows-64": "0.15.12",
+        "esbuild-windows-arm64": "0.15.12"
+      }
+    },
+    "node_modules/esbuild-android-64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.12.tgz",
+      "integrity": "sha512-MJKXwvPY9g0rGps0+U65HlTsM1wUs9lbjt5CU19RESqycGFDRijMDQsh68MtbzkqWSRdEtiKS1mtPzKneaAI0Q==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-android-arm64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.12.tgz",
+      "integrity": "sha512-Hc9SEcZbIMhhLcvhr1DH+lrrec9SFTiRzfJ7EGSBZiiw994gfkVV6vG0sLWqQQ6DD7V4+OggB+Hn0IRUdDUqvA==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-darwin-64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.12.tgz",
+      "integrity": "sha512-qkmqrTVYPFiePt5qFjP8w/S+GIUMbt6k8qmiPraECUWfPptaPJUGkCKrWEfYFRWB7bY23FV95rhvPyh/KARP8Q==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-darwin-arm64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.12.tgz",
+      "integrity": "sha512-z4zPX02tQ41kcXMyN3c/GfZpIjKoI/BzHrdKUwhC/Ki5BAhWv59A9M8H+iqaRbwpzYrYidTybBwiZAIWCLJAkw==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-freebsd-64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.12.tgz",
+      "integrity": "sha512-XFL7gKMCKXLDiAiBjhLG0XECliXaRLTZh6hsyzqUqPUf/PY4C6EJDTKIeqqPKXaVJ8+fzNek88285krSz1QECw==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-freebsd-arm64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.12.tgz",
+      "integrity": "sha512-jwEIu5UCUk6TjiG1X+KQnCGISI+ILnXzIzt9yDVrhjug2fkYzlLbl0K43q96Q3KB66v6N1UFF0r5Ks4Xo7i72g==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-32": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.12.tgz",
+      "integrity": "sha512-uSQuSEyF1kVzGzuIr4XM+v7TPKxHjBnLcwv2yPyCz8riV8VUCnO/C4BF3w5dHiVpCd5Z1cebBtZJNlC4anWpwA==",
+      "cpu": [
+        "ia32"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.12.tgz",
+      "integrity": "sha512-QcgCKb7zfJxqT9o5z9ZUeGH1k8N6iX1Y7VNsEi5F9+HzN1OIx7ESxtQXDN9jbeUSPiRH1n9cw6gFT3H4qbdvcA==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-arm": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.12.tgz",
+      "integrity": "sha512-Wf7T0aNylGcLu7hBnzMvsTfEXdEdJY/hY3u36Vla21aY66xR0MS5I1Hw8nVquXjTN0A6fk/vnr32tkC/C2lb0A==",
+      "cpu": [
+        "arm"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-arm64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.12.tgz",
+      "integrity": "sha512-HtNq5xm8fUpZKwWKS2/YGwSfTF+339L4aIA8yphNKYJckd5hVdhfdl6GM2P3HwLSCORS++++7++//ApEwXEuAQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-mips64le": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.12.tgz",
+      "integrity": "sha512-Qol3+AvivngUZkTVFgLpb0H6DT+N5/zM3V1YgTkryPYFeUvuT5JFNDR3ZiS6LxhyF8EE+fiNtzwlPqMDqVcc6A==",
+      "cpu": [
+        "mips64el"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-ppc64le": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.12.tgz",
+      "integrity": "sha512-4D8qUCo+CFKaR0cGXtGyVsOI7w7k93Qxb3KFXWr75An0DHamYzq8lt7TNZKoOq/Gh8c40/aKaxvcZnTgQ0TJNg==",
+      "cpu": [
+        "ppc64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-riscv64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.12.tgz",
+      "integrity": "sha512-G9w6NcuuCI6TUUxe6ka0enjZHDnSVK8bO+1qDhMOCtl7Tr78CcZilJj8SGLN00zO5iIlwNRZKHjdMpfFgNn1VA==",
+      "cpu": [
+        "riscv64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-linux-s390x": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.12.tgz",
+      "integrity": "sha512-Lt6BDnuXbXeqSlVuuUM5z18GkJAZf3ERskGZbAWjrQoi9xbEIsj/hEzVnSAFLtkfLuy2DE4RwTcX02tZFunXww==",
+      "cpu": [
+        "s390x"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-netbsd-64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.12.tgz",
+      "integrity": "sha512-jlUxCiHO1dsqoURZDQts+HK100o0hXfi4t54MNRMCAqKGAV33JCVvMplLAa2FwviSojT/5ZG5HUfG3gstwAG8w==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-openbsd-64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.12.tgz",
+      "integrity": "sha512-1o1uAfRTMIWNOmpf8v7iudND0L6zRBYSH45sofCZywrcf7NcZA+c7aFsS1YryU+yN7aRppTqdUK1PgbZVaB1Dw==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-sunos-64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.12.tgz",
+      "integrity": "sha512-nkl251DpoWoBO9Eq9aFdoIt2yYmp4I3kvQjba3jFKlMXuqQ9A4q+JaqdkCouG3DHgAGnzshzaGu6xofGcXyPXg==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-windows-32": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.12.tgz",
+      "integrity": "sha512-WlGeBZHgPC00O08luIp5B2SP4cNCp/PcS+3Pcg31kdcJPopHxLkdCXtadLU9J82LCfw4TVls21A6lilQ9mzHrw==",
+      "cpu": [
+        "ia32"
+      ],
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-windows-64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.12.tgz",
+      "integrity": "sha512-VActO3WnWZSN//xjSfbiGOSyC+wkZtI8I4KlgrTo5oHJM6z3MZZBCuFaZHd8hzf/W9KPhF0lY8OqlmWC9HO5AA==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/esbuild-windows-arm64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.12.tgz",
+      "integrity": "sha512-Of3MIacva1OK/m4zCNIvBfz8VVROBmQT+gRX6pFTLPngFYcj6TFH/12VveAqq1k9VB2l28EoVMNMUCcmsfwyuA==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
     "node_modules/escalade": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
@@ -6308,14 +6669,6 @@
         "url": "https://github.com/sponsors/jonschlinkert"
       }
     },
-    "node_modules/micromodal": {
-      "version": "0.4.6",
-      "resolved": "https://registry.npmjs.org/micromodal/-/micromodal-0.4.6.tgz",
-      "integrity": "sha512-2VDso2a22jWPpqwuWT/4RomVpoU3Bl9qF9D01xzwlNp5UVsImeA0gY4nSpF44vqcQtQOtkiMUV9EZkAJSRxBsg==",
-      "engines": {
-        "node": ">=10"
-      }
-    },
     "node_modules/mime-db": {
       "version": "1.45.0",
       "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz",
@@ -7399,6 +7752,21 @@
         "node": ">= 0.8.0"
       }
     },
+    "node_modules/prettier": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.0.tgz",
+      "integrity": "sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==",
+      "dev": true,
+      "bin": {
+        "prettier": "bin-prettier.js"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      },
+      "funding": {
+        "url": "https://github.com/prettier/prettier?sponsor=1"
+      }
+    },
     "node_modules/pretty-format": {
       "version": "26.6.2",
       "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz",
@@ -10177,6 +10545,18 @@
         "minimist": "^1.2.0"
       }
     },
+    "@esbuild/android-arm": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.12.tgz",
+      "integrity": "sha512-IC7TqIqiyE0MmvAhWkl/8AEzpOtbhRNDo7aph47We1NbE5w2bt/Q+giAhe0YYeVpYnIhGMcuZY92qDK6dQauvA==",
+      "optional": true
+    },
+    "@esbuild/linux-loong64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.12.tgz",
+      "integrity": "sha512-tZEowDjvU7O7I04GYvWQOS4yyP9E/7YlsB0jjw1Ycukgr2ycEzKyIk5tms5WnLBymaewc6VmRKnn5IJWgK4eFw==",
+      "optional": true
+    },
     "@eslint/eslintrc": {
       "version": "1.2.3",
       "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.3.tgz",
@@ -10653,11 +11033,6 @@
       "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
       "dev": true
     },
-    "@tsundoku/micromodal_types": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/@tsundoku/micromodal_types/-/micromodal_types-0.0.1.tgz",
-      "integrity": "sha512-9k95tyHczZp/Uwu7SysnekpA2/o/y5gb/jMwqoLuTlJqwIVwnxfpsfmxc/bMfHnct7ESSqmRUJ1qYnUPD9Z7og=="
-    },
     "@types/babel__core": {
       "version": "7.1.14",
       "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.14.tgz",
@@ -12272,6 +12647,155 @@
         "is-symbol": "^1.0.2"
       }
     },
+    "esbuild": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.12.tgz",
+      "integrity": "sha512-PcT+/wyDqJQsRVhaE9uX/Oq4XLrFh0ce/bs2TJh4CSaw9xuvI+xFrH2nAYOADbhQjUgAhNWC5LKoUsakm4dxng==",
+      "requires": {
+        "@esbuild/android-arm": "0.15.12",
+        "@esbuild/linux-loong64": "0.15.12",
+        "esbuild-android-64": "0.15.12",
+        "esbuild-android-arm64": "0.15.12",
+        "esbuild-darwin-64": "0.15.12",
+        "esbuild-darwin-arm64": "0.15.12",
+        "esbuild-freebsd-64": "0.15.12",
+        "esbuild-freebsd-arm64": "0.15.12",
+        "esbuild-linux-32": "0.15.12",
+        "esbuild-linux-64": "0.15.12",
+        "esbuild-linux-arm": "0.15.12",
+        "esbuild-linux-arm64": "0.15.12",
+        "esbuild-linux-mips64le": "0.15.12",
+        "esbuild-linux-ppc64le": "0.15.12",
+        "esbuild-linux-riscv64": "0.15.12",
+        "esbuild-linux-s390x": "0.15.12",
+        "esbuild-netbsd-64": "0.15.12",
+        "esbuild-openbsd-64": "0.15.12",
+        "esbuild-sunos-64": "0.15.12",
+        "esbuild-windows-32": "0.15.12",
+        "esbuild-windows-64": "0.15.12",
+        "esbuild-windows-arm64": "0.15.12"
+      }
+    },
+    "esbuild-android-64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.12.tgz",
+      "integrity": "sha512-MJKXwvPY9g0rGps0+U65HlTsM1wUs9lbjt5CU19RESqycGFDRijMDQsh68MtbzkqWSRdEtiKS1mtPzKneaAI0Q==",
+      "optional": true
+    },
+    "esbuild-android-arm64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.12.tgz",
+      "integrity": "sha512-Hc9SEcZbIMhhLcvhr1DH+lrrec9SFTiRzfJ7EGSBZiiw994gfkVV6vG0sLWqQQ6DD7V4+OggB+Hn0IRUdDUqvA==",
+      "optional": true
+    },
+    "esbuild-darwin-64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.12.tgz",
+      "integrity": "sha512-qkmqrTVYPFiePt5qFjP8w/S+GIUMbt6k8qmiPraECUWfPptaPJUGkCKrWEfYFRWB7bY23FV95rhvPyh/KARP8Q==",
+      "optional": true
+    },
+    "esbuild-darwin-arm64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.12.tgz",
+      "integrity": "sha512-z4zPX02tQ41kcXMyN3c/GfZpIjKoI/BzHrdKUwhC/Ki5BAhWv59A9M8H+iqaRbwpzYrYidTybBwiZAIWCLJAkw==",
+      "optional": true
+    },
+    "esbuild-freebsd-64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.12.tgz",
+      "integrity": "sha512-XFL7gKMCKXLDiAiBjhLG0XECliXaRLTZh6hsyzqUqPUf/PY4C6EJDTKIeqqPKXaVJ8+fzNek88285krSz1QECw==",
+      "optional": true
+    },
+    "esbuild-freebsd-arm64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.12.tgz",
+      "integrity": "sha512-jwEIu5UCUk6TjiG1X+KQnCGISI+ILnXzIzt9yDVrhjug2fkYzlLbl0K43q96Q3KB66v6N1UFF0r5Ks4Xo7i72g==",
+      "optional": true
+    },
+    "esbuild-linux-32": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.12.tgz",
+      "integrity": "sha512-uSQuSEyF1kVzGzuIr4XM+v7TPKxHjBnLcwv2yPyCz8riV8VUCnO/C4BF3w5dHiVpCd5Z1cebBtZJNlC4anWpwA==",
+      "optional": true
+    },
+    "esbuild-linux-64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.12.tgz",
+      "integrity": "sha512-QcgCKb7zfJxqT9o5z9ZUeGH1k8N6iX1Y7VNsEi5F9+HzN1OIx7ESxtQXDN9jbeUSPiRH1n9cw6gFT3H4qbdvcA==",
+      "optional": true
+    },
+    "esbuild-linux-arm": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.12.tgz",
+      "integrity": "sha512-Wf7T0aNylGcLu7hBnzMvsTfEXdEdJY/hY3u36Vla21aY66xR0MS5I1Hw8nVquXjTN0A6fk/vnr32tkC/C2lb0A==",
+      "optional": true
+    },
+    "esbuild-linux-arm64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.12.tgz",
+      "integrity": "sha512-HtNq5xm8fUpZKwWKS2/YGwSfTF+339L4aIA8yphNKYJckd5hVdhfdl6GM2P3HwLSCORS++++7++//ApEwXEuAQ==",
+      "optional": true
+    },
+    "esbuild-linux-mips64le": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.12.tgz",
+      "integrity": "sha512-Qol3+AvivngUZkTVFgLpb0H6DT+N5/zM3V1YgTkryPYFeUvuT5JFNDR3ZiS6LxhyF8EE+fiNtzwlPqMDqVcc6A==",
+      "optional": true
+    },
+    "esbuild-linux-ppc64le": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.12.tgz",
+      "integrity": "sha512-4D8qUCo+CFKaR0cGXtGyVsOI7w7k93Qxb3KFXWr75An0DHamYzq8lt7TNZKoOq/Gh8c40/aKaxvcZnTgQ0TJNg==",
+      "optional": true
+    },
+    "esbuild-linux-riscv64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.12.tgz",
+      "integrity": "sha512-G9w6NcuuCI6TUUxe6ka0enjZHDnSVK8bO+1qDhMOCtl7Tr78CcZilJj8SGLN00zO5iIlwNRZKHjdMpfFgNn1VA==",
+      "optional": true
+    },
+    "esbuild-linux-s390x": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.12.tgz",
+      "integrity": "sha512-Lt6BDnuXbXeqSlVuuUM5z18GkJAZf3ERskGZbAWjrQoi9xbEIsj/hEzVnSAFLtkfLuy2DE4RwTcX02tZFunXww==",
+      "optional": true
+    },
+    "esbuild-netbsd-64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.12.tgz",
+      "integrity": "sha512-jlUxCiHO1dsqoURZDQts+HK100o0hXfi4t54MNRMCAqKGAV33JCVvMplLAa2FwviSojT/5ZG5HUfG3gstwAG8w==",
+      "optional": true
+    },
+    "esbuild-openbsd-64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.12.tgz",
+      "integrity": "sha512-1o1uAfRTMIWNOmpf8v7iudND0L6zRBYSH45sofCZywrcf7NcZA+c7aFsS1YryU+yN7aRppTqdUK1PgbZVaB1Dw==",
+      "optional": true
+    },
+    "esbuild-sunos-64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.12.tgz",
+      "integrity": "sha512-nkl251DpoWoBO9Eq9aFdoIt2yYmp4I3kvQjba3jFKlMXuqQ9A4q+JaqdkCouG3DHgAGnzshzaGu6xofGcXyPXg==",
+      "optional": true
+    },
+    "esbuild-windows-32": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.12.tgz",
+      "integrity": "sha512-WlGeBZHgPC00O08luIp5B2SP4cNCp/PcS+3Pcg31kdcJPopHxLkdCXtadLU9J82LCfw4TVls21A6lilQ9mzHrw==",
+      "optional": true
+    },
+    "esbuild-windows-64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.12.tgz",
+      "integrity": "sha512-VActO3WnWZSN//xjSfbiGOSyC+wkZtI8I4KlgrTo5oHJM6z3MZZBCuFaZHd8hzf/W9KPhF0lY8OqlmWC9HO5AA==",
+      "optional": true
+    },
+    "esbuild-windows-arm64": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.12.tgz",
+      "integrity": "sha512-Of3MIacva1OK/m4zCNIvBfz8VVROBmQT+gRX6pFTLPngFYcj6TFH/12VveAqq1k9VB2l28EoVMNMUCcmsfwyuA==",
+      "optional": true
+    },
     "escalade": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
@@ -14625,11 +15149,6 @@
         }
       }
     },
-    "micromodal": {
-      "version": "0.4.6",
-      "resolved": "https://registry.npmjs.org/micromodal/-/micromodal-0.4.6.tgz",
-      "integrity": "sha512-2VDso2a22jWPpqwuWT/4RomVpoU3Bl9qF9D01xzwlNp5UVsImeA0gY4nSpF44vqcQtQOtkiMUV9EZkAJSRxBsg=="
-    },
     "mime-db": {
       "version": "1.45.0",
       "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz",
@@ -15451,6 +15970,12 @@
       "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
       "dev": true
     },
+    "prettier": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.0.tgz",
+      "integrity": "sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==",
+      "dev": true
+    },
     "pretty-format": {
       "version": "26.6.2",
       "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz",
diff --git a/ui/package.json b/ui/package.json
index c96d69b..dbe9bc7 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -19,6 +19,7 @@
     "color-convert": "^2.0.1",
     "custom_utils": "file:src/base/utils",
     "devtools-protocol": "0.0.847576",
+    "esbuild": "^0.15.12",
     "events": "^3.1.0",
     "hsluv": "^0.1.0",
     "immer": "^9.0.2",
@@ -46,6 +47,7 @@
     "node-watch": "^0.7.1",
     "pixelmatch": "^5.2.1",
     "pngjs": "^6.0.0",
+    "prettier": "^2.8.0",
     "puppeteer": "^14.1.0",
     "rollup": "^2.38.5",
     "rollup-plugin-re": "^1.0.7",
diff --git a/ui/release/channels.json b/ui/release/channels.json
index 65b82e2..6d4559f 100644
--- a/ui/release/channels.json
+++ b/ui/release/channels.json
@@ -2,11 +2,11 @@
   "channels": [
     {
       "name": "stable",
-      "rev": "75b49f42ece55b72377d414fa2d82c12c55cb60b"
+      "rev": "cf5c9753c2729027e283f52e5291e457f814f704"
     },
     {
       "name": "canary",
-      "rev": "5760d032c2c24f15e4eb4ac7b26448b481a73dfa"
+      "rev": "14a08703e3e391b713cf627d24a1ab9b30be99c2"
     },
     {
       "name": "autopush",
diff --git a/ui/src/assets/analyze_page.scss b/ui/src/assets/analyze_page.scss
index bbfed10..4a700a3 100644
--- a/ui/src/assets/analyze_page.scss
+++ b/ui/src/assets/analyze_page.scss
@@ -26,7 +26,7 @@
     font-size: inherit;
     font-family: var(--monospace-font);
     line-height: 1.2em;
-    padding: .5em;
+    padding: 0.5em;
     overflow: auto;
     resize: vertical;
   }
diff --git a/ui/src/assets/common.scss b/ui/src/assets/common.scss
index b5b9580..5470643 100644
--- a/ui/src/assets/common.scss
+++ b/ui/src/assets/common.scss
@@ -11,52 +11,46 @@
 // 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 "fonts";
+
 :root {
-    --sidebar-width: 256px;
-    --topbar-height: 48px;
-    --monospace-font: 'Roboto Mono', monospace;
-    --track-shell-width: 250px;
-    --track-border-color: #00000025;
-    --anim-easing: cubic-bezier(0.4, 0.0, 0.2, 1);
-    --selection-stroke-color: #00344596;
-    --selection-fill-color: #8398e64d;
-    --overview-timeline-non-visible-color: #c8c8c8cc;
-    --details-content-height: 280px;
+  --sidebar-width: 256px;
+  --topbar-height: 48px;
+  --monospace-font: "Roboto Mono", monospace;
+  --track-shell-width: 250px;
+  --track-border-color: #00000025;
+  --anim-easing: cubic-bezier(0.4, 0, 0.2, 1);
+  --selection-stroke-color: #00344596;
+  --selection-fill-color: #8398e64d;
+  --overview-timeline-non-visible-color: #c8c8c8cc;
+  --details-content-height: 280px;
 }
 
-@mixin transition($time:0.1s) {
-    transition: opacity $time ease,
-                color $time ease,
-                background-color $time ease,
-                border-color $time ease,
-                width $time ease,
-                height $time ease,
-                max-width $time ease,
-                max-height $time ease,
-                margin $time ease,
-                transform $time ease,
-                box-shadow $time ease,
-                border-radius $time ease;
+@mixin transition($time: 0.1s) {
+  transition: opacity $time ease, color $time ease, background-color $time ease,
+    border-color $time ease, width $time ease, height $time ease,
+    max-width $time ease, max-height $time ease, margin $time ease,
+    transform $time ease, box-shadow $time ease, border-radius $time ease;
 }
 
 @mixin material-icon($content) {
-    direction: ltr;
-    display: inline-block;
-    font-family: 'Material Icons';
-    font-size: 24px;
-    font-style: normal;
-    font-weight: normal;
-    letter-spacing: normal;
-    line-height: 1;
-    text-transform: none;
-    white-space: nowrap;
-    word-wrap: normal;
-    -webkit-font-feature-settings: 'liga';
-    -webkit-font-smoothing: antialiased;
-    content: $content;
+  direction: ltr;
+  display: inline-block;
+  font-family: "Material Icons";
+  font-size: 24px;
+  font-style: normal;
+  font-weight: normal;
+  letter-spacing: normal;
+  line-height: 1;
+  text-transform: none;
+  white-space: nowrap;
+  word-wrap: normal;
+  -webkit-font-feature-settings: "liga";
+  -webkit-font-smoothing: antialiased;
+  content: $content;
 }
 
-
 @mixin track_shell_title() {
   // line-height is deliberately 1px larger than font-size. Roboto seems to
   // overflow on the bottom on "g"s otherwise.
@@ -66,34 +60,35 @@
   overflow: hidden;
   text-align: left;
   overflow-wrap: break-word;
-  font-family: 'Roboto Condensed', sans-serif;
+  font-family: "Roboto Condensed", sans-serif;
   font-weight: 300;
   letter-spacing: -0.25px;
 }
 
 * {
-    box-sizing: border-box;
-    -webkit-tap-highlight-color: transparent;
-    touch-action: none;
+  box-sizing: border-box;
+  -webkit-tap-highlight-color: transparent;
+  touch-action: none;
 }
 
 html {
-    font-family: Roboto, verdana, sans-serif;
-    height: 100%;
-    width: 100%;
+  font-family: Roboto, verdana, sans-serif;
+  height: 100%;
+  width: 100%;
 }
 
 html,
 body,
-body>main {
-    height: 100%;
-    width: 100%;
-    padding: 0;
-    margin: 0;
-    overscroll-behavior: none;
+body > main {
+  height: 100%;
+  width: 100%;
+  padding: 0;
+  margin: 0;
+  overscroll-behavior: none;
 }
 
-pre, code {
+pre,
+code {
   font-family: var(--monospace-font);
 }
 
@@ -107,30 +102,30 @@
 h1,
 h2,
 h3 {
-    font-family: inherit;
-    font-size: inherit;
-    font-weight: inherit;
-    padding: 0;
-    margin: 0;
+  font-family: inherit;
+  font-size: inherit;
+  font-weight: inherit;
+  padding: 0;
+  margin: 0;
 }
 table {
-    user-select: text;
+  user-select: text;
 }
 
-body>main {
-    display: grid;
-    grid-template-areas:
-      "sidebar topbar"
-      "sidebar alerts"
-      "sidebar page";
-    grid-template-rows: auto auto 1fr;
-    grid-template-columns: auto 1fr;
-    color: #121212;
-    overflow: hidden;
+body > main {
+  display: grid;
+  grid-template-areas:
+    "sidebar topbar"
+    "sidebar alerts"
+    "sidebar page";
+  grid-template-rows: auto auto 1fr;
+  grid-template-columns: auto 1fr;
+  color: #121212;
+  overflow: hidden;
 }
 
 body.filedrag::after {
-  content: 'Drop the trace file to open it';
+  content: "Drop the trace file to open it";
   position: fixed;
   z-index: 99;
   top: 0;
@@ -160,22 +155,22 @@
 }
 
 .full-page-loading-screen {
-    position: absolute;
-    width: 100%;
-    height: 100%;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    flex-direction: row;
-    background: #3e4a5a url('assets/logo-3d.png') no-repeat fixed center;
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  flex-direction: row;
+  background: #3e4a5a url("assets/logo-3d.png") no-repeat fixed center;
 }
 
 .page {
-    grid-area: page;
-    position: relative;
-    display: flex;
-    flex-direction: column;
-    overflow: hidden;
+  grid-area: page;
+  position: relative;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
 }
 
 .split-panel {
@@ -189,8 +184,8 @@
 .alerts {
   grid-area: alerts;
   background-color: #f2f2f2;
-  >div {
-    font-family: 'Raleway', sans-serif;
+  > div {
+    font-family: "Raleway", sans-serif;
     font-weight: 400;
     letter-spacing: 0.25px;
     padding: 1rem;
@@ -200,7 +195,6 @@
       width: 24px;
       height: 24px;
     }
-
   }
 }
 
@@ -208,74 +202,103 @@
   width: 100%;
 }
 
+@mixin table-font-size {
+  font-size: 14px;
+  line-height: 18px;
+}
+
+$table-hover-color: hsl(214, 22%, 90%);
+
+$table-border-color: rgba(60, 76, 92, 0.4);
+
+.pivot-table {
+  @include bottom-panel-font;
+  @include table-font-size;
+
+  width: 100%;
+  border-collapse: collapse;
+
+  thead,
+  i {
+    cursor: pointer;
+  }
+  thead {
+    font-weight: normal;
+  }
+  td {
+    padding: 2px 1px;
+  }
+  td.first {
+    border-left: 1px solid $table-border-color;
+    padding-left: 6px;
+  }
+  tr.header {
+    border-bottom: 1px solid $table-border-color;
+    text-align: center;
+  }
+  thead td.reorderable-cell {
+    cursor: grab;
+  }
+  tr:hover td {
+    background-color: $table-hover-color;
+  }
+  .disabled {
+    cursor: default;
+  }
+  .indent {
+    display: inline-block;
+    // 16px is the width of expand_more/expand_less icon to pad out cells
+    // without the button
+    width: 16px;
+  }
+  strong {
+    font-weight: 400;
+  }
+}
+
 .query-table {
-    width: 100%;
-    font-size: 14px;
-    border: 0;
-    &.pivot-table{
-      thead, i {
-        cursor: pointer;
-        td.drop-location {
-          background-color: hsla(210, 38%, 95%, 1);
-        }
-        .total-aggregation {
-          white-space: pre;
-          font-weight: 600;
-          font-size: 12px
-        }
-      }
-      td {
-        height: 25px;
-      }
-      .disabled {
-        cursor: default;
-      }
-      .indent {
-        display: inline-block;
-        // 24px is the width of expand_more/expand_less icon to pad out cells
-        // without the button
-        width: 24px;
-      }
+  width: 100%;
+  font-size: 14px;
+  border: 0;
+  thead td {
+    position: sticky;
+    top: 0;
+    background-color: hsl(214, 22%, 90%);
+    color: #262f3c;
+    text-align: center;
+    padding: 1px 3px;
+    border-style: solid;
+    border-color: #fff;
+    border-right-width: 1px;
+    border-left-width: 1px;
+  }
+  tbody tr {
+    @include transition();
+    background-color: hsl(214, 22%, 100%);
+    font-family: var(--monospace-font);
+    &:nth-child(even) {
+      background-color: hsl(214, 22%, 95%);
     }
-    thead td {
-        position: sticky;
-        top: 0;
-        background-color: hsl(214, 22%, 90%);
-        color: #262f3c;
-        text-align: center;
-        padding: 1px 3px;
-        border-style: solid;
-        border-color: #fff;
-        border-right-width: 1px;
-        border-left-width: 1px;
+    td:first-child {
+      padding-left: 5px;
     }
-    tbody tr {
-        @include transition();
-        background-color: hsl(214, 22%, 100%);
-        font-family: var(--monospace-font);
-        &:nth-child(even) {
-            background-color: hsl(214, 22%, 95%);
-        }
-        td:first-child {
-            padding-left: 5px;
-        }
-        td:last-child {
-            padding-right: 5px;
-        }
-        &:hover {
-            background-color: hsl(214, 22%, 90%);
-        }
-        &[clickable] {
-          cursor: pointer;
-        }
+    td:last-child {
+      padding-right: 5px;
     }
+    &:hover {
+      background-color: hsl(214, 22%, 90%);
+    }
+    &[clickable] {
+      cursor: pointer;
+    }
+  }
 }
 
 .query-error {
-    padding: 20px 10px;
-    color: hsl(-10, 50%, 50%);
-    font-family: 'Roboto Condensed', sans-serif;
-    font-weight: 300;
+  padding: 20px 10px;
+  color: hsl(-10, 50%, 50%);
+  font-family: "Roboto Condensed", sans-serif;
+  font-weight: 300;
 }
 
 .dropdown {
@@ -311,6 +334,8 @@
     width: 100%;
     text-align: right;
     line-height: 1;
+    display: block; // Required in order for inherited white-space property not
+    // to screw up vertical rendering of the popup menu items.
 
     &:hover {
       background: #c7d0db;
@@ -323,98 +348,98 @@
 }
 
 .track {
+  display: grid;
+  grid-template-columns: auto 1fr;
+  grid-template-rows: 1fr 0;
+
+  &::after {
+    display: block;
+    content: "";
+    grid-column: 1 / span 2;
+    border-top: 1px solid var(--track-border-color);
+    margin-top: -1px;
+    z-index: 2;
+  }
+
+  .track-shell {
+    @include transition();
+    padding-left: 10px;
     display: grid;
-    grid-template-columns: auto 1fr;
-    grid-template-rows: 1fr 0;
+    cursor: grab;
+    grid-template-areas: "title buttons";
+    grid-template-columns: 1fr auto;
+    align-items: center;
+    width: var(--track-shell-width);
+    background: #fff;
+    border-right: 1px solid #c7d0db;
 
-    &::after {
-      display: block;
-      content: '';
-      grid-column: 1 / span 2;
-      border-top: 1px solid var(--track-border-color);
-      margin-top: -1px;
-      z-index: 2;
+    &.drag {
+      background-color: #eee;
+      box-shadow: 0 4px 12px -4px #999 inset;
+    }
+    &.drop-before {
+      box-shadow: 0 4px 2px -1px hsl(213, 40%, 50%) inset;
+    }
+    &.drop-after {
+      box-shadow: 0 -4px 2px -1px hsl(213, 40%, 50%) inset;
     }
 
-    .track-shell {
-        @include transition();
-        padding-left: 10px;
-        display: grid;
-        cursor: grab;
-        grid-template-areas: "title buttons";
-        grid-template-columns: 1fr auto;
-        align-items: center;
-        width: var(--track-shell-width);
-        background: #fff;
-        border-right: 1px solid #c7d0db;
-
-        &.drag {
-          background-color: #eee;
-          box-shadow: 0 4px 12px -4px #999 inset;
-        }
-        &.drop-before {
-          box-shadow: 0 4px 2px -1px hsl(213, 40%, 50%) inset;
-        }
-        &.drop-after {
-          box-shadow: 0 -4px 2px -1px hsl(213, 40%, 50%) inset;
-        }
-
-        &.selected {
-          background-color: #ebeef9;
-        }
-
-        &.alternating-thread-track {
-          background: hsl(214, 22%, 95%);
-        }
-
-        .chip {
-          background-color: #bed6ff;
-          border-radius: 3px;
-          font-size: smaller;
-          padding: 0 0.1rem;
-          margin-left: 1ch;
-        }
-
-        h1 {
-            grid-area: title;
-            color: hsl(213, 22%, 30%);
-            @include track_shell_title();
-        }
-        .track-buttons {
-          grid-area: buttons;
-          display: flex;
-          height: 100%;
-          align-items: center;
-        }
-        .track-button {
-          @include transition();
-          color: rgb(60, 86, 136);
-          cursor: pointer;
-          width: 22px;
-          font-size: 18px;
-          opacity: 0;
-        }
-
-        .track-button.show {
-          opacity: 1;
-        }
-        .track-button.full-height {
-          display: flex;
-          height: 100%;
-          align-items: center;
-
-          &:hover {
-            background-color: #ebeef9;
-          }
-        }
-
-        &:hover .track-button{
-          opacity: 1;
-        }
-        &.flash {
-          background-color: #ffe263;
-        }
+    &.selected {
+      background-color: #ebeef9;
     }
+
+    &.alternating-thread-track {
+      background: hsl(214, 22%, 95%);
+    }
+
+    .chip {
+      background-color: #bed6ff;
+      border-radius: 3px;
+      font-size: smaller;
+      padding: 0 0.1rem;
+      margin-left: 1ch;
+    }
+
+    h1 {
+      grid-area: title;
+      color: hsl(213, 22%, 30%);
+      @include track_shell_title();
+    }
+    .track-buttons {
+      grid-area: buttons;
+      display: flex;
+      height: 100%;
+      align-items: center;
+    }
+    .track-button {
+      @include transition();
+      color: rgb(60, 86, 136);
+      cursor: pointer;
+      width: 22px;
+      font-size: 18px;
+      opacity: 0;
+    }
+
+    .track-button.show {
+      opacity: 1;
+    }
+    .track-button.full-height {
+      display: flex;
+      height: 100%;
+      align-items: center;
+
+      &:hover {
+        background-color: #ebeef9;
+      }
+    }
+
+    &:hover .track-button {
+      opacity: 1;
+    }
+    &.flash {
+      background-color: #ffe263;
+    }
+  }
 }
 
 .scrolling-panel-container {
@@ -422,7 +447,7 @@
   overflow-x: hidden;
   overflow-y: auto;
   flex: 1 1 auto;
-  will-change: transform;  // Force layer creation.
+  will-change: transform; // Force layer creation.
   display: grid;
   grid-template-columns: 1fr;
   grid-template-rows: 1fr;
@@ -476,7 +501,7 @@
 }
 
 .panel {
-  position: relative;  // Otherwise canvas covers panel dom.
+  position: relative; // Otherwise canvas covers panel dom.
 
   &.sticky {
     position: sticky;
@@ -518,7 +543,7 @@
   align-content: center;
   background-color: hsl(213, 22%, 82%);
   color: hsl(213, 22%, 20%);
-  font-family: 'Roboto Condensed', sans-serif;
+  font-family: "Roboto Condensed", sans-serif;
   font-size: 15px;
   font-weight: 400;
   padding: 4px 10px;
@@ -527,16 +552,28 @@
   border-style: solid;
   border-width: 1px 0;
   .code {
-      font-family: var(--monospace-font);
-      font-size: 12px;
-      margin-left: 10px;
-      color: hsl(213, 22%, 40%);
+    font-family: var(--monospace-font);
+    font-size: 12px;
+    margin-left: 10px;
+    color: hsl(213, 22%, 40%);
+  }
+  span {
+    white-space: nowrap;
   }
   span.code {
+    text-overflow: ellipsis;
+    max-width: 50vw;
+    overflow: hidden;
+  }
+  span.spacer {
     flex-grow: 1;
   }
 }
 
+.text-select {
+  user-select: text;
+}
+
 button.query-ctrl {
   background: #262f3c;
   color: white;
@@ -567,7 +604,7 @@
   font-family: var(--monospace-font);
   padding: 10px 24px;
   z-index: 100;
-  background-color: rgba(27, 28, 29, 0.90);
+  background-color: rgba(27, 28, 29, 0.9);
   button {
     text-decoration: underline;
     color: hsl(45, 100%, 48%);
@@ -581,7 +618,7 @@
     top: 10px;
     width: 20px;
     height: 20px;
-    color: var(--stroke-color)
+    color: var(--stroke-color);
   }
   & > section {
     padding: 5px;
@@ -590,7 +627,9 @@
   div {
     margin: 2px 0;
   }
-  table, td, th {
+  table,
+  td,
+  th {
     border: 1px solid var(--stroke-color);
     text-align: center;
     padding: 4px;
@@ -609,16 +648,16 @@
   display: grid;
   grid-template-columns: auto 1fr;
   grid-template-rows: 1fr;
-  transition: background-color .4s, color .4s;
+  transition: background-color 0.4s, color 0.4s;
   height: 40px;
   &::after {
     display: block;
-    content: '';
+    content: "";
     grid-column: 1 / span 2;
     border-top: 1px solid var(--track-border-color);
     margin-top: -1px;
   }
-  &[collapsed=true] {
+  &[collapsed="true"] {
     background-color: var(--collapsed-transparent);
     .shell {
       border-right: 1px solid #c7d0db;
@@ -626,9 +665,9 @@
     }
     .track-button {
       color: rgb(60, 86, 136);
-    };
+    }
   }
-  &[collapsed=false] {
+  &[collapsed="false"] {
     background-color: var(--expanded-transparent);
     color: white;
     font-weight: bold;
@@ -651,7 +690,7 @@
     line-height: 1;
     width: var(--track-shell-width);
     min-height: 40px;
-    transition: background-color .4s;
+    transition: background-color 0.4s;
 
     .track-title {
       user-select: text;
@@ -714,16 +753,15 @@
   height: 10px;
 }
 
-
 .cookie-consent {
   position: absolute;
   z-index: 10;
   left: 10px;
   bottom: 10px;
   width: 550px;
-  background-color:#19212b;
+  background-color: #19212b;
   font-size: 14px;
-  color:rgb(180, 183, 186);
+  color: rgb(180, 183, 186);
   border-radius: 5px;
   padding: 20px;
 
@@ -731,7 +769,7 @@
     display: flex;
     justify-content: flex-end;
     margin-top: 10px;
-    font-size: 15px
+    font-size: 15px;
   }
 
   button {
@@ -756,6 +794,7 @@
 
 .pivot-table-redux {
   user-select: text;
+  padding: 10px;
 
   button.mode-button {
     border-radius: 10px;
@@ -764,11 +803,6 @@
     background-color: #c7d0db;
   }
 
-  &.edit {
-    padding: 10px;
-    display: flex;
-  }
-
   &.query-error {
     color: red;
   }
@@ -785,6 +819,17 @@
   td.menu {
     text-align: left;
   }
+
+  td {
+    // In context menu icon to go on a separate line.
+    // In regular pivot table cells, avoids wrapping the icon spacer to go on
+    // a separate line.
+    white-space: pre;
+
+    i.material-icons {
+      font-size: 16px;
+    }
+  }
 }
 
 .name-completion {
@@ -807,10 +852,50 @@
   }
 
   &.highlight-left {
-    border-left-color: red;
+    background: linear-gradient(90deg, $table-border-color, transparent 20%);
   }
 
   &.highlight-right {
-    border-right-color: red;
+    background: linear-gradient(270deg, $table-border-color, transparent 20%);
+  }
+}
+
+.history-item {
+  border-bottom: 1px solid hsl(213, 22%, 75%);
+  padding: 0.25em 0.5em;
+  max-height: 150px;
+  overflow-y: auto;
+
+  pre {
+    font-size: 10pt;
+    margin: 0;
+    overflow-x: auto;
+  }
+
+  &:hover .history-item-buttons {
+    visibility: visible;
+  }
+}
+
+.history-item-buttons {
+  position: sticky;
+  float: right;
+  top: 0;
+  visibility: hidden;
+
+  button {
+    margin: 0 0.5rem;
+
+    i.material-icons {
+      font-size: 18px;
+    }
+  }
+}
+
+.context-wrapper {
+  white-space: nowrap;
+
+  i.material-icons {
+    margin-left: 0;
   }
 }
diff --git a/ui/src/assets/details.scss b/ui/src/assets/details.scss
index a4f50d9..3faaadd 100644
--- a/ui/src/assets/details.scss
+++ b/ui/src/assets/details.scss
@@ -18,7 +18,7 @@
 
   .handle {
     background-color: hsl(215, 1%, 95%);
-    border: 1px solid rgba(0,0,0,0.1);
+    border: 1px solid rgba(0, 0, 0, 0.1);
     border-bottom: none;
     cursor: row-resize;
     // Disable user selection since this handle is draggable to resize the
@@ -36,7 +36,7 @@
       overflow: hidden;
 
       .tab {
-        font-family: 'Roboto Condensed', sans-serif;
+        font-family: "Roboto Condensed", sans-serif;
         color: #3c4b5d;
         padding: 3px 10px 0 10px;
         margin-top: 3px;
@@ -69,12 +69,12 @@
       font-size: 24px;
       margin-right: 5px;
       margin-top: 1px;
-      &:hover{
+      &:hover {
         cursor: pointer;
       }
       &[disabled] {
         color: rgb(219, 219, 219);
-        &:hover{
+        &:hover {
           cursor: default;
         }
       }
@@ -86,7 +86,7 @@
     }
 
     .handle-title {
-      font-family: 'Roboto Condensed', sans-serif;
+      font-family: "Roboto Condensed", sans-serif;
       font-weight: 300;
       color: #3c4b5d;
       margin-left: 5px;
@@ -94,13 +94,10 @@
       font-size: 13px;
     }
   }
-
 }
 
 .details-panel {
-  font-family: 'Roboto Condensed', sans-serif;
-  font-weight: 300;
-  color: #3c4b5d;
+  @include bottom-panel-font;
 
   .material-icons {
     @include transition(0.3s);
@@ -128,8 +125,9 @@
     &.aggregation {
       padding-top: 5px;
       display: grid;
-      grid-template-areas: "description range"
-                            "heading heading";
+      grid-template-areas:
+        "description range"
+        "heading heading";
       grid-template-columns: 1fr auto;
       .states {
         font-size: 11px;
@@ -146,7 +144,6 @@
             min-width: fit-content;
           }
         }
-
       }
       .time-range {
         text-align: right;
@@ -235,8 +232,7 @@
 
   table {
     @include transition(0.1s);
-    font-size: 14px;
-    line-height: 18px;
+    @include table-font-size;
     width: 100%;
     // Aggregation panel uses multiple table elements that need to be aligned,
     // which is done by using fixed table layout.
@@ -244,8 +240,9 @@
     word-wrap: break-word;
     padding: 0 10px;
     tr:hover {
-      td, th {
-        background-color: hsl(214, 22%, 90%);
+      td,
+      th {
+        background-color: $table-hover-color;
 
         &.no-highlight {
           background-color: white;
@@ -279,9 +276,9 @@
     font-size: 0.875rem;
     padding-left: 1rem;
     padding-right: 1rem;
-    padding-top: .5rem;
-    padding-bottom: .5rem;
-    border-radius: .25rem;
+    padding-top: 0.5rem;
+    padding-bottom: 0.5rem;
+    border-radius: 0.25rem;
     margin-top: 12px;
     margin-left: 10px;
   }
@@ -348,7 +345,7 @@
   display: flex;
   flex-direction: column;
   height: 100%;
-  font-family: 'Roboto Condensed', sans-serif;
+  font-family: "Roboto Condensed", sans-serif;
   font-weight: 300;
   color: #3c4b5d;
 
@@ -365,20 +362,20 @@
   }
 
   button {
-   background: #262f3c;
-   color: white;
-   border-radius: 10px;
-   font-size: 10px;
-   height: 22px;
-   line-height: 18px;
-   min-width: 7em;
-   margin: auto 0 auto 1rem;
+    background: #262f3c;
+    color: white;
+    border-radius: 10px;
+    font-size: 10px;
+    height: 22px;
+    line-height: 18px;
+    min-width: 7em;
+    margin: auto 0 auto 1rem;
   }
 
-  input[type=text] {
+  input[type="text"] {
     flex-grow: 1;
     border-radius: 4px;
-    border:1px solid #dcdcdc;
+    border: 1px solid #dcdcdc;
     padding: 3px;
     margin: 0 10px;
     &:focus {
@@ -392,7 +389,7 @@
   font-weight: bolder;
   font-size: 12px;
   .sum-data {
-    border-bottom: 1px solid rgba(60, 76, 92, 0.4);
+    border-bottom: 1px solid $table-border-color;
   }
 }
 
@@ -401,7 +398,7 @@
   height: 100%;
   display: grid;
   grid-template-rows: auto 1fr;
-  font-family: 'Roboto Condensed', sans-serif;
+  font-family: "Roboto Condensed", sans-serif;
   user-select: text;
 
   header {
@@ -411,71 +408,204 @@
     background-color: white;
     color: #3c4b5d;
     padding: 5px;
+    display: grid;
+    grid-template-columns: auto auto;
+    justify-content: space-between;
+  }
+
+  .log-rows-label {
+    display: flex;
+    align-items: center;
+  }
+
+  .log-filters {
+    display: flex;
+    margin-right: 5px;
+    align-items: center;
+
+    .log-label {
+      padding-right: 0.35rem;
+    }
+
+    .tag-container {
+      height: auto;
+      min-height: 34px;
+      border: 2px solid #737679;
+      padding: 8px;
+      margin: 8px;
+      cursor: text;
+      border-radius: 3px;
+      display: flex;
+      align-items: center;
+
+      box-shadow: 0 2px 6px rgba(25, 25, 25, 0.2);
+
+      .chips .chip {
+        display: inline-block;
+        width: auto;
+        float: left;
+
+        background-color: #0878b2;
+        color: #fff;
+        border-radius: 3px;
+        margin: 2px;
+        overflow: hidden;
+
+        .chip-button {
+          padding: 4px;
+          cursor: pointer;
+          background-color: #054570;
+          display: inline-block;
+        }
+
+        .chip-text {
+          padding: 4px;
+          display: inline-block;
+          pointer-events: none;
+        }
+      }
+
+      .chip-input {
+        margin-left: 5px;
+      }
+    }
+
+    .filter-widget {
+      user-select: none;
+      cursor: pointer;
+      position: relative;
+      display: inline-block;
+    }
+
+    .filter-widget .tooltip {
+      visibility: hidden;
+      width: 120px;
+      background-color: black;
+      color: #fff;
+      text-align: center;
+      border-radius: 6px;
+      padding: 5px 0;
+
+      /* Position the tooltip */
+      position: absolute;
+      z-index: 1;
+      top: 130%;
+      right: 50%;
+    }
+
+    .filter-widget:hover .tooltip {
+      visibility: visible;
+    }
   }
 
   header.stale {
     color: grey;
   }
 
-    .rows {
-      position: relative;
-      direction: ltr;
+  .rows {
+    position: relative;
+    direction: ltr;
+    width: 100%;
+
+    .row {
+      @include transition();
+      position: absolute;
       width: 100%;
+      height: 20px;
+      line-height: 20px;
+      background-color: hsl(214, 22%, 100%);
 
-      .row {
-        @include transition();
-        position: absolute;
-        width: 100%;
-        height: 20px;
-        line-height: 20px;
-        background-color: hsl(214, 22%, 100%);
+      &.D {
+        color: hsl(122, 20%, 40%);
+      }
+      &.V {
+        color: hsl(122, 20%, 30%);
+      }
+      &.I {
+        color: hsl(0, 0%, 20%);
+      }
+      &.W {
+        color: hsl(45, 60%, 45%);
+      }
+      &.E {
+        color: hsl(4, 90%, 58%);
+      }
+      &.F {
+        color: hsl(291, 64%, 42%);
+      }
+      &.stale {
+        color: #aaa;
+      }
+      &:nth-child(even) {
+        background-color: hsl(214, 22%, 95%);
+      }
+      &:hover {
+        background-color: $table-hover-color;
+      }
+      .cell {
+        font-size: 11px;
+        font-family: var(--monospace-font);
+        white-space: nowrap;
+        overflow: scroll;
+        padding-left: 10px;
+        padding-right: 10px;
+        display: inline-block;
+        &:first-child {
+          padding-left: 5px;
+        }
+        &:last-child {
+          padding-right: 5px;
+        }
+        &:only-child {
+          width: 100%;
+        }
 
-        &.D { color: hsl(122, 20%, 40%); }
-        &.V { color: hsl(122, 20%, 30%); }
-        &.I { color: hsl(0, 0%, 20%); }
-        &.W { color: hsl(45, 60%, 45%); }
-        &.E { color: hsl(4, 90%, 58%); }
-        &.F { color: hsl(291, 64%, 42%); }
-        &.stale { color: #aaa; }
-        &:nth-child(even) {
-            background-color: hsl(214, 22%, 95%);
+        // The following children will be used as columns in the table showing
+        // Android logs.
+
+        // 1.Timestamp
+        &:nth-child(1) {
+          width: 7rem;
+          text-overflow: clip;
+          text-align: right;
         }
-        &:hover {
-          background-color: hsl(214, 22%, 90%);
+        // 2.Level
+        &:nth-child(2) {
+          width: 4rem;
         }
-        .cell {
-          font-size: 11px;
-          font-family: var(--monospace-font);
-          white-space: nowrap;
-          overflow: hidden;
-          text-overflow: ellipsis;
-          padding-left: 10px;
-          padding-right: 10px;
-          display: inline-block;
-          &:first-child {
-            padding-left: 5px;
-          }
-          &:last-child {
-            padding-right: 5px;
-          }
-          &:nth-child(1) {
-            width: 110px;
-            text-overflow: clip;
-            text-align: right;
-          }
-          &:nth-child(2) {
-            width: 20px;
-          }
-          &:nth-child(3) {
-            width: 15%;
-          }
+        // 3.Tag
+        &:nth-child(3) {
+          width: 13rem;
+        }
+
+        &.with-process {
+          // 4.Process name
           &:nth-child(4) {
-            width: calc(100% - 110px - 20px - 15%);
+            width: 18rem;
           }
-          &:only-child {
-            width: 100%;
+          // 5.Message - a long string, will take most of the display space.
+          &:nth-child(5) {
+            width: calc(100% - 42rem);
           }
         }
+
+        &.no-process {
+          // 4.Message - a long string, will take most of the display space.
+          &:nth-child(4) {
+            width: calc(100% - 24rem);
+          }
+        }
+
+        &.row-header {
+          text-align: left;
+          font-weight: bold;
+          font-size: 13px;
+        }
+
+        &.row-header:first-child {
+          padding-left: 15px;
+        }
       }
+    }
   }
 }
diff --git a/ui/src/assets/flags_page.scss b/ui/src/assets/flags_page.scss
index 3750dbd..a23c60f 100644
--- a/ui/src/assets/flags_page.scss
+++ b/ui/src/assets/flags_page.scss
@@ -33,7 +33,7 @@
     border: 1px solid rgb(218, 220, 224);
     border-radius: 3px;
     color: rgb(25, 103, 210);
-    font-size: .8125rem;
+    font-size: 0.8125rem;
     padding: 8px 12px;
     cursor: pointer;
     font-weight: 500;
@@ -54,9 +54,9 @@
     background: white;
     border: 1px solid rgb(25, 103, 210);
     color: rgb(25, 103, 210);
-    font-size: .8125rem;
+    font-size: 0.8125rem;
     height: 1.625rem;
-    letter-spacing: .01em;
+    letter-spacing: 0.01em;
     max-width: 150px;
     text-align-last: center;
     width: 100%;
@@ -70,4 +70,3 @@
     font-size: smaller;
   }
 }
-
diff --git a/ui/src/assets/fonts.scss b/ui/src/assets/fonts.scss
new file mode 100644
index 0000000..6f52d19
--- /dev/null
+++ b/ui/src/assets/fonts.scss
@@ -0,0 +1,20 @@
+// Copyright (C) 2023 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.
+
+@mixin bottom-panel-font {
+  font-family: "Roboto Condensed", sans-serif;
+  font-weight: 300;
+  color: #3c4b5d;
+}
+
diff --git a/ui/src/assets/hiring_banner.scss b/ui/src/assets/hiring_banner.scss
index 75a997e..a6807df 100644
--- a/ui/src/assets/hiring_banner.scss
+++ b/ui/src/assets/hiring_banner.scss
@@ -1,5 +1,5 @@
 .hiring-banner {
-  font-family: 'Raleway', sans-serif;
+  font-family: "Raleway", sans-serif;
   font-size: 12px;
   background: #db4634;
   box-shadow: 0 0 3px rgba(0, 0, 0, 30%);
diff --git a/ui/src/assets/home_page.scss b/ui/src/assets/home_page.scss
index f76137c..4d3f5a8 100644
--- a/ui/src/assets/home_page.scss
+++ b/ui/src/assets/home_page.scss
@@ -31,27 +31,29 @@
       font-size: 60px;
       margin: 25px;
       text-align: center;
-      font-family: 'Raleway', sans-serif;
+      font-family: "Raleway", sans-serif;
       font-weight: 100;
       color: #333;
     }
 
     .channel-select {
-      font-family: 'Roboto', sans-serif;
+      font-family: "Roboto", sans-serif;
       font-size: 1.2rem;
       font-weight: 200;
       margin-top: 3em;
       --chan-width: 100px;
       --chan-num: 2;
 
-      input[type=radio] {
+      input[type="radio"] {
         width: 0;
         height: 0;
         margin: 0;
         padding: 0;
         -moz-appearance: none;
         -webkit-appearance: none;
-        &:nth-of-type(1):checked ~ .highlight { margin-left: 0; }
+        &:nth-of-type(1):checked ~ .highlight {
+          margin-left: 0;
+        }
         &:nth-of-type(2):checked ~ .highlight {
           margin-left: 100px;
           background-color: hsl(54, 100%, 40%);
@@ -86,7 +88,7 @@
         z-index: 2;
         text-transform: uppercase;
         font-size: 16px;
-        font-family: 'Raleway';
+        font-family: "Raleway";
         font-weight: 400;
         letter-spacing: 0.3px;
       }
@@ -96,7 +98,10 @@
         height: 100%;
         position: absolute;
         background: hsla(122, 45%, 45%, 0.99);
-        background-image: linear-gradient(rgba(255, 255, 255, 0.2), transparent);
+        background-image: linear-gradient(
+          rgba(255, 255, 255, 0.2),
+          transparent
+        );
         top: 0;
         left: 0;
         z-index: 1;
@@ -110,17 +115,19 @@
         color: #da4534;
         font-weight: 400;
         @include transition();
-        &.show { opacity: 1; }
+        &.show {
+          opacity: 1;
+        }
       }
-    }  // .channel-select
-  }  // .home-page-center
+    } // .channel-select
+  } // .home-page-center
 
   .privacy {
     grid-area: footer;
     text-decoration: none;
-    font-family: 'Roboto', sans-serif;
+    font-family: "Roboto", sans-serif;
     font-weight: 200;
     color: #333;
     font-size: 15px;
   }
-}  // .home-page
+} // .home-page
diff --git a/ui/src/assets/metrics_page.scss b/ui/src/assets/metrics_page.scss
index e9ccf58..3ed881c 100644
--- a/ui/src/assets/metrics_page.scss
+++ b/ui/src/assets/metrics_page.scss
@@ -14,7 +14,7 @@
 
 .metrics-page {
   padding: 30px;
-  font-family: 'Raleway', sans-serif;
+  font-family: "Raleway", sans-serif;
   overflow-y: auto;
 
   .metric-run-button {
@@ -23,12 +23,12 @@
     border-radius: 4px;
     padding: 5px 10px;
     font-weight: bold;
-    font-family: 'Raleway';
+    font-family: "Raleway";
   }
 
   select {
     margin: 10px;
-    font-family: 'Raleway';
+    font-family: "Raleway";
     font-size: 1em;
     border: 1px solid black;
     background-color: #eee;
@@ -37,12 +37,12 @@
   pre {
     background-color: #eee;
     padding: 20px;
-    font-family: 'Roboto Mono';
+    font-family: "Roboto Mono";
     line-height: 1.5em;
     border-radius: 5px;
     overflow-x: auto;
     &.metric-error {
-      color: #EF6C00;
+      color: #ef6c00;
     }
   }
 }
diff --git a/ui/src/assets/modal.scss b/ui/src/assets/modal.scss
index eaea810..76d3cea 100644
--- a/ui/src/assets/modal.scss
+++ b/ui/src/assets/modal.scss
@@ -12,7 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-
 // The opacity changes are only transitional. Once the `modalFadeOut` animation
 // reaches the end, the Mithril component that renders .modal-backdrop
 // (and .modal-dialog) is fully destroyed and removed from the DOM.
@@ -20,13 +19,21 @@
 // hooking the onanimationend events to synchronize the Mithril removal with
 // the end of the CSS animation.
 @keyframes modalFadeOut {
-  from { opacity: 1; }
-  to { opacity: 0; }
+  from {
+    opacity: 1;
+  }
+  to {
+    opacity: 0;
+  }
 }
 
 @keyframes modalFadeIn {
-  from { opacity: 0; }
-  to { opacity: 1; }
+  from {
+    opacity: 0;
+  }
+  to {
+    opacity: 1;
+  }
 }
 
 .modal-backdrop {
@@ -71,7 +78,7 @@
     transform: translate(-50%, 0);
   }
 
-  >header {
+  > header {
     display: flex;
     justify-content: space-between;
     align-items: center;
@@ -79,7 +86,7 @@
     h2 {
       margin-top: 0;
       margin-bottom: 0;
-      font-family: 'Raleway', sans-serif;
+      font-family: "Raleway", sans-serif;
       font-weight: 600;
       font-size: 1.25rem;
       line-height: 1.25;
@@ -91,14 +98,14 @@
       background: transparent;
       border: 0;
     }
-  }  // header
+  } // header
 
   main {
     font-size: 1rem;
     margin-top: 2rem;
     margin-bottom: 2rem;
     line-height: 1.5;
-    color: rgba(0,0,0,.8);
+    color: rgba(0, 0, 0, 0.8);
 
     .small-font {
       font-size: 0.9rem;
@@ -108,16 +115,16 @@
   footer {
     display: flex;
     justify-content: space-around;
-  }  // footer
+  } // footer
 
   .modal-btn {
-    font-size: .875rem;
+    font-size: 0.875rem;
     padding-left: 1rem;
     padding-right: 1rem;
-    padding-top: .5rem;
-    padding-bottom: .5rem;
+    padding-top: 0.5rem;
+    padding-bottom: 0.5rem;
     background-color: #e6e6e6;
-    color: rgba(0,0,0,.8);
+    color: rgba(0, 0, 0, 0.8);
     border: 2px solid transparent;
     border-radius: 4px;
     cursor: pointer;
@@ -125,17 +132,23 @@
     overflow: visible;
     margin: 5px;
     transform: translateZ(0);
-    transition: border-color .25s var(--anim-easing),
-                background-color .25s var(--anim-easing);
+    transition: border-color 0.25s var(--anim-easing),
+      background-color 0.25s var(--anim-easing);
 
-    &:focus { border-color: #03A9F4;}
-    &:hover { background-color: #ececec; }
+    &:focus {
+      border-color: #03a9f4;
+    }
+    &:hover {
+      background-color: #ececec;
+    }
   }
 
   .modal-btn-primary {
     background-color: hsl(215deg, 22%, 19%);
     color: #fff;
-    &:hover { background-color: hsl(215deg, 22%, 35%) }
+    &:hover {
+      background-color: hsl(215deg, 22%, 35%);
+    }
   }
 }
 
@@ -160,7 +173,8 @@
   font-size: 13px;
 }
 
-.modal-logs, .modal-bash {
+.modal-logs,
+.modal-bash {
   white-space: pre-wrap;
   border: 1px solid #999;
   background: #eee;
diff --git a/ui/src/assets/perfetto.scss b/ui/src/assets/perfetto.scss
index 2b4dedb..c6d03b4 100644
--- a/ui/src/assets/perfetto.scss
+++ b/ui/src/assets/perfetto.scss
@@ -12,16 +12,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-@import 'typefaces';
-@import 'common';
-@import 'home_page';
-@import 'analyze_page';
-@import 'metrics_page';
-@import 'sidebar';
-@import 'topbar';
-@import 'record';
-@import 'modal';
-@import 'details';
-@import 'trace_info_page';
-@import 'flags_page';
-@import 'hiring_banner';
+@import "typefaces";
+@import "common";
+@import "home_page";
+@import "analyze_page";
+@import "metrics_page";
+@import "sidebar";
+@import "topbar";
+@import "record";
+@import "modal";
+@import "details";
+@import "trace_info_page";
+@import "flags_page";
+@import "hiring_banner";
diff --git a/ui/src/assets/rec_profiling.png b/ui/src/assets/rec_profiling.png
new file mode 100644
index 0000000..385670c
--- /dev/null
+++ b/ui/src/assets/rec_profiling.png
Binary files differ
diff --git a/ui/src/assets/record.scss b/ui/src/assets/record.scss
index 495fe9e..529e57e 100644
--- a/ui/src/assets/record.scss
+++ b/ui/src/assets/record.scss
@@ -35,8 +35,9 @@
   background-color: #fff;
   display: grid;
   grid-template-rows: auto 1fr;
-  grid-template-areas: "header"
-                       "content";
+  grid-template-areas:
+    "header"
+    "content";
   overflow: hidden;
   z-index: 6;
 
@@ -51,12 +52,149 @@
     width: 100%;
     height: 100%;
     text-align: center;
-    padding: 180px 0;
-    font-family: 'Raleway', sans-serif;
+    padding: 180px 30px;
+    font-family: "Raleway", sans-serif;
     font-size: 25px;
   }
 }
 
+.record-modal {
+  display: flex;
+  flex-direction: column;
+
+  .line {
+    padding: 10px 10px 10px 10px;
+    border-bottom: 1px solid #808080;
+  }
+
+  .record-modal-section {
+    display: flex;
+    flex-direction: row;
+
+    .logo-wrapping {
+      border-radius: 50%;
+      background-color: #f0f0f0;
+      width: 150px;
+      height: 150px;
+      display: inline-block;
+      margin: 50px 30px 0px 0px;
+      align-self: center;
+
+      i.material-icons {
+        color: #0000ff;
+        font-size: 150px;
+      }
+    }
+
+    select {
+      min-width: 300px;
+      min-height: 80px;
+    }
+
+    .record-modal-description {
+      display: flex;
+      flex-direction: column;
+      align-items: left;
+
+      .record-modal-command {
+        display: flex;
+        flex-direction: row;
+        align-items: center;
+        padding: 10px 0px 10px 0px;
+        color: #ffffff;
+
+        .code-snippet {
+          width: 100%;
+        }
+      }
+
+      h3 {
+        padding-top: 15px;
+        align-self: start;
+        font-size: 1.2rem;
+        color: #0000ff;
+      }
+
+      h4 {
+        align-self: start;
+        font-size: 1.1rem;
+      }
+
+      text {
+        padding: 10px 0px 10px 0px;
+        color: #000000;
+      }
+
+      input[type="text"] {
+        flex-grow: 1;
+        border-radius: 4px;
+        border: 1px solid #dcdcdc;
+        padding: 3px;
+        margin: 0 10px;
+        min-width: 170px;
+
+        &:focus {
+          outline: none;
+          box-shadow: 1px 1px 1px rgba(23, 32, 44, 0.3);
+        }
+      }
+    }
+  }
+
+  .record-modal-button,
+  .record-modal-button-high,
+  .record-modal-logo-button {
+    font-size: 0.875rem;
+    padding-left: 1rem;
+    padding-right: 1rem;
+    padding-top: 0.5rem;
+    padding-bottom: 0.5rem;
+    background-color: #0000ff;
+    color: #ffffff;
+    cursor: pointer;
+    -webkit-appearance: button;
+    text-transform: none;
+    overflow: visible;
+    line-height: 1.15;
+    margin: 5px;
+    will-change: transform;
+    -moz-osx-font-smoothing: grayscale;
+    -webkit-backface-visibility: hidden;
+    backface-visibility: hidden;
+    transform: translateZ(0);
+    transition: transform 0.25s ease-out;
+    align-self: end;
+    text-align: center;
+  }
+
+  .record-modal-button,
+  .record-modal-button-high {
+    border-radius: 0.25rem;
+    border-style: none;
+    border-width: 0;
+  }
+
+  .record-modal-button-high:disabled {
+    background-color: #808080;
+  }
+
+  .record-modal-button-high {
+    height: 100%;
+    align-self: center;
+    display: flex;
+    align-items: center;
+  }
+
+  .record-modal-logo-button {
+    border-radius: 50%;
+    width: 35px;
+    height: 35px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+}
+
 .hider {
   @include transition(0.2s);
   position: fixed;
@@ -89,14 +227,14 @@
       width: auto;
       height: 50px;
       margin: 0;
-      >* {
+      > * {
         @include transition(0.2s);
         cursor: pointer;
         border-radius: 10px;
         margin: 10px;
         text-align: center;
         background-color: #eee;
-        font-family: 'Raleway', sans-serif;
+        font-family: "Raleway", sans-serif;
         font-size: 17px;
         @media (max-width: 1280px) {
           font-size: 1.6vw;
@@ -130,12 +268,14 @@
         align-items: center;
       }
 
-      label, select, button {
+      label,
+      select,
+      button {
         font-weight: 300;
         margin: 3px;
         color: #333;
         font-size: 17px;
-        font-family: 'Roboto', sans-serif;
+        font-family: "Roboto", sans-serif;
         align-items: center;
 
         &.error-label {
@@ -154,7 +294,8 @@
         border-radius: 20px;
         padding: 4px;
         height: 30px;
-        &:hover, &:active {
+        &:hover,
+        &:active {
           box-shadow: 0 0 4px 0 #ccc;
           background-color: #fafafa;
         }
@@ -171,7 +312,7 @@
     margin-bottom: 5px;
     background: #f9eeba;
     padding: 10px;
-    font-family: 'Roboto', sans-serif;
+    font-family: "Roboto", sans-serif;
     font-size: 14px;
     line-height: 20px;
   }
@@ -182,7 +323,8 @@
     border-radius: 0;
     border: 1px solid #eee;
     outline: none;
-    &:hover, &:active {
+    &:hover,
+    &:active {
       box-shadow: 0 0 6px #ccc;
     }
   }
@@ -191,14 +333,16 @@
 // The left-hand-side menu with 'Cpu', 'Memory' etc.
 .record-menu {
   grid-area: sidebar;
-  .rec { color: #ee3326; }
+  .rec {
+    color: #ee3326;
+  }
 
   background-color: #fcfcfc;
   border-right: 1px solid #eee;
   padding-bottom: 1em;
 
   header {
-    font-family: 'Roboto', sans-serif;
+    font-family: "Roboto", sans-serif;
     font-size: 14px;
     font-weight: 700;
     margin: 1em;
@@ -210,7 +354,9 @@
     padding: 0;
   }
 
-  a, a:link, a:visited {
+  a,
+  a:link,
+  a:visited {
     text-decoration: none;
   }
 
@@ -220,7 +366,7 @@
     padding: 0 1em;
     font-size: 15px;
     letter-spacing: 0.5px;
-    font-family: 'Raleway', sans-serif;
+    font-family: "Raleway", sans-serif;
     font-weight: 600;
     color: #666;
     display: grid;
@@ -242,7 +388,7 @@
     }
 
     .title {
-      transition: line-height .25s ease;
+      transition: line-height 0.25s ease;
       grid-area: title;
       line-height: 55px;
       display: block;
@@ -259,28 +405,30 @@
 
     &:hover {
       background-color: hsl(214, 0%, 90%);
-      .title { line-height: 50px; }
+      .title {
+        line-height: 50px;
+      }
       .sub {
         opacity: 1;
         transition-duration: 0.25s;
-        transition-delay: 0.0s;
+        transition-delay: 0s;
       }
     }
 
     &.active {
       background-color: hsl(214, 80%, 70%);
-      .title, .sub {
+      .title,
+      .sub {
         color: white;
       }
     }
-  }  // li
+  } // li
 
   &.disabled {
-    opacity: 0.50;
+    opacity: 0.5;
     pointer-events: none;
   }
-}  // record-menu
-
+} // record-menu
 
 .record-section {
   grid-area: section;
@@ -328,7 +476,7 @@
     margin-right: 10px;
     text-align: center;
     justify-items: center;
-    font-family: 'Raleway', sans-serif;
+    font-family: "Raleway", sans-serif;
     padding: 7px;
 
     &:hover:enabled {
@@ -381,7 +529,7 @@
       line-height: 36px;
       padding: 0 10px;
       font-size: 18px;
-      font-family: 'Roboto Condensed', sans-serif;
+      font-family: "Roboto Condensed", sans-serif;
       font-weight: 300;
       color: #666;
       flex-grow: 1;
@@ -394,7 +542,7 @@
       }
       &::placeholder {
         color: #b4b7ba;
-        font-family: 'Raleway', sans-serif;
+        font-family: "Raleway", sans-serif;
         font-weight: 400;
       }
     }
@@ -403,16 +551,20 @@
   // By default space all section elements by the same amount.
   --record-section-padding: 20px;
 
-  >* {
+  > * {
     padding-left: var(--record-section-padding);
     padding-right: var(--record-section-padding);
-    &:first-child { padding-top: 20px; }
-    &:last-child { padding-bottom: 20px; }
+    &:first-child {
+      padding-top: 20px;
+    }
+    &:last-child {
+      padding-bottom: 20px;
+    }
   }
 
-  >header {
+  > header {
     text-align: center;
-    font-family: 'Raleway', sans-serif;
+    font-family: "Raleway", sans-serif;
     font-size: 20px;
     padding: 15px 10px;
     color: #333;
@@ -442,7 +594,7 @@
       background-color: #f9f9f9;
     }
 
-    >img {
+    > img {
       transition: filter 0.2s ease, opacity 0.2s ease;
       grid-area: img;
       width: 210px;
@@ -453,25 +605,27 @@
     }
 
     &:hover {
-      >img { opacity: 1; }
-      >label {
+      > img {
+        opacity: 1;
+      }
+      > label {
         color: #333;
-        input[type=checkbox]::after {
+        input[type="checkbox"]::after {
           background: hsl(207, 60%, 60%);
         }
       }
-    }  // :hover
+    } // :hover
 
-    >label {
+    > label {
       grid-area: label;
       cursor: pointer;
-      font-family: 'Roboto' , sans-serif;
+      font-family: "Roboto", sans-serif;
       font-size: 20px;
       font-weight: 400;
       color: #999;
 
       // The per-probe on-off switch.
-      input[type=checkbox] {
+      input[type="checkbox"] {
         -moz-appearance: none;
         -webkit-appearance: none;
         cursor: pointer;
@@ -499,8 +653,8 @@
           height: 26px;
           border-radius: 100px;
           background: #f5f5f5;
-          box-shadow: 0 3px 3px rgba(0,0,0,0.15);
-          content: '';
+          box-shadow: 0 3px 3px rgba(0, 0, 0, 0.15);
+          content: "";
           transition: all 0.3s ease;
         }
         &:checked {
@@ -513,11 +667,11 @@
           left: 20px;
           background: #27303d;
         }
-      }  // checkbox
-    }  // label
+      } // checkbox
+    } // label
 
     // The content of the probe section.
-    >div {
+    > div {
       grid-area: descr;
       font-size: 14px;
       font-weight: 200;
@@ -541,35 +695,39 @@
         visibility: visible;
         max-height: 100vh;
       }
-      >label span { color: #4e80b7; }
-      >img {
+      > label span {
+        color: #4e80b7;
+      }
+      > img {
         filter: saturate(1);
         opacity: 1;
       }
     }
-  }  // probe
+  } // probe
 
   .toggle {
     transition: color 0.2s ease;
     padding-top: var(--record-section-padding);
 
     &:hover {
-      >img { opacity: 1; }
-      >label {
+      > img {
+        opacity: 1;
+      }
+      > label {
         color: #333;
-        input[type=checkbox]::after {
+        input[type="checkbox"]::after {
           background: hsl(207, 60%, 60%);
         }
       }
-    }  // :hover
+    } // :hover
 
-    >label {
+    > label {
       cursor: pointer;
       font-size: 14px;
       color: var(--record-text-color);
 
       // The per-probe on-off switch.
-      input[type=checkbox] {
+      input[type="checkbox"] {
         -moz-appearance: none;
         -webkit-appearance: none;
         cursor: pointer;
@@ -597,8 +755,8 @@
           height: 20px;
           border-radius: 100px;
           background: #f5f5f5;
-          box-shadow: 0 3px 3px rgba(0,0,0,0.15);
-          content: '';
+          box-shadow: 0 3px 3px rgba(0, 0, 0, 0.15);
+          content: "";
           transition: all 0.3s ease;
         }
         &:checked {
@@ -611,15 +769,16 @@
           left: 12px;
           background: #27303d;
         }
-      }  // checkbox
-    }  // label
+      } // checkbox
+    } // label
 
     // The content of the toggle section.
-    >div.descr {
+    > div.descr {
+      padding-left: 36px;
       font-size: 12px;
       color: #666;
     }
-  }  // toggle
+  } // toggle
 
   // The three "Stop when full", "Ring buffer", "Long trace" buttons.
   .record-mode {
@@ -629,20 +788,20 @@
     grid-template-rows: 1fr;
     padding-top: 0;
 
-    input[type=radio] {
+    input[type="radio"] {
       appearance: none;
       -webkit-appearance: none;
       display: none;
     }
 
-    >* {
+    > * {
       @include transition(0.2s);
       cursor: pointer;
       border-radius: 15px;
       margin: 5px;
       text-align: center;
       background-color: #eee;
-      font-family: 'Raleway', sans-serif;
+      font-family: "Raleway", sans-serif;
       font-size: 20px;
       @media (max-width: 1280px) {
         font-size: 1.6vw;
@@ -663,7 +822,7 @@
         width: 100%;
       }
     }
-  }  // record-mode
+  } // record-mode
 
   // There are two types of sliders:
   // 1) The full-width one (default), e.g. the one used in the main recording
@@ -675,39 +834,41 @@
     display: grid;
     grid-template-columns: 40px 1fr 130px 0;
     grid-template-rows: 30px min-content 1fr;
-    grid-template-areas: "hdr hdr hdr hdr" "descr descr descr descr"
-    "icon slider label unit";
+    grid-template-areas:
+      "hdr hdr hdr hdr" "descr descr descr descr"
+      "icon slider label unit";
     margin-top: var(--record-section-padding);
 
     &.thin {
       grid-template-columns: 1fr 1fr 100px 0;
-      grid-template-areas: "hdr hdr hdr hdr" "descr descr descr descr"
-      "slider slider label unit";
+      grid-template-areas:
+        "hdr hdr hdr hdr" "descr descr descr descr"
+        "slider slider label unit";
     }
 
     &.greyed-out {
       opacity: 0.5;
     }
 
-    >* {
+    > * {
       height: 40px;
       line-height: 40px;
     }
 
-    >header {
+    > header {
       @include transition(0.3s);
       opacity: 0.6;
       color: #333;
       grid-area: hdr;
     }
 
-    &.thin >header {
+    &.thin > header {
       opacity: 1;
       color: var(--record-text-color);
       font-size: 14px;
     }
 
-    &.thin >header.descr {
+    &.thin > header.descr {
       grid-area: descr;
       font-size: 12px;
       color: #666;
@@ -720,19 +881,19 @@
       transition-duration: 0.15s;
     }
 
-    >i {
+    > i {
       grid-area: icon;
       font-size: 32px;
       color: #333;
     }
 
-    input[type=range] {
+    input[type="range"] {
       grid-area: slider;
       width: 100%;
       appearance: none;
       -webkit-appearance: none;
       scroll-snap-type: x mandatory;
-      background-color : transparent;
+      background-color: transparent;
       outline: none;
       margin-left: -10px;
       margin-top: -5px;
@@ -741,7 +902,7 @@
         margin: 10px;
         width: 100%;
         height: 10px;
-        background-color : #ddd;
+        background-color: #ddd;
         border-radius: 4px;
       }
 
@@ -766,11 +927,11 @@
       }
     }
 
-    &.thin input[type=range]::-webkit-slider-runnable-track {
+    &.thin input[type="range"]::-webkit-slider-runnable-track {
       height: 8px;
     }
 
-    &.thin input[type=range]::-webkit-slider-thumb {
+    &.thin input[type="range"]::-webkit-slider-thumb {
       width: 20px;
       border-radius: 100%;
     }
@@ -783,7 +944,7 @@
       padding: 0 5px;
       border-radius: 2px;
       background-color: rgba(255, 255, 255, 60%);
-      font-family: 'Roboto', sans-serif;
+      font-family: "Roboto", sans-serif;
       font-size: 16px;
       font-weight: 100;
       height: 35px;
@@ -796,14 +957,15 @@
         margin: 0;
       }
 
-      &:hover, &:focus {
+      &:hover,
+      &:focus {
         border-bottom-color: hsl(207, 90%, 54%);
-        background-color: hsl(207, 50%, 97%);;
+        background-color: hsl(207, 50%, 97%);
       }
 
       &:invalid {
         border-bottom-color: hsl(9, 90%, 54%);
-        background-color: hsl(9, 50%, 97%);;
+        background-color: hsl(9, 50%, 97%);
       }
     }
 
@@ -843,13 +1005,13 @@
         border-radius: 10px;
         border: 1px solid #eee;
         margin: 0 5px;
-        font-size: .8rem;
+        font-size: 0.8rem;
       }
 
       .checkboxes {
         list-style-type: none;
         padding: 0;
-        font-size: .9rem;
+        font-size: 0.9rem;
 
         li {
           margin: 6px 0;
@@ -866,7 +1028,8 @@
     outline: none;
     -webkit-appearance: none;
 
-    option, optgroup {
+    option,
+    optgroup {
       @include transition();
       min-height: 25px;
       font-size: 12px;
@@ -883,7 +1046,7 @@
       }
       &::before {
         display: none;
-        content: '';
+        content: "";
       }
     }
 
@@ -957,7 +1120,9 @@
     outline: none;
     font-family: var(--monospace-font);
 
-    &::placeholder { color: #aaa; }
+    &::placeholder {
+      color: #aaa;
+    }
   }
 
   textarea.atrace-apps-list {
@@ -965,91 +1130,13 @@
     height: 100px;
   }
 
-  .code-snippet {
-    display: grid;
-    position: relative;
-    padding: 0;
-    margin: var(--record-section-padding);
-    background-color: #111;
-    border-radius: 4px;
-    box-shadow: 0 0 12px #999;
-
-    @keyframes ripple{
-      0% { transform: scale(1.00); }
-      30% { transform: scale(1.20); }
-      60% { transform: scale(1.00); }
-      80% { transform: scale(1.30); }
-      100% { transform: scale(1.20); }
-    }
-
-    &::before {
-      height: 20px;
-      content: "";
-      display: block;
-      background-color: #598eca;
-    }
-
-    &.no-top-bar {
-      white-space: pre;
-      &::before {
-        height: 0;
-      }
-    }
-
-    >code {
-      display: block;
-      margin: 10px 5px 20px 20px;
-      color: #ccc;
-      font-family: var(--monospace-font);
-      font-size: 12px;
-      line-height: 20px;
-      overflow-y: auto;
-      white-space: pre-wrap;
-      word-wrap: break-word;
-
-      // 510px and not 500px, so the overflowing line gets truncated, giving
-      // a clear indication that the code box scrolls.
-      max-height: 510px;
-    }
-
-    >button {
-      @include transition();
-      display: inline-block;
-      position: absolute;
-      top: 30px;
-      right: 20px;
-      color: white;
-      border-radius: 100%;
-      background-color: #333;
-      box-shadow: 0 0 2px rgba(255, 255, 255, 200);
-      padding: 5px;
-      font-size: 16px;
-      line-height: 13px;  // Deliberately smaller to center the icon.
-      user-select: none;
-
-      &:hover {
-        background-color: #444;
-        transform: scale(1.1);
-      }
-    }
-
-    &:active:hover >button:not(:hover) {
-      animation: ripple linear 0.5s;
-      background-color: #701d17;
-      transform: scale(1.1);
-    }
-
-    >button:active:hover {
-      transform: scale(0.9);
-    }
-  }  // code-snippet
-
   &.instructions {
-    label, select {
+    label,
+    select {
       font-weight: 100;
       color: #333;
       font-size: 16px;
-      font-family: 'Roboto', sans-serif;
+      font-family: "Roboto", sans-serif;
     }
 
     .note {
@@ -1057,7 +1144,7 @@
       background: #f9eeba;
       margin: var(--record-section-padding);
       padding: 10px;
-      font-family: 'Roboto', sans-serif;
+      font-family: "Roboto", sans-serif;
       font-size: 14px;
       line-height: 20px;
     }
@@ -1069,7 +1156,8 @@
       border: 1px solid #eee;
       outline: none;
 
-      &:hover, &:active {
+      &:hover,
+      &:active {
         box-shadow: 0 0 6px #ccc;
       }
     }
@@ -1080,14 +1168,14 @@
       align-items: center;
       width: auto;
       height: 70px;
-      >* {
+      > * {
         @include transition(0.2s);
         cursor: pointer;
         border-radius: 10px;
         text-align: center;
         margin: 3px;
         background-color: #eee;
-        font-family: 'Raleway', sans-serif;
+        font-family: "Raleway", sans-serif;
         flex-grow: 1;
         font-size: 17px;
         @media (max-width: 1280px) {
@@ -1114,7 +1202,7 @@
       border-radius: 10px;
       text-align: center;
       justify-items: center;
-      font-family: 'Raleway', sans-serif;
+      font-family: "Raleway", sans-serif;
       padding: 7px;
       background-color: hsl(88, 50%, 67%);
 
@@ -1138,16 +1226,17 @@
       background-color: #eee;
     }
   }
-}  // record-section
+} // record-section
 
 .inline-chip {
   @include transition();
-  &:hover, &:active {
+  &:hover,
+  &:active {
     box-shadow: 0 0 2px 0 #ccc;
     background-color: #fafafa;
   }
 
-  >i.material-icons {
+  > i.material-icons {
     color: rgb(60, 60, 60);
     font-size: 14px;
   }
@@ -1160,7 +1249,98 @@
   border-radius: 9px;
 }
 
-a.inline-chip, a.inline-chip:link, a.inline-chip:visited {
+a.inline-chip,
+a.inline-chip:link,
+a.inline-chip:visited {
   text-decoration: none;
   color: var(--record-text-color);
 }
+
+.code-snippet {
+  display: grid;
+  position: relative;
+  padding: 0;
+  margin: var(--record-section-padding);
+  background-color: #111;
+  border-radius: 4px;
+  box-shadow: 0 0 12px #999;
+
+  @keyframes ripple {
+    0% {
+      transform: scale(1);
+    }
+    30% {
+      transform: scale(1.2);
+    }
+    60% {
+      transform: scale(1);
+    }
+    80% {
+      transform: scale(1.3);
+    }
+    100% {
+      transform: scale(1.2);
+    }
+  }
+
+  &::before {
+    height: 20px;
+    content: "";
+    display: block;
+    background-color: #598eca;
+  }
+
+  &.no-top-bar {
+    white-space: pre;
+    &::before {
+      height: 0;
+    }
+  }
+
+  > code {
+    display: block;
+    margin: 10px 5px 20px 20px;
+    color: #ccc;
+    font-family: var(--monospace-font);
+    font-size: 12px;
+    line-height: 20px;
+    overflow-y: auto;
+    white-space: pre-wrap;
+    word-wrap: break-word;
+
+    // 510px and not 500px, so the overflowing line gets truncated, giving
+    // a clear indication that the code box scrolls.
+    max-height: 510px;
+  }
+
+  > button {
+    @include transition();
+    display: inline-block;
+    position: absolute;
+    top: 30px;
+    right: 20px;
+    color: white;
+    border-radius: 100%;
+    background-color: #333;
+    box-shadow: 0 0 2px rgba(255, 255, 255, 200);
+    padding: 5px;
+    font-size: 16px;
+    line-height: 13px; // Deliberately smaller to center the icon.
+    user-select: none;
+
+    &:hover {
+      background-color: #444;
+      transform: scale(1.1);
+    }
+  }
+
+  &:active:hover > button:not(:hover) {
+    animation: ripple linear 0.5s;
+    background-color: #701d17;
+    transform: scale(1.1);
+  }
+
+  > button:active:hover {
+    transform: scale(0.9);
+  }
+} // code-snippet
diff --git a/ui/src/assets/sidebar.scss b/ui/src/assets/sidebar.scss
index 0dabbda..b0a7092 100644
--- a/ui/src/assets/sidebar.scss
+++ b/ui/src/assets/sidebar.scss
@@ -13,291 +13,298 @@
 // limitations under the License.
 
 .sidebar {
-    --sidebar-padding-bottom: 40px;
-    --sidebar-timing: 0.15s;
-    grid-area: sidebar;
-    z-index: 4;
-    background-color: #262f3c;
-    overflow: hidden;
-    width: var(--sidebar-width);
-    display: flex;
-    position: relative;
-    flex-direction: column;
-    transition: margin-left var(--anim-easing) var(--sidebar-timing),
-                visibility linear var(--sidebar-timing);
-    >* {
-        border-bottom: 1px solid #404854;
+  --sidebar-padding-bottom: 40px;
+  --sidebar-timing: 0.15s;
+  grid-area: sidebar;
+  z-index: 4;
+  background-color: #262f3c;
+  overflow: hidden;
+  width: var(--sidebar-width);
+  display: flex;
+  position: relative;
+  flex-direction: column;
+  transition: margin-left var(--anim-easing) var(--sidebar-timing),
+    visibility linear var(--sidebar-timing);
+  > * {
+    border-bottom: 1px solid #404854;
+  }
+  input[type="file"] {
+    display: none;
+  }
+  > header {
+    font-family: "Roboto Condensed", sans-serif;
+    font-weight: 700;
+    font-size: 24px;
+    height: var(--topbar-height);
+    line-height: var(--topbar-height);
+    vertical-align: middle;
+    padding: 0 20px;
+    color: #fff;
+    overflow: visible;
+    .brand {
+      height: 40px;
+      margin-top: 4px;
     }
-    input[type=file] { display:none; }
-    >header {
-        font-family: 'Roboto Condensed', sans-serif;
-        font-weight: 700;
-        font-size: 24px;
-        height: var(--topbar-height);
-        line-height: var(--topbar-height);
-        vertical-align: middle;
-        padding: 0 20px;
-        color: #fff;
-        overflow: visible;
-        .brand {
-          height: 40px;
-          margin-top: 4px;
-        }
-        &::before {
-          z-index: 10;
-        }
-        &.canary::before, &.autopush::before {
-          display: block;
-          position: absolute;
-          font-size: 10px;
-          line-height: 10px;
-          font-family: 'Raleway', sans-serif;
-          left: 155px;
-          top: 7px;
-        }
-        &.canary::before {
-          content: 'CANARY';
-          color: #ffd700;
-        }
-        &.autopush::before {
-          content: 'AUTOPUSH';
-          color: #aed581;
-        }
+    &::before {
+      z-index: 10;
     }
-    .sidebar-button {
-      position: fixed;
-      z-index: 5;
-      background-color: #262f3c;
-      height: var(--topbar-height);
-      left: calc(var(--sidebar-width) - 50px);
-      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;
-        background-color: transparent;
-        border-radius: unset;
-        border-bottom: none;
-        color: #aaaaaa;
-      }
-    }
-    .sidebar-scroll {
-      overflow-y: auto;
-      flex: 1;
-      &::-webkit-scrollbar {
-        width: 0.5em;
-      }
-      &::-webkit-scrollbar-track {
-        background-color: #19212b;
-        border-radius: 2px;
-      }
-      &::-webkit-scrollbar-thumb {
-        background: #b4b7ba6e;
-        border-radius: 2px;
-      }
-      >.sidebar-scroll-container {
-        position: relative;
-        min-height: 100%;
-        padding-bottom: var(--sidebar-padding-bottom);
-
-        >section {
-            @include transition();
-            padding: 20px 0;
-            max-height: 80px;
-            .section-header {
-                cursor: pointer;
-                >h1,
-                >h2 {
-                    font-family: 'Raleway', sans-serif;
-                    letter-spacing: 0.25px;
-                    overflow: hidden;
-                    text-overflow: ellipsis;
-                    white-space: nowrap;
-                    margin: 0 24px;
-                }
-                >h1 {
-                    color: #fff;
-                    font-size: 15px;
-                    font-weight: 500;
-                }
-                >h2 {
-                    @include transition();
-                    color: rgba(255, 255, 255, 0.5);
-                    font-size: 12px;
-                    margin-top: 8px;
-                    font-weight: 400;
-                }
-                &:before {
-                  @include material-icon('expand_more');
-                  float: right;
-                  color: rgba(255, 255, 255, 0.3);
-                  margin-right: 12px;
-                  margin-top: -4px;
-                }
-            }
-            &:hover {
-                background-color: #373f4b;
-            }
-            &.expanded {
-                background-color: #19212b;
-                max-height: unset;
-                .section-header {
-                  h2 {
-                    opacity: 0;
-                  }
-
-                  &:before {
-                    content: 'expand_less';
-                  }
-                }
-
-                .section-content {
-                    pointer-events: inherit;
-                    opacity: 1;
-                }
-            }
-        }
-
-        .section-content {
-            pointer-events: none;
-            @include transition();
-            opacity: 0;
-            color: #b4b7ba;
-            a {
-                color: #b4b7ba;
-            }
-            ul {
-                list-style-type: none;
-                margin: 0;
-                padding: 0;
-            }
-            li {
-                @include transition();
-                a {
-                    line-height: 24px;
-                    font-size: 14px;
-                    font-weight: 400;
-                    font-family: 'Raleway', sans-serif;
-                    letter-spacing: 0.5px;
-                    padding: 5px 24px;
-                    text-decoration: none;
-                    display: block;
-                    &.pending {
-                      color: rgba(255, 255, 255, 0.3);
-                      &::after {
-                        content: ' ';
-                        display: inline-block;
-                        vertical-align: middle;
-                        box-sizing: border-box;
-                        width: 18px;
-                        height: 18px;
-                        margin-left: 10px;
-                        border-radius: 50%;
-                        border: 2px solid #b4b7ba;
-                        border-color: #b4b7ba transparent;
-                        animation: pending-spinner 1.25s linear infinite;
-                      }
-                      @keyframes pending-spinner {
-                        0% { transform: rotate(0deg); }
-                        100% { transform: rotate(360deg); }
-                      }
-                    }
-                    &[disabled] {
-                      text-decoration: line-through;
-                    }
-                }
-                .material-icons {
-                    margin-right: 10px;
-                }
-                &:hover {
-                    background-color: #373f4b;
-                }
-                .trace-file-name {
-                  white-space: break-spaces;
-                  font-family: 'Roboto Condensed', sans-serif;
-                  word-break: break-all;
-                  font-weight: 300;
-                  letter-spacing: 0;
-                  margin-top: -10px;
-                  color: #fff;
-                }
-            }
-        }
-      }
-    }
-
-    .sidebar-footer {
+    &.canary::before,
+    &.autopush::before {
+      display: block;
       position: absolute;
-      bottom: 0;
-      width: 100%;
-      padding: 2px 10px;
-      display: grid;
-      height: - var(--sidebar-padding-bottom);
-      grid-template-columns: repeat(4, min-content);
-      grid-gap: 10px;
+      font-size: 10px;
+      line-height: 10px;
+      font-family: "Raleway", sans-serif;
+      left: 155px;
+      top: 7px;
+    }
+    &.canary::before {
+      content: "CANARY";
+      color: #ffd700;
+    }
+    &.autopush::before {
+      content: "AUTOPUSH";
+      color: #aed581;
+    }
+  }
+  .sidebar-button {
+    position: fixed;
+    z-index: 5;
+    background-color: #262f3c;
+    height: var(--topbar-height);
+    left: calc(var(--sidebar-width) - 50px);
+    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;
+      background-color: transparent;
+      border-radius: unset;
+      border-bottom: none;
+      color: #aaaaaa;
+    }
+  }
+  .sidebar-scroll {
+    overflow-y: auto;
+    flex: 1;
+    &::-webkit-scrollbar {
+      width: 0.5em;
+    }
+    &::-webkit-scrollbar-track {
+      background-color: #19212b;
+      border-radius: 2px;
+    }
+    &::-webkit-scrollbar-thumb {
+      background: #b4b7ba6e;
+      border-radius: 2px;
+    }
+    > .sidebar-scroll-container {
+      position: relative;
+      min-height: 100%;
+      padding-bottom: var(--sidebar-padding-bottom);
 
-      > button {
-        color: hsl(217, 39%, 94%);
-        i {
-          font-size: 24px;
+      > section {
+        @include transition();
+        padding: 20px 0;
+        max-height: 80px;
+        .section-header {
+          cursor: pointer;
+          > h1,
+          > h2 {
+            font-family: "Raleway", sans-serif;
+            letter-spacing: 0.25px;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
+            margin: 0 24px;
+          }
+          > h1 {
+            color: #fff;
+            font-size: 15px;
+            font-weight: 500;
+          }
+          > h2 {
+            @include transition();
+            color: rgba(255, 255, 255, 0.5);
+            font-size: 12px;
+            margin-top: 8px;
+            font-weight: 400;
+          }
+          &:before {
+            @include material-icon("expand_more");
+            float: right;
+            color: rgba(255, 255, 255, 0.3);
+            margin-right: 12px;
+            margin-top: -4px;
+          }
         }
-
         &:hover {
-          color: hsl(45, 100%, 48%);
+          background-color: #373f4b;
+        }
+        &.expanded {
+          background-color: #19212b;
+          max-height: unset;
+          .section-header {
+            h2 {
+              opacity: 0;
+            }
+
+            &:before {
+              content: "expand_less";
+            }
+          }
+
+          .section-content {
+            pointer-events: inherit;
+            opacity: 1;
+          }
         }
       }
 
-      > .dbg-info-square {
-        width: 24px;
-        height: 22px;
-        line-height: 22px;
-        margin: 1px 0;
-        background: #12161b;
-        color: #4e71b3;
-        border-radius: 5px;
-        font-size: 12px;
-        text-align: center;
-        &.green {
-          background: #7aca75;
-          color: #12161b;
-        }
-        &.amber {
-          background: #FFC107;
-          color: #333;
-        }
-        &.red {
-          background: #d32f2f;
-          color: #fff;
-        }
-        > div {
-          font-size: 10px;
-          line-height: 11px;
-        }
-      }
-
-      .version {
-        position: absolute;
-        right: 8px;
-        bottom: 3px;
-        font-size: 12px;
-        font-family: 'Roboto Condensed', sans-serif;
+      .section-content {
+        pointer-events: none;
+        @include transition();
+        opacity: 0;
+        color: #b4b7ba;
         a {
-          color: rgba(255, 255, 255, 0.5);
-          text-decoration: none;
+          color: #b4b7ba;
         }
-        margin-top: 11px;
+        ul {
+          list-style-type: none;
+          margin: 0;
+          padding: 0;
+        }
+        li {
+          @include transition();
+          a {
+            line-height: 24px;
+            font-size: 14px;
+            font-weight: 400;
+            font-family: "Raleway", sans-serif;
+            letter-spacing: 0.5px;
+            padding: 5px 24px;
+            text-decoration: none;
+            display: block;
+            &.pending {
+              color: rgba(255, 255, 255, 0.3);
+              &::after {
+                content: " ";
+                display: inline-block;
+                vertical-align: middle;
+                box-sizing: border-box;
+                width: 18px;
+                height: 18px;
+                margin-left: 10px;
+                border-radius: 50%;
+                border: 2px solid #b4b7ba;
+                border-color: #b4b7ba transparent;
+                animation: pending-spinner 1.25s linear infinite;
+              }
+              @keyframes pending-spinner {
+                0% {
+                  transform: rotate(0deg);
+                }
+                100% {
+                  transform: rotate(360deg);
+                }
+              }
+            }
+            &[disabled] {
+              text-decoration: line-through;
+            }
+          }
+          .material-icons {
+            margin-right: 10px;
+          }
+          &:hover {
+            background-color: #373f4b;
+          }
+          .trace-file-name {
+            white-space: break-spaces;
+            font-family: "Roboto Condensed", sans-serif;
+            word-break: break-all;
+            font-weight: 300;
+            letter-spacing: 0;
+            margin-top: -10px;
+            color: #fff;
+          }
+        }
       }
     }
+  }
+
+  .sidebar-footer {
+    position: absolute;
+    bottom: 0;
+    width: 100%;
+    padding: 2px 10px;
+    display: grid;
+    height: -var(--sidebar-padding-bottom);
+    grid-template-columns: repeat(4, min-content);
+    grid-gap: 10px;
+
+    > button {
+      color: hsl(217, 39%, 94%);
+      i {
+        font-size: 24px;
+      }
+
+      &:hover {
+        color: hsl(45, 100%, 48%);
+      }
+    }
+
+    > .dbg-info-square {
+      width: 24px;
+      height: 22px;
+      line-height: 22px;
+      margin: 1px 0;
+      background: #12161b;
+      color: #4e71b3;
+      border-radius: 5px;
+      font-size: 12px;
+      text-align: center;
+      &.green {
+        background: #7aca75;
+        color: #12161b;
+      }
+      &.amber {
+        background: #ffc107;
+        color: #333;
+      }
+      &.red {
+        background: #d32f2f;
+        color: #fff;
+      }
+      > div {
+        font-size: 10px;
+        line-height: 11px;
+      }
+    }
+
+    .version {
+      position: absolute;
+      right: 8px;
+      bottom: 3px;
+      font-size: 12px;
+      font-family: "Roboto Condensed", sans-serif;
+      a {
+        color: rgba(255, 255, 255, 0.5);
+        text-decoration: none;
+      }
+      margin-top: 11px;
+    }
+  }
 }
 
 // Hide the footer when running integration tests, as the version code and the
diff --git a/ui/src/assets/topbar.scss b/ui/src/assets/topbar.scss
index 27dd63c..561c9364 100644
--- a/ui/src/assets/topbar.scss
+++ b/ui/src/assets/topbar.scss
@@ -12,189 +12,191 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 @mixin omnibox-width() {
-    width: 90%;
-    max-width: 600px;
+  width: 90%;
+  max-width: 600px;
 }
 
 .topbar {
-    grid-area: topbar;
-    position: relative;
-    z-index: 3;
-    overflow: visible;
-    background-color: hsl(215, 1%, 95%);
-    box-shadow: 0 -3px 14px 2px #bbb;
-    min-height: var(--topbar-height);
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    .omnibox {
-        @include omnibox-width();
-        @include transition(0.25s);
-        display: grid;
-        grid-template-areas: "icon input stepthrough";
-        grid-template-columns: 34px auto max-content;
-        border-radius: 20px;
-        background-color: #fcfcfc;
-        border: 0;
-        line-height: 34px;
-        &:before {
-            @include material-icon('search');
-            margin: 5px;
-            color: #aaa;
-            grid-area: icon;
-        }
-        input {
-            grid-area: input;
-            border: 0;
-            padding: 0 10px;
-            font-size: 18px;
-            font-family: 'Roboto Condensed', sans-serif;
-            font-weight: 300;
-            color: #666;
-            background-color: transparent;
-            &:focus {
-                outline: none;
-            }
-            &::placeholder {
-                color: #b4b7ba;
-                font-family: 'Raleway', sans-serif;
-                font-weight: 400;
-            }
-        }
-        &.command-mode {
-            background-color: #111;
-            border-radius: 0;
-            width: 100%;
-            max-width: 100%;
-            margin-top: 0;
-            border-left: 1px solid #404854;
-            height: var(--topbar-height);
-            input {
-                color: #9ddc67;
-                font-family: var(--monospace-font);
-                padding-left: 0;
-            }
-            &:before {
-                content: 'attach_money';
-                color: #9ddc67;
-                font-size: 26px;
-                padding-top: 5px;
-            }
-        }
-        &.message-mode {
-            background-color: hsl(0, 0%, 89%);
-            border-radius: 4px;
-            input::placeholder {
-                font-weight: 400;
-                font-family: var(--monospace-font);
-                color: hsl(213, 40%, 50%);
-            }
-            &:before {
-                content: 'bubble_chart';
-            }
-        }
-        .stepthrough {
-          grid-area: stepthrough;
-          display: flex;
-          font: inherit;
-          font-size: 14px;
-          font-family: 'Roboto Condensed', sans-serif;
-          font-weight: 300;
-          color: #aaa;
-          .current {
-            padding-right: 10px;
-          }
-          .material-icons.left {
-            border-right: rgb(218, 217, 217) solid 1px;
-          }
-        }
+  grid-area: topbar;
+  position: relative;
+  z-index: 3;
+  overflow: visible;
+  background-color: hsl(215, 1%, 95%);
+  box-shadow: 0 -3px 14px 2px #bbb;
+  min-height: var(--topbar-height);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  .omnibox {
+    @include omnibox-width();
+    @include transition(0.25s);
+    display: grid;
+    grid-template-areas: "icon input stepthrough";
+    grid-template-columns: 34px auto max-content;
+    border-radius: 20px;
+    background-color: #fcfcfc;
+    border: 0;
+    line-height: 34px;
+    &:before {
+      @include material-icon("search");
+      margin: 5px;
+      color: #aaa;
+      grid-area: icon;
     }
-    .progress {
-        position: absolute;
-        bottom: 0;
-        height: 1px;
-        width: 100%;
+    input {
+      grid-area: input;
+      border: 0;
+      padding: 0 10px;
+      font-size: 18px;
+      font-family: "Roboto Condensed", sans-serif;
+      font-weight: 300;
+      color: #666;
+      background-color: transparent;
+      &:focus {
+        outline: none;
       }
-      .progress-anim {
-        &:before {
-            content: '';
-            position: absolute;
-            background-color: hsl(219, 50%, 50%);
-            top: 0;
-            left: 0;
-            bottom: 0;
-            will-change: left, right;
-            animation: indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;
-        }
-        &:after {
-            content: '';
-            position: absolute;
-            background-color: hsl(219, 50%, 50%);
-            top: 0;
-            left: 0;
-            bottom: 0;
-            will-change: left, right;
-            animation: indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite;
-            animation-delay: 1.15s;
-        }
+      &::placeholder {
+        color: #b4b7ba;
+        font-family: "Raleway", sans-serif;
+        font-weight: 400;
+      }
     }
-    @keyframes indeterminate {
-        0% {
-            left: -35%;
-            right: 100%;
-        }
-        60% {
-            left: 100%;
-            right: -90%;
-        }
-        100% {
-            left: 100%;
-            right: -90%;
-        }
+    &.command-mode {
+      background-color: #111;
+      border-radius: 0;
+      width: 100%;
+      max-width: 100%;
+      margin-top: 0;
+      border-left: 1px solid #404854;
+      height: var(--topbar-height);
+      input {
+        color: #9ddc67;
+        font-family: var(--monospace-font);
+        padding-left: 0;
+      }
+      &:before {
+        content: "attach_money";
+        color: #9ddc67;
+        font-size: 26px;
+        padding-top: 5px;
+      }
     }
-    @keyframes indeterminate-short {
-        0% {
-            left: -35%;
-            right: 100%;
-        }
-        60% {
-            left: 100%;
-            right: -90%;
-        }
-        100% {
-            left: 100%;
-            right: -90%;
-        }
+    &.message-mode {
+      background-color: hsl(0, 0%, 89%);
+      border-radius: 4px;
+      input::placeholder {
+        font-weight: 400;
+        font-family: var(--monospace-font);
+        color: hsl(213, 40%, 50%);
+      }
+      &:before {
+        content: "bubble_chart";
+      }
+    }
+    .stepthrough {
+      grid-area: stepthrough;
+      display: flex;
+      font: inherit;
+      font-size: 14px;
+      font-family: "Roboto Condensed", sans-serif;
+      font-weight: 300;
+      color: #aaa;
+      .current {
+        padding-right: 10px;
+      }
+      .material-icons.left {
+        border-right: rgb(218, 217, 217) solid 1px;
+      }
+    }
+  }
+  .progress {
+    position: absolute;
+    bottom: 0;
+    height: 1px;
+    width: 100%;
+  }
+  .progress-anim {
+    &:before {
+      content: "";
+      position: absolute;
+      background-color: hsl(219, 50%, 50%);
+      top: 0;
+      left: 0;
+      bottom: 0;
+      will-change: left, right;
+      animation: indeterminate 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395)
+        infinite;
+    }
+    &:after {
+      content: "";
+      position: absolute;
+      background-color: hsl(219, 50%, 50%);
+      top: 0;
+      left: 0;
+      bottom: 0;
+      will-change: left, right;
+      animation: indeterminate-short 2.1s cubic-bezier(0.165, 0.84, 0.44, 1)
+        infinite;
+      animation-delay: 1.15s;
+    }
+  }
+  @keyframes indeterminate {
+    0% {
+      left: -35%;
+      right: 100%;
+    }
+    60% {
+      left: 100%;
+      right: -90%;
+    }
+    100% {
+      left: 100%;
+      right: -90%;
+    }
+  }
+  @keyframes indeterminate-short {
+    0% {
+      left: -35%;
+      right: 100%;
+    }
+    60% {
+      left: 100%;
+      right: -90%;
+    }
+    100% {
+      left: 100%;
+      right: -90%;
+    }
+  }
+
+  .notification-btn {
+    @include transition(0.25s);
+    font-size: 16px;
+    padding: 8px 10px;
+    margin: 0 10px;
+    border-radius: 2px;
+    background: hsl(210, 10%, 73%);
+    &:hover {
+      background: hsl(210, 10%, 83%);
     }
 
-    .notification-btn {
-        @include transition(0.25s);
-        font-size: 16px;
-        padding: 8px 10px;
-        margin: 0 10px;
-        border-radius: 2px;
-        background: hsl(210, 10%, 73%);
-        &:hover {
-            background: hsl(210, 10%, 83%);
-        }
-
-        &.preferred {
-            background: hsl(210, 98%, 53%);
-            color: #fff;
-            &:hover {
-                background: hsl(210, 98%, 63%);
-            }
-        }
+    &.preferred {
+      background: hsl(210, 98%, 53%);
+      color: #fff;
+      &:hover {
+        background: hsl(210, 98%, 63%);
+      }
     }
+  }
 }
 
 .error {
-    position: absolute;
-    right: 10px;
-    color: #EF6C00;
-    &:hover {
-        cursor:pointer;
-    }
+  position: absolute;
+  right: 10px;
+  color: #ef6c00;
+  &:hover {
+    cursor: pointer;
+  }
 }
 
 .helpful-hint {
@@ -213,7 +215,7 @@
 }
 
 .hint-text {
-  padding-bottom: 5px
+  padding-bottom: 5px;
 }
 
 .hint-dismiss-button {
diff --git a/ui/src/assets/trace_info_page.scss b/ui/src/assets/trace_info_page.scss
index a91dcf9..69baaaf 100644
--- a/ui/src/assets/trace_info_page.scss
+++ b/ui/src/assets/trace_info_page.scss
@@ -25,7 +25,7 @@
     border-radius: 8px;
 
     &.errors {
-      background-color: #F3E5F5;
+      background-color: #f3e5f5;
     }
 
     .metric-error {
@@ -36,7 +36,7 @@
     }
 
     h2 {
-      font-family: 'Raleway', sans-serif;
+      font-family: "Raleway", sans-serif;
       font-weight: 400;
       letter-spacing: 0.25px;
       font-size: 2rem;
diff --git a/ui/src/assets/typefaces.scss b/ui/src/assets/typefaces.scss
index a8661b9..8d82dac 100644
--- a/ui/src/assets/typefaces.scss
+++ b/ui/src/assets/typefaces.scss
@@ -1,94 +1,112 @@
 @font-face {
-  font-family: 'Material Icons';
+  font-family: "Material Icons";
   font-style: normal;
   font-weight: 400;
   font-display: block;
-  src: url(assets/MaterialIcons.woff2) format('woff2');
+  src: url(assets/MaterialIcons.woff2) format("woff2");
 }
 
 /* latin */
 @font-face {
-  font-family: 'Raleway';
+  font-family: "Raleway";
   font-style: normal;
   font-weight: 100;
-  src: url(assets/Raleway-Thin.woff2) format('woff2');
-  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+  src: url(assets/Raleway-Thin.woff2) format("woff2");
+  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
+    U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
+    U+FEFF, U+FFFD;
 }
 
 /* latin */
 @font-face {
-  font-family: 'Raleway';
+  font-family: "Raleway";
   font-style: normal;
   font-weight: 400;
-  src: url(assets/Raleway-Regular.woff2) format('woff2');
-  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+  src: url(assets/Raleway-Regular.woff2) format("woff2");
+  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
+    U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
+    U+FEFF, U+FFFD;
 }
 
 /* latin */
 @font-face {
-  font-family: 'Roboto';
+  font-family: "Roboto";
   font-style: normal;
   font-weight: 100;
-  src: url(assets/Roboto-100.woff2) format('woff2');
-  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+  src: url(assets/Roboto-100.woff2) format("woff2");
+  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
+    U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
+    U+FEFF, U+FFFD;
 }
 
 /* latin */
 @font-face {
-  font-family: 'Roboto';
+  font-family: "Roboto";
   font-style: normal;
   font-weight: 300;
-  src: url(assets/Roboto-300.woff2) format('woff2');
-  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+  src: url(assets/Roboto-300.woff2) format("woff2");
+  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
+    U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
+    U+FEFF, U+FFFD;
 }
 
 /* latin */
 @font-face {
-  font-family: 'Roboto';
+  font-family: "Roboto";
   font-style: normal;
   font-weight: 400;
-  src: url(assets/Roboto-400.woff2) format('woff2');
-  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+  src: url(assets/Roboto-400.woff2) format("woff2");
+  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
+    U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
+    U+FEFF, U+FFFD;
 }
 
 /* latin */
 @font-face {
-  font-family: 'Roboto';
+  font-family: "Roboto";
   font-style: normal;
   font-weight: 500;
-  src: url(assets/Roboto-500.woff2) format('woff2');
-  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+  src: url(assets/Roboto-500.woff2) format("woff2");
+  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
+    U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
+    U+FEFF, U+FFFD;
 }
 
 /* latin */
 @font-face {
-  font-family: 'Roboto Condensed';
+  font-family: "Roboto Condensed";
   font-style: normal;
   font-weight: 300;
-  src: url(assets/RobotoCondensed-Light.woff2) format('woff2');
-  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+  src: url(assets/RobotoCondensed-Light.woff2) format("woff2");
+  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
+    U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
+    U+FEFF, U+FFFD;
 }
 
 /* latin */
 @font-face {
-  font-family: 'Roboto Condensed';
+  font-family: "Roboto Condensed";
   font-style: normal;
   font-weight: 400;
-  src: url(assets/RobotoCondensed-Regular.woff2) format('woff2');
-  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+  src: url(assets/RobotoCondensed-Regular.woff2) format("woff2");
+  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
+    U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
+    U+FEFF, U+FFFD;
 }
 
 /* latin */
 @font-face {
-  font-family: 'Roboto Mono';
+  font-family: "Roboto Mono";
   font-style: normal;
   font-weight: 400;
-  src: url(assets/RobotoMono-Regular.woff2) format('woff2');
-  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+  src: url(assets/RobotoMono-Regular.woff2) format("woff2");
+  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
+    U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
+    U+FEFF, U+FFFD;
 }
 
 .material-icons {
-  font-family: 'Material Icons';
+  font-family: "Material Icons";
   font-weight: normal;
   font-style: normal;
   font-size: 24px;
@@ -100,6 +118,6 @@
   white-space: nowrap;
   word-wrap: normal;
   direction: ltr;
-  -webkit-font-feature-settings: 'liga';
+  -webkit-font-feature-settings: "liga";
   -webkit-font-smoothing: antialiased;
 }
diff --git a/ui/src/base/logging.ts b/ui/src/base/logging.ts
index 75a02ca..dab5565 100644
--- a/ui/src/base/logging.ts
+++ b/ui/src/base/logging.ts
@@ -26,8 +26,8 @@
 }
 
 export function assertTrue(value: boolean, optMsg?: string) {
-  if (value !== true) {
-    throw new Error(optMsg ? optMsg : 'Failed assertion');
+  if (!value) {
+    throw new Error(optMsg ?? 'Failed assertion');
   }
 }
 
diff --git a/ui/src/base/math_utils.ts b/ui/src/base/math_utils.ts
new file mode 100644
index 0000000..f1c1816
--- /dev/null
+++ b/ui/src/base/math_utils.ts
@@ -0,0 +1,23 @@
+// Copyright (C) 2023 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.
+
+// Round a number up to the nearest stepsize.
+export function roundUpNearest(val: number, stepsize: number): number {
+  return stepsize * Math.ceil(val / stepsize);
+}
+
+// Round a number down to the nearest stepsize.
+export function roundDownNearest(val: number, stepsize: number): number {
+  return stepsize * Math.floor(val / stepsize);
+}
diff --git a/ui/src/base/math_utils_unittest.ts b/ui/src/base/math_utils_unittest.ts
new file mode 100644
index 0000000..169b793
--- /dev/null
+++ b/ui/src/base/math_utils_unittest.ts
@@ -0,0 +1,29 @@
+// Copyright (C) 2023 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 {roundDownNearest, roundUpNearest} from './math_utils';
+
+describe('roundUpNearest()', () => {
+  it('rounds decimal values up to the right step size', () => {
+    expect(roundUpNearest(0.1, 0.5)).toBeCloseTo(0.5);
+    expect(roundUpNearest(17.2, 0.5)).toBeCloseTo(17.5);
+  });
+});
+
+describe('roundDownNearest()', () => {
+  it('rounds decimal values down to the right step size', () => {
+    expect(roundDownNearest(0.4, 0.5)).toBeCloseTo(0.0);
+    expect(roundDownNearest(17.4, 0.5)).toBeCloseTo(17.0);
+  });
+});
diff --git a/ui/src/base/string_utils.ts b/ui/src/base/string_utils.ts
index 5a3fb18..bc33b70 100644
--- a/ui/src/base/string_utils.ts
+++ b/ui/src/base/string_utils.ts
@@ -113,5 +113,5 @@
 // The purpose of this function is to use in simple comparisons, to escape
 // strings used in GLOB clauses see escapeQuery function.
 export function sqliteString(str: string): string {
-  return `'${str.replace('\'', '\'\'')}'`;
+  return `'${str.replace(/'/g, '\'\'')}'`;
 }
diff --git a/ui/src/base/string_utils_unittest.ts b/ui/src/base/string_utils_unittest.ts
index b3b3c09..e12c1a4 100644
--- a/ui/src/base/string_utils_unittest.ts
+++ b/ui/src/base/string_utils_unittest.ts
@@ -78,4 +78,5 @@
 test('string_utils.sqliteString', () => {
   expect(sqliteString('that\'s it')).toEqual('\'that\'\'s it\'');
   expect(sqliteString('no quotes')).toEqual('\'no quotes\'');
+  expect(sqliteString(`foo ' bar '`)).toEqual(`'foo '' bar '''`);
 });
diff --git a/ui/src/chrome_extension/index.ts b/ui/src/chrome_extension/index.ts
index b4906c2..e0e97d2 100644
--- a/ui/src/chrome_extension/index.ts
+++ b/ui/src/chrome_extension/index.ts
@@ -55,7 +55,9 @@
   chrome.declarativeContent.onPageChanged.removeRules(undefined, () => {
     chrome.declarativeContent.onPageChanged.addRules([
       enableOnHostWithSuffix('localhost'),
+      enableOnHostWithSuffix('127.0.0.1'),
       enableOnHostWithSuffix('.perfetto.dev'),
+      enableOnHostWithSuffix('.storage.googleapis.com'),
     ]);
   });
 }
diff --git a/ui/src/chrome_extension/manifest.json b/ui/src/chrome_extension/manifest.json
index ef1172a..19edc67 100644
--- a/ui/src/chrome_extension/manifest.json
+++ b/ui/src/chrome_extension/manifest.json
@@ -2,7 +2,7 @@
   "name": "Perfetto UI",
   "key":"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhm3X7qutsrskke84ltokTObnFJakd/d0XFQ6Ox2wQueHTGJM5GUNPTY/x8bdreNtGnfzvt/Sd0vABbR0wsS6lz5yY+g6ksMXJnigFe9N7uz8E3KojDrl3xYjIe+mkiJo8yxxzPydgb7GjQ6jmsX3g+yjj67kXzm9rZFkmoZ5WmqwBZlguPYVRN/W8CIIqBZkC3Qmq6uSG7b/g93YbwqmTmGiL2sAzgvXtqvDOD6503abtQkRC795E4VjJd+ffyeRH38fAEz5ZIrA6GJsfmov1TZTIu1NTwqylSpBYl5as7C6gpmuxDV4SvHvGT2hMQuIufDhZhErjI3B7bcX+XLe1wIDAQAB",
   "description": "Enables the Perfetto trace viewer (https://ui.perfetto.dev) to record Chrome browser traces.",
-  "version": "0.0.0.14",
+  "version": "0.0.0.15",
   "manifest_version": 2,
   "minimum_chrome_version": "81.0.4022.0",
   "permissions": [
@@ -22,7 +22,8 @@
     "matches": [
       "*://localhost/*",
       "*://127.0.0.1/*",
-      "https://*.perfetto.dev/*"
+      "https://*.perfetto.dev/*",
+      "https://storage.googleapis.com/*"
     ]
   }
 }
\ No newline at end of file
diff --git a/ui/src/common/actions.ts b/ui/src/common/actions.ts
index 5bd8c40..c5e40d6 100644
--- a/ui/src/common/actions.ts
+++ b/ui/src/common/actions.ts
@@ -19,15 +19,21 @@
 import {globals} from '../frontend/globals';
 import {
   Aggregation,
-  aggregationKey,
+  AggregationFunction,
   TableColumn,
+  tableColumnEquals,
   toggleEnabled,
 } from '../frontend/pivot_table_redux_types';
-import {DropDirection} from '../frontend/reorderable_cells';
 
 import {randomColor} from './colorizer';
+import {
+  computeIntervals,
+  DropDirection,
+  performReordering,
+} from './dragndrop_logic';
 import {createEmptyState} from './empty_state';
 import {DEFAULT_VIEWING_OPTION, PERF_SAMPLES_KEY} from './flamegraph_util';
+import {traceEventBegin, traceEventEnd, TraceEventScope} from './metatracing';
 import {
   AdbRecordingTarget,
   Area,
@@ -80,6 +86,12 @@
   keepApiOpen?: boolean;
 }
 
+export interface PostedScrollToRange {
+  timeStart: number;
+  timeEnd: number;
+  viewPercentage?: number;
+}
+
 function clearTraceState(state: StateDraft) {
   const nextId = state.nextId;
   const recordConfig = state.recordConfig;
@@ -127,13 +139,14 @@
   state.pinnedTracks = state.pinnedTracks.filter((id) => id !== trackId);
 }
 
+let statusTraceEvent: TraceEventScope|undefined;
+
 export const StateActions = {
 
   openTraceFromFile(state: StateDraft, args: {file: File}): void {
     clearTraceState(state);
     const id = generateNextId(state);
-    state.currentEngineId = id;
-    state.engines[id] = {
+    state.engine = {
       id,
       ready: false,
       source: {type: 'FILE', file: args.file},
@@ -143,8 +156,7 @@
   openTraceFromBuffer(state: StateDraft, args: PostedTrace): void {
     clearTraceState(state);
     const id = generateNextId(state);
-    state.currentEngineId = id;
-    state.engines[id] = {
+    state.engine = {
       id,
       ready: false,
       source: {type: 'ARRAY_BUFFER', ...args},
@@ -154,8 +166,7 @@
   openTraceFromUrl(state: StateDraft, args: {url: string}): void {
     clearTraceState(state);
     const id = generateNextId(state);
-    state.currentEngineId = id;
-    state.engines[id] = {
+    state.engine = {
       id,
       ready: false,
       source: {type: 'URL', url: args.url},
@@ -165,8 +176,7 @@
   openTraceFromHttpRpc(state: StateDraft, _args: {}): void {
     clearTraceState(state);
     const id = generateNextId(state);
-    state.currentEngineId = id;
-    state.engines[id] = {
+    state.engine = {
       id,
       ready: false,
       source: {type: 'HTTP_RPC'},
@@ -467,8 +477,8 @@
   setEngineReady(
       state: StateDraft,
       args: {engineId: string; ready: boolean, mode: EngineMode}): void {
-    const engine = state.engines[args.engineId];
-    if (engine === undefined) {
+    const engine = state.engine;
+    if (engine === undefined || engine.id !== args.engineId) {
       return;
     }
     engine.ready = args.ready;
@@ -482,8 +492,8 @@
   // Marks all engines matching the given |mode| as failed.
   setEngineFailed(state: StateDraft, args: {mode: EngineMode; failure: string}):
       void {
-        for (const engine of Object.values(state.engines)) {
-          if (engine.mode === args.mode) engine.failed = args.failure;
+        if (state.engine !== undefined && state.engine.mode === args.mode) {
+          state.engine.failed = args.failure;
         }
       },
 
@@ -515,6 +525,10 @@
   },
 
   updateStatus(state: StateDraft, args: Status): void {
+    if (statusTraceEvent) {
+      traceEventEnd(statusTraceEvent);
+    }
+    statusTraceEvent = traceEventBegin(args.msg);
     state.status = args;
   },
 
@@ -529,8 +543,8 @@
 
     // If we're loading from a permalink then none of the engines can
     // possibly be ready:
-    for (const engine of Object.values(state.engines)) {
-      engine.ready = false;
+    if (state.engine !== undefined) {
+      state.engine.ready = false;
     }
   },
 
@@ -550,6 +564,19 @@
     }
   },
 
+  addAutomaticNote(
+      state: StateDraft,
+      args: {timestamp: number, color: string, text: string}): void {
+    const id = generateNextId(state);
+    state.notes[id] = {
+      noteType: 'DEFAULT',
+      id,
+      timestamp: args.timestamp,
+      color: args.color,
+      text: args.text,
+    };
+  },
+
   addNote(state: StateDraft, args: {timestamp: number, color: string}): void {
     const id = generateNextId(state);
     state.notes[id] = {
@@ -781,6 +808,17 @@
         };
       },
 
+  selectLog(
+      state: StateDraft, args: {id: number, trackId: string, scroll?: boolean}):
+      void {
+        state.currentSelection = {
+          kind: 'LOG',
+          id: args.id,
+          trackId: args.trackId,
+        };
+        state.pendingScrollId = args.scroll ? args.id : undefined;
+      },
+
   deselect(state: StateDraft, _: {}): void {
     state.currentSelection = null;
   },
@@ -822,7 +860,7 @@
   },
 
   setOmnibox(state: StateDraft, args: OmniboxState): void {
-    state.frontendLocalState.omniboxState = args;
+    state.omniboxState = args;
   },
 
   selectArea(state: StateDraft, args: {area: Area}): void {
@@ -882,6 +920,11 @@
         }
       }
     }
+    // It's super unexpected that |toggleTrackSelection| does not cause
+    // selection to be updated and this leads to bugs for people who do:
+    // if (oldSelection !== state.selection) etc.
+    // To solve this re-create the selection object here:
+    state.currentSelection = Object.assign({}, state.currentSelection);
   },
 
   setVisibleTraceTime(state: StateDraft, args: VisibleState): void {
@@ -1006,6 +1049,17 @@
     state.flamegraphModalDismissed = true;
   },
 
+  addPivotTableAggregation(
+      state: StateDraft, args: {aggregation: Aggregation, after: number}) {
+    state.nonSerializableState.pivotTableRedux.selectedAggregations.splice(
+        args.after, 0, args.aggregation);
+  },
+
+  removePivotTableAggregation(state: StateDraft, args: {index: number}) {
+    state.nonSerializableState.pivotTableRedux.selectedAggregations.splice(
+        args.index, 1);
+  },
+
   setPivotTableQueryRequested(
       state: StateDraft, args: {queryRequested: boolean}) {
     state.nonSerializableState.pivotTableRedux.queryRequested =
@@ -1014,37 +1068,30 @@
 
   setPivotTablePivotSelected(
       state: StateDraft, args: {column: TableColumn, selected: boolean}) {
-    if (args.column.kind === 'argument' || args.column.table === 'slice' ||
-        args.column.table === 'thread_slice') {
-      toggleEnabled(
-          state.nonSerializableState.pivotTableRedux.selectedSlicePivots,
-          args.column,
-          args.selected);
-    } else {
-      toggleEnabled(
-          state.nonSerializableState.pivotTableRedux.selectedPivots,
-          args.column,
-          args.selected);
-    }
+    toggleEnabled(
+        tableColumnEquals,
+        state.nonSerializableState.pivotTableRedux.selectedPivots,
+        args.column,
+        args.selected);
   },
 
-  setPivotTableAggregationSelected(
-      state: StateDraft, args: {column: Aggregation, selected: boolean}) {
-    if (args.selected) {
-      state.nonSerializableState.pivotTableRedux.selectedAggregations.set(
-          aggregationKey(args.column), args.column);
-    } else {
-      state.nonSerializableState.pivotTableRedux.selectedAggregations.delete(
-          aggregationKey(args.column));
-    }
+  setPivotTableAggregationFunction(
+      state: StateDraft, args: {index: number, function: AggregationFunction}) {
+    state.nonSerializableState.pivotTableRedux.selectedAggregations[args.index]
+        .aggregationFunction = args.function;
   },
 
   setPivotTableSortColumn(
-      state: StateDraft, args: {column: TableColumn, order: SortDirection}) {
-    state.nonSerializableState.pivotTableRedux.sortCriteria = {
-      column: args.column,
-      order: args.order,
-    };
+      state: StateDraft,
+      args: {aggregationIndex: number, order: SortDirection}) {
+    state.nonSerializableState.pivotTableRedux.selectedAggregations =
+        state.nonSerializableState.pivotTableRedux.selectedAggregations.map(
+            (agg, index) => ({
+              column: agg.column,
+              aggregationFunction: agg.aggregationFunction,
+              sortDirection: (index === args.aggregationIndex) ? args.order :
+                                                                 undefined,
+            }));
   },
 
   addVisualisedArg(state: StateDraft, args: {argName: string}) {
@@ -1067,40 +1114,50 @@
   changePivotTablePivotOrder(
       state: StateDraft,
       args: {from: number, to: number, direction: DropDirection}) {
-    moveElement(
-        state.nonSerializableState.pivotTableRedux.selectedPivots,
-        args.from,
-        args.to,
-        args.direction);
+    const pivots = state.nonSerializableState.pivotTableRedux.selectedPivots;
+    state.nonSerializableState.pivotTableRedux.selectedPivots =
+        performReordering(
+            computeIntervals(pivots.length, args.from, args.to, args.direction),
+            pivots);
   },
 
-  changePivotTableSlicePivotOrder(
+  changePivotTableAggregationOrder(
       state: StateDraft,
       args: {from: number, to: number, direction: DropDirection}) {
-    moveElement(
-        state.nonSerializableState.pivotTableRedux.selectedSlicePivots,
-        args.from,
-        args.to,
-        args.direction);
+    const aggregations =
+        state.nonSerializableState.pivotTableRedux.selectedAggregations;
+    state.nonSerializableState.pivotTableRedux.selectedAggregations =
+        performReordering(
+            computeIntervals(
+                aggregations.length, args.from, args.to, args.direction),
+            aggregations);
+  },
+
+  setMinimumLogLevel(state: StateDraft, args: {minimumLevel: number}) {
+    state.logFilteringCriteria.minimumLevel = args.minimumLevel;
+  },
+
+  addLogTag(state: StateDraft, args: {tag: string}) {
+    if (!state.logFilteringCriteria.tags.includes(args.tag)) {
+      state.logFilteringCriteria.tags.push(args.tag);
+    }
+  },
+
+  removeLogTag(state: StateDraft, args: {tag: string}) {
+    state.logFilteringCriteria.tags =
+        state.logFilteringCriteria.tags.filter((t) => t !== args.tag);
+  },
+
+  updateLogFilterText(state: StateDraft, args: {textEntry: string}) {
+    state.logFilteringCriteria.textEntry = args.textEntry;
+  },
+
+  toggleCollapseByTextEntry(state: StateDraft, _: {}) {
+    state.logFilteringCriteria.hideNonMatching =
+        !state.logFilteringCriteria.hideNonMatching;
   },
 };
 
-// Move element at `from` index to `direction` of `to` element.
-// Implements logic for reordering table columns via drag'n'drop.
-function moveElement<T>(
-    array: Draft<T[]>, from: number, to: number, direction: DropDirection) {
-  // New location of the "to" element: would be shifted by minus one if "from"
-  // element comes before it.
-  const newTo = to - ((from < to) ? 1 : 0);
-
-  // The resulting index where the "from" element has to be spliced in to.
-  const insertionPoint = newTo + ((direction === 'right') ? 1 : 0);
-
-  const fromElement = array[from];
-  array.splice(from, 1);
-  array.splice(insertionPoint, 0, fromElement);
-}
-
 // When we are on the frontend side, we don't really want to execute the
 // actions above, we just want to serialize them and marshal their
 // arguments, send them over to the controller side and have them being
diff --git a/ui/src/common/actions_unittest.ts b/ui/src/common/actions_unittest.ts
index 50da102..6702e72 100644
--- a/ui/src/common/actions_unittest.ts
+++ b/ui/src/common/actions_unittest.ts
@@ -269,9 +269,8 @@
     });
   });
 
-  const engineKeys = Object.keys(after.engines);
-  expect(engineKeys.length).toBe(1);
-  expect((after.engines[engineKeys[0]].source as TraceUrlSource).url)
+  expect(after.engine).not.toBeUndefined();
+  expect((after.engine!!.source as TraceUrlSource).url)
       .toBe('https://example.com/bar');
   expect(after.recordConfig).toBe(recordConfig);
 });
@@ -299,9 +298,8 @@
     });
   });
 
-  const engineKeys = Object.keys(thrice.engines);
-  expect(engineKeys.length).toBe(1);
-  expect((thrice.engines[engineKeys[0]].source as TraceUrlSource).url)
+  expect(thrice.engine).not.toBeUndefined();
+  expect((thrice.engine!!.source as TraceUrlSource).url)
       .toBe('https://example.com/foo');
   expect(thrice.pinnedTracks.length).toBe(0);
   expect(thrice.scrollingTracks.length).toBe(0);
@@ -317,17 +315,15 @@
 
 test('setEngineReady', () => {
   const state = createEmptyState();
-  let latestEngineId = 'dummy value will be replaced';
-  state.currentEngineId = '100';
   const after = produce(state, (draft) => {
     StateActions.openTraceFromUrl(draft, {
       url: 'https://example.com/bar',
     });
-    latestEngineId = assertExists(draft.currentEngineId);
+    const latestEngineId = assertExists(draft.engine).id;
     StateActions.setEngineReady(
         draft, {engineId: latestEngineId, ready: true, mode: 'WASM'});
   });
-  expect(after.engines[latestEngineId].ready).toBe(true);
+  expect(after.engine!!.ready).toBe(true);
 });
 
 test('sortTracksByPriority', () => {
diff --git a/ui/src/common/cache_manager.ts b/ui/src/common/cache_manager.ts
index 04bbfde..11b3aef 100644
--- a/ui/src/common/cache_manager.ts
+++ b/ui/src/common/cache_manager.ts
@@ -18,7 +18,7 @@
  * containing it is discarded by Chrome (e.g. because the tab was not used for
  * a long time) or when the user accidentally hits reload.
  */
-import {getErrorMessage} from './errors';
+import {ignoreCacheUnactionableErrors} from './errors';
 import {TraceArrayBufferSource, TraceSource} from './state';
 
 const TRACE_CACHE_NAME = 'cached_traces';
@@ -26,26 +26,12 @@
 
 let LAZY_CACHE: Cache|undefined = undefined;
 
-// Occasionally operations using the cache API throw:
-// 'UnknownError: Unexpected internal error. {}'
-// It's not clear under which circumstances this can occur. A dive of
-// the Chromium code didn't shed much light:
-// https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/modules/cache_storage/cache_storage_error.cc;l=26;drc=4cfe86482b000e848009077783ba35f83f3c3cfe
-// https://source.chromium.org/chromium/chromium/src/+/main:content/browser/cache_storage/cache_storage_cache.cc;l=1686;drc=ab68c05beb790d04d1cb7fd8faa0a197fb40d399
-// Given the error is not actionable at present and caching is 'best
-// effort' in any case ignore this error. We will want to throw for
-// errors in general though so as not to hide errors we actually could
-// fix.
-// See b/227785665 for an example.
-function ignoreNonActionableErrors<T>(e: unknown, result: T): T {
-  if (getErrorMessage(e).includes('UnknownError')) {
-    return result;
-  } else {
-    throw e;
+async function getCache(): Promise<Cache|undefined> {
+  if (self.caches === undefined) {
+    // The browser doesn't support cache storage or the page is opened from
+    // a non-secure origin.
+    return undefined;
   }
-}
-
-async function getCache(): Promise<Cache> {
   if (LAZY_CACHE !== undefined) {
     return LAZY_CACHE;
   }
@@ -56,36 +42,40 @@
 async function cacheDelete(key: Request): Promise<boolean> {
   try {
     const cache = await getCache();
+    if (cache === undefined) return false;  // Cache storage not supported.
     return cache.delete(key);
   } catch (e) {
-    return ignoreNonActionableErrors(e, false);
+    return ignoreCacheUnactionableErrors(e, false);
   }
 }
 
 async function cachePut(key: string, value: Response): Promise<void> {
   try {
     const cache = await getCache();
+    if (cache === undefined) return;  // Cache storage not supported.
     cache.put(key, value);
   } catch (e) {
-    ignoreNonActionableErrors(e, undefined);
+    ignoreCacheUnactionableErrors(e, undefined);
   }
 }
 
 async function cacheMatch(key: Request|string): Promise<Response|undefined> {
   try {
     const cache = await getCache();
+    if (cache === undefined) return undefined;  // Cache storage not supported.
     return cache.match(key);
   } catch (e) {
-    return ignoreNonActionableErrors(e, undefined);
+    return ignoreCacheUnactionableErrors(e, undefined);
   }
 }
 
 async function cacheKeys(): Promise<readonly Request[]> {
   try {
     const cache = await getCache();
+    if (cache === undefined) return [];  // Cache storage not supported.
     return cache.keys();
   } catch (e) {
-    return ignoreNonActionableErrors(e, []);
+    return ignoreCacheUnactionableErrors(e, []);
   }
 }
 
diff --git a/ui/src/common/channels.ts b/ui/src/common/channels.ts
index ad0837c..da72836 100644
--- a/ui/src/common/channels.ts
+++ b/ui/src/common/channels.ts
@@ -14,7 +14,7 @@
 
 import {globals} from '../frontend/globals';
 
-const DEFAULT_CHANNEL = 'stable';
+export const DEFAULT_CHANNEL = 'stable';
 const CHANNEL_KEY = 'perfettoUiChannel';
 
 let currentChannel: string|undefined = undefined;
diff --git a/ui/src/common/colorizer.ts b/ui/src/common/colorizer.ts
index 517905c..cbcf70a 100644
--- a/ui/src/common/colorizer.ts
+++ b/ui/src/common/colorizer.ts
@@ -112,7 +112,7 @@
       return DESAT_RED;
     }
     return ORANGE;
-  } else if (state.includes('Sleeping')) {
+  } else if (state.includes('Sleeping') || state.includes('Idle')) {
     return TRANSPARENT_WHITE;
   }
   return INDIGO;
diff --git a/ui/src/common/comparator_builder.ts b/ui/src/common/comparator_builder.ts
new file mode 100644
index 0000000..17d0b64
--- /dev/null
+++ b/ui/src/common/comparator_builder.ts
@@ -0,0 +1,45 @@
+// Copyright (C) 2022 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.
+
+// Simple builder-style class to implement object equality more succinctly.
+export class EqualsBuilder<T> {
+  result = true;
+  first: T;
+  second: T;
+
+  constructor(first: T, second: T) {
+    this.first = first;
+    this.second = second;
+  }
+
+  comparePrimitive(getter: (arg: T) => string | number): EqualsBuilder<T> {
+    if (this.result) {
+      this.result = getter(this.first) === getter(this.second);
+    }
+    return this;
+  }
+
+  compare<S>(
+      comparator: (first: S, second: S) => boolean,
+      getter: (arg: T) => S): EqualsBuilder<T> {
+    if (this.result) {
+      this.result = comparator(getter(this.first), getter(this.second));
+    }
+    return this;
+  }
+
+  equals(): boolean {
+    return this.result;
+  }
+}
diff --git a/ui/src/common/constants.ts b/ui/src/common/constants.ts
index 30a7a2d..cc10366 100644
--- a/ui/src/common/constants.ts
+++ b/ui/src/common/constants.ts
@@ -13,5 +13,3 @@
 // limitations under the License.
 
 export const TRACE_SUFFIX = '.perfetto-trace';
-
-export const TRACE_MARGIN_TIME_S = 1 / 1e7;
diff --git a/ui/src/common/dragndrop_logic.ts b/ui/src/common/dragndrop_logic.ts
new file mode 100644
index 0000000..27280c3
--- /dev/null
+++ b/ui/src/common/dragndrop_logic.ts
@@ -0,0 +1,72 @@
+// Copyright (C) 2022 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 {assertTrue} from '../base/logging';
+
+export type DropDirection = 'left'|'right';
+
+export interface Interval {
+  from: number;
+  to: number;
+}
+
+/*
+ * When a drag'n'drop is performed in a linear sequence, the resulting reordered
+ * array will consist of several contiguous subarrays of the original glued
+ * together.
+ *
+ * This function implements the computation of these intervals.
+ *
+ * The drag'n'drop operation performed is as follows: in the sequence with given
+ * length, the element with index `dragFrom` is dropped on the `direction` to
+ * the element `dragTo`.
+ */
+export function computeIntervals(
+    length: number, dragFrom: number, dragTo: number, direction: DropDirection):
+    Interval[] {
+  assertTrue(dragFrom !== dragTo);
+
+  if (dragTo < dragFrom) {
+    const prefixLen = direction == 'left' ? dragTo : dragTo + 1;
+    return [
+      // First goes unchanged prefix.
+      {from: 0, to: prefixLen},
+      // Then goes dragged element.
+      {from: dragFrom, to: dragFrom + 1},
+      // Then goes suffix up to dragged element (which has already been moved).
+      {from: prefixLen, to: dragFrom},
+      // Then the rest of an array.
+      {from: dragFrom + 1, to: length},
+    ];
+  }
+
+  // Other case: dragTo > dragFrom
+  const prefixLen = direction == 'left' ? dragTo : dragTo + 1;
+  return [
+    {from: 0, to: dragFrom},
+    {from: dragFrom + 1, to: prefixLen},
+    {from: dragFrom, to: dragFrom + 1},
+    {from: prefixLen, to: length},
+  ];
+}
+
+export function performReordering<T>(intervals: Interval[], arr: T[]): T[] {
+  const result: T[] = [];
+
+  for (const interval of intervals) {
+    result.push(...arr.slice(interval.from, interval.to));
+  }
+
+  return result;
+}
diff --git a/ui/src/common/dragndrop_logic_unittest.ts b/ui/src/common/dragndrop_logic_unittest.ts
new file mode 100644
index 0000000..9c1c933
--- /dev/null
+++ b/ui/src/common/dragndrop_logic_unittest.ts
@@ -0,0 +1,45 @@
+// Copyright (C) 2022 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 {
+  computeIntervals,
+  performReordering,
+} from './dragndrop_logic';
+
+describe('performReordering', () => {
+  test('has the same elements in the result', () => {
+    const arr = [1, 2, 3, 4, 5, 6];
+    const arrSet = new Set(arr);
+
+    for (let i = 0; i < arr.length; i++) {
+      for (let j = 0; j < arr.length; j++) {
+        if (i === j) {
+          // The function has a precondition that two indices have to be
+          // different.
+          continue;
+        }
+
+        const permutedLeft =
+            performReordering(computeIntervals(arr.length, i, j, 'left'), arr);
+        expect(new Set(permutedLeft)).toEqual(arrSet);
+        expect(permutedLeft.length).toEqual(arr.length);
+
+        const permutedRight =
+            performReordering(computeIntervals(arr.length, i, j, 'right'), arr);
+        expect(new Set(permutedRight)).toEqual(arrSet);
+        expect(permutedRight.length).toEqual(arr.length);
+      }
+    }
+  });
+});
diff --git a/ui/src/common/empty_state.ts b/ui/src/common/empty_state.ts
index bab7ed4..72b7f8b 100644
--- a/ui/src/common/empty_state.ts
+++ b/ui/src/common/empty_state.ts
@@ -15,7 +15,6 @@
 import {createEmptyRecordConfig} from '../controller/record_config_types';
 import {
   Aggregation,
-  aggregationKey,
 } from '../frontend/pivot_table_redux_types';
 import {
   autosaveConfigStore,
@@ -60,27 +59,22 @@
   return {
     pivotTableRedux: {
       queryResult: null,
-      selectedSlicePivots: [{kind: 'regular', table: 'slice', column: 'name'}],
-      selectedPivots: [],
-      selectedAggregations: keyedMap(
-          aggregationKey,
-          {
-            aggregationFunction: 'SUM',
-            column: {kind: 'regular', table: 'slice', column: 'dur'},
-          },
-          {
-            aggregationFunction: 'SUM',
-            column:
-                {kind: 'regular', table: 'thread_slice', column: 'thread_dur'},
-          },
-          COUNT_AGGREGATION),
+      selectedPivots: [{kind: 'regular', table: 'slice', column: 'name'}],
+      selectedAggregations: [
+        {
+          aggregationFunction: 'SUM',
+          column: {kind: 'regular', table: 'slice', column: 'dur'},
+          sortDirection: 'DESC',
+        },
+        {
+          aggregationFunction: 'SUM',
+          column: {kind: 'regular', table: 'slice', column: 'thread_dur'},
+        },
+        COUNT_AGGREGATION,
+      ],
       constrainToArea: true,
       queryRequested: false,
       argumentNames: [],
-      sortCriteria: {
-        column: {kind: 'regular', table: 'slice', column: 'dur'},
-        order: 'DESC',
-      },
     },
   };
 }
@@ -88,10 +82,8 @@
 export function createEmptyState(): State {
   return {
     version: STATE_VERSION,
-    currentEngineId: undefined,
     nextId: '-1',
     newEngineMode: 'USE_HTTP_RPC_IF_AVAILABLE',
-    engines: {},
     traceTime: {...defaultTraceTime},
     tracks: {},
     uiTrackIdByTraceTrackId: {},
@@ -115,12 +107,6 @@
     lastLoadedConfig: {type: 'NONE'},
 
     frontendLocalState: {
-      omniboxState: {
-        lastUpdate: 0,
-        omnibox: '',
-        mode: 'SEARCH',
-      },
-
       visibleState: {
         ...defaultTraceTime,
         lastUpdate: 0,
@@ -128,6 +114,11 @@
       },
     },
 
+    omniboxState: {
+      omnibox: '',
+      mode: 'SEARCH',
+    },
+
     logsPagination: {
       offset: 0,
       count: 0,
@@ -159,5 +150,13 @@
     fetchChromeCategories: false,
     chromeCategories: undefined,
     nonSerializableState: createEmptyNonSerializableState(),
+
+    logFilteringCriteria: {
+      // The first two log priorities are ignored.
+      minimumLevel: 2,
+      tags: [],
+      textEntry: '',
+      hideNonMatching: true,
+    },
   };
 }
diff --git a/ui/src/common/engine.ts b/ui/src/common/engine.ts
index db10ed2..30c9423 100644
--- a/ui/src/common/engine.ts
+++ b/ui/src/common/engine.ts
@@ -22,6 +22,7 @@
   ComputeMetricResult,
   DisableAndReadMetatraceResult,
   QueryArgs,
+  ResetTraceProcessorArgs,
 } from './protos';
 import {NUM, NUM_NULL, STR} from './query_result';
 import {
@@ -53,6 +54,12 @@
   rawQueryResult: Uint8Array;
 }
 
+export interface TraceProcessorConfig {
+  cropTrackEvents: boolean;
+  ingestFtraceInRawTable: boolean;
+  analyzeTraceProtoContent: boolean;
+}
+
 // Abstract interface of a trace proccessor.
 // This is the TypeScript equivalent of src/trace_processor/rpc.h.
 // There are two concrete implementations:
@@ -74,6 +81,7 @@
   private rxBuf = new ProtoRingBuffer();
   private pendingParses = new Array<Deferred<void>>();
   private pendingEOFs = new Array<Deferred<void>>();
+  private pendingResetTraceProcessors = new Array<Deferred<void>>();
   private pendingQueries = new Array<WritableQueryResult>();
   private pendingRestoreTables = new Array<Deferred<void>>();
   private pendingComputeMetrics = new Array<Deferred<ComputeMetricResult>>();
@@ -170,6 +178,9 @@
       case TPM.TPM_FINALIZE_TRACE_DATA:
         assertExists(this.pendingEOFs.shift()).resolve();
         break;
+      case TPM.TPM_RESET_TRACE_PROCESSOR:
+        assertExists(this.pendingResetTraceProcessors.shift()).resolve();
+        break;
       case TPM.TPM_RESTORE_INITIAL_TABLES:
         assertExists(this.pendingRestoreTables.shift()).resolve();
         break;
@@ -242,6 +253,27 @@
     return asyncRes;  // Linearize with the worker.
   }
 
+  // Updates the TraceProcessor Config. This method creates a new
+  // TraceProcessor instance, so it should be called before passing any trace
+  // data.
+  resetTraceProcessor(
+      {cropTrackEvents, ingestFtraceInRawTable, analyzeTraceProtoContent}:
+          TraceProcessorConfig): Promise<void> {
+    const asyncRes = defer<void>();
+    this.pendingResetTraceProcessors.push(asyncRes);
+    const rpc = TraceProcessorRpc.create();
+    rpc.request = TPM.TPM_RESET_TRACE_PROCESSOR;
+    const args = rpc.resetTraceProcessorArgs = new ResetTraceProcessorArgs();
+    args.dropTrackEventDataBefore = cropTrackEvents ?
+        ResetTraceProcessorArgs.DropTrackEventDataBefore
+            .TRACK_EVENT_RANGE_OF_INTEREST :
+        ResetTraceProcessorArgs.DropTrackEventDataBefore.NO_DROP;
+    args.ingestFtraceInRawTable = ingestFtraceInRawTable;
+    args.analyzeTraceProtoContent = analyzeTraceProtoContent;
+    this.rpcSendRequest(rpc);
+    return asyncRes;
+  }
+
   // Resets the trace processor state by destroying any table/views created by
   // the UI after loading.
   restoreInitialTables(): Promise<void> {
@@ -282,11 +314,17 @@
   // for (const it = res.iter({foo: NUM, bar:STR}); it.valid(); it.next()) {
   //   console.log(it.foo, it.bar);
   // }
-  query(sqlQuery: string): Promise<QueryResult>&QueryResult {
+  //
+  // Optional |tag| (usually a component name) can be provided to allow
+  // attributing trace processor workload to different UI components.
+  query(sqlQuery: string, tag?: string): Promise<QueryResult>&QueryResult {
     const rpc = TraceProcessorRpc.create();
     rpc.request = TPM.TPM_QUERY_STREAMING;
     rpc.queryArgs = new QueryArgs();
     rpc.queryArgs.sqlQuery = sqlQuery;
+    if (tag) {
+      rpc.queryArgs.tag = tag;
+    }
     const result = createQueryResult({
       query: sqlQuery,
     });
@@ -299,9 +337,13 @@
     return this._isMetatracingEnabled;
   }
 
-  enableMetatrace() {
+  enableMetatrace(categories?: perfetto.protos.MetatraceCategories) {
     const rpc = TraceProcessorRpc.create();
     rpc.request = TPM.TPM_ENABLE_METATRACE;
+    if (categories) {
+      rpc.enableMetatraceArgs = new perfetto.protos.EnableMetatraceArgs();
+      rpc.enableMetatraceArgs.categories = categories;
+    }
     this._isMetatracingEnabled = true;
     this.rpcSendRequest(rpc);
   }
@@ -401,4 +443,24 @@
 
     return new TimeSpan(startBound, endBound);
   }
+
+  getProxy(tag: string): EngineProxy {
+    return new EngineProxy(this, tag);
+  }
+}
+
+// Lightweight wrapper over Engine exposing only `query` method and annotating
+// all queries going through it with a tag.
+export class EngineProxy {
+  private engine: Engine;
+  private tag: string;
+
+  constructor(engine: Engine, tag: string) {
+    this.engine = engine;
+    this.tag = tag;
+  }
+
+  query(sqlQuery: string, tag?: string): Promise<QueryResult>&QueryResult {
+    return this.engine.query(sqlQuery, tag || this.tag);
+  }
 }
diff --git a/ui/src/common/errors.ts b/ui/src/common/errors.ts
index dfc2908..9813b0e 100644
--- a/ui/src/common/errors.ts
+++ b/ui/src/common/errors.ts
@@ -49,3 +49,22 @@
   }
   return asString;
 }
+
+// Occasionally operations using the cache API throw:
+// 'UnknownError: Unexpected internal error. {}'
+// It's not clear under which circumstances this can occur. A dive of
+// the Chromium code didn't shed much light:
+// https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/modules/cache_storage/cache_storage_error.cc;l=26;drc=4cfe86482b000e848009077783ba35f83f3c3cfe
+// https://source.chromium.org/chromium/chromium/src/+/main:content/browser/cache_storage/cache_storage_cache.cc;l=1686;drc=ab68c05beb790d04d1cb7fd8faa0a197fb40d399
+// Given the error is not actionable at present and caching is 'best
+// effort' in any case ignore this error. We will want to throw for
+// errors in general though so as not to hide errors we actually could
+// fix.
+// See b/227785665 for an example.
+export function ignoreCacheUnactionableErrors<T>(e: unknown, result: T): T {
+  if (getErrorMessage(e).includes('UnknownError')) {
+    return result;
+  } else {
+    throw e;
+  }
+}
diff --git a/ui/src/common/logs.ts b/ui/src/common/logs.ts
index 7145619..0fc2fae 100644
--- a/ui/src/common/logs.ts
+++ b/ui/src/common/logs.ts
@@ -32,4 +32,6 @@
   priorities: number[];
   tags: string[];
   messages: string[];
+  isHighlighted: boolean[];
+  processName: string[];
 }
diff --git a/ui/src/common/metatracing.ts b/ui/src/common/metatracing.ts
new file mode 100644
index 0000000..c26371d
--- /dev/null
+++ b/ui/src/common/metatracing.ts
@@ -0,0 +1,124 @@
+// Copyright (C) 2022  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 {PerfettoMetatrace, Trace, TracePacket} from '../common/protos';
+import {perfetto} from '../gen/protos';
+
+import {featureFlags} from './feature_flags';
+import {toNs} from './time';
+
+const METATRACING_BUFFER_SIZE = 100000;
+const JS_THREAD_ID = 2;
+
+import MetatraceCategories = perfetto.protos.MetatraceCategories;
+
+const AOMT_FLAG = featureFlags.register({
+  id: 'alwaysOnMetatracing',
+  name: 'Enable always-on-metatracing',
+  description: 'Enables trace events in the UI and trace processor',
+  defaultValue: false,
+});
+
+const AOMT_DETAILED_FLAG = featureFlags.register({
+  id: 'alwaysOnMetatracing_detailed',
+  name: 'Detailed always-on-metatracing',
+  description: 'Enables recording additional events for trace event',
+  defaultValue: false,
+});
+
+function getInitialCategories(): MetatraceCategories|undefined {
+  if (!AOMT_FLAG.get()) return undefined;
+  if (AOMT_DETAILED_FLAG.get()) return MetatraceCategories.ALL;
+  return MetatraceCategories.TOPLEVEL;
+}
+
+let enabledCategories: MetatraceCategories|undefined = getInitialCategories();
+
+export function enableMetatracing(categories?: MetatraceCategories) {
+  enabledCategories = categories || MetatraceCategories.ALL;
+}
+
+export function disableMetatracingAndGetTrace(): Uint8Array {
+  enabledCategories = undefined;
+  return readMetatrace();
+}
+
+export function isMetatracingEnabled(): boolean {
+  return enabledCategories !== undefined;
+}
+
+export function getEnabledMetatracingCategories(): MetatraceCategories|
+    undefined {
+  return enabledCategories;
+}
+
+interface TraceEvent {
+  eventName: string;
+  startNs: number;
+  durNs: number;
+}
+
+const traceEvents: TraceEvent[] = [];
+
+function readMetatrace(): Uint8Array {
+  const eventToPacket = (e: TraceEvent): TracePacket => {
+    return TracePacket.create({
+      timestamp: e.startNs,
+      timestampClockId: 1,
+      perfettoMetatrace: PerfettoMetatrace.create({
+        eventName: e.eventName,
+        threadId: JS_THREAD_ID,
+        eventDurationNs: e.durNs,
+      }),
+    });
+  };
+  const packets: TracePacket[] = [];
+  for (const event of traceEvents) {
+    packets.push(eventToPacket(event));
+  }
+  const trace = Trace.create({
+    packet: packets,
+  });
+  return Trace.encode(trace).finish();
+}
+
+export type TraceEventScope = {
+  startNs: number, eventName: string;
+};
+
+const correctedTimeOrigin = new Date().getTime() - performance.now();
+
+function now(): number {
+  return toNs((correctedTimeOrigin + performance.now()) / 1000);
+}
+
+export function traceEventBegin(eventName: string): TraceEventScope {
+  return {
+    eventName,
+    startNs: now(),
+  };
+}
+
+export function traceEventEnd(traceEvent: TraceEventScope) {
+  if (!isMetatracingEnabled()) return;
+
+  traceEvents.push({
+    eventName: traceEvent.eventName,
+    startNs: traceEvent.startNs,
+    durNs: now() - traceEvent.startNs,
+  });
+  while (traceEvents.length > METATRACING_BUFFER_SIZE) {
+    traceEvents.shift();
+  }
+}
diff --git a/ui/src/common/plugin_api.ts b/ui/src/common/plugin_api.ts
index 0597974..fa8a9fc 100644
--- a/ui/src/common/plugin_api.ts
+++ b/ui/src/common/plugin_api.ts
@@ -12,17 +12,71 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import {EngineProxy} from '../common/engine';
 import {TrackControllerFactory} from '../controller/track_controller';
 import {TrackCreator} from '../frontend/track';
 
+export {EngineProxy} from '../common/engine';
+export {
+  NUM,
+  NUM_NULL,
+  STR,
+  STR_NULL,
+} from '../common/query_result';
+
+export interface TrackInfo {
+  // The id of this 'type' of track. This id is used to select the
+  // correct |TrackCreator| to construct the track.
+  trackKind: string;
+
+  // A human readable name for this specific track. It will normally be
+  // displayed on the left-hand-side of the track.
+  name: string;
+
+  // An opaque config for the track.
+  config: {};
+}
+
+// Called any time a trace is loaded. Plugins should return all
+// potential tracks. Zero or more of the provided tracks may be
+// instantiated depending on the users choices.
+export type TrackProvider = (engine: EngineProxy) => Promise<TrackInfo[]>;
+
 // The public API plugins use to extend the UI. This is passed to each
 // plugin via the exposed 'activate' function.
 export interface PluginContext {
+  // DEPRECATED. In prior versions of the UI tracks were split into a
+  // 'TrackController' and a 'Track'. In more recent versions of the UI
+  // the functionality of |TrackController| has been merged into Track so
+  // |TrackController|s are not necessary in new code.
   registerTrackController(track: TrackControllerFactory): void;
+
+  // Register a |TrackProvider|. |TrackProvider|s return |TrackInfo| for
+  // all potential tracks in a trace. The core UI selects some of these
+  // |TrackInfo|s and constructs concrete Track instances using the
+  // registered |TrackCreator|s.
+  registerTrackProvider(provider: TrackProvider): void;
+
+  // Register a track factory. The core UI invokes |TrackCreator| to
+  // construct tracks discovered by invoking |TrackProvider|s.
+  // The split between 'construction' and 'discovery' allows
+  // plugins to reuse common tracks for new data. For example: the
+  // dev.perfetto.AndroidGpu plugin could register a TrackProvider
+  // which returns GPU counter tracks. The counter track factory itself
+  // could be registered in dev.perfetto.CounterTrack - a whole
+  // different plugin.
   registerTrack(track: TrackCreator): void;
 }
 
 export interface PluginInfo {
+  // A unique string for your plugin. To ensure the name is unique you
+  // may wish to use a URL with reversed components in the manner of
+  // Java package names.
   pluginId: string;
+
+  // This function is called when the plugin is loaded. Generally this
+  // is called at most once shortly after the UI is loaded. However in
+  // some situations it can be called multiple times - for example
+  // when the user is toggling plugins on/off.
   activate: (ctx: PluginContext) => void;
 }
diff --git a/ui/src/common/plugins.ts b/ui/src/common/plugins.ts
index b38c743..aa414bc 100644
--- a/ui/src/common/plugins.ts
+++ b/ui/src/common/plugins.ts
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import {Engine} from '../common/engine';
 import {
   TrackControllerFactory,
   trackControllerRegistry,
@@ -19,19 +20,28 @@
 import {TrackCreator} from '../frontend/track';
 import {trackRegistry} from '../frontend/track_registry';
 
-import {PluginContext, PluginInfo} from './plugin_api';
+import {
+  PluginContext,
+  PluginInfo,
+  TrackInfo,
+  TrackProvider,
+} from './plugin_api';
 import {Registry} from './registry';
 
 // Every plugin gets its own PluginContext. This is how we keep track
 // what each plugin is doing and how we can blame issues on particular
 // plugins.
 export class PluginContextImpl implements PluginContext {
-  pluginName: string;
+  readonly pluginId: string;
+  private trackProviders: TrackProvider[];
 
-  constructor(pluginName: string) {
-    this.pluginName = pluginName;
+  constructor(pluginId: string) {
+    this.pluginId = pluginId;
+    this.trackProviders = [];
   }
 
+  // ==================================================================
+  // The plugin facing API of PluginContext:
   registerTrackController(track: TrackControllerFactory): void {
     trackControllerRegistry.register(track);
   }
@@ -39,8 +49,82 @@
   registerTrack(track: TrackCreator): void {
     trackRegistry.register(track);
   }
+
+  registerTrackProvider(provider: TrackProvider) {
+    this.trackProviders.push(provider);
+  }
+  // ==================================================================
+
+  // ==================================================================
+  // Internal facing API:
+  findPotentialTracks(engine: Engine): Promise<TrackInfo[]>[] {
+    const proxy = engine.getProxy(this.pluginId);
+    return this.trackProviders.map((f) => f(proxy));
+  }
+
+  // Unload the plugin. Ideally no plugin code runs after this point.
+  // PluginContext should unregister everything.
+  revoke() {
+    // TODO(hjd): Remove from trackControllerRegistry, trackRegistry,
+    // etc.
+  }
+  // ==================================================================
 }
 
-export const pluginRegistry = new Registry<PluginInfo>((info) => {
-  return info.pluginId;
-});
+// 'Static' registry of all known plugins.
+export class PluginRegistry extends Registry<PluginInfo> {
+  constructor() {
+    super((info) => info.pluginId);
+  }
+}
+
+export class PluginManager {
+  private registry: PluginRegistry;
+  private contexts: Map<string, PluginContextImpl>;
+
+  constructor(registry: PluginRegistry) {
+    this.registry = registry;
+    this.contexts = new Map();
+  }
+
+  activatePlugin(pluginId: string): void {
+    if (this.isActive(pluginId)) {
+      return;
+    }
+    const pluginInfo = this.registry.get(pluginId);
+    const context = new PluginContextImpl(pluginId);
+    this.contexts.set(pluginId, context);
+    pluginInfo.activate(context);
+  }
+
+  deactivatePlugin(pluginId: string): void {
+    const context = this.getPluginContext(pluginId);
+    if (context === undefined) {
+      return;
+    }
+    context.revoke();
+    this.contexts.delete(pluginId);
+  }
+
+  isActive(pluginId: string): boolean {
+    return this.getPluginContext(pluginId) !== undefined;
+  }
+
+  getPluginContext(pluginId: string): PluginContextImpl|undefined {
+    return this.contexts.get(pluginId);
+  }
+
+  findPotentialTracks(engine: Engine): Promise<TrackInfo[]>[] {
+    const promises = [];
+    for (const context of this.contexts.values()) {
+      for (const promise of context.findPotentialTracks(engine)) {
+        promises.push(promise);
+      }
+    }
+    return promises;
+  }
+}
+
+// TODO(hjd): Sort out the story for global singletons like these:
+export const pluginRegistry = new PluginRegistry();
+export const pluginManager = new PluginManager(pluginRegistry);
diff --git a/ui/src/common/plugins_unittest.ts b/ui/src/common/plugins_unittest.ts
new file mode 100644
index 0000000..47594bc
--- /dev/null
+++ b/ui/src/common/plugins_unittest.ts
@@ -0,0 +1,25 @@
+import {PluginContext} from './plugin_api';
+import {PluginManager, PluginRegistry} from './plugins';
+
+test('can activate plugin', () => {
+  const registry = new PluginRegistry();
+  registry.register({
+    pluginId: 'foo',
+    activate: (_: PluginContext) => {},
+  });
+  const manager = new PluginManager(registry);
+  manager.activatePlugin('foo');
+  expect(manager.isActive('foo')).toBe(true);
+});
+
+test('can deactivate plugin', () => {
+  const registry = new PluginRegistry();
+  registry.register({
+    pluginId: 'foo',
+    activate: (_: PluginContext) => {},
+  });
+  const manager = new PluginManager(registry);
+  manager.activatePlugin('foo');
+  manager.deactivatePlugin('foo');
+  expect(manager.isActive('foo')).toBe(false);
+});
diff --git a/ui/src/common/protos.ts b/ui/src/common/protos.ts
index abe0f90..1fa770d 100644
--- a/ui/src/common/protos.ts
+++ b/ui/src/common/protos.ts
@@ -23,12 +23,14 @@
     protos.perfetto.protos.AndroidPowerConfig.BatteryCounters;
 import BufferConfig = protos.perfetto.protos.TraceConfig.BufferConfig;
 import ChromeConfig = protos.perfetto.protos.ChromeConfig;
+import TrackEventConfig = protos.perfetto.protos.TrackEventConfig;
 import ConsumerPort = protos.perfetto.protos.ConsumerPort;
 import NativeContinuousDumpConfig =
     protos.perfetto.protos.HeapprofdConfig.ContinuousDumpConfig;
 import JavaContinuousDumpConfig =
     protos.perfetto.protos.JavaHprofConfig.ContinuousDumpConfig;
 import DataSourceConfig = protos.perfetto.protos.DataSourceConfig;
+import DataSourceDescriptor = protos.perfetto.protos.DataSourceDescriptor;
 import FtraceConfig = protos.perfetto.protos.FtraceConfig;
 import HeapprofdConfig = protos.perfetto.protos.HeapprofdConfig;
 import JavaHprofConfig = protos.perfetto.protos.JavaHprofConfig;
@@ -53,18 +55,26 @@
 import GetTraceStatsRequest = protos.perfetto.protos.GetTraceStatsRequest;
 import FreeBuffersRequest = protos.perfetto.protos.FreeBuffersRequest;
 import ReadBuffersRequest = protos.perfetto.protos.ReadBuffersRequest;
+import QueryServiceStateRequest =
+    protos.perfetto.protos.QueryServiceStateRequest;
 import EnableTracingResponse = protos.perfetto.protos.EnableTracingResponse;
 import DisableTracingResponse = protos.perfetto.protos.DisableTracingResponse;
 import GetTraceStatsResponse = protos.perfetto.protos.GetTraceStatsResponse;
 import FreeBuffersResponse = protos.perfetto.protos.FreeBuffersResponse;
 import ReadBuffersResponse = protos.perfetto.protos.ReadBuffersResponse;
+import QueryServiceStateResponse =
+    protos.perfetto.protos.QueryServiceStateResponse;
 // Trace Processor protos.
 import QueryArgs = protos.perfetto.protos.QueryArgs;
+import ResetTraceProcessorArgs = protos.perfetto.protos.ResetTraceProcessorArgs;
 import StatusResult = protos.perfetto.protos.StatusResult;
 import ComputeMetricArgs = protos.perfetto.protos.ComputeMetricArgs;
 import ComputeMetricResult = protos.perfetto.protos.ComputeMetricResult;
 import DisableAndReadMetatraceResult =
     protos.perfetto.protos.DisableAndReadMetatraceResult;
+import Trace = protos.perfetto.protos.Trace;
+import TracePacket = protos.perfetto.protos.TracePacket;
+import PerfettoMetatrace = protos.perfetto.protos.PerfettoMetatrace;
 
 export {
   AndroidLogConfig,
@@ -78,6 +88,7 @@
   ComputeMetricResult,
   DataSourceConfig,
   DisableAndReadMetatraceResult,
+  DataSourceDescriptor,
   DisableTracingRequest,
   DisableTracingResponse,
   EnableTracingRequest,
@@ -102,12 +113,19 @@
   MeminfoCounters,
   NativeContinuousDumpConfig,
   ProcessStatsConfig,
+  PerfettoMetatrace,
   ReadBuffersRequest,
   ReadBuffersResponse,
+  QueryServiceStateRequest,
+  QueryServiceStateResponse,
   QueryArgs,
+  ResetTraceProcessorArgs,
   StatCounters,
   StatusResult,
   SysStatsConfig,
+  Trace,
   TraceConfig,
+  TrackEventConfig,
+  TracePacket,
   VmstatCounters,
 };
diff --git a/ui/src/common/query_result.ts b/ui/src/common/query_result.ts
index 347c4ed..9d44c9e 100644
--- a/ui/src/common/query_result.ts
+++ b/ui/src/common/query_result.ts
@@ -57,8 +57,10 @@
 export const STR = 'str';
 export const NUM_NULL: number|null = 1;
 export const STR_NULL: string|null = 'str_null';
+export const BLOB: Uint8Array = new Uint8Array();
+export const BLOB_NULL: Uint8Array|null = new Uint8Array();
 
-export type ColumnType = string|number|null;
+export type ColumnType = string|number|null|Uint8Array;
 
 // Info that could help debug a query error. For example the query
 // in question, the stack where the query was issued, the active
@@ -119,11 +121,32 @@
       return 'STR';
     case STR_NULL:
       return 'STR_NULL';
+    case BLOB:
+      return 'BLOB';
+    case BLOB_NULL:
+      return 'BLOB_NULL';
     default:
       return `INVALID(${t})`;
   }
 }
 
+function isCompatible(actual: CellType, expected: ColumnType): boolean {
+  switch (actual) {
+    case CellType.CELL_NULL:
+      return expected === NUM_NULL || expected === STR_NULL ||
+          expected === BLOB_NULL;
+    case CellType.CELL_VARINT:
+    case CellType.CELL_FLOAT64:
+      return expected === NUM || expected === NUM_NULL;
+    case CellType.CELL_STRING:
+      return expected === STR || expected === STR_NULL;
+    case CellType.CELL_BLOB:
+      return expected === BLOB || expected === BLOB_NULL;
+    default:
+      throw new Error(`Unknown CellType ${actual}`);
+  }
+}
+
 // Disable Long.js support in protobuf. This seems to be enabled only in tests
 // but not in production code. In any case, for now we want casting to number
 // accepting the 2**53 limitation. This is consistent with passing
@@ -183,6 +206,12 @@
   // have been fetched. The promise return value is always the object iself.
   waitAllRows(): Promise<QueryResult>;
 
+  // Returns a promise that is resolved when either:
+  // - more rows are available
+  // - all rows are available
+  // The promise return value is always the object iself.
+  waitMoreRows(): Promise<QueryResult>;
+
   // Can return an empty array if called before the first batch is resolved.
   // This should be called only after having awaited for at least one batch.
   columns(): string[];
@@ -194,10 +223,6 @@
   // Returns the number of SQL statement that produced output rows. This number
   // is <= statementCount().
   statementWithOutputCount(): number;
-
-  // TODO(primiano): next CLs will introduce a waitMoreRows() to allow tracks
-  // to await until some more data (but not necessarily all) is available. For
-  // now everything uses waitAllRows().
 }
 
 // Interface exposed to engine.ts to pump in the data as new row batches arrive.
@@ -248,6 +273,10 @@
   // last result batch has been been retrieved.
   private allRowsPromise?: Deferred<QueryResult>;
 
+  // Promise awaiting on waitMoreRows(). This resolved when the next
+  // batch is appended via appendResultBatch.
+  private moreRowsPromise?: Deferred<QueryResult>;
+
   isComplete(): boolean {
     return this._isComplete;
   }
@@ -288,6 +317,20 @@
     return this.allRowsPromise;
   }
 
+  waitMoreRows(): Promise<QueryResult> {
+    if (this.moreRowsPromise !== undefined) {
+      return this.moreRowsPromise;
+    }
+
+    const moreRowsPromise = defer<QueryResult>();
+    if (this._isComplete) {
+      this.resolveOrReject(moreRowsPromise, this);
+    } else {
+      this.moreRowsPromise = moreRowsPromise;
+    }
+    return moreRowsPromise;
+  }
+
   // --- WritableQueryResult implementation.
 
   // Called by the engine when a new QueryResult is available. Note that a
@@ -369,6 +412,11 @@
       }  // switch (tag)
     }    // while (pos < end)
 
+    if (this.moreRowsPromise !== undefined) {
+      this.resolveOrReject(this.moreRowsPromise, this);
+      this.moreRowsPromise = undefined;
+    }
+
     if (this._isComplete && this.allRowsPromise !== undefined) {
       this.resolveOrReject(this.allRowsPromise, this);
     }
@@ -627,8 +675,7 @@
 
         case CellType.CELL_BLOB:
           const blob = this.blobCells[this.nextBlobCell++];
-          throw new Error(`TODO implement BLOB support (${blob})`);
-          // outRow[colName] = blob;
+          rowData[colName] = blob;
           break;
 
         default:
@@ -696,20 +743,16 @@
       if (expType === undefined) continue;
 
       let err = '';
-      if (actualType === CellType.CELL_NULL &&
-          (expType !== STR_NULL && expType !== NUM_NULL)) {
-        err = 'SQL value is NULL but that was not expected' +
-            ` (expected type: ${columnTypeToString(expType)}). ` +
-            'Did you intend to use NUM_NULL or STR_NULL?';
-      } else if (
-          ((actualType === CellType.CELL_VARINT ||
-            actualType === CellType.CELL_FLOAT64) &&
-           (expType !== NUM && expType !== NUM_NULL)) ||
-          ((actualType === CellType.CELL_STRING) &&
-           (expType !== STR && expType !== STR_NULL))) {
-        err = `Incompatible cell type. Expected: ${
-            columnTypeToString(
-                expType)} actual: ${CELL_TYPE_NAMES[actualType]}`;
+      if (!isCompatible(actualType, expType)) {
+        if (actualType === CellType.CELL_NULL) {
+          err = 'SQL value is NULL but that was not expected' +
+              ` (expected type: ${columnTypeToString(expType)}). ` +
+              'Did you intend to use NUM_NULL, STR_NULL or BLOB_NULL?';
+        } else {
+          err = `Incompatible cell type. Expected: ${
+              columnTypeToString(
+                  expType)} actual: ${CELL_TYPE_NAMES[actualType]}`;
+        }
       }
       if (err.length > 0) {
         throw new Error(
@@ -768,6 +811,9 @@
   waitAllRows() {
     return this.impl.waitAllRows();
   }
+  waitMoreRows() {
+    return this.impl.waitMoreRows();
+  }
   isComplete() {
     return this.impl.isComplete();
   }
diff --git a/ui/src/common/query_result_unittest.ts b/ui/src/common/query_result_unittest.ts
index 5fad6fe..8f508e7 100644
--- a/ui/src/common/query_result_unittest.ts
+++ b/ui/src/common/query_result_unittest.ts
@@ -300,3 +300,45 @@
   }
   expect(() => qr.iter({x_3: NUM})).toThrowError(/\bx_3\b.*not found/);
 });
+
+
+test('QueryResult.WaitMoreRows', async () => {
+  const batchA = QueryResultProto.CellsBatch.create({
+    cells: [T.CELL_VARINT],
+    varintCells: [42],
+    isLastBatch: false,
+  });
+  const resProtoA = QueryResultProto.create({
+    columnNames: ['a_int'],
+    batch: [batchA],
+  });
+
+  const qr = createQueryResult({query: 'Some query'});
+  qr.appendResultBatch(QueryResultProto.encode(resProtoA).finish());
+
+  const batchB = QueryResultProto.CellsBatch.create({
+    cells: [T.CELL_VARINT],
+    varintCells: [43],
+    isLastBatch: true,
+  });
+  const resProtoB = QueryResultProto.create({
+    columnNames: [],
+    batch: [batchB],
+  });
+
+  const waitPromise = qr.waitMoreRows();
+  const appendPromise = new Promise<void>((resolve, _) => {
+    setTimeout(() => {
+      qr.appendResultBatch(QueryResultProto.encode(resProtoB).finish());
+      resolve();
+    }, 0);
+  });
+
+  expect(qr.isComplete()).toBe(false);
+  expect(qr.numRows()).toBe(1);
+
+  await Promise.all([waitPromise, appendPromise]);
+
+  expect(qr.isComplete()).toBe(true);
+  expect(qr.numRows()).toBe(2);
+});
diff --git a/ui/src/common/query_utils.ts b/ui/src/common/query_utils.ts
new file mode 100644
index 0000000..0c9a855
--- /dev/null
+++ b/ui/src/common/query_utils.ts
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+enum EscapeFlag {
+  CaseInsensitive = 1,
+  MatchAny = 2,
+}
+
+function escape(s: string, flags?: number): string {
+  flags = flags === undefined ? 0 : flags;
+  // See https://www.sqlite.org/lang_expr.html#:~:text=A%20string%20constant
+  s = s.replace(/\'/g, '\'\'');
+  s = s.replace(/\[/g, '[[]');
+  if (flags & EscapeFlag.CaseInsensitive) {
+    s = s.replace(/[a-zA-Z]/g, (m) => {
+      const lower = m.toLowerCase();
+      const upper = m.toUpperCase();
+      return `[${lower}${upper}]`;
+    });
+  }
+  s = s.replace(/\?/g, '[?]');
+  s = s.replace(/\*/g, '[*]');
+  if (flags & EscapeFlag.MatchAny) {
+    s = `*${s}*`;
+  }
+  s = `'${s}'`;
+  return s;
+}
+
+export function escapeQuery(s: string): string {
+  return escape(s);
+}
+
+export function escapeSearchQuery(s: string): string {
+  return escape(s, EscapeFlag.CaseInsensitive | EscapeFlag.MatchAny);
+}
+
+export function escapeGlob(s: string): string {
+  // For globs we are only preoccupied by mismatching single quotes.
+  s = s.replace(/\'/g, '\'\'');
+  return `'*${s}*'`;
+}
diff --git a/ui/src/common/query_utils_unittest.ts b/ui/src/common/query_utils_unittest.ts
new file mode 100644
index 0000000..3ac9869
--- /dev/null
+++ b/ui/src/common/query_utils_unittest.ts
@@ -0,0 +1,41 @@
+// Copyright (C) 2022 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 {escapeGlob, escapeQuery, escapeSearchQuery} from './query_utils';
+
+test('escapeQuery', () => {
+  expect(escapeQuery(``)).toEqual(`''`);
+  expect(escapeQuery(`'`)).toEqual(`''''`);
+  expect(escapeQuery(`hello`)).toEqual(`'hello'`);
+  expect(escapeQuery('foo\'bar')).toEqual(`'foo''bar'`);
+  expect(escapeQuery('*_*')).toEqual(`'[*]_[*]'`);
+  expect(escapeQuery('[]?')).toEqual(`'[[]][?]'`);
+});
+
+test('escapeSearchQuery', () => {
+  expect(escapeSearchQuery(``)).toEqual(`'**'`);
+  expect(escapeSearchQuery(`hello`)).toEqual(`'*[hH][eE][lL][lL][oO]*'`);
+  expect(escapeSearchQuery('a\'b')).toEqual(`'*[aA]''[bB]*'`);
+  expect(escapeSearchQuery('*_*')).toEqual(`'*[*]_[*]*'`);
+  expect(escapeSearchQuery('[]?')).toEqual(`'*[[]][?]*'`);
+});
+
+test('escapeGlob', () => {
+  expect(escapeGlob(``)).toEqual(`'**'`);
+  expect(escapeGlob(`'`)).toEqual(`'*''*'`);
+  expect(escapeGlob(`hello`)).toEqual(`'*hello*'`);
+  expect(escapeGlob('foo\'bar')).toEqual(`'*foo''bar*'`);
+  expect(escapeGlob('*_*')).toEqual(`'**_**'`);
+  expect(escapeGlob('[]?')).toEqual(`'*[]?*'`);
+});
diff --git a/ui/src/common/recordingV2/adb_connection_impl.ts b/ui/src/common/recordingV2/adb_connection_impl.ts
new file mode 100644
index 0000000..9fae926
--- /dev/null
+++ b/ui/src/common/recordingV2/adb_connection_impl.ts
@@ -0,0 +1,88 @@
+// Copyright (C) 2022 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 {_TextDecoder} from 'custom_utils';
+
+import {defer} from '../../base/deferred';
+import {ArrayBufferBuilder} from '../array_buffer_builder';
+
+import {AdbFileHandler} from './adb_file_handler';
+import {
+  AdbConnection,
+  ByteStream,
+  OnDisconnectCallback,
+  OnMessageCallback,
+} from './recording_interfaces_v2';
+
+const textDecoder = new _TextDecoder();
+
+export abstract class AdbConnectionImpl implements AdbConnection {
+  // onStatus and onDisconnect are set to callbacks passed from the caller.
+  // This happens for instance in the AndroidWebusbTarget, which instantiates
+  // them with callbacks passed from the UI.
+  onStatus: OnMessageCallback = () => {};
+  onDisconnect: OnDisconnectCallback = (_) => {};
+
+  // Starts a shell command, and returns a promise resolved when the command
+  // completes.
+  async shellAndWaitCompletion(cmd: string): Promise<void> {
+    const adbStream = await this.shell(cmd);
+    const onStreamingEnded = defer<void>();
+
+    // We wait for the stream to be closed by the device, which happens
+    // after the shell command is successfully received.
+    adbStream.addOnStreamCloseCallback(() => {
+      onStreamingEnded.resolve();
+    });
+    return onStreamingEnded;
+  }
+
+  // Starts a shell command, then gathers all its output and returns it as
+  // a string.
+  async shellAndGetOutput(cmd: string): Promise<string> {
+    const adbStream = await this.shell(cmd);
+    const commandOutput = new ArrayBufferBuilder();
+    const onStreamingEnded = defer<string>();
+
+    adbStream.addOnStreamDataCallback((data: Uint8Array) => {
+      commandOutput.append(data);
+    });
+    adbStream.addOnStreamCloseCallback(() => {
+      onStreamingEnded.resolve(
+          textDecoder.decode(commandOutput.toArrayBuffer()));
+    });
+    return onStreamingEnded;
+  }
+
+  async push(binary: Uint8Array, path: string): Promise<void> {
+    const byteStream = await this.openStream('sync:');
+    await (new AdbFileHandler(byteStream)).pushBinary(binary, path);
+    // We need to wait until the bytestream is closed. Otherwise, we can have a
+    // race condition:
+    // If this is the last stream, it will try to disconnect the device. In the
+    // meantime, the caller might create another stream which will try to open
+    // the device.
+    await byteStream.closeAndWaitForTeardown();
+  }
+
+  abstract shell(cmd: string): Promise<ByteStream>;
+
+  abstract canConnectWithoutContention(): Promise<boolean>;
+
+  abstract connectSocket(path: string): Promise<ByteStream>;
+
+  abstract disconnect(disconnectMessage?: string): Promise<void>;
+
+  protected abstract openStream(destination: string): Promise<ByteStream>;
+}
diff --git a/ui/src/common/recordingV2/adb_connection_over_websocket.ts b/ui/src/common/recordingV2/adb_connection_over_websocket.ts
index 5c86098..7287b65 100644
--- a/ui/src/common/recordingV2/adb_connection_over_websocket.ts
+++ b/ui/src/common/recordingV2/adb_connection_over_websocket.ts
@@ -14,82 +14,59 @@
 
 import {_TextDecoder} from 'custom_utils';
 
-import {defer} from '../../base/deferred';
+import {defer, Deferred} from '../../base/deferred';
 
-import {buildAbdWebsocketCommand} from './adb_over_websocket_utils';
-import {ALLOW_USB_DEBUGGING} from './adb_targets_utils';
+import {AdbConnectionImpl} from './adb_connection_impl';
+import {RecordingError} from './recording_error_handling';
 import {
-  AdbConnection,
   ByteStream,
   OnDisconnectCallback,
   OnStreamCloseCallback,
   OnStreamDataCallback,
 } from './recording_interfaces_v2';
+import {
+  ALLOW_USB_DEBUGGING,
+  buildAbdWebsocketCommand,
+  WEBSOCKET_UNABLE_TO_CONNECT,
+} from './recording_utils';
 
 const textDecoder = new _TextDecoder();
 
-export const WEBSOCKET_UNABLE_TO_CONNECT =
-    'Unable to connect to device via websocket.';
-
-export class AdbConnectionOverWebsocket implements AdbConnection {
+export class AdbConnectionOverWebsocket extends AdbConnectionImpl {
   private streams = new Set<AdbOverWebsocketStream>();
 
   onDisconnect: OnDisconnectCallback = (_) => {};
 
   constructor(
-      private deviceSerialNumber: string, private websocketUrl: string) {}
-
-  push(_binary: ArrayBuffer, _path: string): Promise<void> {
-    // TODO(octaviant) implement
-    throw new Error('Not implemented yet');
+      private deviceSerialNumber: string, private websocketUrl: string) {
+    super();
   }
 
-  shell(_cmd: string): Promise<AdbOverWebsocketStream> {
-    // TODO(octaviant) implement
-    throw new Error('Not implemented yet');
+  shell(cmd: string): Promise<AdbOverWebsocketStream> {
+    return this.openStream('shell:' + cmd);
   }
 
-  async connectSocket(path: string): Promise<ByteStream> {
-    const webSocket = new WebSocket(this.websocketUrl);
-    const byteStream =
-        new AdbOverWebsocketStream(webSocket, this.closeStream.bind(this));
-    const byteStreamConnected = defer<AdbOverWebsocketStream>();
-
-    webSocket.onopen = () => webSocket.send(
-        buildAbdWebsocketCommand(`host:transport:${this.deviceSerialNumber}`));
-
-    webSocket.onmessage = async (evt) => {
-      if (!byteStream.isOpen()) {
-        const txt = await evt.data.text();
-        const prefix = txt.substr(0, 4);
-        if (prefix === 'OKAY') {
-          byteStream.setStreamOpen();
-          webSocket.send(buildAbdWebsocketCommand(`localfilesystem:${path}`));
-          byteStreamConnected.resolve(byteStream);
-        } else if (prefix === 'FAIL' && txt.includes('device unauthorized')) {
-          byteStreamConnected.reject(ALLOW_USB_DEBUGGING);
-        } else {
-          byteStreamConnected.reject(WEBSOCKET_UNABLE_TO_CONNECT);
-        }
-        return;
-      }
-
-      // Upon a successful connection we first receive an 'OKAY' message.
-      // After that, we receive messages with traced binary payloads.
-      const arrayBufferResponse = await evt.data.arrayBuffer();
-      if (textDecoder.decode(arrayBufferResponse) !== 'OKAY') {
-        byteStream.onStreamData(new Uint8Array(arrayBufferResponse));
-      }
-    };
-
-    return byteStreamConnected;
+  connectSocket(path: string): Promise<AdbOverWebsocketStream> {
+    return this.openStream(path);
   }
 
-  disconnect(): void {
+  protected async openStream(destination: string):
+      Promise<AdbOverWebsocketStream> {
+    return AdbOverWebsocketStream.create(
+        this.websocketUrl,
+        destination,
+        this.deviceSerialNumber,
+        this.closeStream.bind(this));
+  }
+
+  // The disconnection for AdbConnectionOverWebsocket is synchronous, but this
+  // method is async to have a common interface with other types of connections
+  // which are async.
+  async disconnect(disconnectMessage?: string): Promise<void> {
     for (const stream of this.streams) {
       stream.close();
     }
-    this.onDisconnect();
+    this.onDisconnect(disconnectMessage);
   }
 
   closeStream(stream: AdbOverWebsocketStream): void {
@@ -106,36 +83,146 @@
   }
 }
 
-// An AdbOverWebsocketStream is instantiated after the creation of a socket to
-// the device. Thanks to this, we can send commands and receive their output.
-// Messages are received in the main adb class, and are forwarded to an instance
-// of this class based on a stream id match.
+// An AdbOverWebsocketStream instantiates a websocket connection to the device.
+// It exposes an API to write commands to this websocket and read its output.
 export class AdbOverWebsocketStream implements ByteStream {
-  private _isOpen = false;
-  onStreamData: OnStreamDataCallback = (_) => {};
-  onStreamClose: OnStreamCloseCallback = () => {};
+  private websocket: WebSocket;
+  // commandSentSignal gets resolved if we successfully connect to the device
+  // and send the command this socket wraps. commandSentSignal gets rejected if
+  // we fail to connect to the device.
+  private commandSentSignal = defer<AdbOverWebsocketStream>();
+  // We store a promise for each messge while the message is processed.
+  // This way, if the websocket server closes the connection, we first process
+  // all previously received messages and only afterwards disconnect.
+  // An application is when the stream wraps a shell command. The websocket
+  // server will reply and then immediately disconnect.
+  private messageProcessedSignals: Set<Deferred<void>> = new Set();
 
-  constructor(
-      private websocket: WebSocket,
-      private removeFromConnection: (stream: AdbOverWebsocketStream) => void) {}
+  private _isConnected = false;
+  private onStreamDataCallbacks: OnStreamDataCallback[] = [];
+  private onStreamCloseCallbacks: OnStreamCloseCallback[] = [];
+
+  private constructor(
+      websocketUrl: string, destination: string, deviceSerialNumber: string,
+      private removeFromConnection: (stream: AdbOverWebsocketStream) => void) {
+    this.websocket = new WebSocket(websocketUrl);
+
+    this.websocket.onopen = this.onOpen.bind(this, deviceSerialNumber);
+    this.websocket.onmessage = this.onMessage.bind(this, destination);
+    // The websocket may be closed by the websocket server. This happens
+    // for instance when we get the full result of a shell command.
+    this.websocket.onclose = this.onClose.bind(this);
+  }
+
+  addOnStreamDataCallback(onStreamData: OnStreamDataCallback) {
+    this.onStreamDataCallbacks.push(onStreamData);
+  }
+
+  addOnStreamCloseCallback(onStreamClose: OnStreamCloseCallback) {
+    this.onStreamCloseCallbacks.push(onStreamClose);
+  }
+
+  // Used by the connection object to signal newly received data, not exposed
+  // in the interface.
+  signalStreamData(data: Uint8Array): void {
+    for (const onStreamData of this.onStreamDataCallbacks) {
+      onStreamData(data);
+    }
+  }
+
+  // Used by the connection object to signal the stream is closed, not exposed
+  // in the interface.
+  signalStreamClosed(): void {
+    for (const onStreamClose of this.onStreamCloseCallbacks) {
+      onStreamClose();
+    }
+    this.onStreamDataCallbacks = [];
+    this.onStreamCloseCallbacks = [];
+  }
 
   // We close the websocket and notify the AdbConnection to remove this stream.
   close(): void {
-    this.websocket.close();
-    this._isOpen = false;
+    // If the websocket connection is still open (ie. the close did not
+    // originate from the server), we close the websocket connection.
+    if (this.websocket.readyState === this.websocket.OPEN) {
+      this.websocket.close();
+      // We remove the 'onclose' callback so the 'close' method doesn't get
+      // executed twice.
+      this.websocket.onclose = null;
+    }
+    this._isConnected = false;
     this.removeFromConnection(this);
-    this.onStreamClose();
+    this.signalStreamClosed();
+  }
+
+  // For websocket, the teardown happens synchronously.
+  async closeAndWaitForTeardown(): Promise<void> {
+    this.close();
   }
 
   write(msg: string|Uint8Array): void {
     this.websocket.send(msg);
   }
 
-  isOpen(): boolean {
-    return this._isOpen;
+  isConnected(): boolean {
+    return this._isConnected;
   }
 
-  setStreamOpen(): void {
-    this._isOpen = true;
+  private async onOpen(deviceSerialNumber: string): Promise<void> {
+    this.websocket.send(
+        buildAbdWebsocketCommand(`host:transport:${deviceSerialNumber}`));
+  }
+
+  private async onMessage(destination: string, evt: MessageEvent):
+      Promise<void> {
+    const messageProcessed = defer<void>();
+    this.messageProcessedSignals.add(messageProcessed);
+    try {
+      if (!this._isConnected) {
+        const txt = await evt.data.text();
+        const prefix = txt.substr(0, 4);
+        if (prefix === 'OKAY') {
+          this._isConnected = true;
+          this.websocket.send(buildAbdWebsocketCommand(destination));
+          this.commandSentSignal.resolve(this);
+        } else if (prefix === 'FAIL' && txt.includes('device unauthorized')) {
+          this.commandSentSignal.reject(
+              new RecordingError(ALLOW_USB_DEBUGGING));
+          this.close();
+        } else {
+          this.commandSentSignal.reject(
+              new RecordingError(WEBSOCKET_UNABLE_TO_CONNECT));
+          this.close();
+        }
+      } else {
+        // Upon a successful connection we first receive an 'OKAY' message.
+        // After that, we receive messages with traced binary payloads.
+        const arrayBufferResponse = await evt.data.arrayBuffer();
+        if (textDecoder.decode(arrayBufferResponse) !== 'OKAY') {
+          this.signalStreamData(new Uint8Array(arrayBufferResponse));
+        }
+      }
+      messageProcessed.resolve();
+    } finally {
+      this.messageProcessedSignals.delete(messageProcessed);
+    }
+  }
+
+  private async onClose(): Promise<void> {
+    // Wait for all messages to be processed before closing the connection.
+    await Promise.allSettled(this.messageProcessedSignals);
+    this.close();
+  }
+
+  static create(
+      websocketUrl: string, destination: string, deviceSerialNumber: string,
+      removeFromConnection: (stream: AdbOverWebsocketStream) => void):
+      Promise<AdbOverWebsocketStream> {
+    return (new AdbOverWebsocketStream(
+                websocketUrl,
+                destination,
+                deviceSerialNumber,
+                removeFromConnection))
+        .commandSentSignal;
   }
 }
diff --git a/ui/src/common/recordingV2/adb_connection_over_webusb.ts b/ui/src/common/recordingV2/adb_connection_over_webusb.ts
index dd9be7f..d957aab 100644
--- a/ui/src/common/recordingV2/adb_connection_over_webusb.ts
+++ b/ui/src/common/recordingV2/adb_connection_over_webusb.ts
@@ -17,24 +17,19 @@
 import {defer, Deferred} from '../../base/deferred';
 import {assertExists, assertFalse, assertTrue} from '../../base/logging';
 import {CmdType} from '../../controller/adb_interfaces';
-import {ArrayBufferBuilder} from '../array_buffer_builder';
 
-import {AdbFileHandler} from './adb_file_handler';
-import {findInterfaceAndEndpoint} from './adb_over_webusb_utils';
-import {ALLOW_USB_DEBUGGING} from './adb_targets_utils';
+import {AdbConnectionImpl} from './adb_connection_impl';
 import {AdbKeyManager, maybeStoreKey} from './auth/adb_key_manager';
 import {
   RecordingError,
   wrapRecordingError,
 } from './recording_error_handling';
 import {
-  AdbConnection,
   ByteStream,
-  OnDisconnectCallback,
-  OnMessageCallback,
   OnStreamCloseCallback,
   OnStreamDataCallback,
 } from './recording_interfaces_v2';
+import {ALLOW_USB_DEBUGGING, findInterfaceAndEndpoint} from './recording_utils';
 
 const textEncoder = new _TextEncoder();
 const textDecoder = new _TextDecoder();
@@ -72,7 +67,7 @@
   localStreamId: number;
 }
 
-export class AdbConnectionOverWebusb implements AdbConnection {
+export class AdbConnectionOverWebusb extends AdbConnectionImpl {
   private state: AdbState = AdbState.DISCONNECTED;
   private connectingStreams = new Map<number, Deferred<AdbOverWebusbStream>>();
   private streams = new Set<AdbOverWebusbStream>();
@@ -92,12 +87,6 @@
 
   private pendingConnPromises: Array<Deferred<void>> = [];
 
-  // onStatus and onDisconnect are set to callbacks passed from the caller.
-  // This happens for instance in the AndroidWebusbTarget, which instantiates
-  // them with callbacks passed from the UI.
-  onStatus: OnMessageCallback = () => {};
-  onDisconnect: OnDisconnectCallback = (_) => {};
-
   // We use a key pair for authenticating with the device, which we do in
   // two ways:
   // - Firstly, signing with the private key.
@@ -106,37 +95,17 @@
   // Once we've sent the public key, for future recordings we only need to
   // sign with the private key, so the user doesn't need to give permissions
   // again.
-  constructor(private device: USBDevice, private keyManager: AdbKeyManager) {}
-
-  // Starts a shell command, then gathers all its output and returns it as
-  // a string.
-  async shellAndGetOutput(cmd: string): Promise<string> {
-    const adbStream = await this.shell(cmd);
-    const commandOutput = new ArrayBufferBuilder();
-    const onStreamingEnded = defer<string>();
-
-    adbStream.onStreamData = (data: Uint8Array) => {
-      commandOutput.append(data);
-    };
-    adbStream.onStreamClose = () => {
-      onStreamingEnded.resolve(
-          textDecoder.decode(commandOutput.toArrayBuffer()));
-    };
-    return onStreamingEnded;
+  constructor(private device: USBDevice, private keyManager: AdbKeyManager) {
+    super();
   }
 
-  async push(binary: Uint8Array, path: string): Promise<void> {
-    const byteStream = await this.openStream('sync:');
-    await (new AdbFileHandler(byteStream)).pushBinary(binary, path);
-    byteStream.close();
-  }
 
   shell(cmd: string): Promise<AdbOverWebusbStream> {
     return this.openStream('shell:' + cmd);
   }
 
   connectSocket(path: string): Promise<AdbOverWebusbStream> {
-    return this.openStream('localfilesystem:' + path);
+    return this.openStream(path);
   }
 
   async canConnectWithoutContention(): Promise<boolean> {
@@ -151,7 +120,8 @@
     }
   }
 
-  private async openStream(destination: string): Promise<AdbOverWebusbStream> {
+  protected async openStream(destination: string):
+      Promise<AdbOverWebusbStream> {
     const streamId = ++this.lastStreamId;
     const connectingStream = defer<AdbOverWebusbStream>();
     this.connectingStreams.set(streamId, connectingStream);
@@ -169,7 +139,9 @@
 
     if (this.state === AdbState.DISCONNECTED) {
       await this.device.open();
-      await this.device.reset();
+      if (!(await this.canConnectWithoutContention())) {
+        await this.device.reset();
+      }
       const usbInterfaceNumber = await this.setupUsbInterface();
       await this.device.claimInterface(usbInterfaceNumber);
     }
@@ -212,15 +184,15 @@
 
     this.streams.delete(stream);
     if (this.streams.size === 0) {
-      // We disconnect BEFORE calling `onStreamClose`. Otherwise there can be a
-      // race condition:
+      // We disconnect BEFORE calling `signalStreamClosed`. Otherwise, there can
+      // be a race condition:
       // Stream A: streamA.onStreamClose
       // Stream B: device.open
       // Stream A: device.releaseInterface
       // Stream B: device.transferOut -> CRASH
       await this.disconnect();
     }
-    stream.onStreamClose();
+    stream.signalStreamClosed();
   }
 
   streamWrite(msg: string|Uint8Array, stream: AdbOverWebusbStream): void {
@@ -313,7 +285,13 @@
         this.isUsbReceiveLoopRunning = false;
         return;
       }
-      assertTrue(res.status === 'ok');
+      if (res.status !== 'ok') {
+        // Log and ignore messages with invalid status. These can occur
+        // when the device is connected/disconnected repeatedly.
+        console.error(
+            `Received message with unexpected status '${res.status}'`);
+        continue;
+      }
 
       const msg = AdbMsg.decodeHeader(res.data!);
       if (msg.dataLen > 0) {
@@ -423,7 +401,7 @@
         const stream = assertExists(this.getStreamForLocalStreamId(msg.arg1));
         await this.sendMessage(
             'OKAY', stream.localStreamId, stream.remoteStreamId);
-        stream.onStreamData(msg.data);
+        stream.signalStreamData(msg.data);
       } else {
         this.isUsbReceiveLoopRunning = false;
         throw new RecordingError(
@@ -479,13 +457,12 @@
 // of this class based on a stream id match.
 export class AdbOverWebusbStream implements ByteStream {
   private adbConnection: AdbConnectionOverWebusb;
-  private _isOpen: boolean;
+  private _isConnected: boolean;
+  private onStreamDataCallbacks: OnStreamDataCallback[] = [];
+  private onStreamCloseCallbacks: OnStreamCloseCallback[] = [];
   localStreamId: number;
   remoteStreamId = -1;
 
-  onStreamData: OnStreamDataCallback = (_) => {};
-  onStreamClose: OnStreamCloseCallback = () => {};
-
   constructor(
       adb: AdbConnectionOverWebusb, localStreamId: number,
       remoteStreamId: number) {
@@ -493,20 +470,51 @@
     this.localStreamId = localStreamId;
     this.remoteStreamId = remoteStreamId;
     // When the stream is created, the connection has been already established.
-    this._isOpen = true;
+    this._isConnected = true;
   }
 
+  addOnStreamDataCallback(onStreamData: OnStreamDataCallback): void {
+    this.onStreamDataCallbacks.push(onStreamData);
+  }
+
+  addOnStreamCloseCallback(onStreamClose: OnStreamCloseCallback): void {
+    this.onStreamCloseCallbacks.push(onStreamClose);
+  }
+
+  // Used by the connection object to signal newly received data, not exposed
+  // in the interface.
+  signalStreamData(data: Uint8Array): void {
+    for (const onStreamData of this.onStreamDataCallbacks) {
+      onStreamData(data);
+    }
+  }
+
+  // Used by the connection object to signal the stream is closed, not exposed
+  // in the interface.
+  signalStreamClosed(): void {
+    for (const onStreamClose of this.onStreamCloseCallbacks) {
+      onStreamClose();
+    }
+    this.onStreamDataCallbacks = [];
+    this.onStreamCloseCallbacks = [];
+  }
+
+
   close(): void {
-    this.adbConnection.streamClose(this);
-    this._isOpen = false;
+    this.closeAndWaitForTeardown();
+  }
+
+  async closeAndWaitForTeardown(): Promise<void> {
+    this._isConnected = false;
+    await this.adbConnection.streamClose(this);
   }
 
   write(msg: string|Uint8Array): void {
     this.adbConnection.streamWrite(msg, this);
   }
 
-  isOpen(): boolean {
-    return this._isOpen;
+  isConnected(): boolean {
+    return this._isConnected;
   }
 }
 
diff --git a/ui/src/common/recordingV2/adb_file_handler.ts b/ui/src/common/recordingV2/adb_file_handler.ts
index 59584ef..d659adb 100644
--- a/ui/src/common/recordingV2/adb_file_handler.ts
+++ b/ui/src/common/recordingV2/adb_file_handler.ts
@@ -20,6 +20,10 @@
 
 import {RecordingError} from './recording_error_handling';
 import {ByteStream} from './recording_interfaces_v2';
+import {
+  BINARY_PUSH_FAILURE,
+  BINARY_PUSH_UNKNOWN_RESPONSE,
+} from './recording_utils';
 
 // https://cs.android.com/android/platform/superproject/+/master:packages/
 // modules/adb/file_sync_protocol.h;l=144
@@ -33,9 +37,6 @@
 
 const textDecoder = new _TextDecoder();
 
-export const BINARY_PUSH_FAILURE = 'BinaryPushFailure';
-export const BINARY_PUSH_UNKNOWN_RESPONSE = 'BinaryPushUnknownResponse';
-
 // For details about the protocol, see:
 // https://cs.android.com/android/platform/superproject/+/master:packages/modules/adb/SYNC.TXT
 export class AdbFileHandler {
@@ -50,9 +51,9 @@
     this.isPushOngoing = true;
     const transferFinished = defer<void>();
 
-    this.byteStream.onStreamData = (data) =>
-        this.onStreamData(data, transferFinished);
-    this.byteStream.onStreamClose = () => this.isPushOngoing = false;
+    this.byteStream.addOnStreamDataCallback(
+        (data) => this.onStreamData(data, transferFinished));
+    this.byteStream.addOnStreamCloseCallback(() => this.isPushOngoing = false);
 
     const sendMessage = new ArrayBufferBuilder();
     // 'SEND' is the API method used to send a file to device.
@@ -65,6 +66,7 @@
     sendMessage.append(path);
     sendMessage.append(',');
     sendMessage.append(FILE_PERMISSIONS.toString());
+    this.byteStream.write(new Uint8Array(sendMessage.toArrayBuffer()));
 
     while (!(await this.sendNextDataChunk(binary)))
       ;
diff --git a/ui/src/common/recordingV2/adb_over_websocket_utils.ts b/ui/src/common/recordingV2/adb_over_websocket_utils.ts
deleted file mode 100644
index 8a6c240..0000000
--- a/ui/src/common/recordingV2/adb_over_websocket_utils.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (C) 2022 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.
-
-// The messages read by the adb server have their length prepended in hex.
-// This method adds the length at the beginning of the message.
-// Example: 'host:track-devices' -> '0012host:track-devices'
-// go/codesearch/aosp-android11/system/core/adb/SERVICES.TXT
-export function buildAbdWebsocketCommand(cmd: string) {
-  const hdr = cmd.length.toString(16).padStart(4, '0');
-  return hdr + cmd;
-}
diff --git a/ui/src/common/recordingV2/adb_over_webusb_utils.ts b/ui/src/common/recordingV2/adb_over_webusb_utils.ts
deleted file mode 100644
index 05c627a..0000000
--- a/ui/src/common/recordingV2/adb_over_webusb_utils.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright (C) 2022 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 interface UsbInterfaceAndEndpoint {
-  readonly configurationValue: number;
-  readonly usbInterfaceNumber: number;
-  readonly endpoints: USBEndpoint[];
-}
-
-export const ADB_DEVICE_FILTER = {
-  classCode: 255,    // USB vendor specific code
-  subclassCode: 66,  // Android vendor specific subclass
-  protocolCode: 1,   // Adb protocol
-};
-
-export function findInterfaceAndEndpoint(device: USBDevice):
-    UsbInterfaceAndEndpoint|undefined {
-  const adbDeviceFilter = ADB_DEVICE_FILTER;
-  for (const config of device.configurations) {
-    for (const interface_ of config.interfaces) {
-      for (const alt of interface_.alternates) {
-        if (alt.interfaceClass === adbDeviceFilter.classCode &&
-            alt.interfaceSubclass === adbDeviceFilter.subclassCode &&
-            alt.interfaceProtocol === adbDeviceFilter.protocolCode) {
-          return {
-            configurationValue: config.configurationValue,
-            usbInterfaceNumber: interface_.interfaceNumber,
-            endpoints: alt.endpoints,
-          };
-        }  // if (alternate)
-      }    // for (interface.alternates)
-    }      // for (configuration.interfaces)
-  }        // for (configurations)
-
-  return undefined;
-}
diff --git a/ui/src/common/recordingV2/adb_targets_utils.ts b/ui/src/common/recordingV2/adb_targets_utils.ts
deleted file mode 100644
index 23ba922..0000000
--- a/ui/src/common/recordingV2/adb_targets_utils.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2022 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.
-
-// In case the device doesn't have the tracebox, we upload the latest version
-// to this path.
-export const TRACEBOX_DEVICE_PATH = '/data/local/tmp/tracebox';
-
-// Experimentally, this takes 900ms on the first fetch and 20-30ms after
-// because of caching.
-export const TRACEBOX_FETCH_TIMEOUT = 30000;
-
-// Message shown to the user when they need to allow authentication on the
-// device in order to connect.
-export const ALLOW_USB_DEBUGGING =
-    'Please allow USB debugging on device and try again.';
diff --git a/ui/src/common/recordingV2/auth/credentials_interfaces.d.ts b/ui/src/common/recordingV2/auth/credentials_interfaces.d.ts
index 2ee28a1..f127ad5 100644
--- a/ui/src/common/recordingV2/auth/credentials_interfaces.d.ts
+++ b/ui/src/common/recordingV2/auth/credentials_interfaces.d.ts
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 // Typescript interfaces for PasswordCredential don't exist as of
-// lib.dom es2018 (see tsconfig.json), so we had to define them here.
+// lib.dom es2020 (see tsconfig.json), so we had to define them here.
 declare global {
   export interface PasswordCredentialData {
     readonly id: string;
diff --git a/ui/src/common/recordingV2/chrome_traced_tracing_session.ts b/ui/src/common/recordingV2/chrome_traced_tracing_session.ts
index 6128658..e36a5aa 100644
--- a/ui/src/common/recordingV2/chrome_traced_tracing_session.ts
+++ b/ui/src/common/recordingV2/chrome_traced_tracing_session.ts
@@ -34,18 +34,17 @@
   ISlice,
   TraceConfig,
 } from '../protos';
-
-import {
-  BUFFER_USAGE_INCORRECT_FORMAT,
-  BUFFER_USAGE_NOT_ACCESSIBLE,
-  EXTENSION_ID,
-  MALFORMED_EXTENSION_MESSAGE,
-} from './chrome_utils';
 import {RecordingError} from './recording_error_handling';
 import {
   TracingSession,
   TracingSessionListener,
 } from './recording_interfaces_v2';
+import {
+  BUFFER_USAGE_INCORRECT_FORMAT,
+  BUFFER_USAGE_NOT_ACCESSIBLE,
+  EXTENSION_ID,
+  MALFORMED_EXTENSION_MESSAGE,
+} from './recording_utils';
 
 // This class implements the protocol described in
 // https://perfetto.dev/docs/design-docs/api-and-abi#tracing-protocol-abi
diff --git a/ui/src/common/recordingV2/chrome_utils.ts b/ui/src/common/recordingV2/chrome_utils.ts
deleted file mode 100644
index 2cc4b89..0000000
--- a/ui/src/common/recordingV2/chrome_utils.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2022 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 EXTENSION_ID = 'lfmkphfpdbjijhpomgecfikhfohaoine';
-export const EXTENSION_URL =
-    `https://chrome.google.com/webstore/detail/perfetto-ui/${EXTENSION_ID}`;
-export const EXTENSION_NAME = 'Chrome extension';
-export const EXTENSION_NOT_INSTALLED =
-    `To trace Chrome from the Perfetto UI, you need to install our
-    ${EXTENSION_URL} and then reload this page.`;
-
-export const MALFORMED_EXTENSION_MESSAGE = 'Malformed extension message.';
-export const BUFFER_USAGE_NOT_ACCESSIBLE = 'Buffer usage not accessible';
-export const BUFFER_USAGE_INCORRECT_FORMAT =
-    'The buffer usage data has am incorrect format';
diff --git a/ui/src/common/recordingV2/host_os_byte_stream.ts b/ui/src/common/recordingV2/host_os_byte_stream.ts
new file mode 100644
index 0000000..dd46ba8
--- /dev/null
+++ b/ui/src/common/recordingV2/host_os_byte_stream.ts
@@ -0,0 +1,84 @@
+// Copyright (C) 2022 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 {defer} from '../../base/deferred';
+
+import {
+  ByteStream,
+  OnStreamCloseCallback,
+  OnStreamDataCallback,
+} from './recording_interfaces_v2';
+
+// A HostOsByteStream instantiates a websocket connection to the host OS.
+// It exposes an API to write commands to this websocket and read its output.
+export class HostOsByteStream implements ByteStream {
+  // handshakeSignal will be resolved with the stream when the websocket
+  // connection becomes open.
+  private handshakeSignal = defer<HostOsByteStream>();
+  private _isConnected: boolean = false;
+  private websocket: WebSocket;
+  private onStreamDataCallbacks: OnStreamDataCallback[] = [];
+  private onStreamCloseCallbacks: OnStreamCloseCallback[] = [];
+
+  private constructor(websocketUrl: string) {
+    this.websocket = new WebSocket(websocketUrl);
+    this.websocket.onmessage = this.onMessage.bind(this);
+    this.websocket.onopen = this.onOpen.bind(this);
+  }
+
+  addOnStreamDataCallback(onStreamData: OnStreamDataCallback): void {
+    this.onStreamDataCallbacks.push(onStreamData);
+  }
+
+  addOnStreamCloseCallback(onStreamClose: OnStreamCloseCallback): void {
+    this.onStreamCloseCallbacks.push(onStreamClose);
+  }
+
+  close(): void {
+    this.websocket.close();
+    for (const onStreamClose of this.onStreamCloseCallbacks) {
+      onStreamClose();
+    }
+    this.onStreamDataCallbacks = [];
+    this.onStreamCloseCallbacks = [];
+  }
+
+  async closeAndWaitForTeardown(): Promise<void> {
+    this.close();
+  }
+
+  isConnected(): boolean {
+    return this._isConnected;
+  }
+
+  write(msg: string|Uint8Array): void {
+    this.websocket.send(msg);
+  }
+
+  private async onMessage(evt: MessageEvent) {
+    for (const onStreamData of this.onStreamDataCallbacks) {
+      const arrayBufferResponse = await evt.data.arrayBuffer();
+      onStreamData(new Uint8Array(arrayBufferResponse));
+    }
+  }
+
+  private onOpen() {
+    this._isConnected = true;
+    this.handshakeSignal.resolve(this);
+  }
+
+  static create(websocketUrl: string): Promise<HostOsByteStream> {
+    return (new HostOsByteStream(websocketUrl)).handshakeSignal;
+  }
+}
diff --git a/ui/src/common/recordingV2/recording_config_utils.ts b/ui/src/common/recordingV2/recording_config_utils.ts
index 312ebbc..6a329b3 100644
--- a/ui/src/common/recordingV2/recording_config_utils.ts
+++ b/ui/src/common/recordingV2/recording_config_utils.ts
@@ -31,39 +31,47 @@
   ProcessStatsConfig,
   SysStatsConfig,
   TraceConfig,
+  TrackEventConfig,
   VmstatCounters,
 } from '../protos';
 
-import {RecordingTargetV2, TargetInfo} from './recording_interfaces_v2';
+import {TargetInfo} from './recording_interfaces_v2';
 
 export interface ConfigProtoEncoded {
   configProtoText?: string;
   configProtoBase64?: string;
+  hasDataSources: boolean;
 }
 
 export class RecordingConfigUtils {
   private lastConfig?: RecordConfig;
+  private lastTargetInfo?: TargetInfo;
   private configProtoText?: string;
   private configProtoBase64?: string;
+  private hasDataSources: boolean = false;
 
-  fetchLatestRecordCommand(
-      recordConfig: RecordConfig,
-      target: RecordingTargetV2): ConfigProtoEncoded {
-    if (recordConfig === this.lastConfig) {
+  fetchLatestRecordCommand(recordConfig: RecordConfig, targetInfo: TargetInfo):
+      ConfigProtoEncoded {
+    if (recordConfig === this.lastConfig &&
+        targetInfo === this.lastTargetInfo) {
       return {
         configProtoText: this.configProtoText,
         configProtoBase64: this.configProtoBase64,
+        hasDataSources: this.hasDataSources,
       };
     }
     this.lastConfig = recordConfig;
-    const configProto =
-        TraceConfig.encode(genTraceConfig(this.lastConfig, target.getInfo()))
-            .finish();
+    this.lastTargetInfo = targetInfo;
+
+    const traceConfig = genTraceConfig(recordConfig, targetInfo);
+    const configProto = TraceConfig.encode(traceConfig).finish();
     this.configProtoText = toPbtxt(configProto);
     this.configProtoBase64 = base64Encode(configProto);
+    this.hasDataSources = traceConfig.dataSources.length > 0;
     return {
       configProtoText: this.configProtoText,
       configProtoBase64: this.configProtoBase64,
+      hasDataSources: this.hasDataSources,
     };
   }
 }
@@ -137,10 +145,15 @@
     ftraceEvents.add('power/suspend_resume');
   }
 
+  let sysStatsCfg: SysStatsConfig|undefined = undefined;
+
   if (uiCfg.cpuFreq) {
     ftraceEvents.add('power/cpu_frequency');
     ftraceEvents.add('power/cpu_idle');
     ftraceEvents.add('power/suspend_resume');
+
+    sysStatsCfg = new SysStatsConfig();
+    sysStatsCfg.cpufreqPeriodMs = uiCfg.cpuFreqPollMs;
   }
 
   if (uiCfg.gpuFreq) {
@@ -194,10 +207,8 @@
     ftraceEvents.add('power/suspend_resume');
   }
 
-  let sysStatsCfg: SysStatsConfig|undefined = undefined;
-
   if (uiCfg.cpuCoarse) {
-    sysStatsCfg = new SysStatsConfig();
+    if (sysStatsCfg === undefined) sysStatsCfg = new SysStatsConfig();
     sysStatsCfg.statPeriodMs = uiCfg.cpuCoarsePollMs;
     sysStatsCfg.statCounters = [
       SysStatsConfig.StatCounters.STAT_CPU_TIMES,
@@ -348,18 +359,30 @@
     }
   }
 
+  if (uiCfg.androidGameInterventionList) {
+    const ds = new TraceConfig.DataSource();
+    ds.config = new DataSourceConfig();
+    ds.config.name = 'android.game_interventions';
+    if (targetInfo.targetType !== 'CHROME') {
+      protoCfg.dataSources.push(ds);
+    }
+  }
+
   if (uiCfg.chromeLogs) {
     chromeCategories.add('log');
   }
 
   if (uiCfg.taskScheduling) {
     chromeCategories.add('toplevel');
+    chromeCategories.add('toplevel.flow');
+    chromeCategories.add('scheduler');
     chromeCategories.add('sequence_manager');
     chromeCategories.add('disabled-by-default-toplevel.flow');
   }
 
   if (uiCfg.ipcFlows) {
     chromeCategories.add('toplevel');
+    chromeCategories.add('toplevel.flow');
     chromeCategories.add('disabled-by-default-ipc.flow');
     chromeCategories.add('mojom');
   }
@@ -411,6 +434,8 @@
     const configStruct = {
       record_mode: chromeRecordMode,
       included_categories: [...chromeCategories.values()],
+      // Only include explicitly selected categories
+      excluded_categories: ['*'],
       memory_dump_config: {},
     };
     if (chromeCategories.has('disabled-by-default-memory-infra')) {
@@ -423,44 +448,51 @@
         }],
       };
     }
-    const traceConfigJson = JSON.stringify(configStruct);
+    const chromeConfig = new ChromeConfig();
+    chromeConfig.clientPriority = ChromeConfig.ClientPriority.USER_INITIATED;
+    chromeConfig.privacyFilteringEnabled = uiCfg.chromePrivacyFiltering;
+    chromeConfig.traceConfig = JSON.stringify(configStruct);
 
     const traceDs = new TraceConfig.DataSource();
     traceDs.config = new DataSourceConfig();
     traceDs.config.name = 'org.chromium.trace_event';
-    traceDs.config.chromeConfig = new ChromeConfig();
-    traceDs.config.chromeConfig.clientPriority =
-        ChromeConfig.ClientPriority.USER_INITIATED;
-    traceDs.config.chromeConfig.traceConfig = traceConfigJson;
+    traceDs.config.chromeConfig = chromeConfig;
     protoCfg.dataSources.push(traceDs);
 
+    // Configure "track_event" datasource for the Chrome SDK build.
+    const trackEventDs = new TraceConfig.DataSource();
+    trackEventDs.config = new DataSourceConfig();
+    trackEventDs.config.name = 'track_event';
+    trackEventDs.config.chromeConfig = chromeConfig;
+    trackEventDs.config.trackEventConfig = new TrackEventConfig();
+    trackEventDs.config.trackEventConfig.disabledCategories = ['*'];
+    trackEventDs.config.trackEventConfig.enabledCategories =
+        [...chromeCategories.values(), '__metadata'];
+    trackEventDs.config.trackEventConfig.enableThreadTimeSampling = true;
+    trackEventDs.config.trackEventConfig.timestampUnitMultiplier = 1000;
+    trackEventDs.config.trackEventConfig.filterDynamicEventNames =
+        uiCfg.chromePrivacyFiltering;
+    trackEventDs.config.trackEventConfig.filterDebugAnnotations =
+        uiCfg.chromePrivacyFiltering;
+    protoCfg.dataSources.push(trackEventDs);
 
     const metadataDs = new TraceConfig.DataSource();
     metadataDs.config = new DataSourceConfig();
     metadataDs.config.name = 'org.chromium.trace_metadata';
-    metadataDs.config.chromeConfig = new ChromeConfig();
-    metadataDs.config.chromeConfig.clientPriority =
-        ChromeConfig.ClientPriority.USER_INITIATED;
-    metadataDs.config.chromeConfig.traceConfig = traceConfigJson;
+    metadataDs.config.chromeConfig = chromeConfig;
     protoCfg.dataSources.push(metadataDs);
 
     if (chromeCategories.has('disabled-by-default-memory-infra')) {
       const memoryDs = new TraceConfig.DataSource();
       memoryDs.config = new DataSourceConfig();
       memoryDs.config.name = 'org.chromium.memory_instrumentation';
-      memoryDs.config.chromeConfig = new ChromeConfig();
-      memoryDs.config.chromeConfig.clientPriority =
-          ChromeConfig.ClientPriority.USER_INITIATED;
-      memoryDs.config.chromeConfig.traceConfig = traceConfigJson;
+      memoryDs.config.chromeConfig = chromeConfig;
       protoCfg.dataSources.push(memoryDs);
 
       const HeapProfDs = new TraceConfig.DataSource();
       HeapProfDs.config = new DataSourceConfig();
       HeapProfDs.config.name = 'org.chromium.native_heap_profiler';
-      HeapProfDs.config.chromeConfig = new ChromeConfig();
-      HeapProfDs.config.chromeConfig.clientPriority =
-          ChromeConfig.ClientPriority.USER_INITIATED;
-      HeapProfDs.config.chromeConfig.traceConfig = traceConfigJson;
+      HeapProfDs.config.chromeConfig = chromeConfig;
       protoCfg.dataSources.push(HeapProfDs);
     }
 
@@ -469,10 +501,7 @@
       const dataSource = new TraceConfig.DataSource();
       dataSource.config = new DataSourceConfig();
       dataSource.config.name = 'org.chromium.sampler_profiler';
-      dataSource.config.chromeConfig = new ChromeConfig();
-      dataSource.config.chromeConfig.clientPriority =
-          ChromeConfig.ClientPriority.USER_INITIATED;
-      dataSource.config.chromeConfig.traceConfig = traceConfigJson;
+      dataSource.config.chromeConfig = chromeConfig;
       protoCfg.dataSources.push(dataSource);
     }
   }
diff --git a/ui/src/common/recordingV2/recording_error_handling.ts b/ui/src/common/recordingV2/recording_error_handling.ts
index 63e8ba9..ca03929 100644
--- a/ui/src/common/recordingV2/recording_error_handling.ts
+++ b/ui/src/common/recordingV2/recording_error_handling.ts
@@ -24,22 +24,19 @@
 } from '../../frontend/error_dialog';
 import {getErrorMessage} from '../errors';
 
-import {
-  WEBSOCKET_UNABLE_TO_CONNECT,
-} from './adb_connection_over_websocket';
-import {
-  BINARY_PUSH_FAILURE,
-  BINARY_PUSH_UNKNOWN_RESPONSE,
-} from './adb_file_handler';
-import {ALLOW_USB_DEBUGGING} from './adb_targets_utils';
-import {EXTENSION_NOT_INSTALLED} from './chrome_utils';
 import {OnMessageCallback} from './recording_interfaces_v2';
 import {
+  ALLOW_USB_DEBUGGING,
+  BINARY_PUSH_FAILURE,
+  BINARY_PUSH_UNKNOWN_RESPONSE,
+  EXTENSION_NOT_INSTALLED,
+  NO_DEVICE_SELECTED,
   PARSING_UNABLE_TO_DECODE_METHOD,
   PARSING_UNKNWON_REQUEST_ID,
   PARSING_UNRECOGNIZED_MESSAGE,
   PARSING_UNRECOGNIZED_PORT,
-} from './traced_tracing_session';
+  WEBSOCKET_UNABLE_TO_CONNECT,
+} from './recording_utils';
 
 
 // The pattern for handling recording error can have the following nesting in
@@ -80,6 +77,8 @@
         'Unable to claim interface.',
         'The specified endpoint is not part of a claimed and selected ' +
             'alternate interface.',
+        // thrown when calling the 'reset' method on a WebUSB device.
+        'Unable to reset the device.',
       ].some((partOfMessage) => message.includes(partOfMessage))) {
     showWebUSBErrorV2();
   } else if (
@@ -96,7 +95,7 @@
                  message,
                  [BINARY_PUSH_FAILURE, BINARY_PUSH_UNKNOWN_RESPONSE])) {
     showFailedToPushBinary(message.substring(message.indexOf(':') + 1));
-  } else if (message === 'No device selected.') {
+  } else if (message === NO_DEVICE_SELECTED) {
     showNoDeviceSelected();
   } else if (WEBSOCKET_UNABLE_TO_CONNECT === message) {
     showWebsocketConnectionIssue(message);
diff --git a/ui/src/common/recordingV2/recording_interfaces_v2.ts b/ui/src/common/recordingV2/recording_interfaces_v2.ts
index c4aef52..974d277 100644
--- a/ui/src/common/recordingV2/recording_interfaces_v2.ts
+++ b/ui/src/common/recordingV2/recording_interfaces_v2.ts
@@ -75,17 +75,16 @@
 }
 
 export interface ChromeTargetInfo extends TargetInfoBase {
-  isExtensionInstalled: boolean;
   targetType: 'CHROME'|'CHROME_OS';
 }
 
-export interface LinuxTargetInfo extends TargetInfoBase {
-  targetType: 'LINUX';
+export interface HostOsTargetInfo extends TargetInfoBase {
+  targetType: 'LINUX'|'MACOS';
 }
 
 // Holds information about a target. It's used by the UI and the logic which
 // generates a config.
-export type TargetInfo = AndroidTargetInfo|ChromeTargetInfo|LinuxTargetInfo;
+export type TargetInfo = AndroidTargetInfo|ChromeTargetInfo|HostOsTargetInfo;
 
 // RecordingTargetV2 is subclassed by Android devices and the Chrome browser/OS.
 // It creates tracing sessions which are used by the UI. For Android, it manages
@@ -95,9 +94,8 @@
   // well known key/value pairs: OS, targetType('ANDROID', 'CHROME', etc.)
   getInfo(): TargetInfo;
 
-  // Disconnects the target. Depending on target type, this can be
-  // asynchronous (Example: WebUSB) or synchronous (Example: Websocket).
-  disconnect(disconnectMessage?: string): Promise<void>|void;
+  // Disconnects the target.
+  disconnect(disconnectMessage?: string): Promise<void>;
 
   // Returns true if we are able to connect to the target without interfering
   // with other processes. For example, for adb devices connected over WebUSB,
@@ -107,6 +105,10 @@
   // effect take the connection away from other processes.
   canConnectWithoutContention(): Promise<boolean>;
 
+  // Whether the recording target can be used in a tracing session. For example,
+  // virtual targets do not support a tracing session.
+  canCreateTracingSession(recordingMode?: string): boolean;
+
   // Some target information can only be obtained after connecting to the
   // target. This will establish a connection and retrieve data such as
   // dataSources and apiLevel for Android.
@@ -155,15 +157,23 @@
   // this will be false when we can not claim the interface (Which most likely
   // means that 'adb server' is running locally.).
   canConnectWithoutContention(): Promise<boolean>;
+
+  // Ends the connection.
+  disconnect(disconnectMessage?: string): Promise<void>;
 }
 
 // A stream for a connection between a target and a tracing session.
 export interface ByteStream {
-  onStreamData: OnStreamDataCallback;
-  onStreamClose: OnStreamCloseCallback;
-  isOpen(): boolean;
+  // The caller can add callbacks, to be executed when the stream receives new
+  // data or when it finished closing itself.
+  addOnStreamDataCallback(onStreamData: OnStreamDataCallback): void;
+  addOnStreamCloseCallback(onStreamClose: OnStreamCloseCallback): void;
+
+  isConnected(): boolean;
   write(data: string|Uint8Array): void;
+
   close(): void;
+  closeAndWaitForTeardown(): Promise<void>;
 }
 
 // Handles binary messages received over the ByteStream.
diff --git a/ui/src/common/recordingV2/recording_page_controller.ts b/ui/src/common/recordingV2/recording_page_controller.ts
new file mode 100644
index 0000000..a77edf1
--- /dev/null
+++ b/ui/src/common/recordingV2/recording_page_controller.ts
@@ -0,0 +1,522 @@
+// Copyright (C) 2022 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 {assertExists, assertTrue} from '../../base/logging';
+import {globals} from '../../frontend/globals';
+import {autosaveConfigStore} from '../../frontend/record_config';
+import {
+  DEFAULT_ADB_WEBSOCKET_URL,
+  DEFAULT_TRACED_WEBSOCKET_URL,
+} from '../../frontend/recording/recording_ui_utils';
+import {
+  couldNotClaimInterface,
+} from '../../frontend/recording/reset_interface_modal';
+import {Actions} from '../actions';
+import {TRACE_SUFFIX} from '../constants';
+import {TraceConfig} from '../protos';
+import {currentDateHourAndMinute} from '../time';
+
+import {genTraceConfig} from './recording_config_utils';
+import {RecordingError, showRecordingModal} from './recording_error_handling';
+import {
+  RecordingTargetV2,
+  TargetInfo,
+  TracingSession,
+  TracingSessionListener,
+} from './recording_interfaces_v2';
+import {
+  BUFFER_USAGE_NOT_ACCESSIBLE,
+  RECORDING_IN_PROGRESS,
+} from './recording_utils';
+import {
+  ANDROID_WEBSOCKET_TARGET_FACTORY,
+  AndroidWebsocketTargetFactory,
+} from './target_factories/android_websocket_target_factory';
+import {
+  ANDROID_WEBUSB_TARGET_FACTORY,
+} from './target_factories/android_webusb_target_factory';
+import {
+  HOST_OS_TARGET_FACTORY,
+  HostOsTargetFactory,
+} from './target_factories/host_os_target_factory';
+import {targetFactoryRegistry} from './target_factory_registry';
+
+// The recording page can be in any of these states. It can transition between
+// states:
+// a) because of a user actions - pressing a UI button ('Start', 'Stop',
+//    'Cancel', 'Force reset' of the target), selecting a different target in
+//    the UI, authorizing authentication on an Android device,
+//    pulling the cable which connects an Android device.
+// b) automatically - if there is no need to reset the device or if the user
+//    has previously authorised the device to be debugged via USB.
+//
+// Recording state machine: https://screenshot.googleplex.com/BaX5EGqQMajgV7G
+export enum RecordingState {
+  NO_TARGET = 0,
+  TARGET_SELECTED = 1,
+  // P1 stands for 'Part 1', where we first connect to the device in order to
+  // obtain target information.
+  ASK_TO_FORCE_P1 = 2,
+  AUTH_P1 = 3,
+  TARGET_INFO_DISPLAYED = 4,
+  // P2 stands for 'Part 2', where we connect to device for the 2nd+ times, to
+  // record a tracing session.
+  ASK_TO_FORCE_P2 = 5,
+  AUTH_P2 = 6,
+  RECORDING = 7,
+  WAITING_FOR_TRACE_DISPLAY = 8,
+}
+
+// Wraps a tracing session promise while the promise is being resolved (e.g.
+// while we are awaiting for ADB auth).
+class TracingSessionWrapper {
+  private tracingSession?: TracingSession = undefined;
+  private isCancelled = false;
+  // We only execute the logic in the callbacks if this TracingSessionWrapper
+  // is the one referenced by the controller. Otherwise this can hold a
+  // tracing session which the user has already cancelled, so it shouldn't
+  // influence the UI.
+  private tracingSessionListener: TracingSessionListener = {
+    onTraceData: (trace: Uint8Array) =>
+        this.controller.maybeOnTraceData(this, trace),
+    onStatus: (message) => this.controller.maybeOnStatus(this, message),
+    onDisconnect: (errorMessage?: string) =>
+        this.controller.maybeOnDisconnect(this, errorMessage),
+    onError: (errorMessage: string) =>
+        this.controller.maybeOnError(this, errorMessage),
+  };
+
+  private target: RecordingTargetV2;
+  private controller: RecordingPageController;
+
+  constructor(target: RecordingTargetV2, controller: RecordingPageController) {
+    this.target = target;
+    this.controller = controller;
+  }
+
+  async start(traceConfig: TraceConfig) {
+    let stateGeneratioNr = this.controller.getStateGeneration();
+    const createSession = async () => {
+      try {
+        this.controller.maybeSetState(
+            this, RecordingState.AUTH_P2, stateGeneratioNr);
+        stateGeneratioNr += 1;
+
+        const session =
+            await this.target.createTracingSession(this.tracingSessionListener);
+
+        // We check the `isCancelled` to see if the user has cancelled the
+        // tracing session before it becomes available in TracingSessionWrapper.
+        if (this.isCancelled) {
+          session.cancel();
+          return;
+        }
+
+        this.tracingSession = session;
+        this.controller.maybeSetState(
+            this, RecordingState.RECORDING, stateGeneratioNr);
+        // When the session is resolved, the traceConfig has been instantiated.
+        this.tracingSession.start(assertExists(traceConfig));
+      } catch (e) {
+        this.tracingSessionListener.onError(e.message);
+      }
+    };
+
+    if (await this.target.canConnectWithoutContention()) {
+      await createSession();
+    } else {
+      // If we need to reset the connection to be able to connect, we ask
+      // the user if they want to reset the connection.
+      this.controller.maybeSetState(
+          this, RecordingState.ASK_TO_FORCE_P2, stateGeneratioNr);
+      stateGeneratioNr += 1;
+      couldNotClaimInterface(
+          createSession, () => this.controller.maybeClearRecordingState(this));
+    }
+  }
+
+  async fetchTargetInfo() {
+    let stateGeneratioNr = this.controller.getStateGeneration();
+    const createSession = async () => {
+      try {
+        this.controller.maybeSetState(
+            this, RecordingState.AUTH_P1, stateGeneratioNr);
+        stateGeneratioNr += 1;
+        await this.target.fetchTargetInfo(this.tracingSessionListener);
+        this.controller.maybeSetState(
+            this, RecordingState.TARGET_INFO_DISPLAYED, stateGeneratioNr);
+      } catch (e) {
+        this.tracingSessionListener.onError(e.message);
+      }
+    };
+
+    if (await this.target.canConnectWithoutContention()) {
+      await createSession();
+    } else {
+      // If we need to reset the connection to be able to connect, we ask
+      // the user if they want to reset the connection.
+      this.controller.maybeSetState(
+          this, RecordingState.ASK_TO_FORCE_P1, stateGeneratioNr);
+      stateGeneratioNr += 1;
+      couldNotClaimInterface(
+          createSession,
+          () => this.controller.maybeSetState(
+              this, RecordingState.TARGET_SELECTED, stateGeneratioNr));
+    }
+  }
+
+  cancel() {
+    if (this.tracingSession) {
+      this.tracingSession.cancel();
+    } else {
+      // In some cases, the tracingSession may not be available to the
+      // TracingSessionWrapper when the user cancels it.
+      // For instance:
+      //  1. The user clicked 'Start'.
+      //  2. They clicked 'Stop' without authorizing on the device.
+      //  3. They clicked 'Start'.
+      //  4. They authorized on the device.
+      // In these cases, we want to cancel the tracing session as soon as it
+      // becomes available. Therefore, we keep the `isCancelled` boolean and
+      // check it when we receive the tracing session.
+      this.isCancelled = true;
+    }
+    this.controller.maybeClearRecordingState(this);
+  }
+
+  stop() {
+    const stateGeneratioNr = this.controller.getStateGeneration();
+    if (this.tracingSession) {
+      this.tracingSession.stop();
+      this.controller.maybeSetState(
+          this, RecordingState.WAITING_FOR_TRACE_DISPLAY, stateGeneratioNr);
+    } else {
+      // In some cases, the tracingSession may not be available to the
+      // TracingSessionWrapper when the user stops it.
+      // For instance:
+      //  1. The user clicked 'Start'.
+      //  2. They clicked 'Stop' without authorizing on the device.
+      //  3. They clicked 'Start'.
+      //  4. They authorized on the device.
+      // In these cases, we want to cancel the tracing session as soon as it
+      // becomes available. Therefore, we keep the `isCancelled` boolean and
+      // check it when we receive the tracing session.
+      this.isCancelled = true;
+      this.controller.maybeClearRecordingState(this);
+    }
+  }
+
+  getTraceBufferUsage(): Promise<number> {
+    if (!this.tracingSession) {
+      throw new RecordingError(BUFFER_USAGE_NOT_ACCESSIBLE);
+    }
+    return this.tracingSession.getTraceBufferUsage();
+  }
+}
+
+// Keeps track of the state the Ui is in. Has methods which are executed on
+// user actions such as starting/stopping/cancelling a tracing session.
+export class RecordingPageController {
+  // State of the recording page. This is set by user actions and/or automatic
+  // transitions. This is queried by the UI in order to
+  private state: RecordingState = RecordingState.NO_TARGET;
+  // Currently selected target.
+  private target?: RecordingTargetV2 = undefined;
+  // We wrap the tracing session in an object, because for some targets
+  // (Ex: Android) it is only created after we have succesfully authenticated
+  // with the target.
+  private tracingSessionWrapper?: TracingSessionWrapper = undefined;
+  // How much of the buffer is used for the current tracing session.
+  private bufferUsagePercentage: number = 0;
+  // A counter for state modifications. We use this to ensure that state
+  // transitions don't override one another in async functions.
+  private stateGeneration = 0;
+
+  getBufferUsagePercentage(): number {
+    return this.bufferUsagePercentage;
+  }
+
+  getState(): RecordingState {
+    return this.state;
+  }
+
+  getStateGeneration(): number {
+    return this.stateGeneration;
+  }
+
+  maybeSetState(
+      tracingSessionWrapper: TracingSessionWrapper, state: RecordingState,
+      stateGeneration: number): void {
+    if (this.tracingSessionWrapper !== tracingSessionWrapper) {
+      return;
+    }
+    if (stateGeneration !== this.stateGeneration) {
+      throw new RecordingError('Recording page state transition out of order.');
+    }
+    this.setState(state);
+    globals.dispatch(Actions.setRecordingStatus({status: undefined}));
+    globals.rafScheduler.scheduleFullRedraw();
+  }
+
+  maybeClearRecordingState(tracingSessionWrapper: TracingSessionWrapper): void {
+    if (this.tracingSessionWrapper === tracingSessionWrapper) {
+      this.clearRecordingState();
+    }
+  }
+
+  maybeOnTraceData(
+      tracingSessionWrapper: TracingSessionWrapper, trace: Uint8Array) {
+    if (this.tracingSessionWrapper !== tracingSessionWrapper) {
+      return;
+    }
+    globals.dispatch(Actions.openTraceFromBuffer({
+      title: 'Recorded trace',
+      buffer: trace.buffer,
+      fileName: `trace_${currentDateHourAndMinute()}${TRACE_SUFFIX}`,
+    }));
+    this.clearRecordingState();
+  }
+
+  maybeOnStatus(tracingSessionWrapper: TracingSessionWrapper, message: string) {
+    if (this.tracingSessionWrapper !== tracingSessionWrapper) {
+      return;
+    }
+    // For the 'Recording in progress for 7000ms we don't show a
+    // modal.'
+    if (message.startsWith(RECORDING_IN_PROGRESS)) {
+      globals.dispatch(Actions.setRecordingStatus({status: message}));
+    } else {
+      // For messages such as 'Please allow USB debugging on your
+      // device, which require a user action, we show a modal.
+      showRecordingModal(message);
+    }
+  }
+
+  maybeOnDisconnect(
+      tracingSessionWrapper: TracingSessionWrapper, errorMessage?: string) {
+    if (this.tracingSessionWrapper !== tracingSessionWrapper) {
+      return;
+    }
+    if (errorMessage) {
+      showRecordingModal(errorMessage);
+    }
+    this.clearRecordingState();
+    this.onTargetChange();
+  }
+
+  maybeOnError(
+      tracingSessionWrapper: TracingSessionWrapper, errorMessage: string) {
+    if (this.tracingSessionWrapper !== tracingSessionWrapper) {
+      return;
+    }
+    showRecordingModal(errorMessage);
+    this.clearRecordingState();
+  }
+
+  getTargetInfo(): TargetInfo|undefined {
+    if (!this.target) {
+      return undefined;
+    }
+    return this.target.getInfo();
+  }
+
+  canCreateTracingSession() {
+    if (!this.target) {
+      return false;
+    }
+    return this.target.canCreateTracingSession();
+  }
+
+  selectTarget(selectedTarget?: RecordingTargetV2) {
+    assertTrue(
+        RecordingState.NO_TARGET <= this.state &&
+        this.state < RecordingState.RECORDING);
+    // If the selected target exists and is the same as the previous one, we
+    // don't need to do anything.
+    if (selectedTarget && selectedTarget === this.target) {
+      return;
+    }
+
+    // We assign the new target and redraw the page.
+    this.target = selectedTarget;
+
+    if (!this.target) {
+      this.setState(RecordingState.NO_TARGET);
+      globals.rafScheduler.scheduleFullRedraw();
+      return;
+    }
+    this.setState(RecordingState.TARGET_SELECTED);
+    globals.rafScheduler.scheduleFullRedraw();
+
+    this.tracingSessionWrapper = this.createTracingSessionWrapper(this.target);
+    this.tracingSessionWrapper.fetchTargetInfo();
+  }
+
+  async addAndroidDevice(): Promise<void> {
+    try {
+      const target =
+          await targetFactoryRegistry.get(ANDROID_WEBUSB_TARGET_FACTORY)
+              .connectNewTarget();
+      this.selectTarget(target);
+    } catch (e) {
+      if (e instanceof RecordingError) {
+        showRecordingModal(e.message);
+      } else {
+        throw e;
+      }
+    }
+  }
+
+  onTargetSelection(targetName: string): void {
+    assertTrue(
+        RecordingState.NO_TARGET <= this.state &&
+        this.state < RecordingState.RECORDING);
+    const allTargets = targetFactoryRegistry.listTargets();
+    this.selectTarget(allTargets.find((t) => t.getInfo().name === targetName));
+  }
+
+  onStartRecordingPressed(): void {
+    assertTrue(RecordingState.TARGET_INFO_DISPLAYED === this.state);
+    location.href = '#!/record/instructions';
+    autosaveConfigStore.save(globals.state.recordConfig);
+
+    const target = this.getTarget();
+    const targetInfo = target.getInfo();
+    globals.logging.logEvent(
+        'Record Trace', `Record trace (${targetInfo.targetType})`);
+    const traceConfig = genTraceConfig(globals.state.recordConfig, targetInfo);
+
+    this.tracingSessionWrapper = this.createTracingSessionWrapper(target);
+    this.tracingSessionWrapper.start(traceConfig);
+  }
+
+  onCancel() {
+    assertTrue(
+        RecordingState.AUTH_P2 <= this.state &&
+        this.state <= RecordingState.RECORDING);
+    // The 'Cancel' button will only be shown after a `tracingSessionWrapper`
+    // is created.
+    this.getTracingSessionWrapper().cancel();
+  }
+
+  onStop() {
+    assertTrue(
+        RecordingState.AUTH_P2 <= this.state &&
+        this.state <= RecordingState.RECORDING);
+    // The 'Stop' button will only be shown after a `tracingSessionWrapper`
+    // is created.
+    this.getTracingSessionWrapper().stop();
+  }
+
+  async fetchBufferUsage() {
+    assertTrue(this.state >= RecordingState.AUTH_P2);
+    if (!this.tracingSessionWrapper) return;
+    const session = this.tracingSessionWrapper;
+
+    try {
+      const usage = await session.getTraceBufferUsage();
+      if (this.tracingSessionWrapper === session) {
+        this.bufferUsagePercentage = usage;
+      }
+    } catch (e) {
+      // We ignore RecordingErrors because they are not necessary for the trace
+      // to be successfully collected.
+      if (!(e instanceof RecordingError)) {
+        throw e;
+      }
+    }
+    // We redraw if:
+    // 1. We received a correct buffer usage value.
+    // 2. We receive a RecordingError.
+    globals.rafScheduler.scheduleFullRedraw();
+  }
+
+  initFactories() {
+    assertTrue(this.state <= RecordingState.TARGET_INFO_DISPLAYED);
+    for (const targetFactory of targetFactoryRegistry.listTargetFactories()) {
+      if (targetFactory) {
+        targetFactory.setOnTargetChange(this.onTargetChange.bind(this));
+      }
+    }
+
+    if (targetFactoryRegistry.has(ANDROID_WEBSOCKET_TARGET_FACTORY)) {
+      const websocketTargetFactory =
+          targetFactoryRegistry.get(ANDROID_WEBSOCKET_TARGET_FACTORY) as
+          AndroidWebsocketTargetFactory;
+      websocketTargetFactory.tryEstablishWebsocket(DEFAULT_ADB_WEBSOCKET_URL);
+    }
+    if (targetFactoryRegistry.has(HOST_OS_TARGET_FACTORY)) {
+      const websocketTargetFactory =
+          targetFactoryRegistry.get(HOST_OS_TARGET_FACTORY) as
+          HostOsTargetFactory;
+      websocketTargetFactory.tryEstablishWebsocket(
+          DEFAULT_TRACED_WEBSOCKET_URL);
+    }
+  }
+
+  shouldShowTargetSelection(): boolean {
+    return RecordingState.NO_TARGET <= this.state &&
+        this.state < RecordingState.RECORDING;
+  }
+
+  shouldShowStopCancelButtons(): boolean {
+    return RecordingState.AUTH_P2 <= this.state &&
+        this.state <= RecordingState.RECORDING;
+  }
+
+  private onTargetChange() {
+    const allTargets = targetFactoryRegistry.listTargets();
+    // If the change happens for an existing target, the controller keeps the
+    // currently selected target in focus.
+    if (this.target && allTargets.includes(this.target)) {
+      globals.rafScheduler.scheduleFullRedraw();
+      return;
+    }
+    // If the change happens to a new target or the controller does not have a
+    // defined target, the selection process again is run again.
+    this.selectTarget();
+  }
+
+  private createTracingSessionWrapper(target: RecordingTargetV2):
+      TracingSessionWrapper {
+    return new TracingSessionWrapper(target, this);
+  }
+
+  private clearRecordingState(): void {
+    this.bufferUsagePercentage = 0;
+    this.tracingSessionWrapper = undefined;
+    this.setState(RecordingState.TARGET_INFO_DISPLAYED);
+    globals.dispatch(Actions.setRecordingStatus({status: undefined}));
+    // Redrawing because this method has changed the RecordingState, which will
+    // affect the display of the record_page.
+    globals.rafScheduler.scheduleFullRedraw();
+  }
+
+  private setState(state: RecordingState) {
+    this.state = state;
+    this.stateGeneration += 1;
+  }
+
+  private getTarget(): RecordingTargetV2 {
+    assertTrue(RecordingState.TARGET_INFO_DISPLAYED === this.state);
+    return assertExists(this.target);
+  }
+
+  private getTracingSessionWrapper(): TracingSessionWrapper {
+    assertTrue(
+        RecordingState.ASK_TO_FORCE_P2 <= this.state &&
+        this.state <= RecordingState.RECORDING);
+    return assertExists(this.tracingSessionWrapper);
+  }
+}
diff --git a/ui/src/common/recordingV2/recording_utils.ts b/ui/src/common/recordingV2/recording_utils.ts
new file mode 100644
index 0000000..72d2761
--- /dev/null
+++ b/ui/src/common/recordingV2/recording_utils.ts
@@ -0,0 +1,156 @@
+// Copyright (C) 2022 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.
+
+// Begin Websocket ////////////////////////////////////////////////////////
+
+export const WEBSOCKET_UNABLE_TO_CONNECT =
+    'Unable to connect to device via websocket.';
+
+// https://www.rfc-editor.org/rfc/rfc6455#section-7.4.1
+export const WEBSOCKET_CLOSED_ABNORMALLY_CODE = 1006;
+
+// The messages read by the adb server have their length prepended in hex.
+// This method adds the length at the beginning of the message.
+// Example: 'host:track-devices' -> '0012host:track-devices'
+// go/codesearch/aosp-android11/system/core/adb/SERVICES.TXT
+export function buildAbdWebsocketCommand(cmd: string) {
+  const hdr = cmd.length.toString(16).padStart(4, '0');
+  return hdr + cmd;
+}
+
+// Sample user agent for Chrome on Mac OS:
+// 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36
+// (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'
+export function isMacOs(userAgent: string) {
+  return userAgent.toLowerCase().includes(' mac os ');
+}
+
+// Sample user agent for Chrome on Linux:
+// Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)
+// Chrome/105.0.0.0 Safari/537.36
+export function isLinux(userAgent: string) {
+  return userAgent.toLowerCase().includes(' linux ');
+}
+
+// Sample user agent for Chrome on Chrome OS:
+// "Mozilla/5.0 (X11; CrOS x86_64 14816.99.0) AppleWebKit/537.36
+// (KHTML, like Gecko) Chrome/103.0.5060.114 Safari/537.36"
+// This condition is wider, in the unlikely possibility of different casing,
+export function isCrOS(userAgent: string) {
+  return userAgent.toLowerCase().includes(' cros ');
+}
+
+// End Websocket //////////////////////////////////////////////////////////
+
+// Begin Adb //////////////////////////////////////////////////////////////
+
+export const BINARY_PUSH_FAILURE = 'BinaryPushFailure';
+export const BINARY_PUSH_UNKNOWN_RESPONSE = 'BinaryPushUnknownResponse';
+
+// In case the device doesn't have the tracebox, we upload the latest version
+// to this path.
+export const TRACEBOX_DEVICE_PATH = '/data/local/tmp/tracebox';
+
+// Experimentally, this takes 900ms on the first fetch and 20-30ms after
+// because of caching.
+export const TRACEBOX_FETCH_TIMEOUT = 30000;
+
+// Message shown to the user when they need to allow authentication on the
+// device in order to connect.
+export const ALLOW_USB_DEBUGGING =
+    'Please allow USB debugging on device and try again.';
+
+// If the Android device has the tracing service on it (from API version 29),
+// then we can connect to this consumer socket.
+export const DEFAULT_TRACED_CONSUMER_SOCKET_PATH =
+    'localfilesystem:/dev/socket/traced_consumer';
+
+// If the Android device does not have the tracing service on it (before API
+// version 29), we will have to push the tracebox on the device. Then, we
+// can connect to this consumer socket (using it does not require system admin
+// privileges).
+export const CUSTOM_TRACED_CONSUMER_SOCKET_PATH =
+    'localabstract:traced_consumer';
+
+// End Adb /////////////////////////////////////////////////////////////////
+
+
+// Begin Webusb ///////////////////////////////////////////////////////////
+
+export const NO_DEVICE_SELECTED = 'No device selected.';
+
+export interface UsbInterfaceAndEndpoint {
+  readonly configurationValue: number;
+  readonly usbInterfaceNumber: number;
+  readonly endpoints: USBEndpoint[];
+}
+
+export const ADB_DEVICE_FILTER = {
+  classCode: 255,    // USB vendor specific code
+  subclassCode: 66,  // Android vendor specific subclass
+  protocolCode: 1,   // Adb protocol
+};
+
+export function findInterfaceAndEndpoint(device: USBDevice):
+    UsbInterfaceAndEndpoint|undefined {
+  const adbDeviceFilter = ADB_DEVICE_FILTER;
+  for (const config of device.configurations) {
+    for (const interface_ of config.interfaces) {
+      for (const alt of interface_.alternates) {
+        if (alt.interfaceClass === adbDeviceFilter.classCode &&
+            alt.interfaceSubclass === adbDeviceFilter.subclassCode &&
+            alt.interfaceProtocol === adbDeviceFilter.protocolCode) {
+          return {
+            configurationValue: config.configurationValue,
+            usbInterfaceNumber: interface_.interfaceNumber,
+            endpoints: alt.endpoints,
+          };
+        }  // if (alternate)
+      }    // for (interface.alternates)
+    }      // for (configuration.interfaces)
+  }        // for (configurations)
+
+  return undefined;
+}
+
+// End Webusb //////////////////////////////////////////////////////////////
+
+
+// Begin Chrome ///////////////////////////////////////////////////////////
+
+export const EXTENSION_ID = 'lfmkphfpdbjijhpomgecfikhfohaoine';
+export const EXTENSION_URL =
+    `https://chrome.google.com/webstore/detail/perfetto-ui/${EXTENSION_ID}`;
+export const EXTENSION_NAME = 'Chrome extension';
+export const EXTENSION_NOT_INSTALLED =
+    `To trace Chrome from the Perfetto UI, you need to install our
+    ${EXTENSION_URL} and then reload this page.`;
+
+export const MALFORMED_EXTENSION_MESSAGE = 'Malformed extension message.';
+export const BUFFER_USAGE_NOT_ACCESSIBLE = 'Buffer usage not accessible';
+export const BUFFER_USAGE_INCORRECT_FORMAT =
+    'The buffer usage data has am incorrect format';
+
+// End Chrome /////////////////////////////////////////////////////////////
+
+
+// Begin Traced //////////////////////////////////////////////////////////
+
+export const RECORDING_IN_PROGRESS = 'Recording in progress';
+export const PARSING_UNKNWON_REQUEST_ID = 'Unknown request id';
+export const PARSING_UNABLE_TO_DECODE_METHOD = 'Unable to decode method';
+export const PARSING_UNRECOGNIZED_PORT = 'Unrecognized consumer port response';
+export const PARSING_UNRECOGNIZED_MESSAGE = 'Unrecognized frame message';
+
+// End Traced ///////////////////////////////////////////////////////////
diff --git a/ui/src/common/recordingV2/target_factories/android_websocket_target_factory.ts b/ui/src/common/recordingV2/target_factories/android_websocket_target_factory.ts
index 87e2250..c33bc64 100644
--- a/ui/src/common/recordingV2/target_factories/android_websocket_target_factory.ts
+++ b/ui/src/common/recordingV2/target_factories/android_websocket_target_factory.ts
@@ -13,12 +13,15 @@
 // limitations under the License.
 
 import {RECORDING_V2_FLAG} from '../../feature_flags';
-import {buildAbdWebsocketCommand} from '../adb_over_websocket_utils';
 import {
   OnTargetChangeCallback,
   RecordingTargetV2,
   TargetFactory,
 } from '../recording_interfaces_v2';
+import {
+  buildAbdWebsocketCommand,
+  WEBSOCKET_CLOSED_ABNORMALLY_CODE,
+} from '../recording_utils';
 import {targetFactoryRegistry} from '../target_factory_registry';
 import {AndroidWebsocketTarget} from '../targets/android_websocket_target';
 
@@ -27,8 +30,6 @@
 // https://cs.android.com/android/platform/superproject/+/master:packages/
 // modules/adb/SERVICES.TXT;l=135
 const PREFIX_LENGTH = 4;
-// https://www.rfc-editor.org/rfc/rfc6455#section-7.4.1
-const WEBSOCKET_CLOSED_ABNORMALLY_CODE = 1006;
 
 // information received over the websocket regarding a device
 // Ex: "${serialNumber} authorized"
@@ -106,7 +107,7 @@
   constructor(
       private websocket: WebSocket,
       private maybeClearConnection: (connection: WebsocketConnection) => void,
-      private onTargetChange?: OnTargetChangeCallback) {
+      private onTargetChange: OnTargetChangeCallback) {
     this.initWebsocket();
   }
 
@@ -155,6 +156,9 @@
       target.disconnect();
     }
     this.targets.clear();
+    if (this.onTargetChange) {
+      this.onTargetChange();
+    }
   }
 
   getUrl() {
@@ -181,13 +185,15 @@
         this.targets.set(
             listedDevice.serialNumber,
             new AndroidWebsocketTarget(
-                listedDevice.serialNumber, this.websocket.url));
+                listedDevice.serialNumber,
+                this.websocket.url,
+                this.onTargetChange));
         targetsUpdated = true;
       }
     }
 
     // Notify the calling code that the list of targets has been updated.
-    if (targetsUpdated && this.onTargetChange) {
+    if (targetsUpdated) {
       this.onTargetChange();
     }
   }
@@ -195,7 +201,7 @@
 
 export class AndroidWebsocketTargetFactory implements TargetFactory {
   readonly kind = ANDROID_WEBSOCKET_TARGET_FACTORY;
-  private onTargetChange?: OnTargetChangeCallback;
+  private onTargetChange: OnTargetChangeCallback = () => {};
   private websocketConnection?: WebsocketConnection;
 
   getName() {
diff --git a/ui/src/common/recordingV2/target_factories/android_webusb_target_factory.ts b/ui/src/common/recordingV2/target_factories/android_webusb_target_factory.ts
index 3fef224..e498d63 100644
--- a/ui/src/common/recordingV2/target_factories/android_webusb_target_factory.ts
+++ b/ui/src/common/recordingV2/target_factories/android_webusb_target_factory.ts
@@ -13,11 +13,8 @@
 // limitations under the License.
 
 import {assertExists} from '../../../base/logging';
+import {getErrorMessage} from '../../errors';
 import {RECORDING_V2_FLAG} from '../../feature_flags';
-import {
-  ADB_DEVICE_FILTER,
-  findInterfaceAndEndpoint,
-} from '../adb_over_webusb_utils';
 import {AdbKeyManager} from '../auth/adb_key_manager';
 import {RecordingError} from '../recording_error_handling';
 import {
@@ -25,6 +22,7 @@
   RecordingTargetV2,
   TargetFactory,
 } from '../recording_interfaces_v2';
+import {ADB_DEVICE_FILTER, findInterfaceAndEndpoint} from '../recording_utils';
 import {targetFactoryRegistry} from '../target_factory_registry';
 import {AndroidWebusbTarget} from '../targets/android_webusb_target';
 
@@ -44,7 +42,7 @@
 
 export class AndroidWebusbTargetFactory implements TargetFactory {
   readonly kind = ANDROID_WEBUSB_TARGET_FACTORY;
-  onTargetChange?: OnTargetChangeCallback;
+  onTargetChange: OnTargetChangeCallback = () => {};
   private recordingProblems: string[] = [];
   private targets: Map<string, AndroidWebusbTarget> =
       new Map<string, AndroidWebusbTarget>();
@@ -69,15 +67,20 @@
   }
 
   async connectNewTarget(): Promise<RecordingTargetV2> {
-    const device: USBDevice =
-        await this.usb.requestDevice({filters: [ADB_DEVICE_FILTER]});
+    let device: USBDevice;
+    try {
+      device = await this.usb.requestDevice({filters: [ADB_DEVICE_FILTER]});
+    } catch (e) {
+      throw new RecordingError(getErrorMessage(e));
+    }
+
     const deviceValid = this.checkDeviceValidity(device);
     if (!deviceValid.isValid) {
       throw new RecordingError(deviceValid.issues.join('\n'));
     }
 
     const androidTarget =
-        new AndroidWebusbTarget(this, device, this.keyManager);
+        new AndroidWebusbTarget(device, this.keyManager, this.onTargetChange);
     this.targets.set(assertExists(device.serialNumber), androidTarget);
     return androidTarget;
   }
@@ -91,7 +94,8 @@
       if (this.checkDeviceValidity(device).isValid) {
         this.targets.set(
             assertExists(device.serialNumber),
-            new AndroidWebusbTarget(this, device, this.keyManager));
+            new AndroidWebusbTarget(
+                device, this.keyManager, this.onTargetChange));
       }
     }
 
@@ -99,10 +103,9 @@
       if (this.checkDeviceValidity(ev.device).isValid) {
         this.targets.set(
             assertExists(ev.device.serialNumber),
-            new AndroidWebusbTarget(this, ev.device, this.keyManager));
-        if (this.onTargetChange) {
-          this.onTargetChange();
-        }
+            new AndroidWebusbTarget(
+                ev.device, this.keyManager, this.onTargetChange));
+        this.onTargetChange();
       }
     });
 
@@ -113,9 +116,7 @@
       await assertExists(this.targets.get(serialNumber))
           .disconnect(`Device with serial ${serialNumber} was disconnected.`);
       this.targets.delete(serialNumber);
-      if (this.onTargetChange) {
-        this.onTargetChange();
-      }
+      this.onTargetChange();
     });
   }
 
diff --git a/ui/src/common/recordingV2/target_factories/chrome_target_factory.ts b/ui/src/common/recordingV2/target_factories/chrome_target_factory.ts
index e012bcf..1650934 100644
--- a/ui/src/common/recordingV2/target_factories/chrome_target_factory.ts
+++ b/ui/src/common/recordingV2/target_factories/chrome_target_factory.ts
@@ -12,34 +12,47 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import {EXTENSION_NOT_INSTALLED} from '../chrome_utils';
 import {RecordingError} from '../recording_error_handling';
 import {
   OnTargetChangeCallback,
   RecordingTargetV2,
   TargetFactory,
 } from '../recording_interfaces_v2';
+import {
+  EXTENSION_ID,
+  EXTENSION_NOT_INSTALLED,
+  isCrOS,
+} from '../recording_utils';
 import {targetFactoryRegistry} from '../target_factory_registry';
 import {ChromeTarget} from '../targets/chrome_target';
 
-const CHROME_TARGET_FACTORY = 'ChromeTargetFactory';
-
-// Sample user agent for Chrome on Chrome OS:
-// "Mozilla/5.0 (X11; CrOS x86_64 14816.99.0) AppleWebKit/537.36
-// (KHTML, like Gecko) Chrome/103.0.5060.114 Safari/537.36"
-// This condition is wider, in the unlikely possibility of different casing,
-export function isCrOS(userAgent: string) {
-  return userAgent.toLowerCase().includes(' cros ');
-}
+export const CHROME_TARGET_FACTORY = 'ChromeTargetFactory';
 
 export class ChromeTargetFactory implements TargetFactory {
   readonly kind = CHROME_TARGET_FACTORY;
-  private targets: ChromeTarget[];
+  // We only check the connection once at the beginning to:
+  // a) Avoid creating a 'Port' object every time 'getInfo' is called.
+  // b) When a new Port is created, the extension starts communicating with it
+  // and leaves aside the old Port objects, so creating a new Port would break
+  // any ongoing tracing session.
+  isExtensionInstalled: boolean = false;
+  private targets: ChromeTarget[] = [];
 
   constructor() {
-    this.targets = [new ChromeTarget('Chrome', 'CHROME')];
+    this.init();
+  }
+
+  init() {
+    const testPort = chrome.runtime.connect(EXTENSION_ID);
+    this.isExtensionInstalled = !!testPort;
+    testPort.disconnect();
+
+    if (!this.isExtensionInstalled) {
+      return;
+    }
+    this.targets.push(new ChromeTarget('Chrome', 'CHROME'));
     if (isCrOS(navigator.userAgent)) {
-      this.targets.push(new ChromeTarget('Chrome', 'CHROME_OS'));
+      this.targets.push(new ChromeTarget('ChromeOS', 'CHROME_OS'));
     }
   }
 
@@ -55,7 +68,7 @@
 
   listRecordingProblems(): string[] {
     const recordingProblems = [];
-    if (!this.targets[0].getInfo().isExtensionInstalled) {
+    if (!this.isExtensionInstalled) {
       recordingProblems.push(EXTENSION_NOT_INSTALLED);
     }
     return recordingProblems;
diff --git a/ui/src/common/recordingV2/target_factories/chrome_target_factory_unittest.ts b/ui/src/common/recordingV2/target_factories/chrome_target_factory_unittest.ts
index 478a3f4..c12365d 100644
--- a/ui/src/common/recordingV2/target_factories/chrome_target_factory_unittest.ts
+++ b/ui/src/common/recordingV2/target_factories/chrome_target_factory_unittest.ts
@@ -12,17 +12,29 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import {isCrOS} from './chrome_target_factory';
+import {isCrOS, isLinux, isMacOs} from '../recording_utils';
 
 test('parse Chrome on Chrome OS user agent', () => {
   const userAgent = 'Mozilla/5.0 (X11; CrOS x86_64 14816.99.0) ' +
       'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.114 ' +
       'Safari/537.36';
   expect(isCrOS(userAgent)).toBe(true);
+  expect(isMacOs(userAgent)).toBe(false);
+  expect(isLinux(userAgent)).toBe(false);
 });
 
 test('parse Chrome on Mac user agent', () => {
   const userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ' +
       'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36';
   expect(isCrOS(userAgent)).toBe(false);
+  expect(isMacOs(userAgent)).toBe(true);
+  expect(isLinux(userAgent)).toBe(false);
+});
+
+test('parse Chrome on Linux user agent', () => {
+  const userAgent = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 ' +
+      '(KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36';
+  expect(isCrOS(userAgent)).toBe(false);
+  expect(isMacOs(userAgent)).toBe(false);
+  expect(isLinux(userAgent)).toBe(true);
 });
diff --git a/ui/src/common/recordingV2/target_factories/host_os_target_factory.ts b/ui/src/common/recordingV2/target_factories/host_os_target_factory.ts
new file mode 100644
index 0000000..d7342c0
--- /dev/null
+++ b/ui/src/common/recordingV2/target_factories/host_os_target_factory.ts
@@ -0,0 +1,81 @@
+// Copyright (C) 2022 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 {RecordingError} from '../recording_error_handling';
+import {
+  OnTargetChangeCallback,
+  RecordingTargetV2,
+  TargetFactory,
+} from '../recording_interfaces_v2';
+import {isLinux, isMacOs} from '../recording_utils';
+import {targetFactoryRegistry} from '../target_factory_registry';
+import {HostOsTarget} from '../targets/host_os_target';
+
+export const HOST_OS_TARGET_FACTORY = 'HostOsTargetFactory';
+
+export class HostOsTargetFactory implements TargetFactory {
+  readonly kind = HOST_OS_TARGET_FACTORY;
+  private target?: HostOsTarget;
+  private onTargetChange: OnTargetChangeCallback = () => {};
+
+  connectNewTarget(): Promise<RecordingTargetV2> {
+    throw new RecordingError(
+        'Can not create a new Host OS target.' +
+        'The Host OS target is created at factory initialisation.');
+  }
+
+  getName(): string {
+    return 'HostOs';
+  }
+
+  listRecordingProblems(): string[] {
+    return [];
+  }
+
+  listTargets(): RecordingTargetV2[] {
+    if (this.target) {
+      return [this.target];
+    }
+    return [];
+  }
+
+  tryEstablishWebsocket(websocketUrl: string) {
+    if (this.target) {
+      if (this.target.getUrl() === websocketUrl) {
+        return;
+      } else {
+        this.target.disconnect();
+      }
+    }
+    this.target = new HostOsTarget(
+        websocketUrl, this.maybeClearTarget.bind(this), this.onTargetChange);
+    this.onTargetChange();
+  }
+
+  maybeClearTarget(target: HostOsTarget): void {
+    if (this.target === target) {
+      this.target = undefined;
+      this.onTargetChange();
+    }
+  }
+
+  setOnTargetChange(onTargetChange: OnTargetChangeCallback): void {
+    this.onTargetChange = onTargetChange;
+  }
+}
+
+// We instantiate the host target factory only on Mac and Linux.
+if (isMacOs(navigator.userAgent) || isLinux(navigator.userAgent)) {
+  targetFactoryRegistry.register(new HostOsTargetFactory());
+}
diff --git a/ui/src/common/recordingV2/target_factories/index.ts b/ui/src/common/recordingV2/target_factories/index.ts
index da8b328..3c1e3af 100644
--- a/ui/src/common/recordingV2/target_factories/index.ts
+++ b/ui/src/common/recordingV2/target_factories/index.ts
@@ -15,3 +15,5 @@
 import './android_webusb_target_factory';
 import './android_websocket_target_factory';
 import './chrome_target_factory';
+import './host_os_target_factory';
+import './virtual_target_factory';
diff --git a/ui/src/common/recordingV2/target_factories/virtual_target_factory.ts b/ui/src/common/recordingV2/target_factories/virtual_target_factory.ts
new file mode 100644
index 0000000..b882168
--- /dev/null
+++ b/ui/src/common/recordingV2/target_factories/virtual_target_factory.ts
@@ -0,0 +1,59 @@
+// Copyright (C) 2022 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 {RecordingError} from '../recording_error_handling';
+import {
+  OnTargetChangeCallback,
+  RecordingTargetV2,
+  TargetFactory,
+} from '../recording_interfaces_v2';
+import {targetFactoryRegistry} from '../target_factory_registry';
+import {AndroidVirtualTarget} from '../targets/android_virtual_target';
+
+const VIRTUAL_TARGET_FACTORY = 'VirtualTargetFactory';
+
+export class VirtualTargetFactory implements TargetFactory {
+  readonly kind: string = VIRTUAL_TARGET_FACTORY;
+  private targets: AndroidVirtualTarget[];
+
+  constructor() {
+    this.targets = [];
+    this.targets.push(new AndroidVirtualTarget('Android Q', 29));
+    this.targets.push(new AndroidVirtualTarget('Android P', 28));
+    this.targets.push(new AndroidVirtualTarget('Android O-', 27));
+  }
+
+  connectNewTarget(): Promise<RecordingTargetV2> {
+    throw new RecordingError(
+        'Can not create a new virtual target.' +
+        'All virtual targets are created at factory initialisation.');
+  }
+
+  getName(): string {
+    return 'Virtual';
+  }
+
+  listRecordingProblems(): string[] {
+    return [];
+  }
+
+  listTargets(): RecordingTargetV2[] {
+    return this.targets;
+  }
+
+  // Virtual targets won't change.
+  setOnTargetChange(_: OnTargetChangeCallback): void {}
+}
+
+targetFactoryRegistry.register(new VirtualTargetFactory());
diff --git a/ui/src/common/recordingV2/targets/android_target.ts b/ui/src/common/recordingV2/targets/android_target.ts
new file mode 100644
index 0000000..b39266c
--- /dev/null
+++ b/ui/src/common/recordingV2/targets/android_target.ts
@@ -0,0 +1,154 @@
+// Copyright (C) 2022 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 {fetchWithTimeout} from '../../../base/http_utils';
+import {VERSION} from '../../../gen/perfetto_version';
+import {AdbConnectionImpl} from '../adb_connection_impl';
+import {
+  DataSource,
+  OnTargetChangeCallback,
+  RecordingTargetV2,
+  TargetInfo,
+  TracingSession,
+  TracingSessionListener,
+} from '../recording_interfaces_v2';
+import {
+  CUSTOM_TRACED_CONSUMER_SOCKET_PATH,
+  DEFAULT_TRACED_CONSUMER_SOCKET_PATH,
+  TRACEBOX_DEVICE_PATH,
+  TRACEBOX_FETCH_TIMEOUT,
+} from '../recording_utils';
+import {TracedTracingSession} from '../traced_tracing_session';
+
+export abstract class AndroidTarget implements RecordingTargetV2 {
+  private consumerSocketPath = DEFAULT_TRACED_CONSUMER_SOCKET_PATH;
+  protected androidApiLevel?: number;
+  protected dataSources?: DataSource[];
+
+  protected constructor(
+      private adbConnection: AdbConnectionImpl,
+      private onTargetChange: OnTargetChangeCallback) {}
+
+  abstract getInfo(): TargetInfo;
+
+  // This is called when a usb USBConnectionEvent of type 'disconnect' event is
+  // emitted. This event is emitted when the USB connection is lost (example:
+  // when the user unplugged the connecting cable).
+  async disconnect(disconnectMessage?: string): Promise<void> {
+    await this.adbConnection.disconnect(disconnectMessage);
+  }
+
+  // Starts a tracing session in order to fetch information such as apiLevel
+  // and dataSources from the device. Then, it cancels the session.
+  async fetchTargetInfo(listener: TracingSessionListener): Promise<void> {
+    const tracingSession = await this.createTracingSession(listener);
+    tracingSession.cancel();
+  }
+
+  // We do not support long tracing on Android.
+  canCreateTracingSession(recordingMode: string): boolean {
+    return recordingMode !== 'LONG_TRACE';
+  }
+
+  async createTracingSession(tracingSessionListener: TracingSessionListener):
+      Promise<TracingSession> {
+    this.adbConnection.onStatus = tracingSessionListener.onStatus;
+    this.adbConnection.onDisconnect = tracingSessionListener.onDisconnect;
+
+    if (!this.androidApiLevel) {
+      // 1. Fetch the API version from the device.
+      const version = await this.adbConnection.shellAndGetOutput(
+          'getprop ro.build.version.sdk');
+      this.androidApiLevel = Number(version);
+
+      this.onTargetChange();
+
+      // 2. For older OS versions we push the tracebox binary.
+      if (this.androidApiLevel < 29) {
+        await this.pushTracebox();
+        this.consumerSocketPath = CUSTOM_TRACED_CONSUMER_SOCKET_PATH;
+
+        await this.adbConnection.shellAndWaitCompletion(
+            this.composeTraceboxCommand('traced'));
+        await this.adbConnection.shellAndWaitCompletion(
+            this.composeTraceboxCommand('traced_probes'));
+      }
+    }
+
+    const adbStream =
+        await this.adbConnection.connectSocket(this.consumerSocketPath);
+
+    // 3. Start a tracing session.
+    const tracingSession =
+        new TracedTracingSession(adbStream, tracingSessionListener);
+    await tracingSession.initConnection();
+
+    if (!this.dataSources) {
+      // 4. Fetch dataSources from QueryServiceState.
+      this.dataSources = await tracingSession.queryServiceState();
+
+      this.onTargetChange();
+    }
+    return tracingSession;
+  }
+
+  async pushTracebox() {
+    const arch = await this.fetchArchitecture();
+    const shortVersion = VERSION.split('-')[0];
+    const requestUrl =
+        `https://commondatastorage.googleapis.com/perfetto-luci-artifacts/${
+            shortVersion}/${arch}/tracebox`;
+    const fetchResponse = await fetchWithTimeout(
+        requestUrl, {method: 'get'}, TRACEBOX_FETCH_TIMEOUT);
+    const traceboxBin = await fetchResponse.arrayBuffer();
+    await this.adbConnection.push(
+        new Uint8Array(traceboxBin), TRACEBOX_DEVICE_PATH);
+
+    // We explicitly set the tracebox permissions because adb does not reliably
+    // set permissions when uploading the binary.
+    await this.adbConnection.shellAndWaitCompletion(
+        `chmod 755 ${TRACEBOX_DEVICE_PATH}`);
+  }
+
+  async fetchArchitecture() {
+    const abiList = await this.adbConnection.shellAndGetOutput(
+        'getprop ro.vendor.product.cpu.abilist');
+    // If multiple ABIs are allowed, the 64bit ones should have higher priority.
+    if (abiList.includes('arm64-v8a')) {
+      return 'android-arm64';
+    } else if (abiList.includes('x86')) {
+      return 'android-x86';
+    } else if (abiList.includes('armeabi-v7a') || abiList.includes('armeabi')) {
+      return 'android-arm';
+    } else if (abiList.includes('x86_64')) {
+      return 'android-x64';
+    }
+    // Most devices have arm64 architectures, so we should return this if
+    // nothing else is found.
+    return 'android-arm64';
+  }
+
+  canConnectWithoutContention(): Promise<boolean> {
+    return this.adbConnection.canConnectWithoutContention();
+  }
+
+  composeTraceboxCommand(applet: string) {
+    // 1. Set the consumer socket.
+    return 'PERFETTO_CONSUMER_SOCK_NAME=@traced_consumer ' +
+        // 2. Set the producer socket.
+        'PERFETTO_PRODUCER_SOCK_NAME=@traced_producer ' +
+        // 3. Start the applet in the background.
+        `/data/local/tmp/tracebox ${applet} --background`;
+  }
+}
diff --git a/ui/src/common/recordingV2/targets/android_virtual_target.ts b/ui/src/common/recordingV2/targets/android_virtual_target.ts
new file mode 100644
index 0000000..b632699
--- /dev/null
+++ b/ui/src/common/recordingV2/targets/android_virtual_target.ts
@@ -0,0 +1,55 @@
+// Copyright (C) 2022 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 {RecordingError} from '../recording_error_handling';
+import {
+  RecordingTargetV2,
+  TargetInfo,
+  TracingSession,
+  TracingSessionListener,
+} from '../recording_interfaces_v2';
+
+export class AndroidVirtualTarget implements RecordingTargetV2 {
+  constructor(private name: string, private androidApiLevel: number) {}
+
+  canConnectWithoutContention(): Promise<boolean> {
+    return Promise.resolve(true);
+  }
+
+  canCreateTracingSession(): boolean {
+    return false;
+  }
+
+  createTracingSession(_: TracingSessionListener): Promise<TracingSession> {
+    throw new RecordingError(
+        'Can not create tracing session for a virtual target');
+  }
+
+  disconnect(_?: string): Promise<void> {
+    throw new RecordingError('Can not disconnect from a virtual target');
+  }
+
+  fetchTargetInfo(_: TracingSessionListener): Promise<void> {
+    return Promise.resolve();
+  }
+
+  getInfo(): TargetInfo {
+    return {
+      name: this.name,
+      androidApiLevel: this.androidApiLevel,
+      targetType: 'ANDROID',
+      dataSources: [],
+    };
+  }
+}
diff --git a/ui/src/common/recordingV2/targets/android_websocket_target.ts b/ui/src/common/recordingV2/targets/android_websocket_target.ts
index c0b851a..ab4f130 100644
--- a/ui/src/common/recordingV2/targets/android_websocket_target.ts
+++ b/ui/src/common/recordingV2/targets/android_websocket_target.ts
@@ -14,61 +14,27 @@
 
 import {AdbConnectionOverWebsocket} from '../adb_connection_over_websocket';
 import {
-  RecordingTargetV2,
+  OnTargetChangeCallback,
   TargetInfo,
-  TracingSession,
-  TracingSessionListener,
 } from '../recording_interfaces_v2';
-import {TracedTracingSession} from '../traced_tracing_session';
+import {AndroidTarget} from './android_target';
 
-export class AndroidWebsocketTarget implements RecordingTargetV2 {
-  private adbConnection: AdbConnectionOverWebsocket;
-
-  constructor(private serialNumber: string, websocketUrl: string) {
-    this.adbConnection =
-        new AdbConnectionOverWebsocket(serialNumber, websocketUrl);
+export class AndroidWebsocketTarget extends AndroidTarget {
+  constructor(
+      private serialNumber: string, websocketUrl: string,
+      onTargetChange: OnTargetChangeCallback) {
+    super(
+        new AdbConnectionOverWebsocket(serialNumber, websocketUrl),
+        onTargetChange);
   }
 
   getInfo(): TargetInfo {
     return {
       targetType: 'ANDROID',
-      // TODO(octaviant): fetch the OS from the adb connection
-      // once aosp/2127460 is in
-      androidApiLevel: undefined,
-      dataSources: [],
+      // 'androidApiLevel' will be populated after ADB authorization.
+      androidApiLevel: this.androidApiLevel,
+      dataSources: this.dataSources || [],
       name: this.serialNumber + ' WebSocket',
     };
   }
-
-  // This is called when the websocket url changes and we need to disconnect
-  // the targets connected to the old URL.
-  disconnect(): void {
-    this.adbConnection.disconnect();
-  }
-
-
-  // Starts a tracing session in order to fetch information such as apiLevel
-  // and dataSources from the device. Then, it cancels the session.
-  async fetchTargetInfo(tracingSessionListener: TracingSessionListener):
-      Promise<void> {
-    const tracingSession =
-        await this.createTracingSession(tracingSessionListener);
-    tracingSession.cancel();
-  }
-
-  async createTracingSession(tracingSessionListener: TracingSessionListener):
-      Promise<TracingSession> {
-    this.adbConnection.onDisconnect = tracingSessionListener.onDisconnect;
-    const adbStream =
-        await this.adbConnection.connectSocket('/dev/socket/traced_consumer');
-
-    const tracingSession =
-        new TracedTracingSession(adbStream, tracingSessionListener);
-    await tracingSession.initConnection();
-    return tracingSession;
-  }
-
-  canConnectWithoutContention(): Promise<boolean> {
-    return this.adbConnection.canConnectWithoutContention();
-  }
 }
diff --git a/ui/src/common/recordingV2/targets/android_webusb_target.ts b/ui/src/common/recordingV2/targets/android_webusb_target.ts
index f2fb30d..4a69f13 100644
--- a/ui/src/common/recordingV2/targets/android_webusb_target.ts
+++ b/ui/src/common/recordingV2/targets/android_webusb_target.ts
@@ -12,34 +12,20 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import {fetchWithTimeout} from '../../../base/http_utils';
 import {assertExists} from '../../../base/logging';
-import {VERSION} from '../../../gen/perfetto_version';
 import {AdbConnectionOverWebusb} from '../adb_connection_over_webusb';
-import {
-  TRACEBOX_DEVICE_PATH,
-  TRACEBOX_FETCH_TIMEOUT,
-} from '../adb_targets_utils';
 import {AdbKeyManager} from '../auth/adb_key_manager';
 import {
-  RecordingTargetV2,
+  OnTargetChangeCallback,
   TargetInfo,
-  TracingSession,
-  TracingSessionListener,
 } from '../recording_interfaces_v2';
-import {
-  AndroidWebusbTargetFactory,
-} from '../target_factories/android_webusb_target_factory';
-import {TracedTracingSession} from '../traced_tracing_session';
+import {AndroidTarget} from './android_target';
 
-export class AndroidWebusbTarget implements RecordingTargetV2 {
-  private adbConnection: AdbConnectionOverWebusb;
-  private androidApiLevel?: number;
-
+export class AndroidWebusbTarget extends AndroidTarget {
   constructor(
-      private factory: AndroidWebusbTargetFactory, private device: USBDevice,
-      keyManager: AdbKeyManager) {
-    this.adbConnection = new AdbConnectionOverWebusb(device, keyManager);
+      private device: USBDevice, keyManager: AdbKeyManager,
+      onTargetChange: OnTargetChangeCallback) {
+    super(new AdbConnectionOverWebusb(device, keyManager), onTargetChange);
   }
 
   getInfo(): TargetInfo {
@@ -47,95 +33,10 @@
         assertExists(this.device.serialNumber) + ' WebUsb';
     return {
       targetType: 'ANDROID',
-      // The method 'fetchInfo' will populate this after ADB authorization.
+      // 'androidApiLevel' will be populated after ADB authorization.
       androidApiLevel: this.androidApiLevel,
-      dataSources: [],
+      dataSources: this.dataSources || [],
       name,
     };
   }
-
-  // This is called when a usb USBConnectionEvent of type 'disconnect' event is
-  // emitted. This event is emitted when the USB connection is lost(example:
-  // when the user unplugged the connecting cable).
-  async disconnect(disconnectMessage?: string): Promise<void> {
-    await this.adbConnection.disconnect(disconnectMessage);
-  }
-
-  // Starts a tracing session in order to fetch information such as apiLevel
-  // and dataSources from the device. Then, it cancels the session.
-  async fetchTargetInfo(tracingSessionListener: TracingSessionListener):
-      Promise<void> {
-    const tracingSession =
-        await this.createTracingSession(tracingSessionListener);
-    tracingSession.cancel();
-  }
-
-  async createTracingSession(tracingSessionListener: TracingSessionListener):
-      Promise<TracingSession> {
-    this.adbConnection.onStatus = tracingSessionListener.onStatus;
-    this.adbConnection.onDisconnect = tracingSessionListener.onDisconnect;
-    const adbStream =
-        await this.adbConnection.connectSocket('/dev/socket/traced_consumer');
-
-    if (!this.androidApiLevel) {
-      const version = await this.adbConnection.shellAndGetOutput(
-          'getprop ro.build.version.sdk');
-      this.androidApiLevel = Number(version);
-      if (this.factory.onTargetChange) {
-        this.factory.onTargetChange();
-      }
-
-      // For older OS versions we push the tracebox binary.
-      if (this.androidApiLevel < 29) {
-        await this.pushTracebox();
-      }
-    }
-
-    const tracingSession =
-        new TracedTracingSession(adbStream, tracingSessionListener);
-    await tracingSession.initConnection();
-    return tracingSession;
-  }
-
-  async pushTracebox() {
-    const arch = await this.fetchArchitecture();
-    const shortVersion = VERSION.split('-')[0];
-    const traceboxBin =
-        await (
-            await fetchWithTimeout(
-                `https://commondatastorage.googleapis.com/perfetto-luci-artifacts/${
-                    shortVersion}/${arch}/tracebox`,
-                {method: 'get'},
-                TRACEBOX_FETCH_TIMEOUT))
-            .arrayBuffer();
-    await this.adbConnection.push(
-        new Uint8Array(traceboxBin), TRACEBOX_DEVICE_PATH);
-
-    // We explicitly set the tracebox permissions because adb does not reliably
-    // set permissions when uploading the binary.
-    await this.adbConnection.shellAndGetOutput(
-        `chmod 755 ${TRACEBOX_DEVICE_PATH}`);
-  }
-
-  async fetchArchitecture() {
-    const abiList = await this.adbConnection.shellAndGetOutput(
-        'getprop ro.vendor.product.cpu.abilist');
-    // If multiple ABIs are allowed, the 64bit ones should have higher priority.
-    if (abiList.includes('arm64-v8a')) {
-      return 'android-arm64';
-    } else if (abiList.includes('x86')) {
-      return 'android-x86';
-    } else if (abiList.includes('armeabi-v7a') || abiList.includes('armeabi')) {
-      return 'android-arm';
-    } else if (abiList.includes('x86_64')) {
-      return 'android-x64';
-    }
-    // Most devices have arm64 architectures, so we should return this if
-    // nothing else is found.
-    return 'android-arm64';
-  }
-
-  canConnectWithoutContention(): Promise<boolean> {
-    return this.adbConnection.canConnectWithoutContention();
-  }
 }
diff --git a/ui/src/common/recordingV2/targets/chrome_target.ts b/ui/src/common/recordingV2/targets/chrome_target.ts
index 39b13eb..f87b7b1 100644
--- a/ui/src/common/recordingV2/targets/chrome_target.ts
+++ b/ui/src/common/recordingV2/targets/chrome_target.ts
@@ -13,7 +13,6 @@
 // limitations under the License.
 
 import {ChromeTracedTracingSession} from '../chrome_traced_tracing_session';
-import {EXTENSION_ID} from '../chrome_utils';
 import {
   ChromeTargetInfo,
   OnTargetChangeCallback,
@@ -25,29 +24,24 @@
 export class ChromeTarget implements RecordingTargetV2 {
   onTargetChange?: OnTargetChangeCallback;
   private chromeCategories?: string[];
-  // We only check the connection once at the beginning to:
-  // a) Avoid creating a 'Port' object every time 'getInfo' is called.
-  // b) When a new Port is created, the extension starts communicating with it
-  // and leaves aside the old Port objects, so creating a new Port would break
-  // any ongoing tracing session.
-  private isExtensionInstalled: boolean;
 
-  constructor(private name: string, private targetType: 'CHROME'|'CHROME_OS') {
-    const testPort = chrome.runtime.connect(EXTENSION_ID);
-    this.isExtensionInstalled = !!testPort;
-    testPort.disconnect();
-  }
+  constructor(private name: string, private targetType: 'CHROME'|'CHROME_OS') {}
 
   getInfo(): ChromeTargetInfo {
     return {
       targetType: this.targetType,
       name: this.name,
-      isExtensionInstalled: this.isExtensionInstalled,
       dataSources:
           [{name: 'chromeCategories', descriptor: this.chromeCategories}],
     };
   }
 
+  // Chrome targets are created after we check that the extension is installed,
+  // so they support tracing sessions.
+  canCreateTracingSession(): boolean {
+    return true;
+  }
+
   async createTracingSession(tracingSessionListener: TracingSessionListener):
       Promise<TracingSession> {
     const tracingSession =
diff --git a/ui/src/common/recordingV2/targets/host_os_target.ts b/ui/src/common/recordingV2/targets/host_os_target.ts
new file mode 100644
index 0000000..6b13025
--- /dev/null
+++ b/ui/src/common/recordingV2/targets/host_os_target.ts
@@ -0,0 +1,137 @@
+// Copyright (C) 2022 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 {HostOsByteStream} from '../host_os_byte_stream';
+import {RecordingError} from '../recording_error_handling';
+import {
+  DataSource,
+  HostOsTargetInfo,
+  OnDisconnectCallback,
+  OnTargetChangeCallback,
+  RecordingTargetV2,
+  TracingSession,
+  TracingSessionListener,
+} from '../recording_interfaces_v2';
+import {
+  isLinux,
+  isMacOs,
+  WEBSOCKET_CLOSED_ABNORMALLY_CODE,
+} from '../recording_utils';
+import {TracedTracingSession} from '../traced_tracing_session';
+
+export class HostOsTarget implements RecordingTargetV2 {
+  private readonly targetType: 'LINUX'|'MACOS';
+  private readonly name: string;
+  private websocket: WebSocket;
+  private streams = new Set<HostOsByteStream>();
+  private dataSources?: DataSource[];
+  private onDisconnect: OnDisconnectCallback = (_) => {};
+
+  constructor(
+      websocketUrl: string,
+      private maybeClearTarget: (target: HostOsTarget) => void,
+      private onTargetChange: OnTargetChangeCallback) {
+    if (isMacOs(navigator.userAgent)) {
+      this.name = 'MacOS';
+      this.targetType = 'MACOS';
+    } else if (isLinux(navigator.userAgent)) {
+      this.name = 'Linux';
+      this.targetType = 'LINUX';
+    } else {
+      throw new RecordingError(
+          'Host OS target created on an unsupported operating system.');
+    }
+
+    this.websocket = new WebSocket(websocketUrl);
+    this.websocket.onclose = this.onClose.bind(this);
+    // 'onError' gets called when the websocketURL where the UI tries to connect
+    // is disallowed by the Content Security Policy. In this case, we disconnect
+    // the target.
+    this.websocket.onerror = this.disconnect.bind(this);
+  }
+
+  getInfo(): HostOsTargetInfo {
+    return {
+      targetType: this.targetType,
+      name: this.name,
+      dataSources: this.dataSources || [],
+    };
+  }
+
+  canCreateTracingSession(): boolean {
+    return true;
+  }
+
+  async createTracingSession(tracingSessionListener: TracingSessionListener):
+      Promise<TracingSession> {
+    this.onDisconnect = tracingSessionListener.onDisconnect;
+
+    const osStream = await HostOsByteStream.create(this.getUrl());
+    this.streams.add(osStream);
+    const tracingSession =
+        new TracedTracingSession(osStream, tracingSessionListener);
+    await tracingSession.initConnection();
+
+    if (!this.dataSources) {
+      this.dataSources = await tracingSession.queryServiceState();
+      this.onTargetChange();
+    }
+    return tracingSession;
+  }
+
+  // Starts a tracing session in order to fetch data sources from the
+  // device. Then, it cancels the session.
+  async fetchTargetInfo(tracingSessionListener: TracingSessionListener):
+      Promise<void> {
+    const tracingSession =
+        await this.createTracingSession(tracingSessionListener);
+    tracingSession.cancel();
+  }
+
+  async disconnect(): Promise<void> {
+    if (this.websocket.readyState === this.websocket.OPEN) {
+      this.websocket.close();
+      // We remove the 'onclose' callback so the 'disconnect' method doesn't get
+      // executed twice.
+      this.websocket.onclose = null;
+    }
+    for (const stream of this.streams) {
+      stream.close();
+    }
+    // We remove the existing target from the factory if present.
+    this.maybeClearTarget(this);
+    // We run the onDisconnect callback in case this target is used for tracing.
+    this.onDisconnect();
+  }
+
+  // We can connect to the Host OS without taking the connection away from
+  // another process.
+  async canConnectWithoutContention(): Promise<boolean> {
+    return true;
+  }
+
+  getUrl() {
+    return this.websocket.url;
+  }
+
+  private onClose(ev: CloseEvent): void {
+    if (ev.code === WEBSOCKET_CLOSED_ABNORMALLY_CODE) {
+      console.info(
+          `It's safe to ignore the 'WebSocket connection to ${
+              this.getUrl()} error above, if present. It occurs when ` +
+          'checking the connection to the local Websocket server.');
+    }
+    this.disconnect();
+  }
+}
diff --git a/ui/src/common/recordingV2/traced_tracing_session.ts b/ui/src/common/recordingV2/traced_tracing_session.ts
index 2257f85..c8a1d10 100644
--- a/ui/src/common/recordingV2/traced_tracing_session.ts
+++ b/ui/src/common/recordingV2/traced_tracing_session.ts
@@ -29,21 +29,29 @@
   IMethodInfo,
   IPCFrame,
   ISlice,
+  QueryServiceStateRequest,
+  QueryServiceStateResponse,
   ReadBuffersRequest,
   ReadBuffersResponse,
   TraceConfig,
 } from '../protos';
 
-import {
-  BUFFER_USAGE_INCORRECT_FORMAT,
-  BUFFER_USAGE_NOT_ACCESSIBLE,
-} from './chrome_utils';
 import {RecordingError} from './recording_error_handling';
 import {
   ByteStream,
+  DataSource,
   TracingSession,
   TracingSessionListener,
 } from './recording_interfaces_v2';
+import {
+  BUFFER_USAGE_INCORRECT_FORMAT,
+  BUFFER_USAGE_NOT_ACCESSIBLE,
+  PARSING_UNABLE_TO_DECODE_METHOD,
+  PARSING_UNKNWON_REQUEST_ID,
+  PARSING_UNRECOGNIZED_MESSAGE,
+  PARSING_UNRECOGNIZED_PORT,
+  RECORDING_IN_PROGRESS,
+} from './recording_utils';
 
 // See wire_protocol.proto for more details.
 const WIRE_PROTOCOL_HEADER_SIZE = 4;
@@ -55,12 +63,6 @@
 const TRACE_PACKET_PROTO_TAG =
     (TRACE_PACKET_PROTO_ID << 3) | PROTO_LEN_DELIMITED_WIRE_TYPE;
 
-export const RECORDING_IN_PROGRESS = 'Recording in progress';
-export const PARSING_UNKNWON_REQUEST_ID = 'Unknown request id';
-export const PARSING_UNABLE_TO_DECODE_METHOD = 'Unable to decode method';
-export const PARSING_UNRECOGNIZED_PORT = 'Unrecognized consumer port response';
-export const PARSING_UNRECOGNIZED_MESSAGE = 'Unrecognized frame message';
-
 function parseMessageSize(buffer: Uint8Array) {
   const dv = new DataView(buffer.buffer, buffer.byteOffset, buffer.length);
   return dv.getUint32(0, true);
@@ -87,6 +89,15 @@
   // Accumulates trace packets into a proto trace file..
   private traceProtoWriter = protobuf.Writer.create();
 
+  // Accumulates DataSource objects from QueryServiceStateResponse,
+  // which can have >1 replies for each query
+  // go/codesearch/android/external/perfetto/protos/
+  // perfetto/ipc/consumer_port.proto;l=243-246
+  private pendingDataSources: DataSource[] = [];
+
+  // For concurrent calls to 'QueryServiceState', we return the same value.
+  private pendingQssMessage?: Deferred<DataSource[]>;
+
   // Wire protocol request ID. After each request it is increased. It is needed
   // to keep track of the type of request, and parse the response correctly.
   private requestId = 1;
@@ -99,8 +110,22 @@
   constructor(
       private byteStream: ByteStream,
       private tracingSessionListener: TracingSessionListener) {
-    this.byteStream.onStreamData = (data) => this.handleReceivedData(data);
-    this.byteStream.onStreamClose = () => this.clearState();
+    this.byteStream.addOnStreamDataCallback(
+        (data) => this.handleReceivedData(data));
+    this.byteStream.addOnStreamCloseCallback(() => this.clearState());
+  }
+
+  queryServiceState(): Promise<DataSource[]> {
+    if (this.pendingQssMessage) {
+      return this.pendingQssMessage;
+    }
+
+    const requestProto =
+        QueryServiceStateRequest.encode(new QueryServiceStateRequest())
+            .finish();
+    this.rpcInvoke('QueryServiceState', requestProto);
+
+    return this.pendingQssMessage = defer<DataSource[]>();
   }
 
   start(config: TraceConfig): void {
@@ -126,7 +151,9 @@
   }
 
   async getTraceBufferUsage(): Promise<number> {
-    if (!this.byteStream.isOpen()) {
+    if (!this.byteStream.isConnected()) {
+      // TODO(octaviant): make this more in line with the other trace buffer
+      //  error cases.
       return 0;
     }
     const bufferStats = await this.getBufferStats();
@@ -149,7 +176,7 @@
     return percentageUsed;
   }
 
-  async initConnection(): Promise<void> {
+  initConnection(): Promise<void> {
     // bind IPC methods
     const requestId = this.requestId++;
     const frame = new IPCFrame({
@@ -193,11 +220,12 @@
       statsMessage.reject(new RecordingError(BUFFER_USAGE_NOT_ACCESSIBLE));
     }
     this.pendingStatsMessages = [];
+    this.pendingDataSources = [];
+    this.pendingQssMessage = undefined;
   }
 
-  private async rpcInvoke(methodName: string, argsProto: Uint8Array):
-      Promise<void> {
-    if (!this.byteStream.isOpen()) {
+  private rpcInvoke(methodName: string, argsProto: Uint8Array): void {
+    if (!this.byteStream.isConnected()) {
       return;
     }
     const method = this.availableMethods.find((m) => m.name === methodName);
@@ -351,6 +379,22 @@
         // connection.
       } else if (method === 'DisableTracing') {
         // No action required. Same reasoning as for FreeBuffers.
+      } else if (method === 'QueryServiceState') {
+        const dataSources =
+            (data as QueryServiceStateResponse)?.serviceState?.dataSources ||
+            [];
+        for (const dataSource of dataSources) {
+          const name = dataSource?.dsDescriptor?.name;
+          if (name) {
+            this.pendingDataSources.push(
+                {name, descriptor: dataSource.dsDescriptor});
+          }
+        }
+        if (msgInvokeMethodReply.hasMore === false) {
+          assertExists(this.pendingQssMessage).resolve(this.pendingDataSources);
+          this.pendingDataSources = [];
+          this.pendingQssMessage = undefined;
+        }
       } else {
         this.raiseError(`${PARSING_UNRECOGNIZED_PORT}: ${method}`);
       }
@@ -365,9 +409,11 @@
   }
 }
 
-const decoders = new Map<string, Function>()
-                     .set('EnableTracing', EnableTracingResponse.decode)
-                     .set('FreeBuffers', FreeBuffersResponse.decode)
-                     .set('ReadBuffers', ReadBuffersResponse.decode)
-                     .set('DisableTracing', DisableTracingResponse.decode)
-                     .set('GetTraceStats', GetTraceStatsResponse.decode);
+const decoders =
+    new Map<string, Function>()
+        .set('EnableTracing', EnableTracingResponse.decode)
+        .set('FreeBuffers', FreeBuffersResponse.decode)
+        .set('ReadBuffers', ReadBuffersResponse.decode)
+        .set('DisableTracing', DisableTracingResponse.decode)
+        .set('GetTraceStats', GetTraceStatsResponse.decode)
+        .set('QueryServiceState', QueryServiceStateResponse.decode);
diff --git a/ui/src/common/recordingV2/websocket_menu_controller.ts b/ui/src/common/recordingV2/websocket_menu_controller.ts
new file mode 100644
index 0000000..0237810
--- /dev/null
+++ b/ui/src/common/recordingV2/websocket_menu_controller.ts
@@ -0,0 +1,74 @@
+// Copyright (C) 2022 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 {
+  ADB_ENDPOINT,
+  DEFAULT_WEBSOCKET_URL,
+  TRACED_ENDPOINT,
+} from '../../frontend/recording/recording_ui_utils';
+
+import {TargetFactory} from './recording_interfaces_v2';
+import {
+  ANDROID_WEBSOCKET_TARGET_FACTORY,
+  AndroidWebsocketTargetFactory,
+} from './target_factories/android_websocket_target_factory';
+import {
+  HOST_OS_TARGET_FACTORY,
+  HostOsTargetFactory,
+} from './target_factories/host_os_target_factory';
+import {targetFactoryRegistry} from './target_factory_registry';
+
+// The WebsocketMenuController will handle paths for all factories which
+// connect over websocket. At present, these are:
+// - adb websocket factory
+// - host OS websocket factory
+export class WebsocketMenuController {
+  private path: string = DEFAULT_WEBSOCKET_URL;
+
+  getPath(): string {
+    return this.path;
+  }
+
+  setPath(path: string): void {
+    this.path = path;
+  }
+
+  onPathChange(): void {
+    if (targetFactoryRegistry.has(ANDROID_WEBSOCKET_TARGET_FACTORY)) {
+      const androidTargetFactory =
+          targetFactoryRegistry.get(ANDROID_WEBSOCKET_TARGET_FACTORY) as
+          AndroidWebsocketTargetFactory;
+      androidTargetFactory.tryEstablishWebsocket(this.path + ADB_ENDPOINT);
+    }
+
+    if (targetFactoryRegistry.has(HOST_OS_TARGET_FACTORY)) {
+      const hostTargetFactory =
+          targetFactoryRegistry.get(HOST_OS_TARGET_FACTORY) as
+          HostOsTargetFactory;
+      hostTargetFactory.tryEstablishWebsocket(this.path + TRACED_ENDPOINT);
+    }
+  }
+
+  getTargetFactories(): TargetFactory[] {
+    const targetFactories = [];
+    if (targetFactoryRegistry.has(ANDROID_WEBSOCKET_TARGET_FACTORY)) {
+      targetFactories.push(
+          targetFactoryRegistry.get(ANDROID_WEBSOCKET_TARGET_FACTORY));
+    }
+    if (targetFactoryRegistry.has(HOST_OS_TARGET_FACTORY)) {
+      targetFactories.push(targetFactoryRegistry.get(HOST_OS_TARGET_FACTORY));
+    }
+    return targetFactories;
+  }
+}
diff --git a/ui/src/common/selection_observer.ts b/ui/src/common/selection_observer.ts
new file mode 100644
index 0000000..0bd284c
--- /dev/null
+++ b/ui/src/common/selection_observer.ts
@@ -0,0 +1,32 @@
+// Copyright (C) 2022 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 {Selection} from './state';
+
+export type SelectionChangedObserver =
+    (newSelection?: Selection, oldSelection?: Selection) => void;
+
+const selectionObservers: SelectionChangedObserver[] = [];
+
+export function onSelectionChanged(
+    newSelection?: Selection, oldSelection?: Selection) {
+  for (const observer of selectionObservers) {
+    observer(newSelection, oldSelection);
+  }
+}
+
+export function addSelectionChangeObserver(observer: SelectionChangedObserver):
+    void {
+  selectionObservers.push(observer);
+}
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index cc6dbea..b16eba6 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -27,16 +27,22 @@
  */
 export interface ObjectById<Class extends{id: string}> { [id: string]: Class; }
 
-export type Timestamped<T> = {
-  [P in keyof T]: T[P];
-}&{lastUpdate: number};
+export interface Timestamped {
+  lastUpdate: number;
+}
 
 export type OmniboxMode = 'SEARCH'|'COMMAND';
 
-export type OmniboxState = Timestamped<{omnibox: string; mode: OmniboxMode}>;
+export interface OmniboxState {
+  omnibox: string;
+  mode: OmniboxMode;
+}
 
-export type VisibleState =
-    Timestamped<{startSec: number; endSec: number; resolution: number;}>;
+export interface VisibleState extends Timestamped {
+  startSec: number;
+  endSec: number;
+  resolution: number;
+}
 
 export interface AreaSelection {
   kind: 'AREA';
@@ -85,7 +91,14 @@
 // 19: Added visualisedArgs state.
 // 20: Refactored thread sorting order.
 // 21: Updated perf sample selection to include a ts range instead of single ts
-export const STATE_VERSION = 21;
+// 22: Add log selection kind.
+// 23: Add log filtering criteria for Android log entries.
+// 24: Store only a single Engine.
+// 25: Move omnibox state off VisibleState.
+// 26: Add tags for filtering Android log entries.
+// 27. Add a text entry for filtering Android log entries.
+// 28. Add a boolean indicating if non matching log entries are hidden.
+export const STATE_VERSION = 28;
 
 export const SCROLLING_TRACK_GROUP = 'ScrollingTracks';
 
@@ -153,7 +166,7 @@
 export enum ProfileType {
   HEAP_PROFILE = 'heap_profile',
   NATIVE_HEAP_PROFILE = 'heap_profile:libc.malloc',
-  JAVA_HEAP_PROFILE = 'heap_profile:com.android.art',
+  JAVA_HEAP_SAMPLES = 'heap_profile:com.android.art',
   JAVA_HEAP_GRAPH = 'graph',
   PERF_SAMPLE = 'perf',
 }
@@ -258,7 +271,6 @@
 }
 
 export interface FrontendLocalState {
-  omniboxState: OmniboxState;
   visibleState: VisibleState;
 }
 
@@ -346,10 +358,16 @@
   id: number;
 }
 
-type Selection =
+export interface LogSelection {
+  kind: 'LOG';
+  id: number;
+  trackId: string;
+}
+
+export type Selection =
     (NoteSelection|SliceSelection|CounterSelection|HeapProfileSelection|
      CpuProfileSampleSelection|ChromeSliceSelection|ThreadStateSelection|
-     AreaSelection|PerfSamplesSelection)&{trackId?: string};
+     AreaSelection|PerfSamplesSelection|LogSelection)&{trackId?: string};
 export type SelectionKind = Selection['kind'];  // 'THREAD_STATE' | 'SLICE' ...
 
 export interface LogsPagination {
@@ -386,7 +404,6 @@
 // correctly. Generated together with the text of query and passed without the
 // change to the query response.
 export interface PivotTableReduxQueryMetadata {
-  tableName: string;
   pivotColumns: TableColumn[];
   aggregationColumns: Aggregation[];
 }
@@ -421,21 +438,15 @@
   // Query response
   queryResult: PivotTableReduxResult|null;
 
-  // Selected pivots for tables other than slice/thread_slice.
+  // Selected pivots for tables other than slice.
   // Because of the query generation, pivoting happens first on non-slice
   // pivots; therefore, those can't be put after slice pivots. In order to
   // maintain the separation more clearly, slice and non-slice pivots are
   // located in separate arrays.
   selectedPivots: RegularColumn[];
 
-  // Selected pivots for slice/thread_slice table.
-  selectedSlicePivots: TableColumn[];
-
   // Selected aggregation columns. Stored same way as pivots.
-  selectedAggregations: Map<string, Aggregation>;
-
-  // Present if the result should be sorted, and in which direction.
-  sortCriteria?: {column: TableColumn, order: SortDirection};
+  selectedAggregations: Aggregation[];
 
   // Whether the pivot table results should be constrained to the selected area.
   constrainToArea: boolean;
@@ -468,9 +479,15 @@
   pivotTableRedux: PivotTableReduxState;
 }
 
+export interface LogFilteringCriteria {
+  minimumLevel: number;
+  tags: string[];
+  textEntry: string;
+  hideNonMatching: boolean;
+}
+
 export interface State {
   version: number;
-  currentEngineId?: string;
   nextId: string;
 
   /**
@@ -484,7 +501,7 @@
    * Open traces.
    */
   newEngineMode: NewEngineMode;
-  engines: ObjectById<EngineConfig>;
+  engine?: EngineConfig;
   traceTime: TraceTime;
   traceUuid?: string;
   trackGroups: ObjectById<TrackGroupState>;
@@ -556,6 +573,12 @@
   // using permalink. Can be used to store those parts of the state that can't
   // be serialized at the moment, such as ES6 Set and Map.
   nonSerializableState: NonSerializableState;
+
+  // Android logs filtering state.
+  logFilteringCriteria: LogFilteringCriteria;
+
+  // Omnibox info.
+  omniboxState: OmniboxState;
 }
 
 export const defaultTraceTime = {
@@ -595,8 +618,8 @@
 }
 
 export function hasActiveProbes(config: RecordConfig) {
-  const fieldsWithEmptyResult =
-      new Set<string>(['hpBlockClient', 'allAtraceApps']);
+  const fieldsWithEmptyResult = new Set<string>(
+      ['hpBlockClient', 'allAtraceApps', 'chromePrivacyFiltering']);
   let key: keyof RecordConfig;
   for (key in config) {
     if (typeof (config[key]) === 'boolean' && config[key] === true &&
@@ -622,7 +645,7 @@
 }
 
 export function getBuiltinChromeCategoryList(): string[] {
-  // List of static Chrome categories, last updated at 2021-09-09 from HEAD of
+  // List of static Chrome categories, last updated at 2022-12-05 from HEAD of
   // Chromium's //base/trace_event/builtin_categories.h.
   return [
     'accessibility',
@@ -648,12 +671,14 @@
     'CacheStorage',
     'Calculators',
     'CameraStream',
+    'cppgc',
     'camera',
     'cast_app',
     'cast_perf_test',
     'cast.mdns',
     'cast.mdns.socket',
     'cast.stream',
+    'catan_investigation',
     'cc',
     'cc.debug',
     'cdp.perf',
@@ -662,6 +687,7 @@
     'compositor',
     'content',
     'content_capture',
+    'delegated_ink_trails',
     'device',
     'devtools',
     'devtools.contrast',
@@ -680,12 +706,14 @@
     'explore_sites',
     'FileSystem',
     'file_system_provider',
+    'fledge',
     'fonts',
     'GAMEPAD',
     'gpu',
     'gpu.angle',
     'gpu.capture',
     'headless',
+    'history',
     'hwoverlays',
     'identity',
     'ime',
@@ -727,22 +755,25 @@
     'ppapi',
     'ppapi_proxy',
     'print',
+    'raf_investigation',
     'rail',
     'renderer',
     'renderer_host',
     'renderer.scheduler',
     'RLZ',
+    'ServiceWorker',
+    'SiteEngagement',
     'safe_browsing',
+    'scheduler',
+    'scheduler.long_tasks',
     'screenlock_monitor',
     'segmentation_platform',
     'sequence_manager',
     'service_manager',
-    'ServiceWorker',
     'sharing',
     'shell',
     'shortcut_viewer',
     'shutdown',
-    'SiteEngagement',
     'skia',
     'sql',
     'stadia_media',
@@ -763,12 +794,15 @@
     'views.frame',
     'viz',
     'vk',
+    'wakeup.flow',
     'wayland',
     'webaudio',
     'weblayer',
     'WebCore',
     'webrtc',
+    'webrtc_stats',
     'xr',
+    'disabled-by-default-android_view_hierarchy',
     'disabled-by-default-animation-worklet',
     'disabled-by-default-audio',
     'disabled-by-default-audio-worklet',
@@ -778,9 +812,9 @@
     'disabled-by-default-blink.debug.layout',
     'disabled-by-default-blink.debug.layout.trees',
     'disabled-by-default-blink.feature_usage',
-    'disabled-by-default-blink_gc',
     'disabled-by-default-blink.image_decoding',
     'disabled-by-default-blink.invalidation',
+    'disabled-by-default-identifiability',
     'disabled-by-default-cc',
     'disabled-by-default-cc.debug',
     'disabled-by-default-cc.debug.cdp-perf',
@@ -791,6 +825,7 @@
     'disabled-by-default-cc.debug.scheduler.now',
     'disabled-by-default-content.verbose',
     'disabled-by-default-cpu_profiler',
+    'disabled-by-default-cppgc',
     'disabled-by-default-cpu_profiler.debug',
     'disabled-by-default-devtools.screenshot',
     'disabled-by-default-devtools.timeline',
@@ -799,6 +834,7 @@
     'disabled-by-default-devtools.timeline.invalidationTracking',
     'disabled-by-default-devtools.timeline.layers',
     'disabled-by-default-devtools.timeline.picture',
+    'disabled-by-default-devtools.timeline.stack',
     'disabled-by-default-file',
     'disabled-by-default-fonts',
     'disabled-by-default-gpu_cmd_queue',
@@ -824,7 +860,6 @@
     'disabled-by-default-power',
     'disabled-by-default-renderer.scheduler',
     'disabled-by-default-renderer.scheduler.debug',
-    'disabled-by-default-sandbox',
     'disabled-by-default-sequence_manager',
     'disabled-by-default-sequence_manager.debug',
     'disabled-by-default-sequence_manager.verbose_snapshots',
@@ -842,6 +877,7 @@
     'disabled-by-default-v8.gc',
     'disabled-by-default-v8.gc_stats',
     'disabled-by-default-v8.ic_stats',
+    'disabled-by-default-v8.inspector',
     'disabled-by-default-v8.runtime',
     'disabled-by-default-v8.runtime_stats',
     'disabled-by-default-v8.runtime_stats_sampling',
diff --git a/ui/src/common/state_unittest.ts b/ui/src/common/state_unittest.ts
index 89abb68..2b318b8 100644
--- a/ui/src/common/state_unittest.ts
+++ b/ui/src/common/state_unittest.ts
@@ -17,7 +17,7 @@
 
 test('createEmptyState', () => {
   const state: State = createEmptyState();
-  expect(state.currentEngineId).toEqual(undefined);
+  expect(state.engine).toEqual(undefined);
 });
 
 test('getContainingTrackId', () => {
diff --git a/ui/src/common/thread_state.ts b/ui/src/common/thread_state.ts
index 7d61ea9..d38de4a 100644
--- a/ui/src/common/thread_state.ts
+++ b/ui/src/common/thread_state.ts
@@ -21,7 +21,7 @@
   'X': 'Exit (Dead)',
   'Z': 'Exit (Zombie)',
   'x': 'Task Dead',
-  'I': 'Task Dead',
+  'I': 'Idle',
   'K': 'Wake Kill',
   'W': 'Waking',
   'P': 'Parked',
diff --git a/ui/src/common/upload_utils.ts b/ui/src/common/upload_utils.ts
index 2ff42bb..875b072 100644
--- a/ui/src/common/upload_utils.ts
+++ b/ui/src/common/upload_utils.ts
@@ -54,9 +54,26 @@
   return hash;
 }
 
-export async function toSha256(str: string): Promise<string> {
-  // TODO(hjd): TypeScript bug with definition of TextEncoder.
-  const buffer = new (TextEncoder as any)('utf-8').encode(str);
+// This has a bug:
+// x.toString(16) doesn't zero pad so if the digest is:
+// [23, 7, 42, ...]
+// You get:
+// ['17', '7', '2a', ...] = 1772a...
+// Rather than:
+// ['17', '07', '2a', ...] = 17072a...
+// As you ought to (and as the hexdigest is computed by e.g. Python).
+// Unfortunately there are a lot of old permalinks out there so we
+// still need this broken implementation to check their hashes.
+export async function buggyToSha256(str: string): Promise<string> {
+  const buffer = new TextEncoder().encode(str);
   const digest = await crypto.subtle.digest('SHA-256', buffer);
   return Array.from(new Uint8Array(digest)).map((x) => x.toString(16)).join('');
 }
+
+export async function toSha256(str: string): Promise<string> {
+  const buffer = new TextEncoder().encode(str);
+  const digest = await crypto.subtle.digest('SHA-256', buffer);
+  return Array.from(new Uint8Array(digest))
+      .map((x) => x.toString(16).padStart(2, '0'))
+      .join('');
+}
diff --git a/ui/src/controller/aggregation/aggregation_controller.ts b/ui/src/controller/aggregation/aggregation_controller.ts
index 0510ca0..4fa1ecc 100644
--- a/ui/src/controller/aggregation/aggregation_controller.ts
+++ b/ui/src/controller/aggregation/aggregation_controller.ts
@@ -146,6 +146,8 @@
           column.data[i] = isStringColumn(column) ? internString('NULL') : 0;
         } else if (typeof item === 'string') {
           column.data[i] = internString(item);
+        } else if (item instanceof Uint8Array) {
+          column.data[i] = internString('<Binary blob>');
         } else {
           column.data[i] = item;
         }
diff --git a/ui/src/controller/app_controller.ts b/ui/src/controller/app_controller.ts
index d0d1cea..c711559 100644
--- a/ui/src/controller/app_controller.ts
+++ b/ui/src/controller/app_controller.ts
@@ -48,7 +48,8 @@
           RecordController,
           {app: globals, extensionPort: this.extensionPort}));
     }
-    for (const engineCfg of Object.values(globals.state.engines)) {
+    if (globals.state.engine !== undefined) {
+      const engineCfg = globals.state.engine;
       childControllers.push(Child(engineCfg.id, TraceController, engineCfg.id));
     }
     return childControllers;
diff --git a/ui/src/controller/cpu_profile_controller.ts b/ui/src/controller/cpu_profile_controller.ts
index 8bf031d..db695ac 100644
--- a/ui/src/controller/cpu_profile_controller.ts
+++ b/ui/src/controller/cpu_profile_controller.ts
@@ -115,7 +115,7 @@
             WHERE symbol.symbol_set_id = spf.symbol_set_id
             LIMIT 1
           ),
-          spf.name
+          COALESCE(spf.deobfuscated_name, spf.name)
         ) AS name,
         spm.name AS mapping
       FROM cpu_profile_stack_sample AS samples
diff --git a/ui/src/controller/flamegraph_controller.ts b/ui/src/controller/flamegraph_controller.ts
index bbbecb4..6d4f36d 100644
--- a/ui/src/controller/flamegraph_controller.ts
+++ b/ui/src/controller/flamegraph_controller.ts
@@ -60,7 +60,7 @@
   switch (type) {
     case ProfileType.HEAP_PROFILE:
     case ProfileType.NATIVE_HEAP_PROFILE:
-    case ProfileType.JAVA_HEAP_PROFILE:
+    case ProfileType.JAVA_HEAP_SAMPLES:
       return 'native';
     case ProfileType.JAVA_HEAP_GRAPH:
       return 'graph';
diff --git a/ui/src/controller/logs_controller.ts b/ui/src/controller/logs_controller.ts
index 38cdf44..336007a 100644
--- a/ui/src/controller/logs_controller.ts
+++ b/ui/src/controller/logs_controller.ts
@@ -21,23 +21,26 @@
   LogExistsKey,
 } from '../common/logs';
 import {NUM, STR} from '../common/query_result';
+import {escapeGlob, escapeQuery} from '../common/query_utils';
+import {LogFilteringCriteria} from '../common/state';
 import {fromNs, TimeSpan, toNsCeil, toNsFloor} from '../common/time';
 import {publishTrackData} from '../frontend/publish';
 
 import {Controller} from './controller';
-import {App} from './globals';
+import {App, globals} from './globals';
 
 async function updateLogBounds(
     engine: Engine, span: TimeSpan): Promise<LogBounds> {
   const vizStartNs = toNsFloor(span.start);
   const vizEndNs = toNsCeil(span.end);
 
-  const countResult = await engine.query(`
-     select
+  const countResult = await engine.query(`select
       ifnull(min(ts), 0) as minTs,
       ifnull(max(ts), 0) as maxTs,
       count(ts) as countTs
-     from android_logs where ts >= ${vizStartNs} and ts <= ${vizEndNs}`);
+     from filtered_logs
+        where ts >= ${vizStartNs}
+        and ts <= ${vizEndNs}`);
 
   const countRow = countResult.firstRow({minTs: NUM, maxTs: NUM, countTs: NUM});
 
@@ -46,12 +49,12 @@
   const total = countRow.countTs;
 
   const minResult = await engine.query(`
-     select ifnull(max(ts), 0) as maxTs from android_logs where ts < ${
+     select ifnull(max(ts), 0) as maxTs from filtered_logs where ts < ${
       vizStartNs}`);
   const startNs = minResult.firstRow({maxTs: NUM}).maxTs;
 
   const maxResult = await engine.query(`
-     select ifnull(min(ts), 0) as minTs from android_logs where ts > ${
+     select ifnull(min(ts), 0) as minTs from filtered_logs where ts > ${
       vizEndNs}`);
   const endNs = maxResult.firstRow({minTs: NUM}).minTs;
 
@@ -80,8 +83,11 @@
           ts,
           prio,
           ifnull(tag, '[NULL]') as tag,
-          ifnull(msg, '[NULL]') as msg
-        from android_logs
+          ifnull(msg, '[NULL]') as msg,
+          is_msg_highlighted as isMsgHighlighted,
+          is_process_highlighted as isProcessHighlighted,
+          ifnull(process_name, '') as processName
+        from filtered_logs
         where ${vizSqlBounds}
         order by ts
         limit ${pagination.start}, ${pagination.count}
@@ -91,13 +97,26 @@
   const priorities = [];
   const tags = [];
   const messages = [];
+  const isHighlighted = [];
+  const processName = [];
 
-  const it = rowsResult.iter({ts: NUM, prio: NUM, tag: STR, msg: STR});
+  const it = rowsResult.iter({
+    ts: NUM,
+    prio: NUM,
+    tag: STR,
+    msg: STR,
+    isMsgHighlighted: NUM,
+    isProcessHighlighted: NUM,
+    processName: STR,
+  });
   for (; it.valid(); it.next()) {
     timestamps.push(it.ts);
     priorities.push(it.prio);
     tags.push(it.tag);
     messages.push(it.msg);
+    isHighlighted.push(
+        it.isMsgHighlighted === 1 || it.isProcessHighlighted === 1);
+    processName.push(it.processName);
   }
 
   return {
@@ -106,6 +125,8 @@
     priorities,
     tags,
     messages,
+    isHighlighted,
+    processName,
   };
 }
 
@@ -147,12 +168,15 @@
 }
 
 /**
- * LogsController looks at two parts of the state:
+ * LogsController looks at three parts of the state:
  * 1. The visible trace window
  * 2. The requested offset and count the log lines to display
+ * 3. The log filtering criteria.
  * And keeps two bits of published information up to date:
  * 1. The total number of log messages in visible range
  * 2. The logs lines that should be displayed
+ * Based on the log filtering criteria, it also builds the filtered_logs view
+ * and keeps it up to date.
  */
 export class LogsController extends Controller<'main'> {
   private app: App;
@@ -160,6 +184,9 @@
   private span: TimeSpan;
   private pagination: Pagination;
   private hasLogs = false;
+  private logFilteringCriteria?: LogFilteringCriteria;
+  private requestingData = false;
+  private queuedRunRequest = false;
 
   constructor(args: LogsControllerArgs) {
     super('main');
@@ -187,7 +214,21 @@
 
   run() {
     if (!this.hasLogs) return;
+    if (this.requestingData) {
+      this.queuedRunRequest = true;
+      return;
+    }
+    this.requestingData = true;
+    this.updateLogTracks().finally(() => {
+      this.requestingData = false;
+      if (this.queuedRunRequest) {
+        this.queuedRunRequest = false;
+        this.run();
+      }
+    });
+  }
 
+  private async updateLogTracks() {
     const traceTime = this.app.state.frontendLocalState.visibleState;
     const newSpan = new TimeSpan(traceTime.startSec, traceTime.endSec);
     const oldSpan = this.span;
@@ -204,36 +245,81 @@
     const requestedPagination = new Pagination(offset, count);
     const oldPagination = this.pagination;
 
-    const needSpanUpdate = !oldSpan.equals(newSpan);
-    const needPaginationUpdate = !oldPagination.contains(requestedPagination);
+    const newFilteringCriteria =
+        this.logFilteringCriteria !== globals.state.logFilteringCriteria;
+    const needBoundsUpdate = !oldSpan.equals(newSpan) || newFilteringCriteria;
+    const needEntriesUpdate =
+        !oldPagination.contains(requestedPagination) || needBoundsUpdate;
 
-    // TODO(hjd): We could waste a lot of time queueing useless updates here.
-    // We should avoid enqueuing a request when one is in progress.
-    if (needSpanUpdate) {
+    if (newFilteringCriteria) {
+      this.logFilteringCriteria = globals.state.logFilteringCriteria;
+      await this.engine.query('drop view if exists filtered_logs');
+
+      const globMatch = LogsController.composeGlobMatch(
+          this.logFilteringCriteria.hideNonMatching,
+          this.logFilteringCriteria.textEntry);
+      let selectedRows = `select prio, ts, tag, msg,
+          process.name as process_name, ${globMatch}
+          from android_logs
+          left join thread using(utid)
+          left join process using(upid)
+          where prio >= ${this.logFilteringCriteria.minimumLevel}`;
+      if (this.logFilteringCriteria.tags.length) {
+        selectedRows += ` and tag in (${
+            LogsController.serializeTags(this.logFilteringCriteria.tags)})`;
+      }
+
+      // We extract only the rows which will be visible.
+      await this.engine.query(`create view filtered_logs as select *
+        from (${selectedRows})
+        where is_msg_chosen is 1 or is_process_chosen is 1`);
+    }
+
+    if (needBoundsUpdate) {
       this.span = newSpan;
-      updateLogBounds(this.engine, newSpan).then((data) => {
-        if (!newSpan.equals(this.span)) return;
-        publishTrackData({
-          id: LogBoundsKey,
-          data,
-        });
+      const logBounds = await updateLogBounds(this.engine, newSpan);
+      publishTrackData({
+        id: LogBoundsKey,
+        data: logBounds,
       });
     }
 
-    // TODO(hjd): We could waste a lot of time queueing useless updates here.
-    // We should avoid enqueuing a request when one is in progress.
-    if (needSpanUpdate || needPaginationUpdate) {
+    if (needEntriesUpdate) {
       this.pagination = requestedPagination.grow(100);
-
-      updateLogEntries(this.engine, newSpan, this.pagination).then((data) => {
-        if (!this.pagination.contains(requestedPagination)) return;
-        publishTrackData({
-          id: LogEntriesKey,
-          data,
-        });
+      const logEntries =
+          await updateLogEntries(this.engine, newSpan, this.pagination);
+      publishTrackData({
+        id: LogEntriesKey,
+        data: logEntries,
       });
     }
+  }
 
-    return [];
+  private static serializeTags(tags: string[]) {
+    return tags.map((tag) => escapeQuery(tag)).join();
+  }
+
+  private static composeGlobMatch(isCollaped: boolean, textEntry: string) {
+    if (isCollaped) {
+      // If the entries are collapsed, we won't highlight any lines.
+      return `msg glob ${escapeGlob(textEntry)} as is_msg_chosen,
+        (process.name is not null and process.name glob ${
+          escapeGlob(textEntry)}) as is_process_chosen,
+        0 as is_msg_highlighted,
+        0 as is_process_highlighted`;
+    } else if (!textEntry) {
+      // If there is no text entry, we will show all lines, but won't highlight.
+      // any.
+      return `1 as is_msg_chosen,
+        1 as is_process_chosen,
+        0 as is_msg_highlighted,
+        0 as is_process_highlighted`;
+    } else {
+      return `1 as is_msg_chosen,
+        1 as is_process_chosen,
+        msg glob ${escapeGlob(textEntry)} as is_msg_highlighted,
+        (process.name is not null and process.name glob ${
+          escapeGlob(textEntry)}) as is_process_highlighted`;
+    }
   }
 }
diff --git a/ui/src/controller/permalink_controller.ts b/ui/src/controller/permalink_controller.ts
index b2f1bf2..349a02a 100644
--- a/ui/src/controller/permalink_controller.ts
+++ b/ui/src/controller/permalink_controller.ts
@@ -21,10 +21,11 @@
   createEmptyNonSerializableState,
   createEmptyState,
 } from '../common/empty_state';
-import {State} from '../common/state';
+import {EngineConfig, ObjectById, State} from '../common/state';
 import {STATE_VERSION} from '../common/state';
 import {
   BUCKET_NAME,
+  buggyToSha256,
   saveState,
   saveTrace,
   toSha256,
@@ -38,6 +39,19 @@
 import {RecordConfig, recordConfigValidator} from './record_config_types';
 import {runValidator} from './validators';
 
+interface MultiEngineState {
+  currentEngineId?: string;
+  engines: ObjectById<EngineConfig>
+}
+
+function isMultiEngineState(state: State|
+                            MultiEngineState): state is MultiEngineState {
+  if ((state as MultiEngineState).engines !== undefined) {
+    return true;
+  }
+  return false;
+}
+
 export class PermalinkController extends Controller<'main'> {
   private lastRequestId?: string;
   constructor() {
@@ -97,17 +111,22 @@
   private static upgradeState(state: State): State {
     if (state.version !== STATE_VERSION) {
       const newState = createEmptyState();
-      let maxEngineId = Number.MIN_SAFE_INTEGER;
-      // Copy the URL of the trace into the empty state.
-      for (const cfg of Object.values(state.engines)) {
-        newState
-            .engines[cfg.id] = {id: cfg.id, ready: false, source: cfg.source};
-        maxEngineId = Math.max(maxEngineId, Number(cfg.id));
+      // Old permalinks from state versions prior to version 24
+      // have multiple engines of which only one is identified as the
+      // current engine via currentEngineId. Handle this case:
+      if (isMultiEngineState(state)) {
+        const engineId = state.currentEngineId;
+        if (engineId !== undefined) {
+          newState.engine = state.engines[engineId];
+        }
+      } else {
+        newState.engine = state.engine;
       }
-      if (maxEngineId !== Number.MIN_SAFE_INTEGER) {
-        // set the current engine Id to the maximum engine Id in the permalink
-        newState.currentEngineId = String(maxEngineId);
+
+      if (newState.engine !== undefined) {
+        newState.engine.ready = false;
       }
+
       const message = `Unable to parse old state version. Discarding state ` +
           `and loading trace.`;
       console.warn(message);
@@ -153,7 +172,7 @@
         const url = await saveTrace(dataToUpload);
         // Convert state to use URLs and remove permalink.
         uploadState = produce(globals.state, (draft) => {
-          draft.engines[engine.id].source = {type: 'URL', url};
+          assertExists(draft.engine).source = {type: 'URL', url};
           draft.permalink = {};
         });
       }
@@ -179,7 +198,14 @@
     const stateHash = await toSha256(text);
     const state = JSON.parse(text);
     if (stateHash !== id) {
-      throw new Error(`State hash does not match ${id} vs. ${stateHash}`);
+      // Old permalinks incorrectly dropped some digits from the
+      // hexdigest of the SHA256. We don't want to invalidate those
+      // links so we also compute the old string and try that here
+      // also.
+      const buggyStateHash = await buggyToSha256(text);
+      if (buggyStateHash !== id) {
+        throw new Error(`State hash does not match ${id} vs. ${stateHash}`);
+      }
     }
     if (!this.isRecordConfig(state)) {
       return this.upgradeState(state);
diff --git a/ui/src/controller/pivot_table_redux_controller.ts b/ui/src/controller/pivot_table_redux_controller.ts
index 4d849f2..4f57891 100644
--- a/ui/src/controller/pivot_table_redux_controller.ts
+++ b/ui/src/controller/pivot_table_redux_controller.ts
@@ -15,6 +15,7 @@
  */
 
 import {Actions} from '../common/actions';
+import {DEFAULT_CHANNEL, getCurrentChannel} from '../common/channels';
 import {Engine} from '../common/engine';
 import {featureFlags} from '../common/feature_flags';
 import {ColumnType, STR} from '../common/query_result';
@@ -30,6 +31,7 @@
   generateQueryFromState,
 } from '../frontend/pivot_table_redux_query_generator';
 import {
+  Aggregation,
   PivotTree,
 } from '../frontend/pivot_table_redux_types';
 
@@ -40,68 +42,90 @@
   id: 'pivotTableRedux',
   name: 'Pivot tables V2',
   description: 'Second version of pivot table',
-  defaultValue: false,
+  // Enabled in canary and autopush by default.
+  defaultValue: getCurrentChannel() !== DEFAULT_CHANNEL,
 });
 
 // Auxiliary class to build the tree from query response.
 class TreeBuilder {
   private readonly root: PivotTree;
-  lastRow: ColumnType[];
   pivotColumns: number;
-  aggregateColumns: number;
+  aggregateColumns: Aggregation[];
 
   constructor(
-      pivotColumns: number, aggregateColumns: number, firstRow: ColumnType[]) {
+      pivotColumns: number, aggregateColumns: Aggregation[],
+      firstRow: ColumnType[]) {
     this.pivotColumns = pivotColumns;
     this.aggregateColumns = aggregateColumns;
-    this.root = this.createNode(0, firstRow);
+    this.root = this.createNode(firstRow);
     let tree = this.root;
     for (let i = 0; i + 1 < this.pivotColumns; i++) {
       const value = firstRow[i];
-      tree = TreeBuilder.insertChild(
-          tree, value, this.createNode(i + 1, firstRow));
+      tree = this.insertChild(tree, value, this.createNode(firstRow));
     }
     tree.rows.push(firstRow);
-    this.lastRow = firstRow;
   }
 
   // Add incoming row to the tree being built.
   ingestRow(row: ColumnType[]) {
     let tree = this.root;
+    this.updateAggregates(tree, row);
     for (let i = 0; i + 1 < this.pivotColumns; i++) {
       const nextTree = tree.children.get(row[i]);
       if (nextTree === undefined) {
         // Insert the new node into the tree, and make variable `tree` point
         // to the newly created node.
-        tree =
-            TreeBuilder.insertChild(tree, row[i], this.createNode(i + 1, row));
+        tree = this.insertChild(tree, row[i], this.createNode(row));
       } else {
+        this.updateAggregates(nextTree, row);
         tree = nextTree;
       }
     }
     tree.rows.push(row);
-    this.lastRow = row;
   }
 
   build(): PivotTree {
     return this.root;
   }
 
+  updateAggregates(tree: PivotTree, row: ColumnType[]) {
+    for (let i = 0; i < this.aggregateColumns.length; i++) {
+      const agg = this.aggregateColumns[i];
+
+      const currAgg = tree.aggregates[i];
+      const childAgg = row[aggregationIndex(this.pivotColumns, i)];
+      if (typeof currAgg === 'number' && typeof childAgg === 'number') {
+        switch (agg.aggregationFunction) {
+          case 'COUNT':
+          case 'SUM':
+            tree.aggregates[i] = currAgg + childAgg;
+            break;
+          case 'MAX':
+            tree.aggregates[i] = Math.max(currAgg, childAgg);
+            break;
+          case 'MIN':
+            tree.aggregates[i] = Math.min(currAgg, childAgg);
+            break;
+        }
+      }
+    }
+  }
+
   // Helper method that inserts child node into the tree and returns it, used
   // for more concise modification of local variable pointing to the current
   // node being built.
-  static insertChild(tree: PivotTree, key: ColumnType, child: PivotTree):
-      PivotTree {
+  insertChild(tree: PivotTree, key: ColumnType, child: PivotTree): PivotTree {
     tree.children.set(key, child);
+
     return child;
   }
 
   // Initialize PivotTree from a row.
-  createNode(depth: number, row: ColumnType[]): PivotTree {
+  createNode(row: ColumnType[]): PivotTree {
     const aggregates = [];
 
-    for (let j = 0; j < this.aggregateColumns; j++) {
-      aggregates.push(row[aggregationIndex(this.pivotColumns, j, depth)]);
+    for (let j = 0; j < this.aggregateColumns.length; j++) {
+      aggregates.push(row[aggregationIndex(this.pivotColumns, j)]);
     }
 
     return {
@@ -202,7 +226,7 @@
 
     const treeBuilder = new TreeBuilder(
         query.metadata.pivotColumns.length,
-        query.metadata.aggregationColumns.length,
+        query.metadata.aggregationColumns,
         nextRow());
     while (it.valid()) {
       treeBuilder.ingestRow(nextRow());
@@ -240,13 +264,6 @@
     }
 
     const pivotTableState = globals.state.nonSerializableState.pivotTableRedux;
-
-    if (pivotTableState.queryRequested) {
-      globals.dispatch(
-          Actions.setPivotTableQueryRequested({queryRequested: false}));
-      this.processQuery(generateQueryFromState(pivotTableState));
-    }
-
     const selection = globals.state.currentSelection;
 
     if (pivotTableState.queryRequested ||
diff --git a/ui/src/controller/record_config_types.ts b/ui/src/controller/record_config_types.ts
index ebc112e..fc306b2 100644
--- a/ui/src/controller/record_config_types.ts
+++ b/ui/src/controller/record_config_types.ts
@@ -34,6 +34,7 @@
 
   cpuSched: bool(),
   cpuFreq: bool(),
+  cpuFreqPollMs: num(1000),
   cpuSyscall: bool(),
 
   gpuFreq: bool(),
@@ -51,6 +52,7 @@
   androidLogs: bool(),
   androidLogBuffers: arrayOf(str()),
   androidFrameTimeline: bool(),
+  androidGameInterventionList: bool(),
 
   cpuCoarse: bool(),
   cpuCoarsePollMs: num(1000),
@@ -89,6 +91,7 @@
 
   chromeCategoriesSelected: arrayOf(str()),
   chromeHighOverheadCategoriesSelected: arrayOf(str()),
+  chromePrivacyFiltering: bool(),
 
   chromeLogs: bool(),
   taskScheduling: bool(),
diff --git a/ui/src/controller/record_controller.ts b/ui/src/controller/record_controller.ts
index 03bc58f..aef43ab 100644
--- a/ui/src/controller/record_controller.ts
+++ b/ui/src/controller/record_controller.ts
@@ -104,8 +104,11 @@
       name: '',
     };
   } else if (targetType === 'CHROME' || targetType === 'CHROME_OS') {
-    targetInfo =
-        {targetType, isExtensionInstalled: false, dataSources: [], name: ''};
+    targetInfo = {
+      targetType,
+      dataSources: [],
+      name: '',
+    };
   } else {
     targetInfo = {targetType, dataSources: [], name: ''};
   }
diff --git a/ui/src/controller/record_controller_jsdomtest.ts b/ui/src/controller/record_controller_jsdomtest.ts
index 5f539d2..f160290 100644
--- a/ui/src/controller/record_controller_jsdomtest.ts
+++ b/ui/src/controller/record_controller_jsdomtest.ts
@@ -153,19 +153,55 @@
   const traceConfigSource = assertExists(sources[0].config);
   expect(traceConfigSource.name).toBe('org.chromium.trace_event');
   const chromeConfig = assertExists(traceConfigSource.chromeConfig);
+  expect(chromeConfig.privacyFilteringEnabled).toBe(false);
   const traceConfig = assertExists(chromeConfig.traceConfig);
 
-  const metadataConfigSource = assertExists(sources[1].config);
+  const trackEventConfigSource = assertExists(sources[1].config);
+  expect(trackEventConfigSource.name).toBe('track_event');
+  const trackEventConfig =
+      assertExists(trackEventConfigSource.trackEventConfig);
+  expect(trackEventConfig.filterDynamicEventNames).toBe(false);
+  expect(trackEventConfig.filterDebugAnnotations).toBe(false);
+  const chromeConfigT = assertExists(trackEventConfigSource.chromeConfig);
+  const traceConfigT = assertExists(chromeConfigT.traceConfig);
+
+  const metadataConfigSource = assertExists(sources[2].config);
   expect(metadataConfigSource.name).toBe('org.chromium.trace_metadata');
   const chromeConfigM = assertExists(metadataConfigSource.chromeConfig);
   const traceConfigM = assertExists(chromeConfigM.traceConfig);
 
   const expectedTraceConfig = '{"record_mode":"record-until-full",' +
       '"included_categories":' +
-      '["toplevel","disabled-by-default-ipc.flow","mojom","v8"],' +
+      '["toplevel","toplevel.flow","disabled-by-default-ipc.flow",' +
+      '"mojom","v8"],' +
+      '"excluded_categories":["*"],' +
       '"memory_dump_config":{}}';
-  expect(traceConfigM).toEqual(expectedTraceConfig);
   expect(traceConfig).toEqual(expectedTraceConfig);
+  expect(traceConfigT).toEqual(expectedTraceConfig);
+  expect(traceConfigM).toEqual(expectedTraceConfig);
+});
+
+test('ChromeConfig with privacy filtering', () => {
+  const config = createEmptyRecordConfig();
+  config.ipcFlows = true;
+  config.jsExecution = true;
+  config.mode = 'STOP_WHEN_FULL';
+  config.chromePrivacyFiltering = true;
+  const result =
+      TraceConfig.decode(genConfigProto(config, {os: 'C', name: 'Chrome'}));
+  const sources = assertExists(result.dataSources);
+
+  const traceConfigSource = assertExists(sources[0].config);
+  expect(traceConfigSource.name).toBe('org.chromium.trace_event');
+  const chromeConfig = assertExists(traceConfigSource.chromeConfig);
+  expect(chromeConfig.privacyFilteringEnabled).toBe(true);
+
+  const trackEventConfigSource = assertExists(sources[1].config);
+  expect(trackEventConfigSource.name).toBe('track_event');
+  const trackEventConfig =
+      assertExists(trackEventConfigSource.trackEventConfig);
+  expect(trackEventConfig.filterDynamicEventNames).toBe(true);
+  expect(trackEventConfig.filterDebugAnnotations).toBe(true);
 });
 
 test('ChromeMemoryConfig', () => {
@@ -181,28 +217,35 @@
   const chromeConfig = assertExists(traceConfigSource.chromeConfig);
   const traceConfig = assertExists(chromeConfig.traceConfig);
 
-  const metadataConfigSource = assertExists(sources[1].config);
+  const trackEventConfigSource = assertExists(sources[1].config);
+  expect(trackEventConfigSource.name).toBe('track_event');
+  const chromeConfigT = assertExists(trackEventConfigSource.chromeConfig);
+  const traceConfigT = assertExists(chromeConfigT.traceConfig);
+
+  const metadataConfigSource = assertExists(sources[2].config);
   expect(metadataConfigSource.name).toBe('org.chromium.trace_metadata');
   const chromeConfigM = assertExists(metadataConfigSource.chromeConfig);
   const traceConfigM = assertExists(chromeConfigM.traceConfig);
 
-  const miConfigSource = assertExists(sources[2].config);
+  const miConfigSource = assertExists(sources[3].config);
   expect(miConfigSource.name).toBe('org.chromium.memory_instrumentation');
   const chromeConfigI = assertExists(miConfigSource.chromeConfig);
   const traceConfigI = assertExists(chromeConfigI.traceConfig);
 
-  const hpConfigSource = assertExists(sources[3].config);
+  const hpConfigSource = assertExists(sources[4].config);
   expect(hpConfigSource.name).toBe('org.chromium.native_heap_profiler');
   const chromeConfigH = assertExists(hpConfigSource.chromeConfig);
   const traceConfigH = assertExists(chromeConfigH.traceConfig);
 
-  const expectedTraceConfig = '{\"record_mode\":\"record-until-full\",' +
-      '\"included_categories\":[\"disabled-by-default-memory-infra\"],' +
-      '\"memory_dump_config\":{\"allowed_dump_modes\":[\"background\",' +
-      '\"light\",\"detailed\"],\"triggers\":[{\"min_time_between_dumps_ms\":' +
-      '10000,\"mode\":\"detailed\",\"type\":\"periodic_interval\"}]}}';
-  expect(traceConfigM).toEqual(expectedTraceConfig);
+  const expectedTraceConfig = '{"record_mode":"record-until-full",' +
+      '"included_categories":["disabled-by-default-memory-infra"],' +
+      '"excluded_categories":["*"],' +
+      '"memory_dump_config":{"allowed_dump_modes":["background",' +
+      '"light","detailed"],"triggers":[{"min_time_between_dumps_ms":' +
+      '10000,"mode":"detailed","type":"periodic_interval"}]}}';
   expect(traceConfig).toEqual(expectedTraceConfig);
+  expect(traceConfigT).toEqual(expectedTraceConfig);
+  expect(traceConfigM).toEqual(expectedTraceConfig);
   expect(traceConfigI).toEqual(expectedTraceConfig);
   expect(traceConfigH).toEqual(expectedTraceConfig);
 });
@@ -220,22 +263,28 @@
   const traceEventChromeConfig = assertExists(traceConfigSource.chromeConfig);
   const traceEventConfig = assertExists(traceEventChromeConfig.traceConfig);
 
-  const metadataConfigSource = assertExists(sources[1].config);
+  const trackEventConfigSource = assertExists(sources[1].config);
+  expect(trackEventConfigSource.name).toBe('track_event');
+  const chromeConfigT = assertExists(trackEventConfigSource.chromeConfig);
+  const traceConfigT = assertExists(chromeConfigT.traceConfig);
+
+  const metadataConfigSource = assertExists(sources[2].config);
   expect(metadataConfigSource.name).toBe('org.chromium.trace_metadata');
   const traceMetadataChromeConfig =
       assertExists(metadataConfigSource.chromeConfig);
   const traceMetadataConfig =
       assertExists(traceMetadataChromeConfig.traceConfig);
 
-  const profilerConfigSource = assertExists(sources[2].config);
+  const profilerConfigSource = assertExists(sources[3].config);
   expect(profilerConfigSource.name).toBe('org.chromium.sampler_profiler');
   const profilerChromeConfig = assertExists(profilerConfigSource.chromeConfig);
   const profilerConfig = assertExists(profilerChromeConfig.traceConfig);
 
-  const expectedTraceConfig = '{\"record_mode\":\"record-until-full\",' +
-      '\"included_categories\":[\"disabled-by-default-cpu_profiler\"],' +
-      '\"memory_dump_config\":{}}';
+  const expectedTraceConfig = '{"record_mode":"record-until-full",' +
+      '"included_categories":["disabled-by-default-cpu_profiler"],' +
+      '"excluded_categories":["*"],"memory_dump_config":{}}';
   expect(traceEventConfig).toEqual(expectedTraceConfig);
+  expect(traceConfigT).toEqual(expectedTraceConfig);
   expect(traceMetadataConfig).toEqual(expectedTraceConfig);
   expect(profilerConfig).toEqual(expectedTraceConfig);
 });
@@ -253,21 +302,27 @@
   const traceEventChromeConfig = assertExists(traceConfigSource.chromeConfig);
   const traceEventConfig = assertExists(traceEventChromeConfig.traceConfig);
 
-  const metadataConfigSource = assertExists(sources[1].config);
+  const trackEventConfigSource = assertExists(sources[1].config);
+  expect(trackEventConfigSource.name).toBe('track_event');
+  const chromeConfigT = assertExists(trackEventConfigSource.chromeConfig);
+  const traceConfigT = assertExists(chromeConfigT.traceConfig);
+
+  const metadataConfigSource = assertExists(sources[2].config);
   expect(metadataConfigSource.name).toBe('org.chromium.trace_metadata');
   const traceMetadataChromeConfig =
       assertExists(metadataConfigSource.chromeConfig);
   const traceMetadataConfig =
       assertExists(traceMetadataChromeConfig.traceConfig);
 
-  const profilerConfigSource = assertExists(sources[2].config);
+  const profilerConfigSource = assertExists(sources[3].config);
   expect(profilerConfigSource.name).toBe('org.chromium.sampler_profiler');
   const profilerChromeConfig = assertExists(profilerConfigSource.chromeConfig);
   const profilerConfig = assertExists(profilerChromeConfig.traceConfig);
 
-  const expectedTraceConfig = '{\"record_mode\":\"record-until-full\",' +
-      '\"included_categories\":[\"disabled-by-default-cpu_profiler.debug\"],' +
-      '\"memory_dump_config\":{}}';
+  const expectedTraceConfig = '{"record_mode":"record-until-full",' +
+      '"included_categories":["disabled-by-default-cpu_profiler.debug"],' +
+      '"excluded_categories":["*"],"memory_dump_config":{}}';
+  expect(traceConfigT).toEqual(expectedTraceConfig);
   expect(traceEventConfig).toEqual(expectedTraceConfig);
   expect(traceMetadataConfig).toEqual(expectedTraceConfig);
   expect(profilerConfig).toEqual(expectedTraceConfig);
@@ -287,17 +342,24 @@
   const chromeConfig = assertExists(traceConfigSource.chromeConfig);
   const traceConfig = assertExists(chromeConfig.traceConfig);
 
-  const metadataConfigSource = assertExists(sources[1].config);
+  const trackEventConfigSource = assertExists(sources[1].config);
+  expect(trackEventConfigSource.name).toBe('track_event');
+  const chromeConfigT = assertExists(trackEventConfigSource.chromeConfig);
+  const traceConfigT = assertExists(chromeConfigT.traceConfig);
+
+  const metadataConfigSource = assertExists(sources[2].config);
   expect(metadataConfigSource.name).toBe('org.chromium.trace_metadata');
   const chromeConfigM = assertExists(metadataConfigSource.chromeConfig);
   const traceConfigM = assertExists(chromeConfigM.traceConfig);
 
   const expectedTraceConfig = '{"record_mode":"record-continuously",' +
       '"included_categories":' +
-      '["toplevel","disabled-by-default-ipc.flow","mojom","v8"],' +
-      '"memory_dump_config":{}}';
-  expect(traceConfigM).toEqual(expectedTraceConfig);
+      '["toplevel","toplevel.flow","disabled-by-default-ipc.flow",' +
+      '"mojom","v8"],' +
+      '"excluded_categories":["*"],"memory_dump_config":{}}';
   expect(traceConfig).toEqual(expectedTraceConfig);
+  expect(traceConfigT).toEqual(expectedTraceConfig);
+  expect(traceConfigM).toEqual(expectedTraceConfig);
 });
 
 test('ChromeConfigLongTrace', () => {
@@ -314,17 +376,24 @@
   const chromeConfig = assertExists(traceConfigSource.chromeConfig);
   const traceConfig = assertExists(chromeConfig.traceConfig);
 
-  const metadataConfigSource = assertExists(sources[1].config);
+  const trackEventConfigSource = assertExists(sources[1].config);
+  expect(trackEventConfigSource.name).toBe('track_event');
+  const chromeConfigT = assertExists(trackEventConfigSource.chromeConfig);
+  const traceConfigT = assertExists(chromeConfigT.traceConfig);
+
+  const metadataConfigSource = assertExists(sources[2].config);
   expect(metadataConfigSource.name).toBe('org.chromium.trace_metadata');
   const chromeConfigM = assertExists(metadataConfigSource.chromeConfig);
   const traceConfigM = assertExists(chromeConfigM.traceConfig);
 
   const expectedTraceConfig = '{"record_mode":"record-continuously",' +
       '"included_categories":' +
-      '["toplevel","disabled-by-default-ipc.flow","mojom","v8"],' +
-      '"memory_dump_config":{}}';
-  expect(traceConfigM).toEqual(expectedTraceConfig);
+      '["toplevel","toplevel.flow","disabled-by-default-ipc.flow",' +
+      '"mojom","v8"],' +
+      '"excluded_categories":["*"],"memory_dump_config":{}}';
   expect(traceConfig).toEqual(expectedTraceConfig);
+  expect(traceConfigT).toEqual(expectedTraceConfig);
+  expect(traceConfigM).toEqual(expectedTraceConfig);
 });
 
 test('ChromeConfigToPbtxt', () => {
diff --git a/ui/src/controller/search_controller.ts b/ui/src/controller/search_controller.ts
index 9244e6a..fde726d 100644
--- a/ui/src/controller/search_controller.ts
+++ b/ui/src/controller/search_controller.ts
@@ -12,27 +12,17 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import {TRACE_MARGIN_TIME_S} from '../common/constants';
+import {sqliteString} from '../base/string_utils';
 import {Engine} from '../common/engine';
 import {NUM, STR} from '../common/query_result';
+import {escapeSearchQuery} from '../common/query_utils';
 import {CurrentSearchResults, SearchSummary} from '../common/search_data';
 import {TimeSpan} from '../common/time';
 import {publishSearch, publishSearchResult} from '../frontend/publish';
 
 import {Controller} from './controller';
 import {App} from './globals';
-
-export function escapeQuery(s: string): string {
-  // See https://www.sqlite.org/lang_expr.html#:~:text=A%20string%20constant
-  s = s.replace(/\'/g, '\'\'');
-  s = s.replace(/_/g, '^_');
-  s = s.replace(/%/g, '^%');
-  return `'%${s}%' escape '^'`;
-}
-
-export function escapeSingleQuotes(s: string): string {
-  return s.replace(/\'/g, '\'\'');
-}
+import {toNs} from '../common/time';
 
 export interface SearchControllerArgs {
   engine: Engine;
@@ -78,7 +68,7 @@
     }
 
     const visibleState = this.app.state.frontendLocalState.visibleState;
-    const omniboxState = this.app.state.frontendLocalState.omniboxState;
+    const omniboxState = this.app.state.omniboxState;
     if (visibleState === undefined || omniboxState === undefined ||
         omniboxState.mode === 'COMMAND') {
       return;
@@ -91,9 +81,14 @@
         newSearch === this.previousSearch) {
       return;
     }
-    this.previousSpan = new TimeSpan(
-        Math.max(newSpan.start - newSpan.duration, -TRACE_MARGIN_TIME_S),
-        newSpan.end + newSpan.duration);
+
+
+    // TODO(hjd): We should restrict this to the start of the trace but
+    // that is not easily available here.
+    // N.B. Timestamps can be negative.
+    const start = newSpan.start - newSpan.duration;
+    const end = newSpan.end + newSpan.duration;
+    this.previousSpan = new TimeSpan(start, end);
     this.previousResolution = newResolution;
     this.previousSearch = newSearch;
     if (newSearch === '' || newSearch.length < 4) {
@@ -113,8 +108,8 @@
       return;
     }
 
-    let startNs = Math.round(newSpan.start * 1e9);
-    let endNs = Math.round(newSpan.end * 1e9);
+    let startNs = toNs(newSpan.start);
+    let endNs = toNs(newSpan.end);
 
     // TODO(hjd): We shouldn't need to be so defensive here:
     if (!Number.isFinite(startNs)) {
@@ -152,7 +147,7 @@
       resolution: number): Promise<SearchSummary> {
     const quantumNs = Math.round(resolution * 10 * 1e9);
 
-    const searchLiteral = escapeQuery(search);
+    const searchLiteral = escapeSearchQuery(search);
 
     startNs = Math.floor(startNs / quantumNs) * quantumNs;
 
@@ -164,8 +159,8 @@
       where rowid = 0;`);
 
     const utidRes = await this.query(`select utid from thread join process
-      using(upid) where thread.name like ${searchLiteral}
-      or process.name like ${searchLiteral}`);
+      using(upid) where thread.name glob ${searchLiteral}
+      or process.name glob ${searchLiteral}`);
 
     const utids = [];
     for (const it = utidRes.iter({utid: NUM}); it.valid(); it.next()) {
@@ -189,7 +184,7 @@
               select
               quantum_ts
               from search_summary_slice_span
-              where name like ${searchLiteral}
+              where name glob ${searchLiteral}
           )
           group by quantum_ts
           order by quantum_ts;`);
@@ -211,7 +206,7 @@
   }
 
   private async specificSearch(search: string) {
-    const searchLiteral = escapeQuery(search);
+    const searchLiteral = escapeSearchQuery(search);
     // TODO(hjd): we should avoid recomputing this every time. This will be
     // easier once the track table has entries for all the tracks.
     const cpuToTrackId = new Map();
@@ -224,8 +219,8 @@
 
     const utidRes = await this.query(`select utid from thread join process
     using(upid) where
-      thread.name like ${searchLiteral} or
-      process.name like ${searchLiteral}`);
+      thread.name glob ${searchLiteral} or
+      process.name glob ${searchLiteral}`);
     const utids = [];
     for (const it = utidRes.iter({utid: NUM}); it.valid(); it.next()) {
       utids.push(it.utid);
@@ -247,10 +242,10 @@
       track_id as sourceId,
       0 as utid
       from slice
-      where slice.name like ${searchLiteral}
+      where slice.name glob ${searchLiteral}
         or (
-          0 != CAST('${escapeSingleQuotes(search)}' AS INT) and
-          sliceId = CAST('${escapeSingleQuotes(search)}' AS INT)
+          0 != CAST(${(sqliteString(search))} AS INT) and
+          sliceId = CAST(${(sqliteString(search))} AS INT)
         )
     union
     select
@@ -261,8 +256,18 @@
       0 as utid
       from slice
       join args using(arg_set_id)
-      where string_value like ${searchLiteral}
-    order by ts`);
+      where string_value glob ${searchLiteral} or key glob ${searchLiteral}
+    union
+    select
+      id as sliceId,
+      ts,
+      'log' as source,
+      0 as sourceId,
+      utid
+    from android_logs where msg glob ${searchLiteral}
+    order by ts
+
+    `);
 
     const rows = queryRes.numRows();
     const searchResults: CurrentSearchResults = {
@@ -282,6 +287,12 @@
         trackId = cpuToTrackId.get(it.sourceId);
       } else if (it.source === 'track') {
         trackId = this.app.state.uiTrackIdByTraceTrackId[it.sourceId];
+      } else if (it.source === 'log') {
+        const logTracks = Object.values(this.app.state.tracks)
+                              .filter((t) => t.kind === 'AndroidLogTrack');
+        if (logTracks.length > 0) {
+          trackId = logTracks[0].id;
+        }
       }
 
       // The .get() calls above could return undefined, this isn't just an else.
diff --git a/ui/src/controller/search_controller_unittest.ts b/ui/src/controller/search_controller_unittest.ts
deleted file mode 100644
index 9dec627..0000000
--- a/ui/src/controller/search_controller_unittest.ts
+++ /dev/null
@@ -1,22 +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.
-
-import {escapeQuery} from './search_controller';
-
-test('escapeQuery', () => {
-  expect(escapeQuery(``)).toEqual(`'%%' escape '^'`);
-  expect(escapeQuery(`hello`)).toEqual(`'%hello%' escape '^'`);
-  expect(escapeQuery('foo\'bar')).toEqual(`'%foo''bar%' escape '^'`);
-  expect(escapeQuery('%_%')).toEqual(`'%^%^_^%%' escape '^'`);
-});
diff --git a/ui/src/controller/selection_controller.ts b/ui/src/controller/selection_controller.ts
index 6fe9a12..134d78b 100644
--- a/ui/src/controller/selection_controller.ts
+++ b/ui/src/controller/selection_controller.ts
@@ -104,13 +104,11 @@
     const table = selection.table;
 
     let leafTable: string;
-    let promisedDescription: Promise<Map<string, string>>;
     let promisedArgs: Promise<Args>;
     // TODO(b/155483804): This is a hack to ensure annotation slices are
     // selectable for now. We should tidy this up when improving this class.
     if (table === 'annotation') {
       leafTable = 'annotation_slice';
-      promisedDescription = Promise.resolve(new Map());
       promisedArgs = Promise.resolve(new Map());
     } else {
       const result = await this.args.engine.query(`
@@ -130,7 +128,6 @@
 
       leafTable = row.leafTable;
       const argSetId = row.argSetId;
-      promisedDescription = this.describeSlice(selectedId);
       promisedArgs = this.getArgs(argSetId);
     }
 
@@ -139,8 +136,7 @@
         selectedId};
     `);
 
-    const [details, args, description] =
-        await Promise.all([promisedDetails, promisedArgs, promisedDescription]);
+    const [details, args] = await Promise.all([promisedDetails, promisedArgs]);
 
     if (details.numRows() <= 0) return;
     const rowIter = details.iter({});
@@ -222,7 +218,6 @@
       category,
       args,
       argsTree,
-      description,
     };
 
     if (trackId !== undefined) {
@@ -269,27 +264,6 @@
     }
   }
 
-  async describeSlice(id: number): Promise<Map<string, string>> {
-    const map = new Map<string, string>();
-    if (id === -1) return map;
-    const query = `
-      select
-        ifnull(description, '') as description,
-        ifnull(doc_link, '') as docLink
-      from describe_slice
-      where slice_id = ${id}
-    `;
-    const result = await this.args.engine.query(query);
-    const it = result.iter({description: STR, docLink: STR});
-    for (; it.valid(); it.next()) {
-      const description = it.description;
-      const docLink = it.docLink;
-      map.set('Description', description);
-      map.set('Documentation', docLink);
-    }
-    return map;
-  }
-
   async getArgs(argId: number): Promise<Args> {
     const args = new Map<string, Arg>();
     const query = `
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index 8fe8486..dd62689 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -18,19 +18,25 @@
   DeferredAction,
 } from '../common/actions';
 import {cacheTrace} from '../common/cache_manager';
-import {TRACE_MARGIN_TIME_S} from '../common/constants';
 import {Engine} from '../common/engine';
 import {featureFlags, Flag, PERF_SAMPLE_FLAG} from '../common/feature_flags';
 import {HttpRpcEngine} from '../common/http_rpc_engine';
+import {
+  getEnabledMetatracingCategories,
+  isMetatracingEnabled,
+} from '../common/metatracing';
 import {NUM, NUM_NULL, QueryError, STR, STR_NULL} from '../common/query_result';
+import {onSelectionChanged} from '../common/selection_observer';
 import {defaultTraceTime, EngineMode, ProfileType} from '../common/state';
 import {TimeSpan, toNs, toNsCeil, toNsFloor} from '../common/time';
 import {resetEngineWorker, WasmEngineProxy} from '../common/wasm_engine_proxy';
+import {BottomTabList} from '../frontend/bottom_tab';
 import {
   globals as frontendGlobals,
   QuantizedLoad,
   ThreadDesc,
 } from '../frontend/globals';
+import {showModal} from '../frontend/modal';
 import {
   publishHasFtrace,
   publishMetricError,
@@ -75,7 +81,10 @@
 import {LoadingManager} from './loading_manager';
 import {LogsController} from './logs_controller';
 import {MetricsController} from './metrics_controller';
-import {PivotTableReduxController} from './pivot_table_redux_controller';
+import {
+  PIVOT_TABLE_REDUX_FLAG,
+  PivotTableReduxController,
+} from './pivot_table_redux_controller';
 import {QueryController, QueryControllerArgs} from './query_controller';
 import {SearchController} from './search_controller';
 import {
@@ -104,7 +113,6 @@
   'android_dma_heap',
   'android_surfaceflinger',
   'android_batt',
-  'android_sysui_cuj',
   'android_camera',
   'android_other_traces',
   'chrome_dropped_frames',
@@ -125,6 +133,61 @@
   return [flag, m];
 });
 
+const ENABLE_CHROME_RELIABLE_RANGE_ZOOM_FLAG = featureFlags.register({
+  id: 'enableChromeReliableRangeZoom',
+  name: 'Enable Chrome reliable range zoom',
+  description: 'Automatically zoom into the reliable range for Chrome traces',
+  defaultValue: false,
+});
+
+const ENABLE_CHROME_RELIABLE_RANGE_ANNOTATION_FLAG = featureFlags.register({
+  id: 'enableChromeReliableRangeAnnotation',
+  name: 'Enable Chrome reliable range annotation',
+  description: 'Automatically adds an annotation for the reliable range start',
+  defaultValue: false,
+});
+
+// The following flags control TraceProcessor Config.
+const CROP_TRACK_EVENTS_FLAG = featureFlags.register({
+  id: 'cropTrackEvents',
+  name: 'Crop track events',
+  description: 'Ignores track events outside of the range of interest',
+  defaultValue: false,
+});
+const INGEST_FTRACE_IN_RAW_TABLE_FLAG = featureFlags.register({
+  id: 'ingestFtraceInRawTable',
+  name: 'Ingest ftrace in raw table',
+  description: 'Enables ingestion of typed ftrace events into the raw table',
+  defaultValue: true,
+});
+const ANALYZE_TRACE_PROTO_CONTENT_FLAG = featureFlags.register({
+  id: 'analyzeTraceProtoContent',
+  name: 'Analyze trace proto content',
+  description: 'Enables trace proto content analysis',
+  defaultValue: false,
+});
+
+// A local storage key where the indication that JSON warning has been shown is
+// stored.
+const SHOWN_JSON_WARNING_KEY = 'shownJsonWarning';
+
+function showJsonWarning() {
+  showModal({
+    title: 'Warning',
+    content:
+      m('div',
+        m('span',
+          'Perfetto UI features are limited for JSON traces. ',
+          'We recommend recording ',
+          m('a',
+            {href: 'https://perfetto.dev/docs/quickstart/chrome-tracing'},
+            'proto-format traces'),
+          ' from Chrome.'),
+        m('br')),
+    buttons: [],
+  });
+}
+
 // TraceController handles handshakes with the frontend for everything that
 // concerns a single trace. It owns the WASM trace processor engine, handles
 // tracks data and SQL queries. There is one TraceController instance for each
@@ -139,21 +202,21 @@
   }
 
   run() {
-    const engineCfg = assertExists(globals.state.engines[this.engineId]);
+    const engineCfg = assertExists(globals.state.engine);
     switch (this.state) {
       case 'init':
         this.loadTrace()
-            .then((mode) => {
-              globals.dispatch(Actions.setEngineReady({
-                engineId: this.engineId,
-                ready: true,
-                mode,
-              }));
-            })
-            .catch((err) => {
-              this.updateStatus(`${err}`);
-              throw err;
-            });
+          .then((mode) => {
+            globals.dispatch(Actions.setEngineReady({
+              engineId: this.engineId,
+              ready: true,
+              mode,
+            }));
+          })
+          .catch((err) => {
+            this.updateStatus(`${err}`);
+            throw err;
+          });
         this.updateStatus('Opening trace');
         this.setState('loading_trace');
         break;
@@ -183,11 +246,10 @@
         // Create a QueryController for each query.
         for (const queryId of Object.keys(globals.state.queries)) {
           // If the expected engineId was not specified in the query, we
-          // assume it's `state.currentEngineId`. The engineId is not specified
+          // assume it's current engine id. The engineId is not specified
           // for instances with queries created prior to the creation of the
           // first engine.
-          const expectedEngineId = globals.state.queries[queryId].engineId ||
-              frontendGlobals.state.currentEngineId;
+          const expectedEngineId = globals.state.engine?.id;
           // Check that we are executing the query on the correct engine.
           if (expectedEngineId !== engine.id) {
             continue;
@@ -198,7 +260,7 @@
 
         for (const argName of globals.state.visualisedArgs) {
           childControllers.push(
-              Child(argName, VisualisedArgController, {argName, engine}));
+            Child(argName, VisualisedArgController, {argName, engine}));
         }
 
         const selectionArgs: SelectionControllerArgs = {engine};
@@ -217,42 +279,47 @@
         childControllers.push(
           Child('flamegraph', FlamegraphController, flamegraphArgs));
         childControllers.push(Child(
-            'cpu_aggregation',
-            CpuAggregationController,
-            {engine, kind: 'cpu_aggregation'}));
+          'cpu_aggregation',
+          CpuAggregationController,
+          {engine, kind: 'cpu_aggregation'}));
         childControllers.push(Child(
-            'thread_aggregation',
-            ThreadAggregationController,
-            {engine, kind: 'thread_state_aggregation'}));
+          'thread_aggregation',
+          ThreadAggregationController,
+          {engine, kind: 'thread_state_aggregation'}));
         childControllers.push(Child(
-            'cpu_process_aggregation',
-            CpuByProcessAggregationController,
-            {engine, kind: 'cpu_by_process_aggregation'}));
-        childControllers.push(Child(
+          'cpu_process_aggregation',
+          CpuByProcessAggregationController,
+          {engine, kind: 'cpu_by_process_aggregation'}));
+        if (!PIVOT_TABLE_REDUX_FLAG.get()) {
+          // Pivot table is supposed to handle the use cases the slice
+          // aggregation panel is used right now. When a flag to use pivot
+          // tables is enabled, do not add slice aggregation controller.
+          childControllers.push(Child(
             'slice_aggregation',
             SliceAggregationController,
             {engine, kind: 'slice_aggregation'}));
+        }
         childControllers.push(Child(
-            'counter_aggregation',
-            CounterAggregationController,
-            {engine, kind: 'counter_aggregation'}));
+          'counter_aggregation',
+          CounterAggregationController,
+          {engine, kind: 'counter_aggregation'}));
         childControllers.push(Child(
-            'frame_aggregation',
-            FrameAggregationController,
-            {engine, kind: 'frame_aggregation'}));
+          'frame_aggregation',
+          FrameAggregationController,
+          {engine, kind: 'frame_aggregation'}));
         childControllers.push(Child('search', SearchController, {
           engine,
           app: globals,
         }));
         childControllers.push(
-            Child('pivot_table_redux', PivotTableReduxController, {engine}));
+          Child('pivot_table_redux', PivotTableReduxController, {engine}));
 
         childControllers.push(Child('logs', LogsController, {
           engine,
           app: globals,
         }));
         childControllers.push(
-            Child('traceError', TraceErrorController, {engine}));
+          Child('traceError', TraceErrorController, {engine}));
         childControllers.push(Child('metrics', MetricsController, {engine}));
 
         return childControllers;
@@ -283,7 +350,7 @@
       engine = new HttpRpcEngine(this.engineId, LoadingManager.getInstance);
       engine.errorHandler = (err) => {
         globals.dispatch(
-            Actions.setEngineFailed({mode: 'HTTP_RPC', failure: `${err}`}));
+          Actions.setEngineFailed({mode: 'HTTP_RPC', failure: `${err}`}));
         throw err;
       };
     } else {
@@ -292,16 +359,29 @@
       const enginePort = resetEngineWorker();
       engine = new WasmEngineProxy(
         this.engineId, enginePort, LoadingManager.getInstance);
+      engine.resetTraceProcessor({
+        cropTrackEvents: CROP_TRACK_EVENTS_FLAG.get(),
+        ingestFtraceInRawTable: INGEST_FTRACE_IN_RAW_TABLE_FLAG.get(),
+        analyzeTraceProtoContent: ANALYZE_TRACE_PROTO_CONTENT_FLAG.get(),
+      });
     }
     this.engine = engine;
 
+    if (isMetatracingEnabled()) {
+      this.engine.enableMetatrace(
+        assertExists(getEnabledMetatracingCategories()));
+    }
+    frontendGlobals.bottomTabList =
+      new BottomTabList(engine.getProxy('BottomTabList'));
+
     frontendGlobals.engines.set(this.engineId, engine);
     globals.dispatch(Actions.setEngineReady({
       engineId: this.engineId,
       ready: false,
       mode: engineMode,
     }));
-    const engineCfg = assertExists(globals.state.engines[this.engineId]);
+    const engineCfg = assertExists(globals.state.engine);
+    assertTrue(engineCfg.id === this.engineId);
     let traceStream: TraceStream | undefined;
     if (engineCfg.source.type === 'FILE') {
       traceStream = new TraceFileStream(engineCfg.source.file);
@@ -347,20 +427,35 @@
     const traceUuid = await this.cacheCurrentTrace();
 
     const traceTime = await this.engine.getTraceTimeBounds();
-    let startSec = traceTime.start;
-    let endSec = traceTime.end;
-    startSec -= TRACE_MARGIN_TIME_S;
-    endSec += TRACE_MARGIN_TIME_S;
+    const startSec = traceTime.start;
+    const endSec = traceTime.end;
     const traceTimeState = {
       startSec,
       endSec,
     };
 
+    const shownJsonWarning =
+      window.localStorage.getItem(SHOWN_JSON_WARNING_KEY) !== null;
+
+    // Show warning if the trace is in JSON format.
+    const query = `select str_value from metadata where name = 'trace_type'`;
+    const result = await assertExists(this.engine).query(query);
+    const traceType = result.firstRow({str_value: STR}).str_value;
+    const isJsonTrace = traceType == 'json';
+    if (!shownJsonWarning) {
+      // When in embedded mode, the host app will control which trace format
+      // it passes to Perfetto, so we don't need to show this warning.
+      if (isJsonTrace && !frontendGlobals.embeddedMode) {
+        showJsonWarning();
+        // Save that the warning has been shown. Value is irrelevant since only
+        // the presence of key is going to be checked.
+        window.localStorage.setItem(SHOWN_JSON_WARNING_KEY, 'true');
+      }
+    }
+
     const emptyOmniboxState = {
       omnibox: '',
-      mode: frontendGlobals.state.frontendLocalState.omniboxState.mode ||
-          'SEARCH',
-      lastUpdate: Date.now() / 1000,
+      mode: frontendGlobals.state.omniboxState.mode || 'SEARCH',
     };
 
     const actions: DeferredAction[] = [
@@ -370,7 +465,7 @@
     ];
 
     const [startVisibleTime, endVisibleTime] =
-        await computeVisibleTime(startSec, endSec, this.engine);
+      await computeVisibleTime(startSec, endSec, isJsonTrace, this.engine);
     // We don't know the resolution at this point. However this will be
     // replaced in 50ms so a guess is fine.
     const resolution = (endVisibleTime - startVisibleTime) / 1000;
@@ -424,6 +519,26 @@
       await this.selectPerfSample();
     }
 
+    // If the trace was shared via a permalink, it might already have a
+    // selection. Emit onSelectionChanged to ensure that the components (like
+    // current selection details) react to it.
+    if (globals.state.currentSelection !== null) {
+      onSelectionChanged(globals.state.currentSelection, undefined);
+    }
+
+    // Trace Processor doesn't support the reliable range feature for JSON
+    // traces.
+    if (!isJsonTrace && ENABLE_CHROME_RELIABLE_RANGE_ANNOTATION_FLAG.get()) {
+      const reliableRangeStart = await computeTraceReliableRangeStart(engine);
+      if (reliableRangeStart > 0) {
+        globals.dispatch(Actions.addAutomaticNote({
+          timestamp: reliableRangeStart,
+          color: '#ff0000',
+          text: 'Reliable Range Start',
+        }));
+      }
+    }
+
     return engineMode;
   }
 
@@ -440,7 +555,7 @@
     const leftTs = toNs(globals.state.traceTime.startSec);
     const rightTs = toNs(globals.state.traceTime.endSec);
     globals.dispatch(Actions.selectPerfSamples(
-        {id: 0, upid, leftTs, rightTs, type: ProfileType.PERF_SAMPLE}));
+      {id: 0, upid, leftTs, rightTs, type: ProfileType.PERF_SAMPLE}));
   }
 
   private async selectFirstHeapProfile() {
@@ -526,7 +641,7 @@
         `select sum(dur)/${stepSec}/1e9 as load, cpu from sched ` +
         `where ts >= ${startNs} and ts < ${endNs} and utid != 0 ` +
         'group by cpu order by cpu');
-      const schedData: { [key: string]: QuantizedLoad } = {};
+      const schedData: {[key: string]: QuantizedLoad} = {};
       const it = schedResult.iter({load: NUM, cpu: NUM});
       for (; it.valid(); it.next()) {
         const load = it.load;
@@ -561,7 +676,7 @@
          where upid is not null
          group by bucket, upid`);
 
-    const slicesData: { [key: string]: QuantizedLoad[] } = {};
+    const slicesData: {[key: string]: QuantizedLoad[]} = {};
     const it = sliceResult.iter({bucket: NUM, upid: NUM, load: NUM});
     for (; it.valid(); it.next()) {
       const bucket = it.bucket;
@@ -590,7 +705,8 @@
       return '';
     }
     const traceUuid = result.firstRow({uuid: STR}).uuid;
-    const engineConfig = assertExists(globals.state.engines[engine.id]);
+    const engineConfig = assertExists(globals.state.engine);
+    assertTrue(engineConfig.id === this.engineId);
     if (!(await cacheTrace(engineConfig.source, traceUuid))) {
       // If the trace is not cacheable (cacheable means it has been opened from
       // URL or RPC) only append '?local_cache_key' to the URL, without the
@@ -632,9 +748,9 @@
     this.updateStatus('Creating annotation counter table');
     await engine.query(`
       CREATE TABLE annotation_counter(
-        id BIG INT,
+        id BIGINT,
         track_id INT,
-        ts BIG INT,
+        ts BIGINT,
         value DOUBLE,
         PRIMARY KEY (track_id, ts)
       ) WITHOUT ROWID;
@@ -644,8 +760,9 @@
       CREATE TABLE annotation_slice(
         id INTEGER PRIMARY KEY,
         track_id INT,
-        ts BIG INT,
-        dur BIG INT,
+        ts BIGINT,
+        dur BIGINT,
+        thread_dur BIGINT,
         depth INT,
         cat STRING,
         name STRING,
@@ -716,11 +833,14 @@
             WHERE track_type = 'slice'
           `);
           await engine.query(`
-            INSERT INTO annotation_slice(track_id, ts, dur, depth, cat, name)
+            INSERT INTO annotation_slice(
+              track_id, ts, dur, thread_dur, depth, cat, name
+            )
             SELECT
               t.id AS track_id,
               ts,
               dur,
+              NULL as thread_dur,
               0 AS depth,
               a.track_name as cat,
               slice_name AS name
@@ -782,13 +902,25 @@
   }
 }
 
+async function computeTraceReliableRangeStart(engine: Engine): Promise<number> {
+  const result =
+    await engine.query(`SELECT RUN_METRIC('chrome/chrome_reliable_range.sql');
+       SELECT start FROM chrome_reliable_range`);
+  const bounds = result.firstRow({start: NUM});
+  return bounds.start / 1e9;
+}
+
 async function computeVisibleTime(
-    traceStartSec: number, traceEndSec: number, engine: Engine):
-    Promise<[number, number]> {
+  traceStartSec: number,
+  traceEndSec: number,
+  isJsonTrace: boolean,
+  engine: Engine): Promise<[number, number]> {
   // if we have non-default visible state, update the visible time to it
   const previousVisibleState = globals.state.frontendLocalState.visibleState;
   if (!(previousVisibleState.startSec === defaultTraceTime.startSec &&
-        previousVisibleState.endSec === defaultTraceTime.endSec)) {
+    previousVisibleState.endSec === defaultTraceTime.endSec) &&
+    (previousVisibleState.startSec >= traceStartSec &&
+      previousVisibleState.endSec <= traceEndSec)) {
     return [previousVisibleState.startSec, previousVisibleState.endSec];
   }
 
@@ -799,11 +931,19 @@
   // compare start and end with metadata computed by the trace processor
   const mdTime = await engine.getTracingMetadataTimeBounds();
   // make sure the bounds hold
-  if (Math.max(visibleStartSec, mdTime.start - TRACE_MARGIN_TIME_S) <
-      Math.min(visibleEndSec, mdTime.end + TRACE_MARGIN_TIME_S)) {
+  if (Math.max(visibleStartSec, mdTime.start) <
+    Math.min(visibleEndSec, mdTime.end)) {
     visibleStartSec =
-        Math.max(visibleStartSec, mdTime.start - TRACE_MARGIN_TIME_S);
-    visibleEndSec = Math.min(visibleEndSec, mdTime.end + TRACE_MARGIN_TIME_S);
+      Math.max(visibleStartSec, mdTime.start);
+    visibleEndSec = Math.min(visibleEndSec, mdTime.end);
   }
+
+  // Trace Processor doesn't support the reliable range feature for JSON
+  // traces.
+  if (!isJsonTrace && ENABLE_CHROME_RELIABLE_RANGE_ZOOM_FLAG.get()) {
+    const reliableRangeStart = await computeTraceReliableRangeStart(engine);
+    visibleStartSec = Math.max(visibleStartSec, reliableRangeStart);
+  }
+
   return [visibleStartSec, visibleEndSec];
 }
diff --git a/ui/src/controller/trace_stream.ts b/ui/src/controller/trace_stream.ts
index f6c9da6..a980665 100644
--- a/ui/src/controller/trace_stream.ts
+++ b/ui/src/controller/trace_stream.ts
@@ -44,14 +44,14 @@
     this.reader.onloadend = () => this.onLoad();
   }
 
-  onLoad() {
-    const res = assertExists(this.reader.result) as ArrayBuffer;
+  private onLoad() {
     const pendingRead = assertExists(this.pendingRead);
     this.pendingRead = undefined;
     if (this.reader.error) {
       pendingRead.reject(this.reader.error);
       return;
     }
+    const res = assertExists(this.reader.result) as ArrayBuffer;
     this.bytesRead += res.byteLength;
     pendingRead.resolve({
       data: new Uint8Array(res),
diff --git a/ui/src/controller/track_decider.ts b/ui/src/controller/track_decider.ts
index fc5c771..14a77e8 100644
--- a/ui/src/controller/track_decider.ts
+++ b/ui/src/controller/track_decider.ts
@@ -21,8 +21,9 @@
   AddTrackArgs,
   DeferredAction,
 } from '../common/actions';
-import {Engine} from '../common/engine';
+import {Engine, EngineProxy} from '../common/engine';
 import {featureFlags, PERF_SAMPLE_FLAG} from '../common/feature_flags';
+import {pluginManager} from '../common/plugins';
 import {
   NUM,
   NUM_NULL,
@@ -82,9 +83,16 @@
 const F2FS_IOSTAT_GROUP_NAME = 'f2fs_iostat';
 const F2FS_IOSTAT_LAT_TAG = 'f2fs_iostat_latency.';
 const F2FS_IOSTAT_LAT_GROUP_NAME = 'f2fs_iostat_latency';
+const DISK_IOSTAT_TAG = 'diskstat.';
+const DISK_IOSTAT_GROUP_NAME = 'diskstat';
 const UFS_CMD_TAG = 'io.ufs.command.tag';
 const UFS_CMD_TAG_GROUP_NAME = 'io.ufs.command.tags';
 const BUDDY_INFO_TAG = 'mem.buddyinfo';
+// NB: Userspace wakelocks start with "WakeLock" not "Wakelock".
+const KERNEL_WAKELOCK_REGEX = new RegExp('^Wakelock.*$');
+const KERNEL_WAKELOCK_GROUP = 'Kernel wakelocks';
+const NETWORK_TRACK_REGEX = new RegExp('^.* (Received|Transmitted)( KB)?$');
+const NETWORK_TRACK_GROUP = 'Networking';
 
 // Sets the default 'scale' for counter tracks. If the regex matches
 // then the paired mode is used. Entries are in priority order so the
@@ -221,10 +229,10 @@
     }
   }
 
-  async addCpuFreqTracks(): Promise<void> {
+  async addCpuFreqTracks(engine: EngineProxy): Promise<void> {
     const cpus = await this.engine.getCpus();
 
-    const maxCpuFreqResult = await this.engine.query(`
+    const maxCpuFreqResult = await engine.query(`
     select ifnull(max(value), 0) as freq
     from counter c
     inner join cpu_counter_track t on c.track_id = t.id
@@ -237,7 +245,7 @@
       // cpu freq data.
       // TODO(hjd): Find a way to display cpu idle
       // events even if there are no cpu freq events.
-      const cpuFreqIdleResult = await this.engine.query(`
+      const cpuFreqIdleResult = await engine.query(`
       select
         id as cpuFreqId,
         (
@@ -277,22 +285,34 @@
     }
   }
 
-  async addGlobalAsyncTracks(): Promise<void> {
-    const rawGlobalAsyncTracks = await this.engine.query(`
-    SELECT
-      t.name as name,
-      t.track_ids as trackIds,
-      MAX(experimental_slice_layout.layout_depth) as maxDepth
-    FROM (
-      SELECT name, GROUP_CONCAT(track.id) AS track_ids
-      FROM track
-      WHERE track.type = "track" or track.type = "gpu_track"
-      GROUP BY name
-    ) AS t CROSS JOIN experimental_slice_layout
-    WHERE t.track_ids = experimental_slice_layout.filter_track_ids
-    GROUP BY t.track_ids
-    ORDER BY t.name;
-  `);
+  async addGlobalAsyncTracks(engine: EngineProxy): Promise<void> {
+    const rawGlobalAsyncTracks = await engine.query(`
+      with global_tracks as materialized (
+        select
+          track.id,
+          track.name,
+          count(1) cnt
+        from track
+        join slice on slice.track_id = track.id
+        where track.type = "track" or track.type = "gpu_track"
+        group by 1
+        having cnt > 0
+      ),
+      global_tracks_grouped as (
+        select
+          track.name,
+          group_concat(track.id) as trackIds,
+          count(track.id) as trackCount
+        from global_tracks track
+        group by track.name
+      )
+      select
+        t.name as name,
+        t.trackIds as trackIds,
+        max_layout_depth(t.trackCount, t.trackIds) as maxDepth
+      from global_tracks_grouped AS t
+      order by t.name;
+    `);
     const it = rawGlobalAsyncTracks.iter({
       name: STR_NULL,
       trackIds: STR,
@@ -320,9 +340,9 @@
     }
   }
 
-  async addGpuFreqTracks(): Promise<void> {
+  async addGpuFreqTracks(engine: EngineProxy): Promise<void> {
     const numGpus = await this.engine.getNumberOfGpus();
-    const maxGpuFreqResult = await this.engine.query(`
+    const maxGpuFreqResult = await engine.query(`
     select ifnull(max(value), 0) as maximumValue
     from counter c
     inner join gpu_counter_track t on c.track_id = t.id
@@ -334,7 +354,7 @@
     for (let gpu = 0; gpu < numGpus; gpu++) {
       // Only add a gpu freq track if we have
       // gpu freq data.
-      const freqExistsResult = await this.engine.query(`
+      const freqExistsResult = await engine.query(`
       select id
       from gpu_counter_track
       where name = 'gpufreq' and gpu_id = ${gpu}
@@ -357,9 +377,9 @@
     }
   }
 
-  async addGlobalCounterTracks(): Promise<void> {
+  async addGlobalCounterTracks(engine: EngineProxy): Promise<void> {
     // Add global or GPU counter tracks that are not bound to any pid/tid.
-    const globalCounters = await this.engine.query(`
+    const globalCounters = await engine.query(`
     select name, id
     from (
       select name, id
@@ -396,14 +416,14 @@
     }
   }
 
-  async addCpuPerfCounterTracks(): Promise<void> {
+  async addCpuPerfCounterTracks(engine: EngineProxy): Promise<void> {
     // Perf counter tracks are bound to CPUs, follow the scheduling and
     // frequency track naming convention ("Cpu N ...").
     // Note: we might not have a track for a given cpu if no data was seen from
     // it. This might look surprising in the UI, but placeholder tracks are
     // wasteful as there's no way of collapsing global counter tracks at the
     // moment.
-    const result = await this.engine.query(`
+    const result = await engine.query(`
       select printf("Cpu %u %s", cpu, name) as name, id
       from perf_counter_track as pct
       order by perf_session_id asc, pct.name asc, cpu asc
@@ -476,21 +496,21 @@
     this.addTrackGroupActions.push(addGroup);
   }
 
-  async groupGlobalF2fsIostatTracks(tag: string, group: string): Promise<void> {
-    const f2fsIostatTracks: AddTrackArgs[] = [];
+  async groupGlobalIostatTracks(tag: string, group: string): Promise<void> {
+    const iostatTracks: AddTrackArgs[] = [];
     const devMap = new Map<string, string>();
 
     for (const track of this.tracksToAdd) {
       if (track.name.startsWith(tag)) {
-        f2fsIostatTracks.push(track);
+        iostatTracks.push(track);
       }
     }
 
-    if (f2fsIostatTracks.length === 0) {
+    if (iostatTracks.length === 0) {
       return;
     }
 
-    for (const track of f2fsIostatTracks) {
+    for (const track of iostatTracks) {
       const name = track.name.split('.', 3);
 
       if (!devMap.has(name[1])) {
@@ -608,9 +628,55 @@
     }
   }
 
-  async addLogsTrack(): Promise<void> {
+  async groupTracksByRegex(regex: RegExp, groupName: string): Promise<void> {
+    let groupUuid = undefined;
+
+    for (const track of this.tracksToAdd) {
+      if (regex.test(track.name)) {
+        if (groupUuid === undefined) {
+          groupUuid = uuidv4();
+        }
+        track.trackGroup = groupUuid;
+      }
+    }
+
+    if (groupUuid !== undefined) {
+      const summaryTrackId = uuidv4();
+      this.tracksToAdd.push({
+        id: summaryTrackId,
+        engineId: this.engineId,
+        kind: NULL_TRACK_KIND,
+        trackSortKey: PrimaryTrackSortKey.NULL_TRACK,
+        name: groupName,
+        trackGroup: undefined,
+        config: {},
+      });
+
+      const addGroup = Actions.addTrackGroup({
+        engineId: this.engineId,
+        summaryTrackId,
+        name: groupName,
+        id: groupUuid,
+        collapsed: true,
+      });
+      this.addTrackGroupActions.push(addGroup);
+    }
+  }
+
+  applyDefaultCounterScale(): void {
+    for (const track of this.tracksToAdd) {
+      if (track.kind === COUNTER_TRACK_KIND) {
+        const scaleConfig = {
+          scale: getCounterScale(track.name),
+        };
+        track.config = Object.assign({}, track.config, scaleConfig);
+      }
+    }
+  }
+
+  async addLogsTrack(engine: EngineProxy): Promise<void> {
     const result =
-        await this.engine.query(`select count(1) as cnt from android_logs`);
+        await engine.query(`select count(1) as cnt from android_logs`);
     const count = result.firstRow({cnt: NUM}).cnt;
 
     if (count > 0) {
@@ -625,8 +691,8 @@
     }
   }
 
-  async addAnnotationTracks(): Promise<void> {
-    const sliceResult = await this.engine.query(`
+  async addAnnotationTracks(engine: EngineProxy): Promise<void> {
+    const sliceResult = await engine.query(`
       select id, name, upid, group_name
       from annotation_slice_track
       order by name
@@ -699,7 +765,7 @@
       this.addTrackGroupActions.push(addGroup);
     }
 
-    const counterResult = await this.engine.query(`
+    const counterResult = await engine.query(`
     SELECT
       id,
       name,
@@ -742,8 +808,8 @@
     }
   }
 
-  async addThreadStateTracks(): Promise<void> {
-    const result = await this.engine.query(`
+  async addThreadStateTracks(engine: EngineProxy): Promise<void> {
+    const result = await engine.query(`
       select
         utid,
         tid,
@@ -791,8 +857,8 @@
     }
   }
 
-  async addThreadCpuSampleTracks(): Promise<void> {
-    const result = await this.engine.query(`
+  async addThreadCpuSampleTracks(engine: EngineProxy): Promise<void> {
+    const result = await engine.query(`
       select
         utid,
         tid,
@@ -832,8 +898,8 @@
     }
   }
 
-  async addThreadCounterTracks(): Promise<void> {
-    const result = await this.engine.query(`
+  async addThreadCounterTracks(engine: EngineProxy): Promise<void> {
+    const result = await engine.query(`
     select
       thread_counter_track.name as trackName,
       utid,
@@ -887,20 +953,21 @@
           startTs,
           endTs,
           tid,
-          scale: getCounterScale(name),
         },
       });
     }
   }
 
-  async addProcessAsyncSliceTracks(): Promise<void> {
-    const result = await this.engine.query(`
+  async addProcessAsyncSliceTracks(engine: EngineProxy): Promise<void> {
+    const result = await engine.query(`
+      with process_async_tracks as materialized (
         select
           process_track.upid as upid,
           process_track.name as trackName,
-          group_concat(process_track.id) as trackIds,
           process.name as processName,
-          process.pid as pid
+          process.pid as pid,
+          group_concat(process_track.id) as trackIds,
+          count(1) as trackCount
         from process_track
         left join process using(upid)
         where
@@ -909,7 +976,12 @@
         group by
           process_track.upid,
           process_track.name
-  `);
+      )
+      select
+        t.*,
+        max_layout_depth(t.trackCount, t.trackIds) as maxDepth
+      from process_async_tracks t;
+    `);
 
     const it = result.iter({
       upid: NUM,
@@ -917,6 +989,7 @@
       trackIds: STR,
       processName: STR_NULL,
       pid: NUM_NULL,
+      maxDepth: NUM,
     });
     for (; it.valid(); it.next()) {
       const upid = it.upid;
@@ -925,16 +998,10 @@
       const trackIds = rawTrackIds.split(',').map((v) => Number(v));
       const processName = it.processName;
       const pid = it.pid;
+      const maxDepth = it.maxDepth;
 
       const uuid = this.getUuid(0, upid);
 
-      // TODO(hjd): 1+N queries are bad in the track_decider
-      const depthResult = await this.engine.query(`
-      SELECT IFNULL(MAX(layout_depth), 0) as depth
-      FROM experimental_slice_layout('${rawTrackIds}');
-    `);
-      const maxDepth = depthResult.firstRow({depth: NUM}).depth;
-
       const kind = ASYNC_SLICE_TRACK_KIND;
       const name = TrackDecider.getTrackName(
           {name: trackName, upid, pid, processName, kind});
@@ -952,25 +1019,27 @@
     }
   }
 
-  async addActualFramesTracks(): Promise<void> {
-    const result = await this.engine.query(`
+  async addActualFramesTracks(engine: EngineProxy): Promise<void> {
+    const result = await engine.query(`
+      with process_async_tracks as materialized (
         select
-          upid,
-          trackName,
-          trackIds,
+          process_track.upid as upid,
+          process_track.name as trackName,
           process.name as processName,
-          process.pid as pid
-        from (
-          select
-            process_track.upid as upid,
-            process_track.name as trackName,
-            group_concat(process_track.id) as trackIds
-          from process_track
-          where process_track.name like "Actual Timeline"
-          group by
-            process_track.upid,
-            process_track.name
-        ) left join process using(upid)
+          process.pid as pid,
+          group_concat(process_track.id) as trackIds,
+          count(1) as trackCount
+        from process_track
+        left join process using(upid)
+        where process_track.name = "Actual Timeline"
+        group by
+          process_track.upid,
+          process_track.name
+      )
+      select
+        t.*,
+        max_layout_depth(t.trackCount, t.trackIds) as maxDepth
+      from process_async_tracks t;
   `);
 
     const it = result.iter({
@@ -979,6 +1048,7 @@
       trackIds: STR,
       processName: STR_NULL,
       pid: NUM_NULL,
+      maxDepth: NUM,
     });
     for (; it.valid(); it.next()) {
       const upid = it.upid;
@@ -987,16 +1057,10 @@
       const trackIds = rawTrackIds.split(',').map((v) => Number(v));
       const processName = it.processName;
       const pid = it.pid;
+      const maxDepth = it.maxDepth;
 
       const uuid = this.getUuid(0, upid);
 
-      // TODO(hjd): 1+N queries are bad in the track_decider
-      const depthResult = await this.engine.query(`
-      SELECT IFNULL(MAX(layout_depth), 0) as depth
-      FROM experimental_slice_layout('${rawTrackIds}');
-    `);
-      const maxDepth = depthResult.firstRow({depth: NUM}).depth;
-
       const kind = ACTUAL_FRAMES_SLICE_TRACK_KIND;
       const name = TrackDecider.getTrackName(
           {name: trackName, upid, pid, processName, kind});
@@ -1014,25 +1078,27 @@
     }
   }
 
-  async addExpectedFramesTracks(): Promise<void> {
-    const result = await this.engine.query(`
+  async addExpectedFramesTracks(engine: EngineProxy): Promise<void> {
+    const result = await engine.query(`
+      with process_async_tracks as materialized (
         select
-          upid,
-          trackName,
-          trackIds,
+          process_track.upid as upid,
+          process_track.name as trackName,
           process.name as processName,
-          process.pid as pid
-        from (
-          select
-            process_track.upid as upid,
-            process_track.name as trackName,
-            group_concat(process_track.id) as trackIds
-          from process_track
-          where process_track.name like "Expected Timeline"
-          group by
-            process_track.upid,
-            process_track.name
-        ) left join process using(upid)
+          process.pid as pid,
+          group_concat(process_track.id) as trackIds,
+          count(1) as trackCount
+        from process_track
+        left join process using(upid)
+        where process_track.name = "Expected Timeline"
+        group by
+          process_track.upid,
+          process_track.name
+      )
+      select
+        t.*,
+        max_layout_depth(t.trackCount, t.trackIds) as maxDepth
+      from process_async_tracks t;
   `);
 
     const it = result.iter({
@@ -1041,6 +1107,7 @@
       trackIds: STR,
       processName: STR_NULL,
       pid: NUM_NULL,
+      maxDepth: NUM,
     });
 
     for (; it.valid(); it.next()) {
@@ -1050,16 +1117,10 @@
       const trackIds = rawTrackIds.split(',').map((v) => Number(v));
       const processName = it.processName;
       const pid = it.pid;
+      const maxDepth = it.maxDepth;
 
       const uuid = this.getUuid(0, upid);
 
-      // TODO(hjd): 1+N queries are bad in the track_decider
-      const depthResult = await this.engine.query(`
-      SELECT IFNULL(MAX(layout_depth), 0) as depth
-      FROM experimental_slice_layout('${rawTrackIds}');
-    `);
-      const maxDepth = depthResult.firstRow({depth: NUM}).depth;
-
       const kind = EXPECTED_FRAMES_SLICE_TRACK_KIND;
       const name = TrackDecider.getTrackName(
           {name: trackName, upid, pid, processName, kind});
@@ -1077,8 +1138,8 @@
     }
   }
 
-  async addThreadSliceTracks(): Promise<void> {
-    const result = await this.engine.query(`
+  async addThreadSliceTracks(engine: EngineProxy): Promise<void> {
+    const result = await engine.query(`
         select
           thread_track.utid as utid,
           thread_track.id as trackId,
@@ -1088,13 +1149,11 @@
           tid,
           thread.name as threadName,
           max(slice.depth) as maxDepth,
-          (count(thread_slice.id) = count(slice.id)) as onlyThreadSlice,
           process.upid as upid
         from slice
         join thread_track on slice.track_id = thread_track.id
         join thread using(utid)
         left join process using(upid)
-        left join thread_slice on slice.id = thread_slice.id
         group by thread_track.id
   `);
 
@@ -1107,7 +1166,6 @@
       threadName: STR_NULL,
       maxDepth: NUM,
       upid: NUM_NULL,
-      onlyThreadSlice: NUM,
     });
     for (; it.valid(); it.next()) {
       const utid = it.utid;
@@ -1119,7 +1177,6 @@
       const threadName = it.threadName;
       const upid = it.upid;
       const maxDepth = it.maxDepth;
-      const onlyThreadSlice = it.onlyThreadSlice;
 
       const uuid = this.getUuid(utid, upid);
 
@@ -1141,7 +1198,6 @@
           trackId,
           maxDepth,
           tid,
-          isThreadSlice: onlyThreadSlice === 1,
         },
       });
 
@@ -1163,8 +1219,8 @@
     }
   }
 
-  async addProcessCounterTracks(): Promise<void> {
-    const result = await this.engine.query(`
+  async addProcessCounterTracks(engine: EngineProxy): Promise<void> {
+    const result = await engine.query(`
     select
       process_counter_track.id as trackId,
       process_counter_track.name as trackName,
@@ -1209,14 +1265,13 @@
           trackId,
           startTs,
           endTs,
-          scale: getCounterScale(name),
         },
       });
     }
   }
 
-  async addProcessHeapProfileTracks(): Promise<void> {
-    const result = await this.engine.query(`
+  async addProcessHeapProfileTracks(engine: EngineProxy): Promise<void> {
+    const result = await engine.query(`
     select distinct(upid) from heap_profile_allocation
     union
     select distinct(upid) from heap_graph_object
@@ -1235,8 +1290,8 @@
     }
   }
 
-  async addProcessPerfSamplesTracks(): Promise<void> {
-    const result = await this.engine.query(`
+  async addProcessPerfSamplesTracks(engine: EngineProxy): Promise<void> {
+    const result = await engine.query(`
       select distinct upid, pid
       from perf_sample join thread using (utid) join process using (upid)
       where callsite_id is not null
@@ -1282,7 +1337,7 @@
     this.upidToUuid.set(upid, uuid);
   }
 
-  async addKernelThreadGrouping(): Promise<void> {
+  async addKernelThreadGrouping(engine: EngineProxy): Promise<void> {
     // Identify kernel threads if this is a linux system trace, and sufficient
     // process information is available. Kernel threads are identified by being
     // children of kthreadd (always pid 2).
@@ -1292,7 +1347,7 @@
     // which has pid 0 but appears as a distinct process (with its own comm) on
     // each cpu. It'd make sense to exclude its thread state track, but still
     // put process-scoped tracks in this group.
-    const result = await this.engine.query(`
+    const result = await engine.query(`
       select
         t.utid, p.upid, (case p.pid when 2 then 1 else 0 end) isKthreadd
       from
@@ -1348,7 +1403,7 @@
     }
   }
 
-  async addProcessTrackGroups(): Promise<void> {
+  async addProcessTrackGroups(engine: EngineProxy): Promise<void> {
     // We want to create groups of tracks in a specific order.
     // The tracks should be grouped:
     //    by upid
@@ -1361,7 +1416,7 @@
     //  upid
     //  thread name
     //  utid
-    const result = await this.engine.query(`
+    const result = await engine.query(`
     select
       the_tracks.upid,
       the_tracks.utid,
@@ -1371,7 +1426,14 @@
       thread.tid as tid,
       process.name as processName,
       thread.name as threadName,
-      process.arg_set_id as argSetId,
+      ifnull((
+        select group_concat(string_value)
+        from args
+        where
+          process.arg_set_id is not null and
+          arg_set_id = process.arg_set_id and
+          flat_key = 'chrome.process_label'
+      ), '') AS chromeProcessLabels,
       (case process.name
          when 'Browser' then 3
          when 'Gpu' then 2
@@ -1470,7 +1532,7 @@
       processName: STR_NULL,
       hasSched: NUM_NULL,
       hasHeapProfiles: NUM_NULL,
-      argSetId: NUM_NULL,
+      chromeProcessLabels: STR,
     });
     for (; it.valid(); it.next()) {
       const utid = it.utid;
@@ -1482,22 +1544,6 @@
       const hasSched = !!it.hasSched;
       const hasHeapProfiles = !!it.hasHeapProfiles;
 
-      const labels = [];
-      if (it.argSetId !== null) {
-        const result = await this.engine.query(`
-          select string_value as label
-          from args
-          where arg_set_id = ${it.argSetId}
-          and flat_key = 'chrome.process_label'
-        `);
-        const argIt = result.iter({label: STR_NULL});
-        for (; argIt.valid(); argIt.next()) {
-          if (argIt.label !== null) {
-            labels.push(argIt.label);
-          }
-        }
-      }
-
       // Group by upid if present else by utid.
       let pUuid =
           upid === null ? this.utidToUuid.get(utid) : this.upidToUuid.get(upid);
@@ -1519,7 +1565,7 @@
               PrimaryTrackSortKey.PROCESS_SUMMARY_TRACK,
           name: `${upid === null ? tid : pid} summary`,
           config: {pidForColor, upid, utid, tid},
-          labels,
+          labels: it.chromeProcessLabels.split(','),
         });
 
         const name = TrackDecider.getTrackName(
@@ -1567,26 +1613,77 @@
     return threadOrderingMetadata;
   }
 
+  private async defineMaxLayoutDepthSqlFunction(): Promise<void> {
+    await this.engine.query(`
+      select create_function(
+        'max_layout_depth(track_count INT, track_ids STRING)',
+        'INT',
+        '
+          select ifnull(iif(
+            $track_count = 1,
+            (
+              select max(depth)
+              from slice
+              where track_id = cast($track_ids AS int)
+            ),
+            (
+              select max(layout_depth)
+              from experimental_slice_layout($track_ids)
+            )
+          ), 0);
+        '
+      );
+    `);
+  }
+
+  async addPluginTracks(): Promise<void> {
+    const promises = pluginManager.findPotentialTracks(this.engine);
+    const groups = await Promise.all(promises);
+    for (const infos of groups) {
+      for (const info of infos) {
+        this.tracksToAdd.push({
+          engineId: this.engineId,
+          kind: info.trackKind,
+          name: info.name,
+          // TODO(hjd): Fix how sorting works. Plugins should expose
+          // 'sort keys' which the user can use to choose a sort order.
+          trackSortKey: PrimaryTrackSortKey.COUNTER_TRACK,
+          trackGroup: SCROLLING_TRACK_GROUP,
+          config: info.config,
+        });
+      }
+    }
+  }
+
   async decideTracks(): Promise<DeferredAction[]> {
     // Add first the global tracks that don't require per-process track groups.
     if (NULL_TRACKS_FLAG.get()) {
       await this.addNullTracks();
     }
+
+    await this.defineMaxLayoutDepthSqlFunction();
+
     await this.addCpuSchedulingTracks();
-    await this.addCpuFreqTracks();
-    await this.addGlobalAsyncTracks();
-    await this.addGpuFreqTracks();
-    await this.addGlobalCounterTracks();
-    await this.addCpuPerfCounterTracks();
-    await this.addAnnotationTracks();
+    await this.addCpuFreqTracks(
+        this.engine.getProxy('TrackDecider::addCpuFreqTracks'));
+    await this.addGlobalAsyncTracks(
+        this.engine.getProxy('TrackDecider::addGlobalAsyncTracks'));
+    await this.addGpuFreqTracks(
+        this.engine.getProxy('TrackDecider::addGpuFreqTracks'));
+    await this.addCpuPerfCounterTracks(
+        this.engine.getProxy('TrackDecider::addCpuPerfCounterTracks'));
+    await this.addPluginTracks();
+    await this.addAnnotationTracks(
+        this.engine.getProxy('TrackDecider::addAnnotationTracks'));
     await this.groupGlobalIonTracks();
-    await this.groupGlobalF2fsIostatTracks(
-        F2FS_IOSTAT_TAG, F2FS_IOSTAT_GROUP_NAME);
-    await this.groupGlobalF2fsIostatTracks(
+    await this.groupGlobalIostatTracks(F2FS_IOSTAT_TAG, F2FS_IOSTAT_GROUP_NAME);
+    await this.groupGlobalIostatTracks(
         F2FS_IOSTAT_LAT_TAG, F2FS_IOSTAT_LAT_GROUP_NAME);
-    await this.groupGlobalUfsCmdTagTracks(
-        UFS_CMD_TAG, UFS_CMD_TAG_GROUP_NAME);
+    await this.groupGlobalIostatTracks(DISK_IOSTAT_TAG, DISK_IOSTAT_GROUP_NAME);
+    await this.groupGlobalUfsCmdTagTracks(UFS_CMD_TAG, UFS_CMD_TAG_GROUP_NAME);
     await this.groupGlobalBuddyInfoTracks();
+    await this.groupTracksByRegex(KERNEL_WAKELOCK_REGEX, KERNEL_WAKELOCK_GROUP);
+    await this.groupTracksByRegex(NETWORK_TRACK_REGEX, NETWORK_TRACK_GROUP);
 
     // Pre-group all kernel "threads" (actually processes) if this is a linux
     // system trace. Below, addProcessTrackGroups will skip them due to an
@@ -1594,27 +1691,39 @@
     // per-thread tracks. Quirk: since all threads will appear to be
     // TrackKindPriority.MAIN_THREAD, any process-level tracks will end up
     // pushed to the bottom of the group in the UI.
-    await this.addKernelThreadGrouping();
+    await this.addKernelThreadGrouping(
+        this.engine.getProxy('TrackDecider::addKernelThreadGrouping'));
 
     // Create the per-process track groups. Note that this won't necessarily
     // create a track per process. If a process has been completely idle and has
     // no sched events, no track group will be emitted.
     // Will populate this.addTrackGroupActions
-    await this.addProcessTrackGroups();
+    await this.addProcessTrackGroups(
+        this.engine.getProxy('TrackDecider::addProcessTrackGroups'));
 
-    await this.addProcessHeapProfileTracks();
+    await this.addProcessHeapProfileTracks(
+        this.engine.getProxy('TrackDecider::addProcessHeapProfileTracks'));
     if (PERF_SAMPLE_FLAG.get()) {
-      await this.addProcessPerfSamplesTracks();
+      await this.addProcessPerfSamplesTracks(
+          this.engine.getProxy('TrackDecider::addProcessPerfSamplesTracks'));
     }
-    await this.addProcessCounterTracks();
-    await this.addProcessAsyncSliceTracks();
-    await this.addActualFramesTracks();
-    await this.addExpectedFramesTracks();
-    await this.addThreadCounterTracks();
-    await this.addThreadStateTracks();
-    await this.addThreadSliceTracks();
-    await this.addThreadCpuSampleTracks();
-    await this.addLogsTrack();
+    await this.addProcessCounterTracks(
+        this.engine.getProxy('TrackDecider::addProcessCounterTracks'));
+    await this.addProcessAsyncSliceTracks(
+        this.engine.getProxy('TrackDecider::addProcessAsyncSliceTrack'));
+    await this.addActualFramesTracks(
+        this.engine.getProxy('TrackDecider::addActualFramesTracks'));
+    await this.addExpectedFramesTracks(
+        this.engine.getProxy('TrackDecider::addExpectedFramesTracks'));
+    await this.addThreadCounterTracks(
+        this.engine.getProxy('TrackDecider::addThreadCounterTracks'));
+    await this.addThreadStateTracks(
+        this.engine.getProxy('TrackDecider::addThreadStateTracks'));
+    await this.addThreadSliceTracks(
+        this.engine.getProxy('TrackDecider::addThreadSliceTracks'));
+    await this.addThreadCpuSampleTracks(
+        this.engine.getProxy('TrackDecider::addThreadCpuSampleTracks'));
+    await this.addLogsTrack(this.engine.getProxy('TrackDecider::addLogsTrack'));
 
     // TODO(hjd): Move into plugin API.
     {
@@ -1634,6 +1743,8 @@
     this.addTrackGroupActions.push(
         Actions.setUtidToTrackSortKey({threadOrderingMetadata}));
 
+    this.applyDefaultCounterScale();
+
     return this.addTrackGroupActions;
   }
 
diff --git a/ui/src/controller/visualised_args_controller.ts b/ui/src/controller/visualised_args_controller.ts
index f4d48da..9ac6d3d 100644
--- a/ui/src/controller/visualised_args_controller.ts
+++ b/ui/src/controller/visualised_args_controller.ts
@@ -41,7 +41,7 @@
     super('init');
     this.argName = args.argName;
     this.engine = args.engine;
-    this.escapedArgName = this.argName.replace(/\./g, '_');
+    this.escapedArgName = this.argName.replace(/[^a-zA-Z]/g, '_');
     this.tableName = `__arg_visualisation_helper_${this.escapedArgName}_slice`;
     this.addedTrackIds = [];
   }
@@ -63,6 +63,7 @@
             slice.track_id,
             slice.ts,
             slice.dur,
+            slice.thread_dur,
             NULL as cat,
             args.display_value as name
           from slice
diff --git a/ui/src/frontend/analytics.ts b/ui/src/frontend/analytics.ts
index cdb9d12..e49ca11 100644
--- a/ui/src/frontend/analytics.ts
+++ b/ui/src/frontend/analytics.ts
@@ -26,9 +26,11 @@
   // Only initialize logging on the official site and on localhost (to catch
   // analytics bugs when testing locally).
   // Skip analytics is the fragment has "testing=1", this is used by UI tests.
+  // Skip analytics in embeddedMode since iFrames do not have the same access to
+  // local storage.
   if ((window.location.origin.startsWith('http://localhost:') ||
        window.location.origin.endsWith('.perfetto.dev')) &&
-      !globals.testing) {
+      !globals.testing && !globals.embeddedMode) {
     return new AnalyticsImpl();
   }
   return new NullAnalytics();
@@ -69,10 +71,10 @@
     // [1] https://developers.google.com/analytics/devguides/collection/gtagjs .
     gtagGlobals.dataLayer = gtagGlobals.dataLayer || [];
 
-    function gtagFunction(...args: any[]) {
+    function gtagFunction(..._: any[]) {
       // This needs to be a function and not a lambda. |arguments| behaves
       // slightly differently in a lambda and breaks GA.
-      gtagGlobals.dataLayer.push(args);
+      gtagGlobals.dataLayer.push(arguments);
     }
     gtagGlobals.gtag = gtagFunction;
     gtagGlobals.gtag('js', new Date());
diff --git a/ui/src/frontend/analyze_page.ts b/ui/src/frontend/analyze_page.ts
index 346aa26..bfb311d 100644
--- a/ui/src/frontend/analyze_page.ts
+++ b/ui/src/frontend/analyze_page.ts
@@ -19,6 +19,7 @@
 
 import {globals} from './globals';
 import {createPage} from './pages';
+import {QueryHistoryComponent, queryHistoryStorage} from './query_history';
 import {QueryTable} from './query_table';
 
 const INPUT_PLACEHOLDER = 'Enter query and press Cmd/Ctrl + Enter';
@@ -45,6 +46,7 @@
         query = query.substring(selectionStart, selectionEnd);
       }
       if (!query) return;
+      queryHistoryStorage.saveQuery(query);
       globals.dispatch(Actions.executeQuery({queryId: QUERY_ID, query}));
     }
 
@@ -164,6 +166,6 @@
         '.analyze-page',
         m(QueryInput),
         m(QueryTable, {queryId: QUERY_ID}),
-    );
+        m(QueryHistoryComponent));
   },
 });
diff --git a/ui/src/frontend/bottom_tab.ts b/ui/src/frontend/bottom_tab.ts
new file mode 100644
index 0000000..bb7b114
--- /dev/null
+++ b/ui/src/frontend/bottom_tab.ts
@@ -0,0 +1,172 @@
+// Copyright (C) 2022 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 * as m from 'mithril';
+import {v4 as uuidv4} from 'uuid';
+
+import {EngineProxy} from '../common/engine';
+import {Registry} from '../common/registry';
+
+import {Panel, PanelSize, PanelVNode} from './panel';
+
+export interface NewBottomTabArgs {
+  engine: EngineProxy;
+  tag?: string;
+  uuid: string;
+  config: {};
+}
+
+// Interface for allowing registration and creation of bottom tabs.
+// See comments on |TrackCreator| for more details.
+export interface BottomTabCreator {
+  readonly kind: string;
+
+  create(args: NewBottomTabArgs): BottomTab;
+}
+
+export const bottomTabRegistry = Registry.kindRegistry<BottomTabCreator>();
+
+// An interface representing a bottom tab displayed on the panel in the bottom
+// of the ui (e.g. "Current Selection").
+//
+// The implementations of this class are provided by different plugins, which
+// register the implementations with bottomTabRegistry, keyed by a unique name
+// for each type of BottomTab.
+//
+// Lifetime: the instances of this class are owned by BottomTabPanel and exist
+// for as long as a tab header is shown to the user in the bottom tab list (with
+// minor exceptions, like a small grace period between when the tab is related).
+//
+// BottomTab implementations should pass the unique identifier(s) for the
+// content displayed via the |Config| and fetch additional details via Engine
+// instead of relying on getting the data from the global storage. For example,
+// for tabs corresponding to details of the selected objects on a track, a new
+// BottomTab should be created for each new selection.
+export abstract class BottomTabBase<Config = {}> {
+  // Config for this details panel. Should be serializable.
+  protected readonly config: Config;
+  // Engine for running queries and fetching additional data.
+  protected readonly engine: EngineProxy;
+  // Optional tag, which is used to ensure that only one tab
+  // with the same tag can exist - adding a new tab with the same tag
+  // (e.g. 'current_selection') would close the previous one. This
+  // also can be used to close existing tab.
+  readonly tag?: string;
+  // Unique id for this details panel. Can be used to close previously opened
+  // panel.
+  readonly uuid: string;
+
+  constructor(args: NewBottomTabArgs) {
+    this.config = args.config as Config;
+    this.engine = args.engine;
+    this.tag = args.tag;
+    this.uuid = args.uuid;
+  }
+
+  // Entry point for customisation of the displayed title for this panel.
+  abstract getTitle(): string;
+
+  // Generate a mithril node for this component.
+  abstract createPanelVnode(): PanelVNode;
+}
+
+
+// BottomTabBase provides a more generic API allowing users to provide their
+// custom mithril component, which would allow them to listen to mithril
+// lifecycle events. Most cases, however, don't need them and BottomTab
+// provides a simplified API for the common case.
+export abstract class BottomTab<Config = {}> extends BottomTabBase<Config> {
+  constructor(args: NewBottomTabArgs) {
+    super(args);
+  }
+
+  // These methods are direct counterparts to renderCanvas and view with
+  // slightly changes names to prevent cases when `BottomTab` will
+  // be accidentally used a mithril component.
+  abstract renderTabCanvas(ctx: CanvasRenderingContext2D, size: PanelSize):
+      void;
+  abstract viewTab(): void|m.Children;
+
+  createPanelVnode(): m.Vnode<any, any> {
+    return m(BottomTabAdapter, {key: this.uuid, panel: this});
+  }
+}
+
+interface BottomTabAdapterAttrs {
+  panel: BottomTab;
+}
+
+class BottomTabAdapter extends Panel<BottomTabAdapterAttrs> {
+  renderCanvas(
+      ctx: CanvasRenderingContext2D, size: PanelSize,
+      vnode: PanelVNode<BottomTabAdapterAttrs>): void {
+    vnode.attrs.panel.renderTabCanvas(ctx, size);
+  }
+
+  view(vnode: m.CVnode<BottomTabAdapterAttrs>): void|m.Children {
+    return vnode.attrs.panel.viewTab();
+  }
+}
+
+export type AddTabArgs = {
+  kind: string,
+  config: {},
+  tag?: string,
+};
+
+export type AddTabResult = {
+  uuid: string;
+}
+
+export class BottomTabList {
+  tabs: BottomTabBase[] = [];
+  private engine: EngineProxy;
+
+  constructor(engine: EngineProxy) {
+    this.engine = engine;
+  }
+
+  // Add and create a new panel with given kind and config, replacing an
+  // existing panel with the same tag if needed. Returns the uuid of a newly
+  // created panel (which can be used in the future to close it).
+  addTab(args: AddTabArgs): AddTabResult {
+    const uuid = uuidv4();
+    const newPanel = bottomTabRegistry.get(args.kind).create({
+      engine: this.engine,
+      uuid,
+      config: args.config,
+      tag: args.tag,
+    });
+
+    const index =
+        args.tag ? this.tabs.findIndex((tab) => tab.tag === args.tag) : -1;
+    if (index === -1) {
+      this.tabs.push(newPanel);
+    } else {
+      this.tabs[index] = newPanel;
+    }
+
+    return {
+      uuid,
+    };
+  }
+
+  closeTabByTag(tag: string) {
+    this.tabs = this.tabs.filter((panel) => panel.tag !== tag);
+  }
+
+  closeTabById(uuid: string) {
+    this.tabs = this.tabs.filter((panel) => panel.uuid !== uuid);
+  }
+}
diff --git a/ui/src/frontend/chrome_slice_panel.ts b/ui/src/frontend/chrome_slice_panel.ts
index 5273098..f5a45e8 100644
--- a/ui/src/frontend/chrome_slice_panel.ts
+++ b/ui/src/frontend/chrome_slice_panel.ts
@@ -60,16 +60,9 @@
   return `${p1}.${p2}`;
 }
 
-// During building the table, sometimes we want to add an extra cell to the
-// table. It might be either an index of array element (represented as number),
-// a special indentation cell to ensure minimum column width when indenting
-// an object ('whitespace' literal) or just be absent ('none' literal).
-type ExtraCell = number|'whitespace'|'none';
-
 interface Row {
   // How many columns (empty or with an index) precede a key
   indentLevel: number;
-  extraCell: ExtraCell;
   // Optional tooltip to be displayed on the key. Used to display the full key,
   // which has to be reconstructed from the information that might not even be
   // visible on the screen otherwise.
@@ -78,10 +71,9 @@
 }
 
 class TableBuilder {
-  stack: ExtraCell[] = [];
-
   // Row data generated by builder
   rows: Row[] = [];
+  indentLevel = 0;
 
   // Maximum indent level of a key, used to determine total number of columns
   maxIndent = 0;
@@ -90,7 +82,6 @@
   add(key: string, value: Arg) {
     this.rows.push({
       indentLevel: 0,
-      extraCell: 'none',
       contents: {kind: 'TableRow', key, value, isArg: false},
     });
   }
@@ -100,40 +91,30 @@
     this.addTreeInternal(tree, '', '');
   }
 
-  // Return indent level and index for a fresh row
-  private prepareRow(): [number, ExtraCell] {
-    const level = this.stack.length;
-    let index: ExtraCell = 'none';
-    if (level > 0) {
-      index = this.stack[level - 1];
-      if (index !== -1) {
-        this.stack[level - 1] = 'none';
-      }
-    }
-    this.maxIndent = Math.max(this.maxIndent, level);
-    return [level, index];
-  }
-
   private addTreeInternal(
       record: ArgsTree, prefix: string, completePrefix: string) {
     if (isArgTreeArray(record)) {
-      // Add the current prefix as a separate row
-      const row = this.prepareRow();
-      this.rows.push({
-        indentLevel: row[0],
-        extraCell: row[1],
-        contents: {kind: 'TableHeader', header: prefix},
-        tooltip: completePrefix,
-      });
+      if (record.length === 1) {
+        this.addTreeInternal(record[0], `${prefix}[0]`, `${completePrefix}[0]`);
+        return;
+      }
 
+      // Add the current prefix as a separate row
+      if (prefix.length > 0) {
+        this.rows.push({
+          indentLevel: this.indentLevel,
+          contents: {kind: 'TableHeader', header: prefix},
+          tooltip: completePrefix,
+        });
+      }
+
+      this.indentLevel++;
       for (let i = 0; i < record.length; i++) {
-        // Push the current array index to the stack.
-        this.stack.push(i);
         // Prefix is empty for array elements because we don't want to repeat
         // the common prefix
-        this.addTreeInternal(record[i], '', `${completePrefix}[${i}]`);
-        this.stack.pop();
+        this.addTreeInternal(record[i], `[${i}]`, `${completePrefix}[${i}]`);
       }
+      this.indentLevel--;
     } else if (isArgTreeMap(record)) {
       const entries = Object.entries(record);
       if (entries.length === 1) {
@@ -150,28 +131,26 @@
             appendPrefix(completePrefix, key));
       } else {
         if (prefix.length > 0) {
-          const row = this.prepareRow();
+          const row = this.indentLevel;
           this.rows.push({
-            indentLevel: row[0],
-            extraCell: row[1],
+            indentLevel: row,
             contents: {kind: 'TableHeader', header: prefix},
             tooltip: completePrefix,
           });
-          this.stack.push('whitespace');
+          this.indentLevel++;
         }
         for (const [key, value] of entries) {
           this.addTreeInternal(value, key, appendPrefix(completePrefix, key));
         }
         if (prefix.length > 0) {
-          this.stack.pop();
+          this.indentLevel--;
         }
       }
     } else {
       // Leaf value in the tree: add to the table
-      const row = this.prepareRow();
+      const row = this.indentLevel;
       this.rows.push({
-        indentLevel: row[0],
-        extraCell: row[1],
+        indentLevel: row,
         contents: {
           kind: 'TableRow',
           key: prefix,
@@ -384,41 +363,31 @@
 
   renderTable(builder: TableBuilder, additionalClasses: string = ''): m.Vnode {
     const rows: m.Vnode[] = [];
-    const keyColumnCount = builder.maxIndent + 1;
     for (const row of builder.rows) {
       const renderedRow: m.Vnode[] = [];
-      let indent = row.indentLevel;
-      if (row.extraCell !== 'none') {
-        indent--;
-      }
-
-      if (indent > 0) {
-        renderedRow.push(m('td.no-highlight', {colspan: indent}));
-      }
-
-      if (row.extraCell === 'whitespace') {
-        renderedRow.push(m('td.no-highlight.padding', {class: 'array-index'}));
-      } else if (row.extraCell !== 'none') {
-        renderedRow.push(m('td', {class: 'array-index'}, `[${row.extraCell}]`));
-      }
-
+      const paddingLeft = `${row.indentLevel * 20}px`;
       if (isTableHeader(row.contents)) {
-        renderedRow.push(m(
-            'th',
-            {colspan: keyColumnCount + 1 - row.indentLevel, title: row.tooltip},
-            row.contents.header));
+        renderedRow.push(
+            m('th',
+              {
+                colspan: 2,
+                title: row.tooltip,
+                style: {'padding-left': paddingLeft},
+              },
+              row.contents.header));
       } else {
         const contents: any[] = [row.contents.key];
         if (row.contents.isArg) {
-          contents.push(m(PopupMenuButton, {
-            icon: 'arrow_drop_down',
-            items: this.getArgumentContextMenuItems(row.contents),
-          }));
+          contents.push(
+              m('span.context-wrapper', m.trust('&nbsp;'), m(PopupMenuButton, {
+                  icon: 'arrow_drop_down',
+                  items: this.getArgumentContextMenuItems(row.contents),
+                })));
         }
 
         renderedRow.push(
             m('th',
-              {colspan: keyColumnCount - row.indentLevel, title: row.tooltip},
+              {title: row.tooltip, style: {'padding-left': paddingLeft}},
               contents));
         const value = row.contents.value;
         if (typeof value === 'string') {
diff --git a/ui/src/frontend/counter_panel.ts b/ui/src/frontend/counter_panel.ts
index eb5d991..d52d8dd 100644
--- a/ui/src/frontend/counter_panel.ts
+++ b/ui/src/frontend/counter_panel.ts
@@ -32,7 +32,7 @@
           m('.details-panel-heading', m('h2', `Counter Details`)),
           m(
               '.details-table',
-              [m('table.half-width',
+              [m('table',
                  [
                    m('tr', m('th', `Name`), m('td', `${counterInfo.name}`)),
                    m('tr',
diff --git a/ui/src/frontend/css_constants.ts b/ui/src/frontend/css_constants.ts
index 8050b3d..b0231ff 100644
--- a/ui/src/frontend/css_constants.ts
+++ b/ui/src/frontend/css_constants.ts
@@ -23,6 +23,7 @@
 export let SELECTION_FILL_COLOR = '#8398e64d';
 export let OVERVIEW_TIMELINE_NON_VISIBLE_COLOR = '#c8c8c8cc';
 export let DEFAULT_DETAILS_CONTENT_HEIGHT = 280;
+export const SELECTED_LOG_ROWS_COLOR = '#D2EFE0';
 
 export function initCssConstants() {
   TRACK_SHELL_WIDTH = getCssNum('--track-shell-width') || TRACK_SHELL_WIDTH;
diff --git a/ui/src/frontend/details_panel.ts b/ui/src/frontend/details_panel.ts
index 7319297..9b7e617 100644
--- a/ui/src/frontend/details_panel.ts
+++ b/ui/src/frontend/details_panel.ts
@@ -13,11 +13,13 @@
 // limitations under the License.
 
 import * as m from 'mithril';
-import {QueryResponse} from 'src/common/queries';
 
 import {Actions} from '../common/actions';
 import {isEmptyData} from '../common/aggregation_data';
 import {LogExists, LogExistsKey} from '../common/logs';
+import {QueryResponse} from '../common/queries';
+import {addSelectionChangeObserver} from '../common/selection_observer';
+import {Selection} from '../common/state';
 
 import {AggregationPanel} from './aggregation_panel';
 import {ChromeSliceDetailsPanel} from './chrome_slice_panel';
@@ -32,7 +34,7 @@
 } from './flow_events_panel';
 import {globals} from './globals';
 import {LogPanel} from './logs_panel';
-import {NotesEditorPanel} from './notes_panel';
+import {NotesEditorTab} from './notes_panel';
 import {AnyAttrsVnode, PanelContainer} from './panel_container';
 import {PivotTableRedux} from './pivot_table_redux';
 import {QueryTable} from './query_table';
@@ -199,6 +201,41 @@
   return null;
 }
 
+function handleSelectionChange(newSelection?: Selection, _?: Selection): void {
+  const currentSelectionTag = 'current_selection';
+  const bottomTabList = globals.bottomTabList;
+  if (!bottomTabList) return;
+  if (newSelection === undefined) {
+    bottomTabList.closeTabByTag(currentSelectionTag);
+    return;
+  }
+  switch (newSelection.kind) {
+    case 'NOTE':
+      bottomTabList.addTab({
+        kind: NotesEditorTab.kind,
+        tag: currentSelectionTag,
+        config: {
+          id: newSelection.id,
+        },
+      });
+      break;
+    case 'AREA':
+      if (newSelection.noteId !== undefined) {
+        bottomTabList.addTab({
+          kind: NotesEditorTab.kind,
+          tag: currentSelectionTag,
+          config: {
+            id: newSelection.noteId,
+          },
+        });
+      }
+      break;
+    default:
+      bottomTabList.closeTabByTag(currentSelectionTag);
+  }
+}
+addSelectionChangeObserver(handleSelectionChange);
+
 export class DetailsPanel implements m.ClassComponent {
   private detailsHeight = getDetailsHeight();
 
@@ -210,30 +247,24 @@
     }
 
     const detailsPanels: DetailsPanel[] = [];
+
+    if (globals.bottomTabList) {
+      for (const tab of globals.bottomTabList.tabs) {
+        detailsPanels.push({
+          key: tab.uuid,
+          name: tab.getTitle(),
+          vnode: tab.createPanelVnode(),
+        });
+      }
+    }
+
     const curSelection = globals.state.currentSelection;
     if (curSelection) {
       switch (curSelection.kind) {
         case 'NOTE':
-          detailsPanels.push({
-            key: 'current_selection',
-            name: 'Current Selection',
-            vnode: m(NotesEditorPanel, {
-              key: 'notes',
-              id: curSelection.id,
-            }),
-          });
+          // Handled in handleSelectionChange.
           break;
         case 'AREA':
-          if (curSelection.noteId !== undefined) {
-            detailsPanels.push({
-              key: 'current_selection',
-              name: 'Current Selection',
-              vnode: m(NotesEditorPanel, {
-                key: 'area_notes',
-                id: curSelection.noteId,
-              }),
-            });
-          }
           if (globals.flamegraphDetails.isInAreaSelection) {
             detailsPanels.push({
               key: 'flamegraph_selection',
diff --git a/ui/src/frontend/error_dialog.ts b/ui/src/frontend/error_dialog.ts
index d8796c5..7126900 100644
--- a/ui/src/frontend/error_dialog.ts
+++ b/ui/src/frontend/error_dialog.ts
@@ -16,7 +16,7 @@
 
 import {assertExists} from '../base/logging';
 import {RECORDING_V2_FLAG} from '../common/feature_flags';
-import {EXTENSION_URL} from '../common/recordingV2/chrome_utils';
+import {EXTENSION_URL} from '../common/recordingV2/recording_utils';
 import {TraceUrlSource} from '../common/state';
 import {saveTrace} from '../common/upload_utils';
 
diff --git a/ui/src/frontend/flamegraph_panel.ts b/ui/src/frontend/flamegraph_panel.ts
index cadee12..278ebda 100644
--- a/ui/src/frontend/flamegraph_panel.ts
+++ b/ui/src/frontend/flamegraph_panel.ts
@@ -79,7 +79,7 @@
         flamegraphDetails.pids !== undefined &&
         flamegraphDetails.upids !== undefined) {
       this.profileType = profileType(flamegraphDetails.type);
-      this.ts = flamegraphDetails.durNs;
+      this.ts = flamegraphDetails.startNs + flamegraphDetails.durNs;
       this.pids = flamegraphDetails.pids;
       if (flamegraphDetails.flamegraph) {
         this.flamegraph.updateDataIfChanged(
@@ -135,7 +135,8 @@
                     // Required to stop hot-key handling:
                     onkeydown: (e: Event) => e.stopPropagation(),
                   }),
-                  this.profileType === ProfileType.NATIVE_HEAP_PROFILE ?
+                  this.profileType === ProfileType.NATIVE_HEAP_PROFILE ||
+                          this.profileType === ProfileType.JAVA_HEAP_SAMPLES ?
                       m('button.download',
                         {
                           onclick: () => {
@@ -189,8 +190,8 @@
         return 'Heap profile:';
       case ProfileType.NATIVE_HEAP_PROFILE:
         return 'Native heap profile:';
-      case ProfileType.JAVA_HEAP_PROFILE:
-        return 'Java heap profile:';
+      case ProfileType.JAVA_HEAP_SAMPLES:
+        return 'Java heap samples:';
       case ProfileType.JAVA_HEAP_GRAPH:
         return 'Java heap graph:';
       case ProfileType.PERF_SAMPLE:
@@ -214,7 +215,7 @@
         }
       case ProfileType.HEAP_PROFILE:
       case ProfileType.NATIVE_HEAP_PROFILE:
-      case ProfileType.JAVA_HEAP_PROFILE:
+      case ProfileType.JAVA_HEAP_SAMPLES:
       case ProfileType.PERF_SAMPLE:
         return RENDER_SELF_AND_TOTAL;
       default:
@@ -298,7 +299,8 @@
         return [
           this.buildButtonComponent(
               SPACE_MEMORY_ALLOCATED_NOT_FREED_KEY, 'Unreleased size'),
-          this.buildButtonComponent(OBJECTS_ALLOCATED_NOT_FREED_KEY, 'Count'),
+          this.buildButtonComponent(
+              OBJECTS_ALLOCATED_NOT_FREED_KEY, 'Unreleased count'),
           this.buildButtonComponent(
               ALLOC_SPACE_MEMORY_ALLOCATED_KEY, 'Total size'),
           this.buildButtonComponent(OBJECTS_ALLOCATED_KEY, 'Total count'),
@@ -314,7 +316,7 @@
           this.buildButtonComponent(
               OBJECTS_ALLOCATED_KEY, 'Total malloc count'),
         ];
-      case ProfileType.JAVA_HEAP_PROFILE:
+      case ProfileType.JAVA_HEAP_SAMPLES:
         return [
           this.buildButtonComponent(
               ALLOC_SPACE_MEMORY_ALLOCATED_KEY, 'Total allocation size'),
diff --git a/ui/src/frontend/flow_events_panel.ts b/ui/src/frontend/flow_events_panel.ts
index 529b7f0..5ea52fc 100644
--- a/ui/src/frontend/flow_events_panel.ts
+++ b/ui/src/frontend/flow_events_panel.ts
@@ -199,7 +199,7 @@
 
     return m('.details-panel', [
       m('.details-panel-heading', m('h2', `Selected flow events`)),
-      m('.flow-events-table', m('table.half-width', rows)),
+      m('.flow-events-table', m('table', rows)),
     ]);
   }
 
diff --git a/ui/src/frontend/frontend_local_state.ts b/ui/src/frontend/frontend_local_state.ts
index bcf0937..d406835 100644
--- a/ui/src/frontend/frontend_local_state.ts
+++ b/ui/src/frontend/frontend_local_state.ts
@@ -18,14 +18,13 @@
 import {
   Area,
   FrontendLocalState as FrontendState,
-  OmniboxState,
   Timestamped,
   VisibleState,
 } from '../common/state';
 import {TimeSpan} from '../common/time';
 
 import {globals} from './globals';
-import {debounce, ratelimit} from './rate_limiters';
+import {ratelimit} from './rate_limiters';
 import {TimeScale} from './time_scale';
 
 interface Range {
@@ -33,7 +32,7 @@
   end?: number;
 }
 
-function chooseLatest<T extends Timestamped<{}>>(current: T, next: T): T {
+function chooseLatest<T extends Timestamped>(current: T, next: T): T {
   if (next !== current && next.lastUpdate > current.lastUpdate) {
     // |next| is from state. Callers may mutate the return value of
     // this function so we need to clone |next| to prevent bad mutations
@@ -82,12 +81,6 @@
 
   private scrollBarWidth?: number;
 
-  private _omniboxState: OmniboxState = {
-    lastUpdate: 0,
-    omnibox: '',
-    mode: 'SEARCH',
-  };
-
   private _visibleState: VisibleState = {
     lastUpdate: 0,
     startSec: 0,
@@ -147,7 +140,6 @@
     // that is the newer state. All of these complications should vanish when
     // we remove this class.
     const previousVisibleState = this._visibleState;
-    this._omniboxState = chooseLatest(this._omniboxState, state.omniboxState);
     this._visibleState = chooseLatest(this._visibleState, state.visibleState);
     const visibleStateWasUpdated = previousVisibleState !== this._visibleState;
     if (visibleStateWasUpdated) {
@@ -174,21 +166,6 @@
     return this._selectedArea;
   }
 
-  private setOmniboxDebounced = debounce(() => {
-    globals.dispatch(Actions.setOmnibox({...this._omniboxState}));
-  }, 20);
-
-  setOmnibox(value: string, mode: 'SEARCH'|'COMMAND') {
-    this._omniboxState.omnibox = value;
-    this._omniboxState.mode = mode;
-    this._omniboxState.lastUpdate = Date.now() / 1000;
-    this.setOmniboxDebounced();
-  }
-
-  get omnibox(): string {
-    return this._omniboxState.omnibox;
-  }
-
   private ratelimitedUpdateVisible = ratelimit(() => {
     globals.dispatch(Actions.setVisibleTraceTime(this._visibleState));
   }, 50);
diff --git a/ui/src/frontend/globals.ts b/ui/src/frontend/globals.ts
index 7bb5ad2..5b8728a 100644
--- a/ui/src/frontend/globals.ts
+++ b/ui/src/frontend/globals.ts
@@ -28,6 +28,7 @@
 import {fromNs, toNs} from '../common/time';
 
 import {Analytics, initAnalytics} from './analytics';
+import {BottomTabList} from './bottom_tab';
 import {FrontendLocalState} from './frontend_local_state';
 import {RafScheduler} from './raf_scheduler';
 import {Router} from './router';
@@ -184,6 +185,8 @@
 class Globals {
   readonly root = getRoot();
 
+  bottomTabList?: BottomTabList = undefined;
+
   private _testing = false;
   private _dispatch?: Dispatch = undefined;
   private _state?: State = undefined;
@@ -531,10 +534,7 @@
   }
 
   getCurrentEngine(): EngineConfig|undefined {
-    if (!this.state.currentEngineId) {
-      return undefined;
-    }
-    return this.state.engines[this.state.currentEngineId];
+    return this.state.engine;
   }
 
   makeSelection(action: DeferredAction<{}>, tabToOpen = 'current_selection') {
diff --git a/ui/src/frontend/gridline_helper.ts b/ui/src/frontend/gridline_helper.ts
index 6c0b74d..1c9dbfe 100644
--- a/ui/src/frontend/gridline_helper.ts
+++ b/ui/src/frontend/gridline_helper.ts
@@ -12,94 +12,172 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import {TimeSpan} from '../common/time';
-
+import {assertTrue} from '../base/logging';
+import {roundDownNearest} from '../base/math_utils';
 import {TRACK_BORDER_COLOR, TRACK_SHELL_WIDTH} from './css_constants';
+import {globals} from './globals';
 import {TimeScale} from './time_scale';
 
-export const DESIRED_PX_PER_STEP = 80;
-
-// Returns the step size of a grid line in seconds.
-// The returned step size has two properties:
-// (1) It is 1, 2, or 5, multiplied by some integer power of 10.
-// (2) The number steps in |range| produced by |stepSize| is as close as
-//     possible to |desiredSteps|.
-export function getGridStepSize(range: number, desiredSteps: number): number {
+// Returns the optimal step size (in seconds) and tick pattern of ticks within
+// the step. The returned step size has two properties: (1) It is 1, 2, or 5,
+// multiplied by some integer power of 10. (2) It is maximised given the
+// constraint: |range| / stepSize <= |maxNumberOfSteps|.
+export function getStepSize(
+    range: number, maxNumberOfSteps: number): [number, string] {
   // First, get the largest possible power of 10 that is smaller than the
-  // desired step size, and set it to the current step size.
+  // desired step size, and use it as our initial step size.
   // For example, if the range is 2345ms and the desired steps is 10, then the
-  // desired step size is 234.5 and the step size will be set to 100.
-  const desiredStepSize = range / desiredSteps;
-  const zeros = Math.floor(Math.log10(desiredStepSize));
+  // minimum step size is 234.5ms so the step size will initialise to 100.
+  const minStepSize = range / maxNumberOfSteps;
+  const zeros = Math.floor(Math.log10(minStepSize));
   const initialStepSize = Math.pow(10, zeros);
 
-  // This function first calculates how many steps within the range a certain
-  // stepSize will produce, and returns the difference between that and
-  // desiredSteps.
-  const distToDesired = (evaluatedStepSize: number) =>
-      Math.abs(range / evaluatedStepSize - desiredSteps);
-
   // We know that |initialStepSize| is a power of 10, and
   // initialStepSize <= desiredStepSize <= 10 * initialStepSize. There are four
   // possible candidates for final step size: 1, 2, 5 or 10 * initialStepSize.
-  // We pick the candidate that minimizes distToDesired(stepSize).
-  const stepSizeMultipliers = [2, 5, 10];
+  // For our example above, this would result in a step size of 500ms, as both
+  // 100ms and 200ms are smaller than the minimum step size of 234.5ms.
+  // We pick the candidate that minimizes the step size without letting the
+  // number of steps exceed |maxNumberOfSteps|. The factor we pick to also
+  // determines the pattern of ticks. This pattern is represented using a string
+  // where:
+  //  | = Major tick
+  //  : = Medium tick
+  //  . = Minor tick
+  const stepSizeMultipliers: [number, string][] =
+      [[1, '|....:....'], [2, '|.:.'], [5, '|....'], [10, '|....:....']];
 
-  let minimalDistance = distToDesired(initialStepSize);
-  let minimizingStepSize = initialStepSize;
-
-  for (const multiplier of stepSizeMultipliers) {
+  for (const [multiplier, pattern] of stepSizeMultipliers) {
     const newStepSize = multiplier * initialStepSize;
-    const newDistance = distToDesired(newStepSize);
-    if (newDistance < minimalDistance) {
-      minimalDistance = newDistance;
-      minimizingStepSize = newStepSize;
+    const numberOfNewSteps = range / newStepSize;
+    if (numberOfNewSteps <= maxNumberOfSteps) {
+      return [newStepSize, pattern];
     }
   }
-  return minimizingStepSize;
+
+  throw new Error('Something has gone horribly wrong with maths');
 }
 
-// Generator that returns that (given a width im px, span, and scale) returns
-// pairs of [xInPx, timestampInS] pairs describing where gridlines should be
-// drawn.
-export function gridlines(width: number, span: TimeSpan, timescale: TimeScale):
-    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 <= actualSteps; i++) {
-    let xPos = TRACK_SHELL_WIDTH;
-    const timestamp = start + i * step;
-    xPos += Math.floor(timescale.timeToPx(timestamp));
-    if (xPos < TRACK_SHELL_WIDTH) continue;
-    if (xPos > width) break;
-    if (Math.abs(timestamp - previousTimestamp) > Number.EPSILON) {
-      previousTimestamp = timestamp;
-      lines.push([xPos, timestamp]);
+function tickPatternToArray(pattern: string): TickType[] {
+  const array = Array.from(pattern);
+  return array.map((char) => {
+    switch (char) {
+      case '|':
+        return TickType.MAJOR;
+      case ':':
+        return TickType.MEDIUM;
+      case '.':
+        return TickType.MINOR;
+      default:
+        // This is almost certainly a developer/fat-finger error
+        throw Error(`Invalid char "${char}" in pattern "${pattern}"`);
+    }
+  });
+}
+
+// Assuming a number only has one non-zero decimal digit, find the number of
+// decimal places required to accurately print that number. I.e. the parameter
+// we should pass to number.toFixed(x). To account for floating point
+// innaccuracies when representing numbers in base-10, we only take the first
+// nonzero fractional digit into account. E.g.
+//  1.0 -> 0
+//  0.5 -> 1
+//  0.009 -> 3
+//  0.00007 -> 5
+//  30000 -> 0
+//  0.30000000000000004 -> 1
+export function guessDecimalPlaces(val: number): number {
+  const neglog10 = -Math.floor(Math.log10(val));
+  const clamped = Math.max(0, neglog10);
+  return clamped;
+}
+
+export enum TickType {
+  MAJOR,
+  MEDIUM,
+  MINOR
+}
+
+export interface Tick {
+  type: TickType;
+  time: number;
+  position: number;
+}
+
+const MIN_PX_PER_STEP = 80;
+
+// An iterable which generates a series of ticks for a given timescale.
+export class TickGenerator implements Iterable<Tick> {
+  private _tickPattern: TickType[];
+  private _patternSize: number;
+
+  constructor(private scale: TimeScale, {minLabelPx = MIN_PX_PER_STEP} = {}) {
+    assertTrue(minLabelPx > 0, 'minLabelPx cannot be lte 0');
+    assertTrue(scale.widthPx > 0, 'widthPx cannot be lte 0');
+    assertTrue(
+        scale.timeSpan.duration > 0, 'timeSpan.duration cannot be lte 0');
+
+    const desiredSteps = scale.widthPx / minLabelPx;
+    const [size, pattern] = getStepSize(scale.timeSpan.duration, desiredSteps);
+    this._patternSize = size;
+    this._tickPattern = tickPatternToArray(pattern);
+  }
+
+  // Returns an iterable, so this object can be iterated over directly using the
+  // `for x of y` notation. The use of a generator here is just to make things
+  // more elegant than creating an array of ticks and building an iterator for
+  // it.
+  * [Symbol.iterator](): Generator<Tick> {
+    const span = this.scale.timeSpan;
+    const stepSize = this._patternSize / this._tickPattern.length;
+    const start = roundDownNearest(span.start, this._patternSize);
+    const timeAtStep = (i: number) => start + (i * stepSize);
+
+    // Iterating using steps instead of
+    // for (let s = start; s < span.end; s += stepSize) because if start is much
+    // larger than stepSize we can enter an infinite loop due to floating
+    // point precision errors.
+    for (let i = 0; timeAtStep(i) < span.end; i++) {
+      const time = timeAtStep(i);
+      if (time >= span.start) {
+        const position = Math.floor(this.scale.timeToPx(time));
+        const type = this._tickPattern[i % this._tickPattern.length];
+        yield {type, time, position};
+      }
     }
   }
-  return lines;
+
+  // The number of decimal places labels should be printed with, assuming labels
+  // are only printed on major ticks.
+  get digits(): number {
+    return guessDecimalPlaces(this._patternSize);
+  }
+}
+
+// Gets the timescale associated with the current visible window.
+export function timeScaleForVisibleWindow(
+    startPx: number, endPx: number): TimeScale {
+  const span = globals.frontendLocalState.visibleWindowTime;
+  const spanRelative = span.add(-globals.state.traceTime.startSec);
+  return new TimeScale(spanRelative, [startPx, endPx]);
 }
 
 export function drawGridLines(
     ctx: CanvasRenderingContext2D,
-    x: TimeScale,
-    timeSpan: TimeSpan,
     width: number,
     height: number): void {
   ctx.strokeStyle = TRACK_BORDER_COLOR;
   ctx.lineWidth = 1;
 
-  for (const xAndTime of gridlines(width, timeSpan, x)) {
-    ctx.beginPath();
-    ctx.moveTo(xAndTime[0] + 0.5, 0);
-    ctx.lineTo(xAndTime[0] + 0.5, height);
-    ctx.stroke();
+  const timeScale = timeScaleForVisibleWindow(TRACK_SHELL_WIDTH, width);
+  if (timeScale.timeSpan.duration > 0 && timeScale.widthPx > 0) {
+    for (const {type, position} of new TickGenerator(timeScale)) {
+      if (type === TickType.MAJOR) {
+        ctx.beginPath();
+        ctx.moveTo(position + 0.5, 0);
+        ctx.lineTo(position + 0.5, height);
+        ctx.stroke();
+      }
+    }
   }
 }
diff --git a/ui/src/frontend/gridline_helper_unittest.ts b/ui/src/frontend/gridline_helper_unittest.ts
index 98724d5..3b6dcac 100644
--- a/ui/src/frontend/gridline_helper_unittest.ts
+++ b/ui/src/frontend/gridline_helper_unittest.ts
@@ -12,36 +12,303 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import {getGridStepSize} from './gridline_helper';
+import {TimeSpan} from '../common/time';
+
+import {getStepSize, Tick, TickGenerator, TickType} from './gridline_helper';
+import {TimeScale} from './time_scale';
+
+const pattern1 = '|....:....';
+const pattern2 = '|.:.';
+const pattern5 = '|....';
+const timeScale = new TimeScale(new TimeSpan(0, 1), [1, 2]);
 
 test('gridline helper to have sensible step sizes', () => {
-  expect(getGridStepSize(10, 14)).toEqual(1);
-  expect(getGridStepSize(30, 14)).toEqual(2);
-  expect(getGridStepSize(60, 14)).toEqual(5);
-  expect(getGridStepSize(100, 14)).toEqual(10);
+  expect(getStepSize(10, 14)).toEqual([1, pattern1]);
+  expect(getStepSize(30, 14)).toEqual([5, pattern5]);
+  expect(getStepSize(60, 14)).toEqual([5, pattern5]);
+  expect(getStepSize(100, 14)).toEqual([10, pattern1]);
 
-  expect(getGridStepSize(10, 21)).toEqual(0.5);
-  expect(getGridStepSize(30, 21)).toEqual(2);
-  expect(getGridStepSize(60, 21)).toEqual(2);
-  expect(getGridStepSize(100, 21)).toEqual(5);
+  expect(getStepSize(10, 21)).toEqual([0.5, pattern5]);
+  expect(getStepSize(30, 21)).toEqual([2, pattern2]);
+  expect(getStepSize(60, 21)).toEqual([5, pattern5]);
+  expect(getStepSize(100, 21)).toEqual([5, pattern5]);
 
-  expect(getGridStepSize(10, 3)).toEqual(5);
-  expect(getGridStepSize(30, 3)).toEqual(10);
-  expect(getGridStepSize(60, 3)).toEqual(20);
-  expect(getGridStepSize(100, 3)).toEqual(50);
+  expect(getStepSize(10, 3)).toEqual([5, pattern5]);
+  expect(getStepSize(30, 3)).toEqual([10, pattern1]);
+  expect(getStepSize(60, 3)).toEqual([20, pattern2]);
+  expect(getStepSize(100, 3)).toEqual([50, pattern5]);
 
-  expect(getGridStepSize(800, 4)).toEqual(200);
+  expect(getStepSize(800, 4)).toEqual([200, pattern2]);
 });
 
 test('gridline helper to scale to very small and very large values', () => {
-  expect(getGridStepSize(.01, 14)).toEqual(.001);
-  expect(getGridStepSize(10000, 14)).toEqual(1000);
+  expect(getStepSize(.01, 14)).toEqual([.001, pattern1]);
+  expect(getStepSize(10000, 14)).toEqual([1000, pattern1]);
 });
 
 test('gridline helper to always return a reasonable number of steps', () => {
   for (let i = 1; i <= 1000; i++) {
-    const stepSize = getGridStepSize(i, 14);
-    expect(Math.round(i / stepSize)).toBeGreaterThanOrEqual(7);
-    expect(Math.round(i / stepSize)).toBeLessThanOrEqual(21);
+    const [stepSize, _] = getStepSize(i, 14);
+    expect(Math.round(i / stepSize)).toBeGreaterThanOrEqual(6);
+    expect(Math.round(i / stepSize)).toBeLessThanOrEqual(14);
   }
 });
+
+describe('TickGenerator with range 0.0-1.0 and room for 2 labels', () => {
+  let tickGen: TickGenerator|undefined = undefined;
+  beforeAll(() => {
+    const timeSpan = new TimeSpan(0.0, 1.0);
+    const timeScale = new TimeScale(timeSpan, [0, 200]);
+    tickGen = new TickGenerator(timeScale, {minLabelPx: 100});
+  });
+  it('should produce major ticks at 0.5s and minor ticks at 0.1s starting at 0',
+     () => {
+       const expected = [
+         {type: TickType.MAJOR, time: 0.0},
+         {type: TickType.MINOR, time: 0.1},
+         {type: TickType.MINOR, time: 0.2},
+         {type: TickType.MINOR, time: 0.3},
+         {type: TickType.MINOR, time: 0.4},
+         {type: TickType.MAJOR, time: 0.5},
+         {type: TickType.MINOR, time: 0.6},
+         {type: TickType.MINOR, time: 0.7},
+         {type: TickType.MINOR, time: 0.8},
+         {type: TickType.MINOR, time: 0.9},
+       ];
+       const actual = Array.from(tickGen!);
+       expectTicksEqual(actual, expected);
+     });
+  it('should tell us to use 1 decimal place for labels', () => {
+    expect(tickGen!.digits).toEqual(1);
+  });
+});
+
+describe('TickGenerator with range 0.3-1.3 and room for 2 labels', () => {
+  let tickGen: TickGenerator|undefined = undefined;
+  beforeAll(() => {
+    const timeSpan = new TimeSpan(0.3, 1.3);
+    const timeScale = new TimeScale(timeSpan, [0, 200]);
+    tickGen = new TickGenerator(timeScale, {minLabelPx: 100});
+  });
+  it('should produce major ticks at 0.5s and minor ticks at 0.1s starting at 0',
+     () => {
+       const expected = [
+         {type: TickType.MINOR, time: 0.3},
+         {type: TickType.MINOR, time: 0.4},
+         {type: TickType.MAJOR, time: 0.5},
+         {type: TickType.MINOR, time: 0.6},
+         {type: TickType.MINOR, time: 0.7},
+         {type: TickType.MINOR, time: 0.8},
+         {type: TickType.MINOR, time: 0.9},
+         {type: TickType.MAJOR, time: 1.0},
+         {type: TickType.MINOR, time: 1.1},
+         {type: TickType.MINOR, time: 1.2},
+       ];
+       const actual = Array.from(tickGen!);
+       expectTicksEqual(actual, expected);
+     });
+  it('should tell us to use 1 decimal place for labels', () => {
+    expect(tickGen!.digits).toEqual(1);
+  });
+});
+
+describe('TickGenerator with range 0.0-0.2 and room for 1 label', () => {
+  let tickGen: TickGenerator|undefined = undefined;
+  beforeAll(() => {
+    const timeSpan = new TimeSpan(0.0, 0.2);
+    const timeScale = new TimeScale(timeSpan, [0, 100]);
+    tickGen = new TickGenerator(timeScale, {minLabelPx: 100});
+  });
+  it('should produce major ticks at 0.2s and minor ticks at 0.1s starting at 0',
+     () => {
+       const expected = [
+         {type: TickType.MAJOR, time: 0.0},
+         {type: TickType.MINOR, time: 0.05},
+         {type: TickType.MEDIUM, time: 0.1},
+         {type: TickType.MINOR, time: 0.15},
+       ];
+       const actual = Array.from(tickGen!);
+       expectTicksEqual(actual, expected);
+     });
+  it('should tell us to use 1 decimal place for labels', () => {
+    expect(tickGen!.digits).toEqual(1);
+  });
+});
+
+describe('TickGenerator with range 0.0-0.1 and room for 1 label', () => {
+  let tickGen: TickGenerator|undefined = undefined;
+  beforeAll(() => {
+    const timeSpan = new TimeSpan(0.0, 0.1);
+    const timeScale = new TimeScale(timeSpan, [0, 100]);
+    tickGen = new TickGenerator(timeScale, {minLabelPx: 100});
+  });
+  it('should produce major ticks at 0.1s & minor ticks at 0.02s starting at 0',
+     () => {
+       const expected = [
+         {type: TickType.MAJOR, time: 0.0},
+         {type: TickType.MINOR, time: 0.01},
+         {type: TickType.MINOR, time: 0.02},
+         {type: TickType.MINOR, time: 0.03},
+         {type: TickType.MINOR, time: 0.04},
+         {type: TickType.MEDIUM, time: 0.05},
+         {type: TickType.MINOR, time: 0.06},
+         {type: TickType.MINOR, time: 0.07},
+         {type: TickType.MINOR, time: 0.08},
+         {type: TickType.MINOR, time: 0.09},
+       ];
+       const actual = Array.from(tickGen!);
+       expect(tickGen!.digits).toEqual(1);
+       expectTicksEqual(actual, expected);
+     });
+  it('should tell us to use 1 decimal place for labels', () => {
+    expect(tickGen!.digits).toEqual(1);
+  });
+});
+
+describe('TickGenerator with a very small timespan', () => {
+  let tickGen: TickGenerator|undefined = undefined;
+  beforeAll(() => {
+    const timeSpan = new TimeSpan(0.0, 1e-9);
+    const timeScale = new TimeScale(timeSpan, [0, 100]);
+    tickGen = new TickGenerator(timeScale, {minLabelPx: 100});
+  });
+  it('should generate minor ticks at 2e-10s and one major tick at the start',
+     () => {
+       const expected = [
+         {type: TickType.MAJOR, time: 0.0},
+         {type: TickType.MINOR, time: 1e-10},
+         {type: TickType.MINOR, time: 2e-10},
+         {type: TickType.MINOR, time: 3e-10},
+         {type: TickType.MINOR, time: 4e-10},
+         {type: TickType.MEDIUM, time: 5e-10},
+         {type: TickType.MINOR, time: 6e-10},
+         {type: TickType.MINOR, time: 7e-10},
+         {type: TickType.MINOR, time: 8e-10},
+         {type: TickType.MINOR, time: 9e-10},
+       ];
+       const actual = Array.from(tickGen!);
+       expectTicksEqual(actual, expected);
+     });
+  it('should tell us to use 9 decimal places for labels', () => {
+    expect(tickGen!.digits).toEqual(9);
+  });
+});
+
+describe('TickGenerator with a very large timespan', () => {
+  let tickGen: TickGenerator|undefined = undefined;
+  beforeAll(() => {
+    const timeSpan = new TimeSpan(0.0, 1e9);
+    const timeScale = new TimeScale(timeSpan, [0, 100]);
+    tickGen = new TickGenerator(timeScale, {minLabelPx: 100});
+  });
+  it('should generate minor ticks at 2e8 and one major tick at the start',
+     () => {
+       const expected = [
+         {type: TickType.MAJOR, time: 0.0},
+         {type: TickType.MINOR, time: 1e8},
+         {type: TickType.MINOR, time: 2e8},
+         {type: TickType.MINOR, time: 3e8},
+         {type: TickType.MINOR, time: 4e8},
+         {type: TickType.MEDIUM, time: 5e8},
+         {type: TickType.MINOR, time: 6e8},
+         {type: TickType.MINOR, time: 7e8},
+         {type: TickType.MINOR, time: 8e8},
+         {type: TickType.MINOR, time: 9e8},
+       ];
+       const actual = Array.from(tickGen!);
+       expectTicksEqual(actual, expected);
+     });
+  it('should tell us to use 0 decimal places for labels', () => {
+    expect(tickGen!.digits).toEqual(0);
+  });
+});
+
+describe('TickGenerator where the timespan has a dynamic range of 1e12', () => {
+  // This is the equivalent of zooming in to the nanosecond level, 1000 seconds
+  // into a trace Note: this is about the limit of what this generator can
+  // handle.
+  let tickGen: TickGenerator|undefined = undefined;
+  beforeAll(() => {
+    const timeSpan = new TimeSpan(1000, 1000.000000001);
+    const timeScale = new TimeScale(timeSpan, [0, 100]);
+    tickGen = new TickGenerator(timeScale, {minLabelPx: 100});
+  });
+  it('should generate minor ticks at 1e-10s and one major tick at the start',
+     () => {
+       const expected = [
+         {type: TickType.MAJOR, time: 1000.0000000000},
+         {type: TickType.MINOR, time: 1000.0000000001},
+         {type: TickType.MINOR, time: 1000.0000000002},
+         {type: TickType.MINOR, time: 1000.0000000003},
+         {type: TickType.MINOR, time: 1000.0000000004},
+         {type: TickType.MEDIUM, time: 1000.0000000005},
+         {type: TickType.MINOR, time: 1000.0000000006},
+         {type: TickType.MINOR, time: 1000.0000000007},
+         {type: TickType.MINOR, time: 1000.0000000008},
+         {type: TickType.MINOR, time: 1000.0000000009},
+       ];
+       const actual = Array.from(tickGen!);
+       expectTicksEqual(actual, expected);
+     });
+  it('should tell us to use 9 decimal places for labels', () => {
+    expect(tickGen!.digits).toEqual(9);
+  });
+});
+
+describe(
+    'TickGenerator where the timespan has a ridiculously huge dynamic range',
+    () => {
+      // We don't expect this to work, just wanna make sure it doesn't crash or
+      // get stuck
+      it('should not crash or get stuck in an infinite loop', () => {
+        const timeSpan = new TimeSpan(1000, 1000.000000000001);
+        const timeScale = new TimeScale(timeSpan, [0, 100]);
+        new TickGenerator(timeScale);
+      });
+    });
+
+describe(
+    'TickGenerator where the timespan has a ridiculously huge dynamic range',
+    () => {
+      // We don't expect this to work, just wanna make sure it doesn't crash or
+      // get stuck
+      it('should not crash or get stuck in an infinite loop', () => {
+        const timeSpan = new TimeSpan(1000, 1000.000000000001);
+        const timeScale = new TimeScale(timeSpan, [0, 100]);
+        new TickGenerator(timeScale);
+      });
+    });
+
+test('TickGenerator constructed with a 0 width throws an error', () => {
+  expect(() => {
+    const timeScale = new TimeScale(new TimeSpan(0.0, 1.0), [0, 0]);
+    new TickGenerator(timeScale);
+  }).toThrow(Error);
+});
+
+test(
+    'TickGenerator constructed with desiredPxPerStep of 0 throws an error',
+    () => {
+      expect(() => {
+        new TickGenerator(timeScale, {minLabelPx: 0});
+      }).toThrow(Error);
+    });
+
+test('TickGenerator constructed with a 0 duration throws an error', () => {
+  expect(() => {
+    const timeScale = new TimeScale(new TimeSpan(0.0, 0.0), [0, 1]);
+    new TickGenerator(timeScale);
+  }).toThrow(Error);
+});
+
+function expectTicksEqual(actual: Tick[], expected: any[]) {
+  // TODO(stevegolton) We could write a custom matcher for this; this approach
+  // produces cryptic error messages.
+  expect(actual.length).toEqual(expected.length);
+  for (let i = 0; i < actual.length; ++i) {
+    const ex = expected[i];
+    const ac = actual[i];
+    expect(ac.type).toEqual(ex.type);
+    expect(ac.time).toBeCloseTo(ex.time, 9);
+  }
+}
diff --git a/ui/src/frontend/help_modal.ts b/ui/src/frontend/help_modal.ts
index 7ed2880..3242b2c 100644
--- a/ui/src/frontend/help_modal.ts
+++ b/ui/src/frontend/help_modal.ts
@@ -48,6 +48,20 @@
                 m('td', 'Pan left/right'),
                 ),
             ),
+        m('h2', 'Navigation (Dvorak)'),
+        m(
+            'table',
+            m(
+                'tr',
+                m('td', keycap(','), '/', keycap('o')),
+                m('td', 'Zoom in/out'),
+                ),
+            m(
+                'tr',
+                m('td', keycap('a'), '/', keycap('e')),
+                m('td', 'Pan left/right'),
+                ),
+            ),
         m('h2', 'Mouse Controls'),
         m('table',
           m('tr', m('td', 'Click'), m('td', 'Select event')),
diff --git a/ui/src/frontend/index.ts b/ui/src/frontend/index.ts
index e285f42..35cf958 100644
--- a/ui/src/frontend/index.ts
+++ b/ui/src/frontend/index.ts
@@ -21,7 +21,8 @@
 import {createEmptyState} from '../common/empty_state';
 import {RECORDING_V2_FLAG} from '../common/feature_flags';
 import {initializeImmerJs} from '../common/immer_init';
-import {PluginContextImpl, pluginRegistry} from '../common/plugins';
+import {pluginManager, pluginRegistry} from '../common/plugins';
+import {onSelectionChanged} from '../common/selection_observer';
 import {State} from '../common/state';
 import {initWasm} from '../common/wasm_engine_proxy';
 import {ControllerWorkerInitMessage} from '../common/worker_messages';
@@ -93,6 +94,15 @@
       }
     }
 
+    if (this.state.currentSelection !== oldState.currentSelection) {
+      // TODO(altimin): Currently we are not triggering this when changing
+      // the set of selected tracks via toggling per-track checkboxes.
+      // Fix that.
+      onSelectionChanged(
+          this.state.currentSelection || undefined,
+          oldState.currentSelection || undefined);
+    }
+
     if (patches.length > 0) {
       this.port.postMessage(patches);
     }
@@ -246,6 +256,11 @@
     globals.rafScheduler.scheduleFullRedraw();
     maybeOpenTraceFromRoute(route);
   };
+
+  // This must be called before calling `globals.initialize` so that the
+  // `embeddedMode` global is set.
+  initGlobalsFromQueryString();
+
   globals.initialize(dispatch, router);
   globals.serviceWorkerController.install();
 
@@ -299,8 +314,7 @@
 
   // Initialize all plugins:
   for (const plugin of pluginRegistry.values()) {
-    const context = new PluginContextImpl(plugin.pluginId);
-    plugin.activate(context);
+    pluginManager.activatePlugin(plugin.pluginId);
   }
 }
 
@@ -315,8 +329,6 @@
     m.render(main, globals.router.resolve());
   };
 
-  initGlobalsFromQueryString();
-
   initLiveReloadIfLocalhost();
 
   if (!RECORDING_V2_FLAG.get()) {
@@ -338,7 +350,9 @@
   // accidentially clober the state of an open trace processor instance
   // otherwise.
   CheckHttpRpcConnection().then(() => {
-    installFileDropHandler();
+    if (!globals.embeddedMode) {
+      installFileDropHandler();
+    }
 
     // Don't allow postMessage or opening trace from route when the user says
     // that they want to reuse the already loaded trace in trace processor.
diff --git a/ui/src/frontend/keyboard_event_handler.ts b/ui/src/frontend/keyboard_event_handler.ts
index 58842c4..bc1ae88 100644
--- a/ui/src/frontend/keyboard_event_handler.ts
+++ b/ui/src/frontend/keyboard_event_handler.ts
@@ -243,6 +243,8 @@
       startTs = selectedNote.timestamp;
       endTs = selectedNote.timestamp + INSTANT_FOCUS_DURATION_S;
     }
+  } else if (selection.kind === 'LOG') {
+    // TODO(hjd): Make focus selection work for logs.
   }
 
   return {startTs, endTs};
diff --git a/ui/src/frontend/logs_filters.ts b/ui/src/frontend/logs_filters.ts
new file mode 100644
index 0000000..ca127d2
--- /dev/null
+++ b/ui/src/frontend/logs_filters.ts
@@ -0,0 +1,179 @@
+// Copyright (C) 2022 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 * as m from 'mithril';
+
+import {Actions} from '../common/actions';
+import {globals} from './globals';
+
+export const LOG_PRIORITIES =
+    ['-', '-', 'Verbose', 'Debug', 'Info', 'Warn', 'Error', 'Fatal'];
+const IGNORED_STATES = 2;
+
+interface LogPriorityWidgetAttrs {
+  options: string[];
+  selectedIndex: number;
+  onSelect: (id: number) => void;
+}
+
+interface LogTagChipAttrs {
+  name: string;
+  removeTag: (name: string) => void;
+}
+
+interface LogTagsWidgetAttrs {
+  tags: string[];
+}
+
+interface FilterByTextWidgetAttrs {
+  hideNonMatching: boolean;
+}
+
+class LogPriorityWidget implements m.ClassComponent<LogPriorityWidgetAttrs> {
+  view(vnode: m.Vnode<LogPriorityWidgetAttrs>) {
+    const attrs = vnode.attrs;
+    const optionComponents = [];
+    for (let i = IGNORED_STATES; i < attrs.options.length; i++) {
+      const selected = i === attrs.selectedIndex;
+      optionComponents.push(
+          m('option', {value: i, selected}, attrs.options[i]));
+    }
+    return m(
+        'select',
+        {
+          onchange: (e: InputEvent) => {
+            const selectionValue = (e.target as HTMLSelectElement).value;
+            attrs.onSelect(Number(selectionValue));
+          },
+        },
+        optionComponents,
+    );
+  }
+}
+
+class LogTagChip implements m.ClassComponent<LogTagChipAttrs> {
+  view({attrs}: m.CVnode<LogTagChipAttrs>) {
+    return m(
+        '.chip',
+        m('.chip-text', attrs.name),
+        m('button.chip-button',
+          {
+            onclick: () => {
+              attrs.removeTag(attrs.name);
+            },
+          },
+          '×'));
+  }
+}
+
+class LogTagsWidget implements m.ClassComponent<LogTagsWidgetAttrs> {
+  removeTag(tag: string) {
+    globals.dispatch(Actions.removeLogTag({tag}));
+  }
+
+  view(vnode: m.Vnode<LogTagsWidgetAttrs>) {
+    const tags = vnode.attrs.tags;
+    return m(
+        '.tag-container',
+        m('.chips', tags.map((tag) => m(LogTagChip, {
+                               name: tag,
+                               removeTag: this.removeTag.bind(this),
+                             }))),
+        m(`input.chip-input[placeholder='Add new tag']`, {
+          onkeydown: (e: KeyboardEvent) => {
+            // This is to avoid zooming on 'w'(and other unexpected effects
+            // of key presses in this input field).
+            e.stopPropagation();
+            const htmlElement = e.target as HTMLInputElement;
+
+            // When the user clicks 'Backspace' we delete the previous tag.
+            if (e.key === 'Backspace' && tags.length > 0 &&
+                htmlElement.value === '') {
+              globals.dispatch(
+                  Actions.removeLogTag({tag: tags[tags.length - 1]}));
+              return;
+            }
+
+            if (e.key !== 'Enter') {
+              return;
+            }
+            if (htmlElement.value === '') {
+              return;
+            }
+            globals.dispatch(
+                Actions.addLogTag({tag: htmlElement.value.trim()}));
+            htmlElement.value = '';
+          },
+        }));
+  }
+}
+
+class LogTextWidget implements m.ClassComponent {
+  view() {
+    return m(
+        '.tag-container', m(`input.chip-input[placeholder='Search log text']`, {
+          onkeydown: (e: KeyboardEvent) => {
+            // This is to avoid zooming on 'w'(and other unexpected effects
+            // of key presses in this input field).
+            e.stopPropagation();
+          },
+
+          onkeyup: (e: KeyboardEvent) => {
+            // We want to use the value of the input field after it has been
+            // updated with the latest key (onkeyup).
+            const htmlElement = e.target as HTMLInputElement;
+            globals.dispatch(
+                Actions.updateLogFilterText({textEntry: htmlElement.value}));
+          },
+        }));
+  }
+}
+
+class FilterByTextWidget implements m.ClassComponent<FilterByTextWidgetAttrs> {
+  view({attrs}: m.Vnode<FilterByTextWidgetAttrs>) {
+    const icon = attrs.hideNonMatching ? 'unfold_less' : 'unfold_more';
+    const tooltip = attrs.hideNonMatching ? 'Expand all and view highlighted' :
+                                            'Collapse all';
+    return m(
+        '.filter-widget',
+        m('.tooltip', tooltip),
+        m('i.material-icons',
+          {
+            onclick: () => {
+              globals.dispatch(Actions.toggleCollapseByTextEntry({}));
+            },
+          },
+          icon));
+  }
+}
+
+export class LogsFilters implements m.ClassComponent {
+  view(_: m.CVnode<{}>) {
+    return m(
+        '.log-filters',
+        m('.log-label', 'Log Level'),
+        m(LogPriorityWidget, {
+          options: LOG_PRIORITIES,
+          selectedIndex: globals.state.logFilteringCriteria.minimumLevel,
+          onSelect: (minimumLevel) => {
+            globals.dispatch(Actions.setMinimumLogLevel({minimumLevel}));
+          },
+        }),
+        m(LogTagsWidget, {tags: globals.state.logFilteringCriteria.tags}),
+        m(LogTextWidget),
+        m(FilterByTextWidget, {
+          hideNonMatching: globals.state.logFilteringCriteria.hideNonMatching,
+        }));
+  }
+}
diff --git a/ui/src/frontend/logs_panel.ts b/ui/src/frontend/logs_panel.ts
index a0b33ae..77728aa 100644
--- a/ui/src/frontend/logs_panel.ts
+++ b/ui/src/frontend/logs_panel.ts
@@ -25,13 +25,13 @@
 import {formatTimestamp} from '../common/time';
 import {TimeSpan} from '../common/time';
 
+import {SELECTED_LOG_ROWS_COLOR} from './css_constants';
 import {globals} from './globals';
+import {LOG_PRIORITIES, LogsFilters} from './logs_filters';
 import {Panel} from './panel';
 
 const ROW_H = 20;
 
-const PRIO_TO_LETTER = ['-', '-', 'V', 'D', 'I', 'W', 'E', 'F'];
-
 export class LogPanel extends Panel<{}> {
   private scrollContainer?: HTMLElement;
   private bounds?: LogBounds;
@@ -67,7 +67,7 @@
     this.recomputeVisibleRowsAndUpdate();
   }
 
-  onupdate(_: m.CVnodeDOM) {
+  onbeforeupdate(_: m.CVnodeDOM) {
     this.bounds = globals.trackDataStore.get(LogBoundsKey) as LogBounds;
     this.entries = globals.trackDataStore.get(LogEntriesKey) as LogEntries;
     this.recomputeVisibleRowsAndUpdate();
@@ -101,29 +101,51 @@
     const isStaleRight = !rightSpan.isInBounds(vis.end);
     const isStale = isStaleLeft || isStaleRight;
     const offset = Math.min(this.visibleRowOffset, total);
-    const visCount = Math.min(total, this.visibleRowCount);
+    const visCount = Math.min(total - offset, this.visibleRowCount);
     return {isStale, total, count: visCount, offset};
   }
 
   view(_: m.CVnode<{}>) {
     const {isStale, total, offset, count} = this.totalRows();
 
+    const hasProcessNames = this.entries &&
+        this.entries.processName.filter((name) => name).length > 0;
+
     const rows: m.Children = [];
+    rows.push(
+        m(`.row`,
+          m('.cell.row-header', 'Timestamp'),
+          m('.cell.row-header', 'Level'),
+          m('.cell.row-header', 'Tag'),
+          hasProcessNames ? m('.cell.with-process.row-header', 'Process name') :
+                            undefined,
+          hasProcessNames ? m('.cell.with-process.row-header', 'Message') :
+                            m('.cell.no-process.row-header', 'Message'),
+          m('br')));
     if (this.entries) {
       const offset = this.entries.offset;
       const timestamps = this.entries.timestamps;
       const priorities = this.entries.priorities;
       const tags = this.entries.tags;
       const messages = this.entries.messages;
+      const processNames = this.entries.processName;
       for (let i = 0; i < this.entries.timestamps.length; i++) {
-        const priorityLetter = PRIO_TO_LETTER[priorities[i]];
+        const priorityLetter = LOG_PRIORITIES[priorities[i]][0];
         const ts = timestamps[i];
         const prioClass = priorityLetter || '';
+        const style: {top: string, backgroundColor?: string} = {
+          // 1.5 is for the width of the header
+          top: `${(offset + i + 1.5) * ROW_H}px`,
+        };
+        if (this.entries.isHighlighted[i]) {
+          style.backgroundColor = SELECTED_LOG_ROWS_COLOR;
+        }
+
         rows.push(
             m(`.row.${prioClass}`,
               {
                 'class': isStale ? 'stale' : '',
-                'style': {top: `${(offset + i) * ROW_H}px`},
+                style,
                 'onmouseover': this.onRowOver.bind(this, ts / 1e9),
                 'onmouseout': this.onRowOut.bind(this),
               },
@@ -131,7 +153,10 @@
                 formatTimestamp(ts / 1e9 - globals.state.traceTime.startSec)),
               m('.cell', priorityLetter || '?'),
               m('.cell', tags[i]),
-              m('.cell', messages[i]),
+              hasProcessNames ? m('.cell.with-process', processNames[i]) :
+                                undefined,
+              hasProcessNames ? m('.cell.with-process', messages[i]) :
+                                m('.cell.no-process', messages[i]),
               m('br')));
       }
     }
@@ -142,7 +167,11 @@
           {
             'class': isStale ? 'stale' : '',
           },
-          `Logs rows [${offset}, ${offset + count}] / ${total}`),
+          [
+            m('.log-rows-label',
+              `Logs rows [${offset}, ${offset + count}] / ${total}`),
+            m(LogsFilters),
+          ]),
         m('.rows', {style: {height: `${total * ROW_H}px`}}, rows));
   }
 
diff --git a/ui/src/frontend/modal.ts b/ui/src/frontend/modal.ts
index e34e130..8c6ec2e 100644
--- a/ui/src/frontend/modal.ts
+++ b/ui/src/frontend/modal.ts
@@ -37,7 +37,7 @@
 //   view() {
 //     return m('main',
 //        m('h2', ...)
-//        modalContainerInstance.render();
+//        m(modalContainerInstance.mithrilComponent);
 //   }
 //
 // - In the view() method of the nested component that wants to show the modal
@@ -212,6 +212,7 @@
   }
 }
 
+
 // This is deliberately NOT a Mithril component. We want to manage the lifetime
 // independently (outside of Mithril), so we can render from outside the current
 // vdom sub-tree. ModalContainer instances should be singletons / globals.
@@ -220,6 +221,36 @@
   private generation = 1; // Start with a generation > `closeGeneration`.
   private closeGeneration = 0;
 
+  // This is the mithril component that is exposed to the embedder (e.g. see
+  // pages.ts). The caller is supposed to hyperscript this while building the
+  // vdom tree that should host the modal dialog.
+  readonly mithrilComponent = {
+    container: this,
+    view:
+        function() {
+          const thiz = this.container;
+          const attrs = thiz.attrs;
+          if (attrs === undefined) {
+            return null;
+          }
+          return [m(Modal, {
+            ...attrs,
+            onClose: () => {
+              // Remember the fact that the dialog was dismissed, in case the
+              // whole ModalContainer gets instantiated from a different page
+              // (which would cause the Modal to be destroyed and recreated).
+              thiz.closeGeneration = thiz.generation;
+              if (thiz.attrs?.onClose !== undefined) {
+                thiz.attrs.onClose();
+                globals.rafScheduler.scheduleFullRedraw();
+              }
+            },
+            close: thiz.closeGeneration === thiz.generation ? true :
+                                                              attrs.close,
+            key: thiz.generation,
+          })];
+        },
+  };
 
   // This should be called to show a new modal dialog. The modal dialog will
   // be shown the next time something calls render() in a Mithril draw pass.
@@ -241,42 +272,10 @@
     this.closeGeneration = this.generation;
     globals.rafScheduler.scheduleFullRedraw();
   }
-
-  // This method should be called in the view() method of the Mithril component
-  // that will host the DOM elements. For instance, in the case of the
-  // full-screen dialog, that is `fullscreenModal` used by pages.ts.
-  // e.g.: view() {
-  //  return m('main',
-  //      ...,
-  //      modalContainerInstance.render(),
-  //    );
-  // }
-  render() {
-    if (this.attrs === undefined) return null;
-
-    // Here we return an array so that Mithril's diff engine destroys and
-    // recreates the component when the generation changes, rather than updating
-    // the same component. `key` works only when returning arrays.
-    return [m(Modal, {
-      ...this.attrs,
-      onClose: () => {
-        // Remember the fact that the dialog was dismissed, in case the whole
-        // ModalContainer gets instantiated from a different page (which would
-        // cause the Modal to be destroyed and recreated).
-        this.closeGeneration = this.generation;
-        if (this.attrs?.onClose !== undefined) {
-          this.attrs.onClose();
-          globals.rafScheduler.scheduleFullRedraw();
-        }
-      },
-      close: this.closeGeneration === this.generation ? true : this.attrs.close,
-      key: this.generation,
-    })];
-  }
 }
 
 // This is the default instance used for full-screen modal dialogs.
-// page.ts calls `fullscreenModalContainer.render()` in its view() pass.
+// page.ts calls `m(fullscreenModalContainer.mithrilComponent)` in its view().
 export const fullscreenModalContainer = new ModalContainer();
 
 
diff --git a/ui/src/frontend/notes_panel.ts b/ui/src/frontend/notes_panel.ts
index 4dc8d08..9896611 100644
--- a/ui/src/frontend/notes_panel.ts
+++ b/ui/src/frontend/notes_panel.ts
@@ -19,10 +19,19 @@
 import {AreaNote, Note} from '../common/state';
 import {timeToString} from '../common/time';
 
+import {
+  BottomTab,
+  bottomTabRegistry,
+  NewBottomTabArgs,
+} from './bottom_tab';
 import {TRACK_SHELL_WIDTH} from './css_constants';
 import {PerfettoMouseEvent} from './events';
 import {globals} from './globals';
-import {gridlines} from './gridline_helper';
+import {
+  TickGenerator,
+  TickType,
+  timeScaleForVisibleWindow,
+} from './gridline_helper';
 import {Panel, PanelSize} from './panel';
 import {isTraceLoaded} from './sidebar';
 
@@ -90,13 +99,15 @@
 
   renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize) {
     const timeScale = globals.frontendLocalState.timeScale;
-    const range = globals.frontendLocalState.visibleWindowTime;
     let aNoteIsHovered = false;
 
     ctx.fillStyle = '#999';
     ctx.fillRect(TRACK_SHELL_WIDTH - 2, 0, 2, size.height);
-    for (const xAndTime of gridlines(size.width, range, timeScale)) {
-      ctx.fillRect(xAndTime[0], 0, 1, size.height);
+    const relScale = timeScaleForVisibleWindow(TRACK_SHELL_WIDTH, size.width);
+    if (relScale.timeSpan.duration > 0 && relScale.widthPx > 0) {
+      for (const {type, position} of new TickGenerator(relScale)) {
+        if (type === TickType.MAJOR) ctx.fillRect(position, 0, 1, size.height);
+      }
     }
 
     ctx.textBaseline = 'bottom';
@@ -259,13 +270,32 @@
   }
 }
 
-interface NotesEditorPanelAttrs {
+interface NotesEditorTabConfig {
   id: string;
 }
 
-export class NotesEditorPanel extends Panel<NotesEditorPanelAttrs> {
-  view({attrs}: m.CVnode<NotesEditorPanelAttrs>) {
-    const note = globals.state.notes[attrs.id];
+export class NotesEditorTab extends BottomTab<NotesEditorTabConfig> {
+  static readonly kind = 'org.perfetto.NotesEditorTab';
+
+  static create(args: NewBottomTabArgs): NotesEditorTab {
+    return new NotesEditorTab(args);
+  }
+
+  constructor(args: NewBottomTabArgs) {
+    super(args);
+  }
+
+  renderTabCanvas() {}
+
+  getTitle() {
+    return 'Current Selection';
+  }
+
+  viewTab() {
+    const note = globals.state.notes[this.config.id];
+    if (note === undefined) {
+      return m('.', `No Note with id ${this.config.id}`);
+    }
     const startTime =
         getStartTimestamp(note) - globals.state.traceTime.startSec;
     return m(
@@ -281,7 +311,7 @@
             onchange: (e: InputEvent) => {
               const newText = (e.target as HTMLInputElement).value;
               globals.dispatch(Actions.changeNoteText({
-                id: attrs.id,
+                id: this.config.id,
                 newText,
               }));
             },
@@ -291,7 +321,7 @@
               onchange: (e: Event) => {
                 const newColor = (e.target as HTMLInputElement).value;
                 globals.dispatch(Actions.changeNoteColor({
-                  id: attrs.id,
+                  id: this.config.id,
                   newColor,
                 }));
               },
@@ -299,7 +329,7 @@
           m('button',
             {
               onclick: () => {
-                globals.dispatch(Actions.removeNote({id: attrs.id}));
+                globals.dispatch(Actions.removeNote({id: this.config.id}));
                 globals.dispatch(Actions.setCurrentTab({tab: undefined}));
                 globals.rafScheduler.scheduleFullRedraw();
               },
@@ -307,6 +337,6 @@
             'Remove')),
     );
   }
-
-  renderCanvas(_ctx: CanvasRenderingContext2D, _size: PanelSize) {}
 }
+
+bottomTabRegistry.register(NotesEditorTab);
diff --git a/ui/src/frontend/overview_timeline_panel.ts b/ui/src/frontend/overview_timeline_panel.ts
index 12b5582..578fa7f 100644
--- a/ui/src/frontend/overview_timeline_panel.ts
+++ b/ui/src/frontend/overview_timeline_panel.ts
@@ -16,7 +16,7 @@
 
 import {assertExists} from '../base/logging';
 import {hueForCpu} from '../common/colorizer';
-import {TimeSpan, timeToString} from '../common/time';
+import {TimeSpan} from '../common/time';
 
 import {
   OVERVIEW_TIMELINE_NON_VISIBLE_COLOR,
@@ -29,6 +29,7 @@
 import {OuterDragStrategy} from './drag/outer_drag_strategy';
 import {DragGestureHandler} from './drag_gesture_handler';
 import {globals} from './globals';
+import {TickGenerator, TickType} from './gridline_helper';
 import {Panel, PanelSize} from './panel';
 import {TimeScale} from './time_scale';
 
@@ -80,21 +81,28 @@
     if (this.timeScale === undefined) return;
     const headerHeight = 25;
     const tracksHeight = size.height - headerHeight;
+    const timeSpan = new TimeSpan(0, this.totTime.duration);
 
-    // Draw time labels on the top header.
-    ctx.font = '10px Roboto Condensed';
-    ctx.fillStyle = '#999';
-    for (let i = 0; i < 100; i++) {
-      const xPos =
-          (i * (this.width - TRACK_SHELL_WIDTH) / 100) + TRACK_SHELL_WIDTH;
-      const t = this.timeScale.pxToTime(xPos);
-      if (xPos <= 0) continue;
-      if (xPos > this.width) break;
-      if (i % 10 === 0) {
-        ctx.fillRect(xPos - 1, 0, 1, headerHeight - 5);
-        ctx.fillText(timeToString(t - this.totTime.start), xPos + 5, 18);
-      } else {
-        ctx.fillRect(xPos - 1, 0, 1, 5);
+    const timeScale = new TimeScale(timeSpan, [TRACK_SHELL_WIDTH, this.width]);
+
+    if (timeScale.widthPx > 0) {
+      const tickGen = new TickGenerator(timeScale);
+
+      // Draw time labels on the top header.
+      ctx.font = '10px Roboto Condensed';
+      ctx.fillStyle = '#999';
+      for (const {type, time, position} of tickGen) {
+        const xPos = Math.round(position);
+        if (xPos <= 0) continue;
+        if (xPos > this.width) break;
+        if (type === TickType.MAJOR) {
+          ctx.fillRect(xPos - 1, 0, 1, headerHeight - 5);
+          ctx.fillText(time.toFixed(tickGen.digits) + ' s', xPos + 5, 18);
+        } else if (type == TickType.MEDIUM) {
+          ctx.fillRect(xPos - 1, 0, 1, 8);
+        } else if (type == TickType.MINOR) {
+          ctx.fillRect(xPos - 1, 0, 1, 5);
+        }
       }
     }
 
diff --git a/ui/src/frontend/pages.ts b/ui/src/frontend/pages.ts
index b58d80a..8ffb511 100644
--- a/ui/src/frontend/pages.ts
+++ b/ui/src/frontend/pages.ts
@@ -56,7 +56,7 @@
         m(Alerts),
         m(component, attrs),
         m(CookieConsent),
-        fullscreenModalContainer.render(),
+        m(fullscreenModalContainer.mithrilComponent),
       ];
       if (globals.state.perfDebug) {
         children.push(m('.perf-stats'));
diff --git a/ui/src/frontend/perf.ts b/ui/src/frontend/perf.ts
index 3a6e938..919f1e6 100644
--- a/ui/src/frontend/perf.ts
+++ b/ui/src/frontend/perf.ts
@@ -40,6 +40,7 @@
   private _count = 0;
   private _mean = 0;
   private _lastValue = 0;
+  private _ptr = 0;
 
   private buffer: number[] = [];
 
@@ -47,10 +48,15 @@
 
   addValue(value: number) {
     this._lastValue = value;
-    this.buffer.push(value);
-    if (this.buffer.length > this._maxBufferSize) {
-      this.buffer.shift();
+    if (this.buffer.length >= this._maxBufferSize) {
+      this.buffer[this._ptr++] = value;
+      if (this._ptr >= this.buffer.length) {
+        this._ptr -= this.buffer.length;
+      }
+    } else {
+      this.buffer.push(value);
     }
+
     this._mean = (this._mean * this._count + value) / (this._count + 1);
     this._count++;
   }
diff --git a/ui/src/frontend/perf_unittest.ts b/ui/src/frontend/perf_unittest.ts
new file mode 100644
index 0000000..5ba357c
--- /dev/null
+++ b/ui/src/frontend/perf_unittest.ts
@@ -0,0 +1,57 @@
+// Copyright (C) 2022 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 {RunningStatistics} from './perf';
+
+test('buffer size is accurate before reaching max capacity', () => {
+  const buf = new RunningStatistics(10);
+
+  for (let i = 0; i < 10; i++) {
+    buf.addValue(i);
+    expect(buf.bufferSize).toEqual(i + 1);
+  }
+});
+
+test('buffer size is accurate after reaching max capacity', () => {
+  const buf = new RunningStatistics(10);
+
+  for (let i = 0; i < 10; i++) {
+    buf.addValue(i);
+  }
+
+  for (let i = 0; i < 10; i++) {
+    buf.addValue(i);
+    expect(buf.bufferSize).toEqual(10);
+  }
+});
+
+test('buffer mean is accurate before reaching max capacity', () => {
+  const buf = new RunningStatistics(10);
+
+  buf.addValue(1);
+  buf.addValue(2);
+  buf.addValue(3);
+
+  expect(buf.bufferMean).toBeCloseTo(2);
+});
+
+test('buffer mean is accurate after reaching max capacity', () => {
+  const buf = new RunningStatistics(10);
+
+  for (let i = 0; i < 20; i++) {
+    buf.addValue(2);
+  }
+
+  expect(buf.bufferMean).toBeCloseTo(2);
+});
diff --git a/ui/src/frontend/pivot_table_redux.ts b/ui/src/frontend/pivot_table_redux.ts
index 4d5f120..f17bb24 100644
--- a/ui/src/frontend/pivot_table_redux.ts
+++ b/ui/src/frontend/pivot_table_redux.ts
@@ -18,12 +18,12 @@
 
 import {sqliteString} from '../base/string_utils';
 import {Actions} from '../common/actions';
+import {DropDirection} from '../common/dragndrop_logic';
 import {COUNT_AGGREGATION} from '../common/empty_state';
 import {ColumnType} from '../common/query_result';
 import {
   Area,
   PivotTableReduxAreaState,
-  PivotTableReduxQuery,
   PivotTableReduxResult,
   SortDirection,
 } from '../common/state';
@@ -40,11 +40,9 @@
 import {
   aggregationIndex,
   areaFilter,
-  expression,
-  generateQuery,
+  extractArgumentExpression,
   sliceAggregationColumns,
   tables,
-  threadSliceAggregationColumns,
 } from './pivot_table_redux_query_generator';
 import {
   Aggregation,
@@ -52,10 +50,9 @@
   columnKey,
   PivotTree,
   TableColumn,
-  tableColumnEquals,
 } from './pivot_table_redux_types';
 import {PopupMenuButton, PopupMenuItem} from './popup_menu';
-import {DropDirection, ReorderableCellGroup} from './reorderable_cells';
+import {ReorderableCell, ReorderableCellGroup} from './reorderable_cells';
 
 
 interface PathItem {
@@ -72,57 +69,55 @@
   value: ColumnType;
 }
 
+function drillFilterColumnName(column: TableColumn): string {
+  switch (column.kind) {
+    case 'argument':
+      return extractArgumentExpression(column.argument, 'slice');
+    case 'regular':
+      return `${column.table}.${column.column}`;
+  }
+}
+
 // Convert DrillFilter to SQL condition to be used in WHERE clause.
 function renderDrillFilter(filter: DrillFilter): string {
-  // TODO(b/231429468): This would not work for non-slice column.
-  const column = expression(filter.column);
+  const column = drillFilterColumnName(filter.column);
   if (filter.value === null) {
     return `${column} IS NULL`;
   } else if (typeof filter.value === 'number') {
     return `${column} = ${filter.value}`;
+  } else if (filter.value instanceof Uint8Array) {
+    throw new Error(`BLOB as DrillFilter not implemented`);
   }
   return `${column} = ${sqliteString(filter.value)}`;
 }
 
 function readableColumnName(column: TableColumn) {
   switch (column.kind) {
-    case 'argument': {
+    case 'argument':
       return `Argument ${column.argument}`;
-    }
-    case 'regular': {
+    case 'regular':
       return `${column.table}.${column.column}`;
-    }
-    default: {
-      throw new Error(`malformed table column ${column}`);
-    }
   }
 }
 
+export function markFirst(index: number) {
+  if (index === 0) {
+    return '.first';
+  }
+  return '';
+}
+
 export class PivotTableRedux extends Panel<PivotTableReduxAttrs> {
   get pivotState() {
     return globals.state.nonSerializableState.pivotTableRedux;
   }
-  get selectedAggregations() {
-    return globals.state.nonSerializableState.pivotTableRedux
-        .selectedAggregations;
-  }
-
   get constrainToArea() {
     return globals.state.nonSerializableState.pivotTableRedux.constrainToArea;
   }
 
   renderCanvas(): void {}
 
-  generateQuery(attrs: PivotTableReduxAttrs): PivotTableReduxQuery {
-    return generateQuery(
-        this.pivotState.selectedPivots,
-        this.pivotState.selectedSlicePivots,
-        this.selectedAggregations,
-        globals.state.areas[attrs.selectionArea.areaId],
-        this.constrainToArea);
-  }
-  renderDrillDownCell(
-      area: Area, result: PivotTableReduxResult, filters: DrillFilter[]) {
+  renderDrillDownCell(area: Area, filters: DrillFilter[]) {
     return m(
         'td',
         m('button',
@@ -134,7 +129,10 @@
                 queryFilters.push(areaFilter(area));
               }
               const query = `
-                select * from ${result.metadata.tableName}
+                select slice.* from slice
+                left join thread_track on slice.track_id = thread_track.id
+                left join thread using (utid)
+                left join process using (upid)
                 where ${queryFilters.join(' and \n')}
               `;
               // TODO(ddrone): the UI of running query as if it was a canned or
@@ -176,7 +174,7 @@
     for (let i = 0; i < tree.aggregates.length; i++) {
       const renderedValue = this.renderCell(
           result.metadata.aggregationColumns[i].column, tree.aggregates[i]);
-      renderedCells.push(m('td', renderedValue));
+      renderedCells.push(m('td' + markFirst(i), renderedValue));
     }
 
     const drillFilters: DrillFilter[] = [];
@@ -187,7 +185,7 @@
       });
     }
 
-    renderedCells.push(this.renderDrillDownCell(area, result, drillFilters));
+    renderedCells.push(this.renderDrillDownCell(area, drillFilters));
     return m('tr', renderedCells);
   }
 
@@ -242,13 +240,13 @@
             {column: result.metadata.pivotColumns[j], value: row[j]});
       }
       for (let j = 0; j < result.metadata.aggregationColumns.length; j++) {
-        const value = row[aggregationIndex(treeDepth, j, treeDepth)];
+        const value = row[aggregationIndex(treeDepth, j)];
         const renderedValue = this.renderCell(
             result.metadata.aggregationColumns[j].column, value);
-        renderedCells.push(m('td', renderedValue));
+        renderedCells.push(m('td.aggregation' + markFirst(j), renderedValue));
       }
 
-      renderedCells.push(this.renderDrillDownCell(area, result, drillFilters));
+      renderedCells.push(this.renderDrillDownCell(area, drillFilters));
       sink.push(m('tr', renderedCells));
     }
   }
@@ -260,7 +258,7 @@
            m('strong', 'Total values:'))];
     for (let i = 0; i < queryResult.tree.aggregates.length; i++) {
       overallValuesRow.push(
-          m('td',
+          m('td' + markFirst(i),
             this.renderCell(
                 queryResult.metadata.aggregationColumns[i].column,
                 queryResult.tree.aggregates[i])));
@@ -269,12 +267,13 @@
     return m('tr', overallValuesRow);
   }
 
-  sortingItem(column: TableColumn, order: SortDirection): PopupMenuItem {
+  sortingItem(aggregationIndex: number, order: SortDirection): PopupMenuItem {
     return {
       itemType: 'regular',
       text: order === 'DESC' ? 'Highest first' : 'Lowest first',
       callback() {
-        globals.dispatch(Actions.setPivotTableSortColumn({column, order}));
+        globals.dispatch(
+            Actions.setPivotTableSortColumn({aggregationIndex, order}));
         globals.dispatch(
             Actions.setPivotTableQueryRequested({queryRequested: true}));
       },
@@ -289,37 +288,28 @@
         readableColumnName(aggregation.column)})`;
   }
 
-  aggregationPopupItem(aggregation: Aggregation, nameOverride?: string):
-      PopupMenuItem {
+  aggregationPopupItem(
+      aggregation: Aggregation, index: number,
+      nameOverride?: string): PopupMenuItem {
     return {
       itemType: 'regular',
       text: nameOverride ?? readableColumnName(aggregation.column),
       callback: () => {
-        globals.dispatch(Actions.setPivotTableAggregationSelected({
-          column: {
-            aggregationFunction: aggregation.aggregationFunction,
-            column: aggregation.column,
-          },
-          selected: true,
-        }));
+        globals.dispatch(
+            Actions.addPivotTableAggregation({aggregation, after: index}));
         globals.dispatch(
             Actions.setPivotTableQueryRequested({queryRequested: true}));
       },
     };
   }
 
-  aggregationPopupTableGroup(
-      table: string, columns: string[], used: Set<string>): PopupMenuItem
-      |undefined {
+  aggregationPopupTableGroup(table: string, columns: string[], index: number):
+      PopupMenuItem|undefined {
     const items = [];
     for (const column of columns) {
       const tableColumn: TableColumn = {kind: 'regular', table, column};
-      if (used.has(columnKey(tableColumn))) {
-        continue;
-      }
-
       items.push(this.aggregationPopupItem(
-          {aggregationFunction: 'SUM', column: tableColumn}));
+          {aggregationFunction: 'SUM', column: tableColumn}, index));
     }
 
     if (items.length === 0) {
@@ -334,22 +324,22 @@
     };
   }
 
-  renderAggregationHeaderCell(aggregation: Aggregation): m.Child {
-    const column = aggregation.column;
+  renderAggregationHeaderCell(
+      aggregation: Aggregation, index: number,
+      removeItem: boolean): ReorderableCell {
     const popupItems: PopupMenuItem[] = [];
     const state = globals.state.nonSerializableState.pivotTableRedux;
     let icon = 'more_horiz';
-    if (state.sortCriteria === undefined ||
-        !tableColumnEquals(column, state.sortCriteria.column)) {
+    if (aggregation.sortDirection === undefined) {
       popupItems.push(
-          this.sortingItem(column, 'DESC'), this.sortingItem(column, 'ASC'));
+          this.sortingItem(index, 'DESC'), this.sortingItem(index, 'ASC'));
     } else {
       // Table is already sorted by the same column, return one item with
       // opposite direction.
       popupItems.push(this.sortingItem(
-          column, state.sortCriteria.order === 'DESC' ? 'ASC' : 'DESC'));
-      icon = state.sortCriteria.order === 'DESC' ? 'arrow_drop_down' :
-                                                   'arrow_drop_up';
+          index, aggregation.sortDirection === 'DESC' ? 'ASC' : 'DESC'));
+      icon = aggregation.sortDirection === 'DESC' ? 'arrow_drop_down' :
+                                                    'arrow_drop_up';
     }
     const otherAggs: AggregationFunction[] = ['SUM', 'MAX', 'MIN'];
     if (aggregation.aggregationFunction !== 'COUNT') {
@@ -362,13 +352,8 @@
           itemType: 'regular',
           text: otherAgg,
           callback() {
-            globals.dispatch(Actions.setPivotTableAggregationSelected(
-                {column: aggregation, selected: false}));
-            globals.dispatch(Actions.setPivotTableAggregationSelected({
-              column:
-                  {aggregationFunction: otherAgg, column: aggregation.column},
-              selected: true,
-            }));
+            globals.dispatch(Actions.setPivotTableAggregationFunction(
+                {index, function: otherAgg}));
             globals.dispatch(
                 Actions.setPivotTableQueryRequested({queryRequested: true}));
           },
@@ -376,40 +361,46 @@
       }
     }
 
-    const usedAggregations: Set<string> = new Set();
-    let hasCount = false;
+    if (removeItem) {
+      popupItems.push({
+        itemType: 'regular',
+        text: 'Remove',
+        callback: () => {
+          globals.dispatch(Actions.removePivotTableAggregation({index}));
+          globals.dispatch(
+              Actions.setPivotTableQueryRequested({queryRequested: true}));
+        },
+      });
+    }
 
+    let hasCount = false;
     for (const agg of state.selectedAggregations.values()) {
       if (agg.aggregationFunction === 'COUNT') {
         hasCount = true;
-        continue;
       }
-
-      usedAggregations.add(columnKey(agg.column));
     }
 
     if (!hasCount) {
       popupItems.push(this.aggregationPopupItem(
-          COUNT_AGGREGATION, 'Add count aggregation'));
+          COUNT_AGGREGATION, index, 'Add count aggregation'));
     }
 
     const sliceAggregationsItem = this.aggregationPopupTableGroup(
-        'slice', sliceAggregationColumns, usedAggregations);
+        'slice', sliceAggregationColumns, index);
     if (sliceAggregationsItem !== undefined) {
       popupItems.push(sliceAggregationsItem);
     }
 
-    const threadSliceAggregationsItem = this.aggregationPopupTableGroup(
-        'thread_slice', threadSliceAggregationColumns, usedAggregations);
-    if (threadSliceAggregationsItem !== undefined) {
-      popupItems.push(threadSliceAggregationsItem);
-    }
-
-    return m(
-        'td', this.readableAggregationName(aggregation), m(PopupMenuButton, {
+    return {
+      extraClass: '.aggregation' + markFirst(index),
+      content: [
+        this.readableAggregationName(aggregation),
+        m(PopupMenuButton, {
           icon,
           items: popupItems,
-        }));
+        }),
+      ],
+    };
   }
 
   showModal = false;
@@ -443,7 +434,7 @@
 
   renderPivotColumnHeader(
       queryResult: PivotTableReduxResult, pivot: TableColumn,
-      selectedPivots: Set<string>): m.Children {
+      selectedPivots: Set<string>): ReorderableCell {
     const items: PopupMenuItem[] = [{
       itemType: 'regular',
       text: 'Add argument pivot',
@@ -497,10 +488,12 @@
       });
     }
 
-    return [
-      readableColumnName(pivot),
-      m(PopupMenuButton, {icon: 'more_horiz', items}),
-    ];
+    return {
+      content: [
+        readableColumnName(pivot),
+        m(PopupMenuButton, {icon: 'more_horiz', items}),
+      ],
+    };
   }
 
   renderResultsTable(attrs: PivotTableReduxAttrs) {
@@ -525,29 +518,26 @@
         state.queryResult,
         renderedRows);
 
-    const selectedPivots = new Set([
-      ...this.pivotState.selectedPivots,
-      ...this.pivotState.selectedSlicePivots,
-    ].map((pivot) => columnKey(pivot)));
+    const selectedPivots =
+        new Set(this.pivotState.selectedPivots.map(columnKey));
     const pivotTableHeaders = state.selectedPivots.map(
         (pivot) =>
             this.renderPivotColumnHeader(queryResult, pivot, selectedPivots));
-    const slicePivotTableHeaders = state.selectedSlicePivots.map(
-        (pivot) =>
-            this.renderPivotColumnHeader(queryResult, pivot, selectedPivots));
 
+    const removeItem = state.queryResult.metadata.aggregationColumns.length > 1;
     const aggregationTableHeaders =
         state.queryResult.metadata.aggregationColumns.map(
-            (aggregation) => this.renderAggregationHeaderCell(aggregation));
+            (aggregation, index) => this.renderAggregationHeaderCell(
+                aggregation, index, removeItem));
 
     return m(
-        'table.query-table.pivot-table',
+        'table.pivot-table',
         m('thead',
           // First row of the table, containing names of pivot and aggregation
           // columns, as well as popup menus to modify the columns. Last cell
           // is empty because of an extra column with "drill down" button for
           // each pivot table row.
-          m('tr',
+          m('tr.header',
             m(ReorderableCellGroup, {
               cells: pivotTableHeaders,
               onReorder: (
@@ -559,16 +549,15 @@
               },
             }),
             m(ReorderableCellGroup, {
-              cells: slicePivotTableHeaders,
+              cells: aggregationTableHeaders,
               onReorder:
                   (from: number, to: number, direction: DropDirection) => {
-                    globals.dispatch(Actions.changePivotTableSlicePivotOrder(
+                    globals.dispatch(Actions.changePivotTableAggregationOrder(
                         {from, to, direction}));
                     globals.dispatch(Actions.setPivotTableQueryRequested(
                         {queryRequested: true}));
                   },
             }),
-            aggregationTableHeaders,
             m('td.menu', m(PopupMenuButton, {
                 icon: 'menu',
                 items: [{
diff --git a/ui/src/frontend/pivot_table_redux_query_generator.ts b/ui/src/frontend/pivot_table_redux_query_generator.ts
index 0fd891a..4ff1c97 100644
--- a/ui/src/frontend/pivot_table_redux_query_generator.ts
+++ b/ui/src/frontend/pivot_table_redux_query_generator.ts
@@ -28,9 +28,7 @@
 import {globals} from './globals';
 import {
   Aggregation,
-  AggregationFunction,
   TableColumn,
-  tableColumnEquals,
 } from './pivot_table_redux_types';
 
 export interface Table {
@@ -40,14 +38,14 @@
 
 export const sliceTable = {
   name: 'slice',
-  columns: ['type', 'ts', 'dur', 'category', 'name'],
+  columns: ['type', 'ts', 'dur', 'category', 'name', 'depth'],
 };
 
 // Columns of `slice` table available for aggregation.
-export const sliceAggregationColumns = ['ts', 'dur', 'depth'];
-
-// Columns of `thread_slice` table available for aggregation.
-export const threadSliceAggregationColumns = [
+export const sliceAggregationColumns = [
+  'ts',
+  'dur',
+  'depth',
   'thread_ts',
   'thread_dur',
   'thread_instruction_count',
@@ -90,22 +88,14 @@
   argument: string;
 }
 
-function outerAggregation(fn: AggregationFunction): AggregationFunction {
-  if (fn === 'COUNT') {
-    return 'SUM';
-  }
-  return fn;
-}
-
 // Exception thrown by query generator in case incoming parameters are not
 // suitable in order to build a correct query; these are caught by the UI and
 // displayed to the user.
 export class QueryGeneratorError extends Error {}
 
 // Internal column name for different rollover levels of aggregate columns.
-function aggregationAlias(
-    aggregationIndex: number, rolloverLevel: number): string {
-  return `agg_${aggregationIndex}_level_${rolloverLevel}`;
+function aggregationAlias(aggregationIndex: number): string {
+  return `agg_${aggregationIndex}`;
 }
 
 export function areaFilter(area: Area): string {
@@ -118,15 +108,10 @@
 
 export function expression(column: TableColumn): string {
   switch (column.kind) {
-    case 'regular': {
-      return column.column;
-    }
-    case 'argument': {
+    case 'regular':
+      return `${column.table}.${column.column}`;
+    case 'argument':
       return extractArgumentExpression(column.argument);
-    }
-    default: {
-      throw new Error(`malformed table column ${column}`);
-    }
   }
 }
 
@@ -138,92 +123,13 @@
       expression(aggregation.column)})`;
 }
 
-export function extractArgumentExpression(argument: string) {
-  return `extract_arg(arg_set_id, ${sqliteString(argument)})`;
+export function extractArgumentExpression(argument: string, table?: string) {
+  const prefix = table === undefined ? '' : `${table}.`;
+  return `extract_arg(${prefix}arg_set_id, ${sqliteString(argument)})`;
 }
 
-function generateInnerQuery(
-    pivots: TableColumn[],
-    aggregations: Aggregation[],
-    table: string,
-    includeTrack: boolean,
-    area: Area,
-    constrainToArea: boolean): {query: string, groupByColumns: string[]} {
-  const aggregationColumns: string[] = [];
-
-  for (let i = 0; i < aggregations.length; i++) {
-    aggregationColumns.push(`${aggregationExpression(aggregations[i])} as ${
-        aggregationAlias(i, 0)}`);
-  }
-
-  const selectColumns: string[] = [];
-  const groupByColumns: string[] = [];
-
-  let argumentCount = 0;
-  for (const column of pivots) {
-    switch (column.kind) {
-      case 'regular': {
-        selectColumns.push(column.column);
-        groupByColumns.push(column.column);
-        break;
-      }
-      case 'argument': {
-        const alias = `pivot_argument_${argumentCount++}`;
-        selectColumns.push(
-            `${extractArgumentExpression(column.argument)} as ${alias}`);
-        groupByColumns.push(alias);
-        break;
-      }
-      default: {
-        throw new Error(`malformed table column ${column}`);
-      }
-    }
-  }
-  if (includeTrack) {
-    selectColumns.push('track_id');
-  }
-
-  const query = `
-    select
-      ${selectColumns.concat(aggregationColumns).join(',\n')}
-    from ${table}
-    ${(constrainToArea ? `where ${areaFilter(area)}` : '')}
-    group by ${
-      groupByColumns.concat(includeTrack ? ['track_id'] : []).join(', ')}
-  `;
-
-  return {query, groupByColumns};
-}
-
-function computeSliceTableAggregations(
-    selectedAggregations: Map<string, Aggregation>):
-    {tableName: string, flatAggregations: Aggregation[]} {
-  let hasThreadSliceColumn = false;
-  const allColumns: Aggregation[] = [];
-  for (const tableColumn of selectedAggregations.values()) {
-    if (tableColumn.column.kind === 'regular' &&
-        tableColumn.column.table === 'thread_slice') {
-      hasThreadSliceColumn = true;
-    }
-    allColumns.push(tableColumn);
-  }
-
-  return {
-    // If any aggregation column from `thread_slice` is present, it's going to
-    // be the base table for the pivot table query. Otherwise, `slice` is used.
-    // This later is going to be controllable by a UI element.
-    tableName: hasThreadSliceColumn ? 'thread_slice' : 'slice',
-    flatAggregations: allColumns,
-  };
-}
-
-// Every aggregation in the request is contained in the result in (number of
-// pivots + 1) times for each rollover level. This helper function returs an
-// index of the necessary column in the response.
-export function aggregationIndex(
-    pivotColumns: number, aggregationNo: number, depth: number) {
-  return pivotColumns + aggregationNo * (pivotColumns + 1) +
-      (pivotColumns - depth);
+export function aggregationIndex(pivotColumns: number, aggregationNo: number) {
+  return pivotColumns + aggregationNo;
 }
 
 export function generateQueryFromState(
@@ -232,108 +138,52 @@
   if (state.selectionArea === undefined) {
     throw new QueryGeneratorError('Should not be called without area');
   }
-  return generateQuery(
-      state.selectedPivots,
-      state.selectedSlicePivots,
-      state.selectedAggregations,
-      globals.state.areas[state.selectionArea.areaId],
-      state.constrainToArea);
-}
 
-export function generateQuery(
-    nonSlicePivots: RegularColumn[],
-    slicePivots: TableColumn[],
-    selectedAggregations: Map<string, Aggregation>,
-    area: Area,
-    constrainToArea: boolean): PivotTableReduxQuery {
-  const sliceTableAggregations =
-      computeSliceTableAggregations(selectedAggregations);
-
-  if (sliceTableAggregations.flatAggregations.length === 0) {
+  const sliceTableAggregations = [...state.selectedAggregations.values()];
+  if (sliceTableAggregations.length === 0) {
     throw new QueryGeneratorError('No aggregations selected');
   }
 
-  if (slicePivots.length === 0 && nonSlicePivots.length === 0) {
-    throw new QueryGeneratorError('No pivots selected');
-  }
+  const pivots = state.selectedPivots;
 
-  const outerAggregations = [];
-  const innerQuery = generateInnerQuery(
-      slicePivots,
-      sliceTableAggregations.flatAggregations,
-      sliceTableAggregations.tableName,
-      nonSlicePivots.length > 0,
-      area,
-      constrainToArea);
+  const aggregations = sliceTableAggregations.map(
+      (agg, index) =>
+          `${aggregationExpression(agg)} as ${aggregationAlias(index)}`);
 
-  const prefixedSlicePivots =
-      innerQuery.groupByColumns.map((p) => `preaggregated.${p}`);
-  const renderedNonSlicePivots =
-      nonSlicePivots.map((pivot) => `${pivot.table}.${pivot.column}`);
-  const totalPivotsArray = renderedNonSlicePivots.concat(prefixedSlicePivots);
-  const sortCriteria =
-      globals.state.nonSerializableState.pivotTableRedux.sortCriteria;
+  const renderedPivots =
+      pivots.map((pivot) => `${pivot.table}.${pivot.column}`);
   const sortClauses: string[] = [];
-  for (let i = 0; i < sliceTableAggregations.flatAggregations.length; i++) {
-    const agg = `preaggregated.${aggregationAlias(i, 0)}`;
-    const fn = outerAggregation(
-        sliceTableAggregations.flatAggregations[i].aggregationFunction);
-    outerAggregations.push(`${fn}(${agg}) as ${aggregationAlias(i, 0)}`);
-
-    for (let level = 1; level < totalPivotsArray.length; level++) {
-      // Peculiar form "SUM(SUM(agg)) over (partition by columns)" here means
-      // following: inner SUM(agg) is an aggregation that is going to collapse
-      // tracks with the same pivot values, which is going to be post-aggregated
-      // by the set of columns by outer **window** SUM function.
-
-      // Need to use complicated query syntax can be avoided by having yet
-      // another nested subquery computing only aggregation values with window
-      // functions in the wrapper, but the generation code is going to be more
-      // complex; so complexity of the query is traded for complexity of the
-      // query generator.
-      outerAggregations.push(`${fn}(${fn}(${agg})) over (partition by ${
-          totalPivotsArray.slice(0, totalPivotsArray.length - level)
-              .join(', ')}) as ${aggregationAlias(i, level)}`);
-    }
-
-    outerAggregations.push(`${fn}(${fn}(${agg})) over () as ${
-        aggregationAlias(i, totalPivotsArray.length)}`);
-
-    if (sortCriteria !== undefined &&
-        tableColumnEquals(
-            sliceTableAggregations.flatAggregations[i].column,
-            sortCriteria.column)) {
-      for (let level = totalPivotsArray.length - 1; level >= 0; level--) {
-        sortClauses.push(`${aggregationAlias(i, level)} ${sortCriteria.order}`);
-      }
+  for (let i = 0; i < sliceTableAggregations.length; i++) {
+    const sortDirection = sliceTableAggregations[i].sortDirection;
+    if (sortDirection !== undefined) {
+      sortClauses.push(`${aggregationAlias(i)} ${sortDirection}`);
     }
   }
 
   const joins = `
-    join thread_track on thread_track.id = preaggregated.track_id
-    join thread using (utid)
-    join process using (upid)
+    left join thread_track on thread_track.id = slice.track_id
+    left join thread using (utid)
+    left join process using (upid)
   `;
 
+  const whereClause = state.constrainToArea ?
+      `where ${areaFilter(globals.state.areas[state.selectionArea.areaId])}` :
+      '';
   const text = `
     select
-      ${
-      renderedNonSlicePivots.concat(prefixedSlicePivots, outerAggregations)
-          .join(',\n')}
-    from (
-      ${innerQuery.query}
-    ) preaggregated
-    ${nonSlicePivots.length > 0 ? joins : ''}
-    group by ${renderedNonSlicePivots.concat(prefixedSlicePivots).join(', ')}
+      ${renderedPivots.concat(aggregations).join(',\n')}
+    from slice
+    ${pivots.length > 0 ? joins : ''}
+    ${whereClause}
+    group by ${renderedPivots.join(', ')}
     ${sortClauses.length > 0 ? ('order by ' + sortClauses.join(', ')) : ''}
   `;
 
   return {
     text,
     metadata: {
-      tableName: sliceTableAggregations.tableName,
-      pivotColumns: (nonSlicePivots as TableColumn[]).concat(slicePivots),
-      aggregationColumns: sliceTableAggregations.flatAggregations,
+      pivotColumns: pivots,
+      aggregationColumns: sliceTableAggregations,
     },
   };
 }
diff --git a/ui/src/frontend/pivot_table_redux_types.ts b/ui/src/frontend/pivot_table_redux_types.ts
index 9d61f79..6daeb30 100644
--- a/ui/src/frontend/pivot_table_redux_types.ts
+++ b/ui/src/frontend/pivot_table_redux_types.ts
@@ -12,7 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import {EqualsBuilder} from '../common/comparator_builder';
 import {ColumnType} from '../common/query_result';
+import {SortDirection} from '../common/state';
 
 // Node in the hierarchical pivot tree. Only leaf nodes contain data from the
 // query result.
@@ -61,14 +63,16 @@
   }
 }
 
-export function toggleEnabled(
-    arr: TableColumn[], column: TableColumn, enabled: boolean): void {
-  if (enabled &&
-      arr.find((value) => tableColumnEquals(column, value)) === undefined) {
+export function toggleEnabled<T>(
+    compare: (fst: T, snd: T) => boolean,
+    arr: T[],
+    column: T,
+    enabled: boolean): void {
+  if (enabled && arr.find((value) => compare(column, value)) === undefined) {
     arr.push(column);
   }
   if (!enabled) {
-    const index = arr.findIndex((value) => tableColumnEquals(column, value));
+    const index = arr.findIndex((value) => compare(column, value));
     if (index !== -1) {
       arr.splice(index, 1);
     }
@@ -78,6 +82,16 @@
 export interface Aggregation {
   aggregationFunction: AggregationFunction;
   column: TableColumn;
+
+  // If the aggregation is sorted, the field contains a sorting direction.
+  sortDirection?: SortDirection;
+}
+
+export function aggregationEquals(agg1: Aggregation, agg2: Aggregation) {
+  return new EqualsBuilder(agg1, agg2)
+      .comparePrimitive((agg) => agg.aggregationFunction)
+      .compare(tableColumnEquals, (agg) => agg.column)
+      .equals();
 }
 
 // Used to convert TableColumn to a string in order to store it in a Map, as
@@ -92,9 +106,6 @@
     case 'regular': {
       return `${tableColumn.table}.${tableColumn.column}`;
     }
-    default: {
-      throw new Error(`malformed table column ${tableColumn}`);
-    }
   }
 }
 
diff --git a/ui/src/frontend/post_message_handler.ts b/ui/src/frontend/post_message_handler.ts
index 5aa077e..4c18e10 100644
--- a/ui/src/frontend/post_message_handler.ts
+++ b/ui/src/frontend/post_message_handler.ts
@@ -14,15 +14,20 @@
 
 import * as m from 'mithril';
 
-import {Actions, PostedTrace} from '../common/actions';
+import {Actions, PostedScrollToRange, PostedTrace} from '../common/actions';
 
 import {globals} from './globals';
 import {showModal} from './modal';
+import {focusHorizontalRange} from './scroll_helper';
 
 interface PostedTraceWrapped {
   perfetto: PostedTrace;
 }
 
+interface PostedScrollToRangeWrapped {
+  perfetto: PostedScrollToRange;
+}
+
 // Returns whether incoming traces should be opened automatically or should
 // instead require a user interaction.
 function isTrustedOrigin(origin: string): boolean {
@@ -33,10 +38,19 @@
   ];
   if (origin === window.origin) return true;
   if (TRUSTED_ORIGINS.includes(origin)) return true;
-  if (new URL(origin).hostname.endsWith('corp.google.com')) return true;
+
+  const hostname = new URL(origin).hostname;
+  if (hostname.endsWith('corp.google.com')) return true;
+  if (hostname === 'localhost' || hostname === '127.0.0.1') return true;
   return false;
 }
 
+// Returns whether we should ignore a given message based on the value of
+// the 'perfettoIgnore' field in the event data.
+function shouldGracefullyIgnoreMessage(messageEvent: MessageEvent) {
+  return messageEvent.data.perfettoIgnore === true;
+}
+
 // The message handler supports loading traces from an ArrayBuffer.
 // There is no other requirement than sending the ArrayBuffer as the |data|
 // property. However, since this will happen across different origins, it is not
@@ -44,6 +58,12 @@
 // 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 (shouldGracefullyIgnoreMessage(messageEvent)) {
+    // This message should not be handled in this handler,
+    // because it will be handled elsewhere.
+    return;
+  }
+
   if (messageEvent.origin === 'https://tagassistant.google.com') {
     // The GA debugger, does a window.open() and sends messages to the GA
     // script. Ignore them.
@@ -84,6 +104,13 @@
     return;
   }
 
+  let postedScrollToRange: PostedScrollToRange;
+  if (isPostedScrollToRange(messageEvent.data)) {
+    postedScrollToRange = messageEvent.data.perfetto;
+    scrollToTimeRange(postedScrollToRange);
+    return;
+  }
+
   let postedTrace: PostedTrace;
   let keepApiOpen = false;
   if (isPostedTraceWrapped(messageEvent.data)) {
@@ -156,7 +183,42 @@
 }
 
 function sanitizeString(str: string): string {
-  return str.replace(/[^A-Za-z0-9.\-_#:/?=&;%+ ]/g, ' ');
+  return str.replace(/[^A-Za-z0-9.\-_#:/?=&;%+$ ]/g, ' ');
+}
+
+function isTraceViewerReady(): boolean {
+  return !!(globals.getCurrentEngine()?.ready);
+}
+
+const _maxScrollToRangeAttempts = 20;
+async function scrollToTimeRange(
+    postedScrollToRange: PostedScrollToRange, maxAttempts?: number) {
+  const ready = isTraceViewerReady();
+  if (!ready) {
+    if (maxAttempts === undefined) {
+      maxAttempts = 0;
+    }
+    if (maxAttempts > _maxScrollToRangeAttempts) {
+      console.warn('Could not scroll to time range. Trace viewer not ready.');
+      return;
+    }
+    setTimeout(scrollToTimeRange, 200, postedScrollToRange, maxAttempts + 1);
+  } else {
+    focusHorizontalRange(
+        postedScrollToRange.timeStart,
+        postedScrollToRange.timeEnd,
+        postedScrollToRange.viewPercentage);
+  }
+}
+
+function isPostedScrollToRange(obj: unknown):
+    obj is PostedScrollToRangeWrapped {
+  const wrapped = obj as PostedScrollToRangeWrapped;
+  if (wrapped.perfetto === undefined) {
+    return false;
+  }
+  return wrapped.perfetto.timeStart !== undefined ||
+      wrapped.perfetto.timeEnd !== undefined;
 }
 
 function isPostedTraceWrapped(obj: any): obj is PostedTraceWrapped {
diff --git a/ui/src/frontend/query_history.ts b/ui/src/frontend/query_history.ts
new file mode 100644
index 0000000..35aa9bf
--- /dev/null
+++ b/ui/src/frontend/query_history.ts
@@ -0,0 +1,161 @@
+// Copyright (C) 2022 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 * as m from 'mithril';
+
+import {Actions} from '../common/actions';
+
+import {globals} from './globals';
+import {
+  arrayOf,
+  bool,
+  record,
+  runValidator,
+  str,
+  ValidatedType,
+} from '../controller/validators';
+import {assertTrue} from '../base/logging';
+
+const QUERY_HISTORY_KEY = 'queryHistory';
+
+export class QueryHistoryComponent implements m.ClassComponent {
+  view(): m.Child {
+    const unstarred: HistoryItemComponentAttrs[] = [];
+    const starred: HistoryItemComponentAttrs[] = [];
+    for (let i = queryHistoryStorage.data.length - 1; i >= 0; i--) {
+      const entry = queryHistoryStorage.data[i];
+      const arr = entry.starred ? starred : unstarred;
+      arr.push({index: i, entry});
+    }
+    return m(
+        '.query-history',
+        m('header.overview',
+          `Query history (${queryHistoryStorage.data.length} queries)`),
+        starred.map((attrs) => m(HistoryItemComponent, attrs)),
+        unstarred.map((attrs) => m(HistoryItemComponent, attrs)));
+  }
+}
+
+export interface HistoryItemComponentAttrs {
+  index: number;
+  entry: QueryHistoryEntry;
+}
+
+export class HistoryItemComponent implements
+    m.ClassComponent<HistoryItemComponentAttrs> {
+  view(vnode: m.Vnode<HistoryItemComponentAttrs>): m.Child {
+    const query = vnode.attrs.entry.query;
+    return m(
+        '.history-item',
+        m('.history-item-buttons',
+          m('button',
+            {
+              onclick: () => {
+                queryHistoryStorage.setStarred(
+                    vnode.attrs.index, !vnode.attrs.entry.starred);
+                globals.rafScheduler.scheduleFullRedraw();
+              },
+            },
+            m('i.material-icons',
+              vnode.attrs.entry.starred ? 'star' : 'star_outline')),
+          m('button',
+            {
+              onclick: () => {
+                globals.dispatch(Actions.executeQuery(
+                    {queryId: 'analyze-page-query', query}));
+              },
+            },
+            m('i.material-icons', 'play_arrow')),
+          m('button',
+            {
+              onclick: () => {
+                queryHistoryStorage.remove(vnode.attrs.index);
+                globals.rafScheduler.scheduleFullRedraw();
+              },
+            },
+            m('i.material-icons', 'delete'))),
+        m('pre', query));
+  }
+}
+
+class HistoryStorage {
+  data: QueryHistory;
+  maxItems = 50;
+
+  constructor() {
+    this.data = this.load();
+  }
+
+  saveQuery(query: string) {
+    const items = this.data;
+    let firstUnstarred = -1;
+    let countUnstarred = 0;
+    for (let i = 0; i < items.length; i++) {
+      if (!items[i].starred) {
+        countUnstarred++;
+        if (firstUnstarred === -1) {
+          firstUnstarred = i;
+        }
+      }
+
+      if (items[i].query === query) {
+        // Query is already in the history, no need to save
+        return;
+      }
+    }
+
+    if (countUnstarred >= this.maxItems) {
+      assertTrue(firstUnstarred !== -1);
+      items.splice(firstUnstarred, 1);
+    }
+
+    items.push({query, starred: false});
+    this.save();
+  }
+
+  setStarred(index: number, starred: boolean) {
+    assertTrue(index >= 0 && index < this.data.length);
+    this.data[index].starred = starred;
+    this.save();
+  }
+
+  remove(index: number) {
+    assertTrue(index >= 0 && index < this.data.length);
+    this.data.splice(index, 1);
+    this.save();
+  }
+
+  private load(): QueryHistory {
+    const value = window.localStorage.getItem(QUERY_HISTORY_KEY);
+    if (value === null) {
+      return [];
+    }
+
+    return runValidator(queryHistoryValidator, JSON.parse(value)).result;
+  }
+
+  private save() {
+    window.localStorage.setItem(QUERY_HISTORY_KEY, JSON.stringify(this.data));
+  }
+}
+
+const queryHistoryEntryValidator = record({query: str(), starred: bool()});
+
+type QueryHistoryEntry = ValidatedType<typeof queryHistoryEntryValidator>;
+
+const queryHistoryValidator = arrayOf(queryHistoryEntryValidator);
+
+type QueryHistory = ValidatedType<typeof queryHistoryValidator>;
+
+export const queryHistoryStorage = new HistoryStorage();
diff --git a/ui/src/frontend/query_table.ts b/ui/src/frontend/query_table.ts
index c5ca901..2469b24 100644
--- a/ui/src/frontend/query_table.ts
+++ b/ui/src/frontend/query_table.ts
@@ -20,7 +20,7 @@
 import {Row} from '../common/query_result';
 import {fromNs} from '../common/time';
 
-import {queryResponseToClipboard} from './clipboard';
+import {copyToClipboard, queryResponseToClipboard} from './clipboard';
 import {globals} from './globals';
 import {Panel} from './panel';
 import {Router} from './router';
@@ -79,7 +79,12 @@
     const cells = [];
     const {row, columns} = vnode.attrs;
     for (const col of columns) {
-      cells.push(m('td', row[col]));
+      const value = row[col];
+      if (value instanceof Uint8Array) {
+        cells.push(m('td', `<BLOB sz=${value.length}>`));
+      } else {
+        cells.push(m('td', value));
+      }
     }
     const containsSliceLocation =
         QueryTableRow.columnsContainsSliceLocation(columns);
@@ -138,8 +143,16 @@
     const headers = [
       m(
           'header.overview',
-          `Query result - ${Math.round(resp.durationMs)} ms`,
-          m('span.code', resp.query),
+          m('span', `Query result - ${Math.round(resp.durationMs)} ms`),
+          m('span.code.text-select', resp.query),
+          m('span.spacer'),
+          m('button.query-ctrl',
+            {
+              onclick: () => {
+                copyToClipboard(resp.query);
+              },
+            },
+            'Copy query'),
           resp.error ? null :
                        m('button.query-ctrl',
                          {
@@ -147,7 +160,7 @@
                              queryResponseToClipboard(resp);
                            },
                          },
-                         'Copy as .tsv'),
+                         'Copy result (.tsv)'),
           m('button.query-ctrl',
             {
               onclick: () => {
diff --git a/ui/src/frontend/raf_scheduler.ts b/ui/src/frontend/raf_scheduler.ts
index cdf3dad..17df15c 100644
--- a/ui/src/frontend/raf_scheduler.ts
+++ b/ui/src/frontend/raf_scheduler.ts
@@ -90,6 +90,7 @@
     this.canvasRedrawCallbacks.delete(cb);
   }
 
+  // Schedule re-rendering of canvas only.
   scheduleRedraw() {
     this.maybeScheduleAnimationFrame(true);
   }
@@ -98,10 +99,11 @@
     this._shutdown = true;
   }
 
-  set domRedraw(cb: RedrawCallback|null) {
-    this._syncDomRedraw = cb || ((_) => {});
+  set domRedraw(cb: RedrawCallback) {
+    this._syncDomRedraw = cb;
   }
 
+  // Schedule re-rendering of virtual DOM and canvas.
   scheduleFullRedraw() {
     this.requestedFullRedraw = true;
     this.maybeScheduleAnimationFrame(true);
diff --git a/ui/src/frontend/record_page.ts b/ui/src/frontend/record_page.ts
index 10e747a..d142ee6 100644
--- a/ui/src/frontend/record_page.ts
+++ b/ui/src/frontend/record_page.ts
@@ -434,7 +434,8 @@
 
   // We don't need commands to start tracing on chrome
   if (isChromeTarget(target)) {
-    return globals.state.extensionInstalled ?
+    return globals.state.extensionInstalled &&
+            !globals.state.recordingInProgress ?
         m('div',
           m('label',
             `To trace Chrome from the Perfetto UI you just have to press
diff --git a/ui/src/frontend/record_page_v2.ts b/ui/src/frontend/record_page_v2.ts
index 6474ab8..105411d 100644
--- a/ui/src/frontend/record_page_v2.ts
+++ b/ui/src/frontend/record_page_v2.ts
@@ -14,54 +14,34 @@
 
 
 import * as m from 'mithril';
+import {Attributes} from 'mithril';
 
 import {assertExists} from '../base/logging';
 import {Actions} from '../common/actions';
-import {TRACE_SUFFIX} from '../common/constants';
-import {TraceConfig} from '../common/protos';
 import {
-  BUFFER_USAGE_INCORRECT_FORMAT,
-  BUFFER_USAGE_NOT_ACCESSIBLE,
-  EXTENSION_NAME,
-  EXTENSION_URL,
-} from '../common/recordingV2/chrome_utils';
-import {
-  genTraceConfig,
   RecordingConfigUtils,
 } from '../common/recordingV2/recording_config_utils';
 import {
-  RecordingError,
-  showRecordingModal,
-} from '../common/recordingV2/recording_error_handling';
-import {
   ChromeTargetInfo,
-  OnTargetChangeCallback,
   RecordingTargetV2,
   TargetInfo,
-  TracingSession,
-  TracingSessionListener,
 } from '../common/recordingV2/recording_interfaces_v2';
 import {
-  ANDROID_WEBSOCKET_TARGET_FACTORY,
-  AndroidWebsocketTargetFactory,
-} from
-    '../common/recordingV2/target_factories/android_websocket_target_factory';
+  RecordingPageController,
+  RecordingState,
+} from '../common/recordingV2/recording_page_controller';
 import {
-  ANDROID_WEBUSB_TARGET_FACTORY,
-} from '../common/recordingV2/target_factories/android_webusb_target_factory';
+  EXTENSION_NAME,
+  EXTENSION_URL,
+} from '../common/recordingV2/recording_utils';
 import {
   targetFactoryRegistry,
 } from '../common/recordingV2/target_factory_registry';
-import {
-  RECORDING_IN_PROGRESS,
-} from '../common/recordingV2/traced_tracing_session';
-import {hasActiveProbes} from '../common/state';
-import {currentDateHourAndMinute} from '../common/time';
 
 import {globals} from './globals';
+import {fullscreenModalContainer} from './modal';
 import {createPage, PageAttrs} from './pages';
-import {publishBufferUsage} from './publish';
-import {autosaveConfigStore, recordConfigStore} from './record_config';
+import {recordConfigStore} from './record_config';
 import {
   Configurations,
   maybeGetActiveCss,
@@ -76,117 +56,28 @@
 import {GpuSettings} from './recording/gpu_settings';
 import {MemorySettings} from './recording/memory_settings';
 import {PowerSettings} from './recording/power_settings';
-import {couldNotClaimInterface} from './recording/recording_modal';
 import {RecordingSectionAttrs} from './recording/recording_sections';
 import {RecordingSettings} from './recording/recording_settings';
+import {
+  FORCE_RESET_MESSAGE,
+} from './recording/recording_ui_utils';
+import {addNewTarget} from './recording/reset_target_modal';
 
-// Wraps all calls to a recording target and handles the errors that can be
-// thrown during these calls.
-async function connectToRecordingTarget(
-    target: RecordingTargetV2,
-    tracingSessionListener: TracingSessionListener,
-    executeConnection: () => Promise<void>) {
-  const createSession = async () => {
-    try {
-      await executeConnection();
-    } catch (e) {
-      tracingSessionListener.onError(e.message);
-    }
-  };
+const START_RECORDING_MESSAGE = 'Start Recording';
 
-  if (await target.canConnectWithoutContention()) {
-    await createSession();
-  } else {
-    couldNotClaimInterface(createSession, clearRecordingState);
-  }
-}
-
-// Wraps a tracing session promise while the promise is being resolved (e.g.
-// while we are awaiting for ADB auth).
-class TracingSessionWrapper {
-  private tracingSession?: TracingSession = undefined;
-  private isCancelled = false;
-
-  constructor(private traceConfig: TraceConfig, target: RecordingTargetV2) {
-    connectToRecordingTarget(target, tracingSessionListener, async () => {
-      const session = await target.createTracingSession(tracingSessionListener);
-      this.onSessionPromiseResolved(session);
-    });
-  }
-
-  cancel() {
-    if (!this.tracingSession) {
-      this.isCancelled = true;
-      return;
-    }
-    this.tracingSession.cancel();
-  }
-
-  stop() {
-    if (!this.tracingSession) {
-      this.isCancelled = true;
-      return;
-    }
-    this.tracingSession.stop();
-  }
-
-  getTraceBufferUsage(): Promise<number> {
-    if (!this.tracingSession) {
-      throw new RecordingError(BUFFER_USAGE_NOT_ACCESSIBLE);
-    }
-    return this.tracingSession.getTraceBufferUsage();
-  }
-
-  private onSessionPromiseResolved(session: TracingSession) {
-    // We cancel the received trace if it is marked as cancelled. For instance:
-    // - The user clicked 'Start', then 'Stop' without authorizing, then 'Start'
-    // and then authorized.
-    if (this.isCancelled) {
-      session.cancel();
-      return;
-    }
-
-    this.tracingSession = session;
-    this.tracingSession.start(this.traceConfig);
-    globals.rafScheduler.scheduleFullRedraw();
-  }
-}
-
-const adbWebsocketUrl = 'ws://127.0.0.1:8037/adb';
+const controller = new RecordingPageController();
 const recordConfigUtils = new RecordingConfigUtils();
-let recordingTargetV2: RecordingTargetV2|undefined = undefined;
-let tracingSessionWrapper: TracingSessionWrapper|undefined = undefined;
+// Whether the target selection modal is displayed.
+let shouldDisplayTargetModal: boolean = false;
 
-const tracingSessionListener: TracingSessionListener = {
-  onTraceData: (trace: Uint8Array) => {
-    globals.dispatch(Actions.openTraceFromBuffer({
-      title: 'Recorded trace',
-      buffer: trace.buffer,
-      fileName: `trace_${currentDateHourAndMinute()}${TRACE_SUFFIX}`,
-    }));
-    clearRecordingState();
-  },
-  onStatus: (message: string) => {
-    // For the 'Recording in progress for 7000ms we don't show a modal.'
-    if (message.startsWith(RECORDING_IN_PROGRESS)) {
-      globals.dispatch(Actions.setRecordingStatus({status: message}));
-    } else {
-      // For messages such as 'Please allow USB debugging on your device, which
-      // require a user action, we show a modal.
-      showRecordingModal(message);
-    }
-  },
-  onDisconnect: (errorMessage?: string) => {
-    if (errorMessage) {
-      showRecordingModal(errorMessage);
-    }
-    clearRecordingState();
-  },
-  onError: (message: string) => {
-    showRecordingModal(message);
-    clearRecordingState();
-  },
-};
+// Options for displaying a target selection menu.
+export interface TargetSelectionOptions {
+  // css attributes passed to the mithril components which displays the target
+  // selection menu.
+  attributes: Attributes;
+  // Whether the selection should be preceded by a text label.
+  shouldDisplayLabel: boolean;
+}
 
 function isChromeTargetInfo(targetInfo: TargetInfo):
     targetInfo is ChromeTargetInfo {
@@ -196,7 +87,7 @@
 function RecordHeader() {
   const platformSelection = RecordingPlatformSelection();
   const statusLabel = RecordingStatusLabel();
-  const buttons = RecordingButtons();
+  const buttons = RecordingButton();
   const notes = RecordingNotes();
   if (!platformSelection && !statusLabel && !buttons && !notes) {
     // The header should not be displayed when it has no content.
@@ -210,54 +101,76 @@
       notes);
 }
 
-
 function RecordingPlatformSelection() {
   // Don't show the platform selector while we are recording a trace.
-  if (tracingSessionWrapper) return undefined;
+  if (controller.getState() >= RecordingState.RECORDING) return undefined;
 
-  const components = [];
-  components.push(
+  return m(
+      '.target',
       m('.chip',
-        {onclick: addAndroidDevice},
-        m('button', 'Add ADB Device'),
-        m('i.material-icons', 'add')));
-
-  if (recordingTargetV2) {
-    const targets = [];
-    let selectedIndex = 0;
-    for (const [i, target] of targetFactoryRegistry.listTargets().entries()) {
-      targets.push(m('option', target.getInfo().name));
-      if (recordingTargetV2 &&
-          target.getInfo().name === recordingTargetV2.getInfo().name) {
-        selectedIndex = i;
-      }
-    }
-    components.push(m(
-        'label',
-        'Target platform:',
-        m('select',
-          {
-            selectedIndex,
-            onchange: (e: Event) => {
-              onTargetSelection((e.target as HTMLSelectElement).value);
-            },
-            onupdate: (select) => {
-              // Work around mithril bug
-              // (https://github.com/MithrilJS/mithril.js/issues/2107): We may
-              // update the select's options while also changing the
-              // selectedIndex at the same time. The update of selectedIndex
-              // may be applied before the new options are added to the select
-              // element. Because the new selectedIndex may be outside of the
-              // select's options at that time, we have to reselect the
-              // correct index here after any new children were added.
-              (select.dom as HTMLSelectElement).selectedIndex = selectedIndex;
-            },
+        {
+          onclick: () => {
+            shouldDisplayTargetModal = true;
+            fullscreenModalContainer.createNew(addNewTargetModal());
+            globals.rafScheduler.scheduleFullRedraw();
           },
-          ...targets),
-        ));
+        },
+        m('button', 'Add new recording target'),
+        m('i.material-icons', 'add')),
+      targetSelection());
+}
+
+function addNewTargetModal() {
+  return {
+    ...addNewTarget(controller),
+    onClose: () => shouldDisplayTargetModal = false,
+  };
+}
+
+export function targetSelection(): m.Vnode|undefined {
+  if (!controller.shouldShowTargetSelection()) {
+    return undefined;
   }
 
-  return m('.target', components);
+  const targets: RecordingTargetV2[] = targetFactoryRegistry.listTargets();
+  const targetNames = [];
+  const targetInfo = controller.getTargetInfo();
+  if (!targetInfo) {
+    targetNames.push(m('option', 'PLEASE_SELECT_TARGET'));
+  }
+
+  let selectedIndex = 0;
+  for (let i = 0; i < targets.length; i++) {
+    const targetName = targets[i].getInfo().name;
+    targetNames.push(m('option', targetName));
+    if (targetInfo && targetName === targetInfo.name) {
+      selectedIndex = i;
+    }
+  }
+
+  return m(
+      'label',
+      'Target platform:',
+      m('select',
+        {
+          selectedIndex,
+          onchange: (e: Event) => {
+            controller.onTargetSelection((e.target as HTMLSelectElement).value);
+          },
+          onupdate: (select) => {
+            // Work around mithril bug
+            // (https://github.com/MithrilJS/mithril.js/issues/2107): We may
+            // update the select's options while also changing the
+            // selectedIndex at the same time. The update of selectedIndex
+            // may be applied before the new options are added to the select
+            // element. Because the new selectedIndex may be outside of the
+            // select's options at that time, we have to reselect the
+            // correct index here after any new children were added.
+            (select.dom as HTMLSelectElement).selectedIndex = selectedIndex;
+          },
+        },
+        ...targetNames),
+  );
 }
 
 // This will display status messages which are informative, but do not require
@@ -269,32 +182,17 @@
   return m('label', recordingStatus);
 }
 
-async function addAndroidDevice(): Promise<void> {
-  try {
-    const target =
-        await targetFactoryRegistry.get(ANDROID_WEBUSB_TARGET_FACTORY)
-            .connectNewTarget();
-    await assignRecordingTarget(target);
-  } catch (e) {
-    if (e instanceof RecordingError) {
-      showRecordingModal(e.message);
-    } else {
-      throw e;
-    }
-  }
-}
-
-function onTargetSelection(targetName: string): void {
-  const allTargets = targetFactoryRegistry.listTargets();
-  assignRecordingTarget(
-      allTargets.find((t) => t.getInfo().name === targetName) || allTargets[0]);
-}
-
 function Instructions(cssClass: string) {
+  if (controller.getState() < RecordingState.TARGET_SELECTED) {
+    return undefined;
+  }
+  // We will have a valid target at this step because we checked the state.
+  const targetInfo = assertExists(controller.getTargetInfo());
+
   return m(
       `.record-section.instructions${cssClass}`,
       m('header', 'Recording command'),
-      (PERSIST_CONFIG_FLAG.get() && !tracingSessionWrapper) ?
+      (PERSIST_CONFIG_FLAG.get()) ?
           m('button.permalinkconfig',
             {
               onclick: () => {
@@ -304,36 +202,20 @@
             },
             'Share recording settings') :
           null,
-      RecordingSnippet(),
+      RecordingSnippet(targetInfo),
       BufferUsageProgressBar(),
       m('.buttons', StopCancelButtons()));
 }
 
-async function fetchBufferUsage() {
-  if (!tracingSessionWrapper) return;
-
-  try {
-    const percentage = await tracingSessionWrapper.getTraceBufferUsage();
-    publishBufferUsage({percentage});
-  } catch (e) {
-    if (e instanceof RecordingError) {
-      if (e.message === BUFFER_USAGE_INCORRECT_FORMAT) {
-        // If we have received an incorrectly formatted message, we will
-        // redraw, so we can query the buffer usage again.
-        globals.rafScheduler.scheduleFullRedraw();
-      }
-      // We ignore other possible tracing buffer message errors because they
-      // are not necessary for the trace to be successfully collected.
-    } else {
-      throw e;
-    }
-  }
-}
-
 function BufferUsageProgressBar() {
-  fetchBufferUsage();
+  // Show the Buffer Usage bar only after we start recording a trace.
+  if (controller.getState() !== RecordingState.RECORDING) {
+    return undefined;
+  }
 
-  const bufferUsage = globals.bufferUsage ? globals.bufferUsage : 0.0;
+  controller.fetchBufferUsage();
+
+  const bufferUsage = controller.getBufferUsagePercentage();
   // Buffer usage is not available yet on Android.
   if (bufferUsage === 0) return undefined;
 
@@ -344,8 +226,12 @@
 }
 
 function RecordingNotes() {
-  const sideloadUrl =
-      'https://perfetto.dev/docs/contributing/build-instructions#get-the-code';
+  if (controller.getState() !== RecordingState.TARGET_INFO_DISPLAYED) {
+    return undefined;
+  }
+  // We will have a valid target at this step because we checked the state.
+  const targetInfo = assertExists(controller.getTargetInfo());
+
   const linuxUrl = 'https://perfetto.dev/docs/quickstart/linux-tracing';
   const cmdlineUrl =
       'https://perfetto.dev/docs/quickstart/android-tracing#perfetto-cmdline';
@@ -354,18 +240,13 @@
 
   const msgFeatNotSupported =
       m('span', `Some probes are only supported in Perfetto versions running
-      on Android Q+. `);
+      on Android Q+. Therefore, Perfetto will sideload the latest version onto 
+      the device.`);
 
-  const msgPerfettoNotSupported =
-      m('span', `Perfetto is not supported natively before Android P. `);
-
-  const msgSideload =
-      m('span',
-        `If you have a rooted device you can `,
-        m('a',
-          {href: sideloadUrl, target: '_blank'},
-          `sideload the latest version of
-         Perfetto.`));
+  const msgPerfettoNotSupported = m(
+      'span',
+      `Perfetto is not supported natively before Android P. Therefore, Perfetto 
+       will sideload the latest version onto the device.`);
 
   const msgLinux =
       m('.note',
@@ -381,13 +262,13 @@
         {href: cmdlineUrl, target: '_blank'},
         `collect the trace using ADB.`));
 
-  const msgZeroProbes =
-      m('.note',
-        'It looks like you didn\'t add any probes. ' +
-            'Please add at least one to get a non-empty trace.');
-
-  if (!hasActiveProbes(globals.state.recordConfig)) {
-    notes.push(msgZeroProbes);
+  if (!recordConfigUtils
+           .fetchLatestRecordCommand(globals.state.recordConfig, targetInfo)
+           .hasDataSources) {
+    notes.push(
+        m('.note',
+          'It looks like you didn\'t add any probes. ' +
+              'Please add at least one to get a non-empty trace.'));
   }
 
   targetFactoryRegistry.listRecordingProblems().map((recordingProblem) => {
@@ -402,24 +283,20 @@
     }
   });
 
-  if (recordingTargetV2) {
-    const targetInfo = recordingTargetV2.getInfo();
-
-    switch (targetInfo.targetType) {
-      case 'LINUX':
-        notes.push(msgLinux);
-        break;
-      case 'ANDROID': {
-        const androidApiLevel = targetInfo.androidApiLevel;
-        if (androidApiLevel === 28) {
-          notes.push(m('.note', msgFeatNotSupported, msgSideload));
-        } else if (androidApiLevel && androidApiLevel <= 27) {
-          notes.push(m('.note', msgPerfettoNotSupported, msgSideload));
-        }
-        break;
+  switch (targetInfo.targetType) {
+    case 'LINUX':
+      notes.push(msgLinux);
+      break;
+    case 'ANDROID': {
+      const androidApiLevel = targetInfo.androidApiLevel;
+      if (androidApiLevel === 28) {
+        notes.push(m('.note', msgFeatNotSupported));
+      } else if (androidApiLevel && androidApiLevel <= 27) {
+        notes.push(m('.note', msgPerfettoNotSupported));
       }
-      default:
+      break;
     }
+    default:
   }
 
   if (globals.state.recordConfig.mode === 'LONG_TRACE') {
@@ -429,11 +306,10 @@
   return notes.length > 0 ? m('div', notes) : undefined;
 }
 
-function RecordingSnippet() {
-  const targetInfo = assertExists(recordingTargetV2).getInfo();
+function RecordingSnippet(targetInfo: TargetInfo) {
   // We don't need commands to start tracing on chrome
   if (isChromeTargetInfo(targetInfo)) {
-    if (tracingSessionWrapper) {
+    if (controller.getState() > RecordingState.AUTH_P2) {
       // If the UI has started tracing, don't display a message guiding the user
       // to start recording.
       return undefined;
@@ -441,17 +317,17 @@
     return m(
         'div',
         m('label', `To trace Chrome from the Perfetto UI you just have to press
-         'Start Recording'.`));
+         '${START_RECORDING_MESSAGE}'.`));
   }
   return m(CodeSnippet, {text: getRecordCommand(targetInfo)});
 }
 
 function getRecordCommand(targetInfo: TargetInfo): string {
-  const data = recordConfigUtils.fetchLatestRecordCommand(
-      globals.state.recordConfig, assertExists(recordingTargetV2));
+  const recordCommand = recordConfigUtils.fetchLatestRecordCommand(
+      globals.state.recordConfig, targetInfo);
 
-  const pbBase64 = data ? data.configProtoBase64 : '';
-  const pbtx = data ? data.configProtoText : '';
+  const pbBase64 = recordCommand ? recordCommand.configProtoBase64 : '';
+  const pbtx = recordCommand ? recordCommand.configProtoText : '';
   let cmd = '';
   if (targetInfo.targetType === 'ANDROID' &&
       targetInfo.androidApiLevel === 28) {
@@ -470,98 +346,46 @@
   return cmd;
 }
 
-function RecordingButtons() {
-  // We don't show the 'Start Recording' button if:
-  // A. There is no connected target.
-  // B. We have already started tracing.
-  // C. There is a connected Android target but we don't have user authorisation
-  // to record a trace.
-  if (!recordingTargetV2) {
+function RecordingButton() {
+  if (controller.getState() !== RecordingState.TARGET_INFO_DISPLAYED ||
+      !controller.canCreateTracingSession()) {
     return undefined;
   }
 
-  // We don't allow the user to press 'Start Recording' multiple times
-  // because this will create multiple connecting tracing sessions, which
-  // will block one another.
-  if (tracingSessionWrapper) {
+  // We know we have a target because we checked the state.
+  const targetInfo = assertExists(controller.getTargetInfo());
+  const hasDataSources =
+      recordConfigUtils
+          .fetchLatestRecordCommand(globals.state.recordConfig, targetInfo)
+          .hasDataSources;
+  if (!hasDataSources) {
     return undefined;
   }
 
-  const targetInfo = recordingTargetV2.getInfo();
-  // The absence of androidApiLevel shows that we have not connected to the
-  // device, therefore we can not start recording.
-  // TODO(octaviant): encapsulation should be stricter here, look into making
-  // this a method
-  if (targetInfo.targetType === 'ANDROID' && !targetInfo.androidApiLevel) {
-    return undefined;
-  }
-
-  const start =
-      m(`button`,
+  return m(
+      '.button',
+      m('button',
         {
-          class: tracingSessionWrapper ? '' : 'selected',
-          onclick: onStartRecordingPressed,
+          class: 'selected',
+          onclick: () => controller.onStartRecordingPressed(),
         },
-        'Start Recording');
-
-  const buttons: m.Children = [];
-  const targetType = targetInfo.targetType;
-  if (targetType === 'ANDROID' &&
-      globals.state.recordConfig.mode !== 'LONG_TRACE') {
-    buttons.push(start);
-  } else if (
-      isChromeTargetInfo(targetInfo) && targetInfo.isExtensionInstalled) {
-    buttons.push(start);
-  }
-  return m('.button', buttons);
+        START_RECORDING_MESSAGE));
 }
 
 function StopCancelButtons() {
   // Show the Stop/Cancel buttons only while we are recording a trace.
-  if (!tracingSessionWrapper) return undefined;
+  if (!controller.shouldShowStopCancelButtons()) {
+    return undefined;
+  }
 
   const stop =
-      m(`button.selected`,
-        {
-          onclick: () => {
-            assertExists(tracingSessionWrapper).stop();
-            clearRecordingState();
-          },
-        },
-        'Stop');
+      m(`button.selected`, {onclick: () => controller.onStop()}, 'Stop');
 
-  const cancel =
-      m(`button`,
-        {
-          onclick: () => {
-            assertExists(tracingSessionWrapper).cancel();
-            clearRecordingState();
-          },
-        },
-        'Cancel');
+  const cancel = m(`button`, {onclick: () => controller.onCancel()}, 'Cancel');
 
   return [stop, cancel];
 }
 
-function onStartRecordingPressed(): void {
-  location.href = '#!/record/instructions';
-  autosaveConfigStore.save(globals.state.recordConfig);
-
-  const target = assertExists(recordingTargetV2);
-  const targetInfo = target.getInfo();
-  globals.logging.logEvent(
-      'Record Trace', `Record trace (${targetInfo.targetType})`);
-  const traceConfig = genTraceConfig(globals.state.recordConfig, targetInfo);
-  tracingSessionWrapper = new TracingSessionWrapper(traceConfig, target);
-  globals.rafScheduler.scheduleFullRedraw();
-}
-
-function clearRecordingState() {
-  publishBufferUsage({percentage: 0});
-  globals.dispatch(Actions.setRecordingStatus({status: undefined}));
-  tracingSessionWrapper = undefined;
-}
-
 function recordMenu(routePage: string) {
   const chromeProbe =
       m('a[href="#!/record/chrome"]',
@@ -606,7 +430,9 @@
           m('.title', 'Advanced settings'),
           m('.sub', 'Complicated stuff for wizards')));
 
-  const targetType = assertExists(recordingTargetV2).getInfo().targetType;
+  // We only display the probes when we have a valid target, so it's not
+  // possible for the target to be undefined here.
+  const targetType = assertExists(controller.getTargetInfo()).targetType;
   const probes = [];
   if (targetType === 'CHROME_OS' || targetType === 'LINUX') {
     probes.push(cpuProbe, powerProbe, memoryProbe, chromeProbe, advancedProbe);
@@ -626,7 +452,9 @@
   return m(
       '.record-menu',
       {
-        class: tracingSessionWrapper ? 'disabled' : '',
+        class: controller.getState() > RecordingState.TARGET_INFO_DISPLAYED ?
+            'disabled' :
+            '',
         onclick: () => globals.rafScheduler.scheduleFullRedraw(),
       },
       m('header', 'Trace config'),
@@ -657,53 +485,27 @@
       m('ul', probes));
 }
 
-const onTargetChange: OnTargetChangeCallback = () => {
-  const allTargets = targetFactoryRegistry.listTargets();
-  if (recordingTargetV2 && allTargets.includes(recordingTargetV2)) {
-    globals.rafScheduler.scheduleFullRedraw();
-    return;
-  }
-  assignRecordingTarget(allTargets[0]);
-};
-
-async function assignRecordingTarget(selectedTarget?: RecordingTargetV2) {
-  // If the selected target is the same as the previous one, we don't need to
-  // do anything.
-  if (selectedTarget === recordingTargetV2) {
-    return;
-  }
-
-  // We assign the new target and redraw the page.
-  recordingTargetV2 = selectedTarget;
-  globals.rafScheduler.scheduleFullRedraw();
-
-  if (!recordingTargetV2) {
-    return;
-  }
-
-  await connectToRecordingTarget(
-      recordingTargetV2, tracingSessionListener, async () => {
-        if (!recordingTargetV2) {
-          return;
-        }
-        await recordingTargetV2.fetchTargetInfo(tracingSessionListener);
-      });
-}
-
 function getRecordContainer(subpage?: string): m.Vnode<any, any> {
   const components: m.Children[] = [RecordHeader()];
-  if (!recordingTargetV2) {
+  if (controller.getState() === RecordingState.NO_TARGET) {
     components.push(m('.full-centered', 'Please connect a valid target.'));
     return m('.record-container', components);
-  }
-
-  const targetInfo = recordingTargetV2.getInfo();
-  // The absence of androidApiLevel shows that we have not connected to the
-  // device because we do not have user authorization.
-  if (targetInfo.targetType === 'ANDROID' && !targetInfo.androidApiLevel) {
+  } else if (controller.getState() <= RecordingState.ASK_TO_FORCE_P1) {
+    components.push(
+        m('.full-centered',
+          'Can not access the device without resetting the ' +
+              `connection. Please refresh the page, then click ` +
+              `'${FORCE_RESET_MESSAGE}.'`));
+    return m('.record-container', components);
+  } else if (controller.getState() === RecordingState.AUTH_P1) {
     components.push(
         m('.full-centered', 'Please allow USB debugging on the device.'));
     return m('.record-container', components);
+  } else if (
+      controller.getState() === RecordingState.WAITING_FOR_TRACE_DISPLAY) {
+    components.push(
+        m('.full-centered', 'Waiting for the trace to be collected.'));
+    return m('.record-container', components);
   }
 
   const pages: m.Children = [];
@@ -732,7 +534,7 @@
   ]);
   for (const [section, component] of settingsSections.entries()) {
     pages.push(m(component, {
-      dataSources: targetInfo.dataSources,
+      dataSources: controller.getTargetInfo()?.dataSources || [],
       cssClass: maybeGetActiveCss(routePage, section),
     } as RecordingSectionAttrs));
   }
@@ -744,29 +546,20 @@
 export const RecordPageV2 = createPage({
 
   oninit(): void {
-    for (const targetFactory of targetFactoryRegistry.listTargetFactories()) {
-      if (targetFactory) {
-        targetFactory.setOnTargetChange(onTargetChange);
-      }
-    }
-
-    if (targetFactoryRegistry.has(ANDROID_WEBSOCKET_TARGET_FACTORY)) {
-      const websocketTargetFactory =
-          targetFactoryRegistry.get(ANDROID_WEBSOCKET_TARGET_FACTORY) as
-          AndroidWebsocketTargetFactory;
-      websocketTargetFactory.tryEstablishWebsocket(adbWebsocketUrl);
-    }
+    controller.initFactories();
   },
 
   view({attrs}: m.Vnode<PageAttrs>): void |
       m.Children {
-        if (!recordingTargetV2) {
-          assignRecordingTarget(targetFactoryRegistry.listTargets()[0]);
+        if (shouldDisplayTargetModal) {
+          fullscreenModalContainer.updateVdom(addNewTargetModal());
         }
 
         return m(
             '.record-page',
-            tracingSessionWrapper ? m('.hider') : [],
+            controller.getState() > RecordingState.TARGET_INFO_DISPLAYED ?
+                m('.hider') :
+                [],
             getRecordContainer(attrs.subpage));
       },
 });
diff --git a/ui/src/frontend/recording/android_settings.ts b/ui/src/frontend/recording/android_settings.ts
index f0b01fd..2536a69 100644
--- a/ui/src/frontend/recording/android_settings.ts
+++ b/ui/src/frontend/recording/android_settings.ts
@@ -13,6 +13,8 @@
 // limitations under the License.
 
 import * as m from 'mithril';
+
+import {DataSourceDescriptor} from '../../common/protos';
 import {globals} from '../globals';
 import {
   Dropdown,
@@ -24,6 +26,7 @@
   Toggle,
   ToggleAttrs,
 } from '../record_widgets';
+
 import {RecordingSectionAttrs} from './recording_sections';
 
 const LOG_BUFFERS = new Map<string, string>();
@@ -36,34 +39,42 @@
 LOG_BUFFERS.set('LID_STATS', 'Stats');
 LOG_BUFFERS.set('LID_SYSTEM', 'System');
 
-const ATRACE_CATEGORIES = new Map<string, string>();
-ATRACE_CATEGORIES.set('adb', 'ADB');
-ATRACE_CATEGORIES.set('aidl', 'AIDL calls');
-ATRACE_CATEGORIES.set('am', 'Activity Manager');
-ATRACE_CATEGORIES.set('audio', 'Audio');
-ATRACE_CATEGORIES.set('binder_driver', 'Binder Kernel driver');
-ATRACE_CATEGORIES.set('binder_lock', 'Binder global lock trace');
-ATRACE_CATEGORIES.set('bionic', 'Bionic C library');
-ATRACE_CATEGORIES.set('camera', 'Camera');
-ATRACE_CATEGORIES.set('dalvik', 'ART & Dalvik');
-ATRACE_CATEGORIES.set('database', 'Database');
-ATRACE_CATEGORIES.set('gfx', 'Graphics');
-ATRACE_CATEGORIES.set('hal', 'Hardware Modules');
-ATRACE_CATEGORIES.set('input', 'Input');
-ATRACE_CATEGORIES.set('network', 'Network');
-ATRACE_CATEGORIES.set('nnapi', 'Neural Network API');
-ATRACE_CATEGORIES.set('pm', 'Package Manager');
-ATRACE_CATEGORIES.set('power', 'Power Management');
-ATRACE_CATEGORIES.set('res', 'Resource Loading');
-ATRACE_CATEGORIES.set('rro', 'Resource Overlay');
-ATRACE_CATEGORIES.set('rs', 'RenderScript');
-ATRACE_CATEGORIES.set('sm', 'Sync Manager');
-ATRACE_CATEGORIES.set('ss', 'System Server');
-ATRACE_CATEGORIES.set('vibrator', 'Vibrator');
-ATRACE_CATEGORIES.set('video', 'Video');
-ATRACE_CATEGORIES.set('view', 'View System');
-ATRACE_CATEGORIES.set('webview', 'WebView');
-ATRACE_CATEGORIES.set('wm', 'Window Manager');
+const DEFAULT_ATRACE_CATEGORIES = new Map<string, string>();
+DEFAULT_ATRACE_CATEGORIES.set('adb', 'ADB');
+DEFAULT_ATRACE_CATEGORIES.set('aidl', 'AIDL calls');
+DEFAULT_ATRACE_CATEGORIES.set('am', 'Activity Manager');
+DEFAULT_ATRACE_CATEGORIES.set('audio', 'Audio');
+DEFAULT_ATRACE_CATEGORIES.set('binder_driver', 'Binder Kernel driver');
+DEFAULT_ATRACE_CATEGORIES.set('binder_lock', 'Binder global lock trace');
+DEFAULT_ATRACE_CATEGORIES.set('bionic', 'Bionic C library');
+DEFAULT_ATRACE_CATEGORIES.set('camera', 'Camera');
+DEFAULT_ATRACE_CATEGORIES.set('dalvik', 'ART & Dalvik');
+DEFAULT_ATRACE_CATEGORIES.set('database', 'Database');
+DEFAULT_ATRACE_CATEGORIES.set('gfx', 'Graphics');
+DEFAULT_ATRACE_CATEGORIES.set('hal', 'Hardware Modules');
+DEFAULT_ATRACE_CATEGORIES.set('input', 'Input');
+DEFAULT_ATRACE_CATEGORIES.set('network', 'Network');
+DEFAULT_ATRACE_CATEGORIES.set('nnapi', 'Neural Network API');
+DEFAULT_ATRACE_CATEGORIES.set('pm', 'Package Manager');
+DEFAULT_ATRACE_CATEGORIES.set('power', 'Power Management');
+DEFAULT_ATRACE_CATEGORIES.set('res', 'Resource Loading');
+DEFAULT_ATRACE_CATEGORIES.set('rro', 'Resource Overlay');
+DEFAULT_ATRACE_CATEGORIES.set('rs', 'RenderScript');
+DEFAULT_ATRACE_CATEGORIES.set('sm', 'Sync Manager');
+DEFAULT_ATRACE_CATEGORIES.set('ss', 'System Server');
+DEFAULT_ATRACE_CATEGORIES.set('vibrator', 'Vibrator');
+DEFAULT_ATRACE_CATEGORIES.set('video', 'Video');
+DEFAULT_ATRACE_CATEGORIES.set('view', 'View System');
+DEFAULT_ATRACE_CATEGORIES.set('webview', 'WebView');
+DEFAULT_ATRACE_CATEGORIES.set('wm', 'Window Manager');
+
+function isDataSourceDescriptor(descriptor: unknown):
+    descriptor is DataSourceDescriptor {
+  if (descriptor instanceof Object) {
+    return (descriptor as DataSourceDescriptor).name !== undefined;
+  }
+  return false;
+}
 
 class AtraceAppsList implements m.ClassComponent {
   view() {
@@ -86,6 +97,25 @@
 export class AndroidSettings implements
     m.ClassComponent<RecordingSectionAttrs> {
   view({attrs}: m.CVnode<RecordingSectionAttrs>) {
+    let atraceCategories = DEFAULT_ATRACE_CATEGORIES;
+    for (const dataSource of attrs.dataSources) {
+      if (dataSource.name !== 'linux.ftrace' ||
+          !isDataSourceDescriptor(dataSource.descriptor)) {
+        continue;
+      }
+      const atraces = dataSource.descriptor.ftraceDescriptor?.atraceCategories;
+      if (!atraces || atraces.length === 0) {
+        break;
+      }
+
+      atraceCategories = new Map<string, string>();
+      for (const atrace of atraces) {
+        if (atrace.name) {
+          atraceCategories.set(atrace.name, atrace.description || '');
+        }
+      }
+    }
+
     return m(
         `.record-section${attrs.cssClass}`,
         m(Probe,
@@ -100,7 +130,7 @@
           m(Dropdown, {
             title: 'Categories',
             cssClass: '.multicolumn.atrace-categories',
-            options: ATRACE_CATEGORIES,
+            options: atraceCategories,
             set: (cfg, val) => cfg.atraceCats = val,
             get: (cfg) => cfg.atraceCats,
           } as DropdownAttrs),
@@ -134,6 +164,14 @@
                       Requires Android 12 (S) or above.`,
           setEnabled: (cfg, val) => cfg.androidFrameTimeline = val,
           isEnabled: (cfg) => cfg.androidFrameTimeline,
+        } as ProbeAttrs),
+        m(Probe, {
+          title: 'Game intervention list',
+          img: '',
+          descr: `List game modes and interventions.
+                    Requires Android 13 (T) or above.`,
+          setEnabled: (cfg, val) => cfg.androidGameInterventionList = val,
+          isEnabled: (cfg) => cfg.androidGameInterventionList,
         } as ProbeAttrs));
   }
 }
diff --git a/ui/src/frontend/recording/chrome_settings.ts b/ui/src/frontend/recording/chrome_settings.ts
index 09b729e..01d9250 100644
--- a/ui/src/frontend/recording/chrome_settings.ts
+++ b/ui/src/frontend/recording/chrome_settings.ts
@@ -17,7 +17,12 @@
 import {DataSource} from '../../common/recordingV2/recording_interfaces_v2';
 import {getBuiltinChromeCategoryList, isChromeTarget} from '../../common/state';
 import {globals} from '../globals';
-import {CategoriesCheckboxList, CompactProbe} from '../record_widgets';
+import {
+  CategoriesCheckboxList,
+  CompactProbe,
+  Toggle,
+  ToggleAttrs,
+} from '../record_widgets';
 
 import {RecordingSectionAttrs} from './recording_sections';
 
@@ -115,6 +120,13 @@
           setEnabled: (cfg, val) => cfg.chromeLogs = val,
           isEnabled: (cfg) => cfg.chromeLogs,
         }),
+        m(Toggle, {
+          title: 'Remove untyped and sensitive data like URLs from the trace',
+          descr: 'Not recommended unless you intend to share the trace' +
+              ' with third-parties.',
+          setEnabled: (cfg, val) => cfg.chromePrivacyFiltering = val,
+          isEnabled: (cfg) => cfg.chromePrivacyFiltering,
+        } as ToggleAttrs),
         m(ChromeCategoriesSelection, attrs));
   }
 }
diff --git a/ui/src/frontend/recording/cpu_settings.ts b/ui/src/frontend/recording/cpu_settings.ts
index 9151f4f..2536c8d 100644
--- a/ui/src/frontend/recording/cpu_settings.ts
+++ b/ui/src/frontend/recording/cpu_settings.ts
@@ -45,13 +45,23 @@
           setEnabled: (cfg, val) => cfg.cpuSched = val,
           isEnabled: (cfg) => cfg.cpuSched,
         } as ProbeAttrs),
-        m(Probe, {
-          title: 'CPU frequency and idle states',
-          img: 'rec_cpu_freq.png',
-          descr: 'Records cpu frequency and idle state changes via ftrace',
-          setEnabled: (cfg, val) => cfg.cpuFreq = val,
-          isEnabled: (cfg) => cfg.cpuFreq,
-        } as ProbeAttrs),
+        m(Probe,
+          {
+            title: 'CPU frequency and idle states',
+            img: 'rec_cpu_freq.png',
+            descr:
+                'Records cpu frequency and idle state changes via ftrace and sysfs',
+            setEnabled: (cfg, val) => cfg.cpuFreq = val,
+            isEnabled: (cfg) => cfg.cpuFreq,
+          } as ProbeAttrs,
+          m(Slider, {
+            title: 'Sysfs poll interval',
+            cssClass: '.thin',
+            values: POLL_INTERVAL_MS,
+            unit: 'ms',
+            set: (cfg, val) => cfg.cpuFreqPollMs = val,
+            get: (cfg) => cfg.cpuFreqPollMs,
+          } as SliderAttrs)),
         m(Probe, {
           title: 'Syscalls',
           img: 'rec_syscalls.png',
diff --git a/ui/src/frontend/recording/memory_settings.ts b/ui/src/frontend/recording/memory_settings.ts
index db03f37..374818b 100644
--- a/ui/src/frontend/recording/memory_settings.ts
+++ b/ui/src/frontend/recording/memory_settings.ts
@@ -67,7 +67,7 @@
     return m(
         `.${attrs.cssClass}`,
         m(Textarea, {
-          title: 'Names or pids of the processes to track',
+          title: 'Names or pids of the processes to track (required)',
           docsLink:
               'https://perfetto.dev/docs/data-sources/native-heap-profiler#heapprofd-targets',
           placeholder: 'One per line, e.g.:\n' +
@@ -165,7 +165,7 @@
     return m(
         `.${attrs.cssClass}`,
         m(Textarea, {
-          title: 'Names or pids of the processes to track',
+          title: 'Names or pids of the processes to track (required)',
           placeholder: 'One per line, e.g.:\n' +
               'com.android.vending\n' +
               '1503',
diff --git a/ui/src/frontend/recording/recording_modal.ts b/ui/src/frontend/recording/recording_modal.ts
deleted file mode 100644
index 953299b..0000000
--- a/ui/src/frontend/recording/recording_modal.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2022 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 * as m from 'mithril';
-
-import {showModal} from '../modal';
-
-export function couldNotClaimInterface(
-    onReset: () => Promise<void>, onCancel: () => void) {
-  let hasPressedAButton = false;
-  showModal({
-    title: 'Could not claim the USB interface',
-    content: m(
-        'div',
-        m('text',
-          'This can happen if you have the Android Debug Bridge ' +
-              '(adb) running on your workstation or any other tool which is ' +
-              'taking exclusive access of the USB interface.'),
-        m('br'),
-        m('br'),
-        m('text.small-font',
-          'Resetting will cause the ADB server to disconnect and ' +
-              'will try to reassign the interface to the current browser.'),
-        ),
-    buttons: [
-      {
-        text: 'Force reset the USB interface',
-        primary: true,
-        id: 'force_USB_interface',
-        action: () => {
-          hasPressedAButton = true;
-          onReset();
-        },
-      },
-      {
-        text: 'Cancel',
-        primary: false,
-        id: 'cancel_USB_interface',
-        action: () => {
-          hasPressedAButton = true;
-          onCancel();
-        },
-      },
-    ],
-  }).then(() => {
-    // If the user has clicked away from the modal, we interpret that as a
-    // 'Cancel'.
-    if (!hasPressedAButton) {
-      onCancel();
-    }
-  });
-}
diff --git a/ui/src/frontend/recording/recording_multiple_choice.ts b/ui/src/frontend/recording/recording_multiple_choice.ts
new file mode 100644
index 0000000..8aa4dc9
--- /dev/null
+++ b/ui/src/frontend/recording/recording_multiple_choice.ts
@@ -0,0 +1,110 @@
+// Copyright (C) 2022 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 * as m from 'mithril';
+
+import {
+  RecordingTargetV2,
+  TargetFactory,
+} from '../../common/recordingV2/recording_interfaces_v2';
+import {
+  RecordingPageController,
+} from '../../common/recordingV2/recording_page_controller';
+import {fullscreenModalContainer} from '../modal';
+
+interface RecordingMultipleChoiceAttrs {
+  targetFactories: TargetFactory[];
+  // Reference to the controller which maintains the state of the recording
+  // page.
+  controller: RecordingPageController;
+}
+
+export class RecordingMultipleChoice implements
+    m.ClassComponent<RecordingMultipleChoiceAttrs> {
+  private selectedIndex: number = -1;
+
+  targetSelection(
+      targets: RecordingTargetV2[],
+      controller: RecordingPageController): m.Vnode|undefined {
+    const targetInfo = controller.getTargetInfo();
+    const targetNames = [];
+    this.selectedIndex = -1;
+    for (let i = 0; i < targets.length; i++) {
+      const targetName = targets[i].getInfo().name;
+      targetNames.push(m('option', targetName));
+      if (targetInfo && targetName === targetInfo.name) {
+        this.selectedIndex = i;
+      }
+    }
+
+    const selectedIndex = this.selectedIndex;
+    return m(
+        'label',
+        m('select',
+          {
+            selectedIndex,
+            onchange: (e: Event) => {
+              controller.onTargetSelection(
+                  (e.target as HTMLSelectElement).value);
+            },
+            onupdate: (select) => {
+              // Work around mithril bug
+              // (https://github.com/MithrilJS/mithril.js/issues/2107): We
+              // may update the select's options while also changing the
+              // selectedIndex at the same time. The update of selectedIndex
+              // may be applied before the new options are added to the
+              // select element. Because the new selectedIndex may be
+              // outside of the select's options at that time, we have to
+              // reselect the correct index here after any new children were
+              // added.
+              (select.dom as HTMLSelectElement).selectedIndex =
+                  this.selectedIndex;
+            },
+            ...{size: targets.length, multiple: 'multiple'},
+          },
+          ...targetNames),
+    );
+  }
+
+  view({attrs}: m.CVnode<RecordingMultipleChoiceAttrs>): m.Vnode[]|undefined {
+    const controller = attrs.controller;
+    if (!controller.shouldShowTargetSelection()) {
+      return undefined;
+    }
+    const targets: RecordingTargetV2[] = [];
+    for (const targetFactory of attrs.targetFactories) {
+      for (const target of targetFactory.listTargets()) {
+        targets.push(target);
+      }
+    }
+    if (targets.length === 0) {
+      return undefined;
+    }
+
+    return [
+      m('text', 'Select target:'),
+      m('.record-modal-command',
+        this.targetSelection(targets, controller),
+        m('button.record-modal-button-high',
+          {
+            disabled: this.selectedIndex === -1,
+            onclick: () => {
+              fullscreenModalContainer.close();
+              controller.onStartRecordingPressed();
+            },
+          },
+          'Connect')),
+    ];
+  }
+}
diff --git a/ui/src/frontend/recording/recording_ui_utils.ts b/ui/src/frontend/recording/recording_ui_utils.ts
new file mode 100644
index 0000000..4a5b604
--- /dev/null
+++ b/ui/src/frontend/recording/recording_ui_utils.ts
@@ -0,0 +1,21 @@
+// Copyright (C) 2022 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 FORCE_RESET_MESSAGE = 'Force reset the USB interface';
+export const DEFAULT_WEBSOCKET_URL = 'ws://127.0.0.1:8037';
+export const ADB_ENDPOINT = '/adb';
+export const TRACED_ENDPOINT = '/traced';
+export const DEFAULT_ADB_WEBSOCKET_URL = DEFAULT_WEBSOCKET_URL + ADB_ENDPOINT;
+export const DEFAULT_TRACED_WEBSOCKET_URL =
+    DEFAULT_WEBSOCKET_URL + TRACED_ENDPOINT;
diff --git a/ui/src/frontend/recording/reset_interface_modal.ts b/ui/src/frontend/recording/reset_interface_modal.ts
new file mode 100644
index 0000000..a629cbf
--- /dev/null
+++ b/ui/src/frontend/recording/reset_interface_modal.ts
@@ -0,0 +1,64 @@
+// Copyright (C) 2022 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 * as m from 'mithril';
+
+import {showModal} from '../modal';
+import {FORCE_RESET_MESSAGE} from './recording_ui_utils';
+
+export function couldNotClaimInterface(
+    onReset: () => Promise<void>, onCancel: () => void) {
+  let hasPressedAButton = false;
+  showModal({
+    title: 'Could not claim the USB interface',
+    content: m(
+        'div',
+        m('text',
+          'This can happen if you have the Android Debug Bridge ' +
+              '(adb) running on your workstation or any other tool which is ' +
+              'taking exclusive access of the USB interface.'),
+        m('br'),
+        m('br'),
+        m('text.small-font',
+          'Resetting will cause the ADB server to disconnect and ' +
+              'will try to reassign the interface to the current browser.'),
+        ),
+    buttons: [
+      {
+        text: FORCE_RESET_MESSAGE,
+        primary: true,
+        id: 'force_USB_interface',
+        action: () => {
+          hasPressedAButton = true;
+          onReset();
+        },
+      },
+      {
+        text: 'Cancel',
+        primary: false,
+        id: 'cancel_USB_interface',
+        action: () => {
+          hasPressedAButton = true;
+          onCancel();
+        },
+      },
+    ],
+  }).then(() => {
+    // If the user has clicked away from the modal, we interpret that as a
+    // 'Cancel'.
+    if (!hasPressedAButton) {
+      onCancel();
+    }
+  });
+}
diff --git a/ui/src/frontend/recording/reset_target_modal.ts b/ui/src/frontend/recording/reset_target_modal.ts
new file mode 100644
index 0000000..a50cce8
--- /dev/null
+++ b/ui/src/frontend/recording/reset_target_modal.ts
@@ -0,0 +1,161 @@
+// Copyright (C) 2022 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 * as m from 'mithril';
+import {
+  RecordingPageController,
+} from '../../common/recordingV2/recording_page_controller';
+import {EXTENSION_URL} from '../../common/recordingV2/recording_utils';
+import {
+  CHROME_TARGET_FACTORY,
+  ChromeTargetFactory,
+} from '../../common/recordingV2/target_factories/chrome_target_factory';
+import {
+  targetFactoryRegistry,
+} from '../../common/recordingV2/target_factory_registry';
+import {
+  WebsocketMenuController,
+} from '../../common/recordingV2/websocket_menu_controller';
+import {fullscreenModalContainer, ModalDefinition} from '../modal';
+import {CodeSnippet} from '../record_widgets';
+
+import {RecordingMultipleChoice} from './recording_multiple_choice';
+
+const RUN_WEBSOCKET_CMD = '# Get tracebox\n' +
+    'curl -LO https://get.perfetto.dev/tracebox\n' +
+    'chmod +x ./tracebox\n' +
+    '# Option A - trace android devices\n' +
+    'adb start-server\n' +
+    '# Option B - trace the host OS\n' +
+    './tracebox traced --background\n' +
+    './tracebox traced_probes --background\n' +
+    '# Start the websocket server\n' +
+    './tracebox websocket_bridge\n';
+
+export function addNewTarget(recordingPageController: RecordingPageController):
+    ModalDefinition {
+  const components = [];
+  components.push(m('text', 'Select platform:'));
+
+  components.push(assembleWebusbSection(recordingPageController));
+
+  components.push(m('.line'));
+  components.push(assembleWebsocketSection(recordingPageController));
+
+  components.push(m('.line'));
+  components.push(assembleChromeSection(recordingPageController));
+
+  return {
+    title: 'Add new recording target',
+    content: m('.record-modal', components),
+  };
+}
+
+function assembleWebusbSection(
+    recordingPageController: RecordingPageController): m.Vnode {
+  return m(
+      '.record-modal-section',
+      m('.logo-wrapping', m('i.material-icons', 'usb')),
+      m('.record-modal-description',
+        m('h3', 'Android device over WebUSB'),
+        m('h4', 'JustWorks from the browser with one click'),
+        m('text',
+          'Android developers: this option cannot co-operate ' +
+              'with the adb host on your machine. Only one entity between ' +
+              'the browser and adb can control the USB endpoint. If adb is ' +
+              'running, you will be prompted to re-assign the device to the ' +
+              'browser. Use the websocket option below to use both ' +
+              'simultaneously.'),
+        m('.record-modal-button',
+          {
+            onclick: () => {
+              fullscreenModalContainer.close();
+              recordingPageController.addAndroidDevice();
+            },
+          },
+          'Connect new WebUSB driver')));
+}
+
+function assembleWebsocketSection(
+    recordingPageController: RecordingPageController): m.Vnode {
+  const websocketComponents = [];
+  websocketComponents.push(
+      m('h3', 'Android / Linux / MacOS device via Websocket'));
+  websocketComponents.push(
+      m('text',
+        'This option assumes that the adb server is already ' +
+            'running on your machine.'),
+      m('.record-modal-command', m(CodeSnippet, {
+          text: RUN_WEBSOCKET_CMD,
+        })));
+
+  websocketComponents.push(m(
+      '.record-modal-command',
+      m('text', 'Websocket bridge address: '),
+      m('input[type=text]', {
+        value: websocketMenuController.getPath(),
+        oninput() {
+          websocketMenuController.setPath(this.value);
+        },
+      }),
+      m('.record-modal-logo-button',
+        {
+          onclick: () => websocketMenuController.onPathChange(),
+        },
+        m('i.material-icons', 'refresh')),
+      ));
+
+  websocketComponents.push(m(RecordingMultipleChoice, {
+    controller: recordingPageController,
+    targetFactories: websocketMenuController.getTargetFactories(),
+  }));
+
+  return m(
+      '.record-modal-section',
+      m('.logo-wrapping', m('i.material-icons', 'settings_ethernet')),
+      m('.record-modal-description', ...websocketComponents));
+}
+
+function assembleChromeSection(
+    recordingPageController: RecordingPageController): m.Vnode|undefined {
+  if (!targetFactoryRegistry.has(CHROME_TARGET_FACTORY)) {
+    return undefined;
+  }
+
+  const chromeComponents = [];
+  chromeComponents.push(m('h3', 'Chrome Browser instance or ChromeOS device'));
+
+  const chromeFactory: ChromeTargetFactory =
+      targetFactoryRegistry.get(CHROME_TARGET_FACTORY) as ChromeTargetFactory;
+
+  if (!chromeFactory.isExtensionInstalled) {
+    chromeComponents.push(
+        m('text',
+          'Install the extension ',
+          m('a', {href: EXTENSION_URL, target: '_blank'}, 'from this link '),
+          'and refresh the page.'));
+  } else {
+    chromeComponents.push(m(RecordingMultipleChoice, {
+      controller: recordingPageController,
+      targetFactories: [chromeFactory],
+    }));
+  }
+
+  return m(
+      '.record-modal-section',
+      m('.logo-wrapping', m('i.material-icons', 'web')),
+      m('.record-modal-description', ...chromeComponents));
+}
+
+const websocketMenuController = new WebsocketMenuController();
diff --git a/ui/src/frontend/reorderable_cells.ts b/ui/src/frontend/reorderable_cells.ts
index 2bdb2b4..e3977a2 100644
--- a/ui/src/frontend/reorderable_cells.ts
+++ b/ui/src/frontend/reorderable_cells.ts
@@ -15,12 +15,18 @@
  */
 
 import * as m from 'mithril';
+
+import {DropDirection} from '../common/dragndrop_logic';
+
 import {globals} from './globals';
 
-export type DropDirection = 'left'|'right';
+export interface ReorderableCell {
+  content: m.Children;
+  extraClass?: string;
+}
 
 export interface ReorderableCellGroupAttrs {
-  cells: m.Children[];
+  cells: ReorderableCell[];
   onReorder: (from: number, to: number, side: DropDirection) => void;
 }
 
@@ -61,7 +67,7 @@
   view(vnode: m.Vnode<ReorderableCellGroupAttrs>): m.Children {
     return vnode.attrs.cells.map(
         (cell, index) => m(
-            'td.reorderable-cell',
+            `td.reorderable-cell${cell.extraClass ?? ''}`,
             {
               draggable: 'draggable',
               class: this.getClassForIndex(index),
@@ -137,7 +143,7 @@
                 globals.rafScheduler.scheduleFullRedraw();
               },
             },
-            cell));
+            cell.content));
   }
 
   oncreate(vnode: m.VnodeDOM<ReorderableCellGroupAttrs, this>) {
diff --git a/ui/src/frontend/router.ts b/ui/src/frontend/router.ts
index 4e4a791..b9add74 100644
--- a/ui/src/frontend/router.ts
+++ b/ui/src/frontend/router.ts
@@ -155,31 +155,20 @@
     const prefixLength = ROUTE_PREFIX.length;
     let route = '';
     if (hash.startsWith(ROUTE_PREFIX)) {
-      route = hash.substr(prefixLength).split('?')[0];
+      route = hash.substring(prefixLength).split('?')[0];
     }
 
     let page = route;
     let subpage = '';
     const splittingPoint = route.indexOf('/', 1);
     if (splittingPoint > 0) {
-      page = route.substr(0, splittingPoint);
-      subpage = route.substr(splittingPoint);
+      page = route.substring(0, splittingPoint);
+      subpage = route.substring(splittingPoint);
     }
 
     const argsStart = hash.indexOf('?');
-    const argsStr = argsStart < 0 ? '' : hash.substr(argsStart + 1);
-    const args = argsStr ? m.parseQueryString(hash.substr(argsStart)) : {};
-
-    // TODO(primiano): remove this in mid-2022. trace_id is the same concept of
-    // local_cache_key. Just at some point we renamed it to make it more obvious
-    // to people that those URLs cannot be copy-pasted in bugs. For now this
-    // handles cases of reloading pages from old version.
-    if ('trace_id' in args) {
-      if (!('local_cache_key' in args)) {
-        args['local_cache_key'] = args['trace_id'];
-      }
-      delete args['trace_id'];
-    }
+    const argsStr = argsStart < 0 ? '' : hash.substring(argsStart + 1);
+    const args = argsStr ? m.parseQueryString(hash.substring(argsStart)) : {};
 
     return {page, subpage, args};
   }
@@ -187,7 +176,7 @@
   // Like parseFragment() but takes a full URL.
   static parseUrl(url: string): Route {
     const hashPos = url.indexOf('#');
-    const fragment = hashPos < 0 ? '' : url.substr(hashPos);
+    const fragment = hashPos < 0 ? '' : url.substring(hashPos);
     return Router.parseFragment(fragment);
   }
 
diff --git a/ui/src/frontend/scroll_helper.ts b/ui/src/frontend/scroll_helper.ts
index 1f0e405..18a9a78 100644
--- a/ui/src/frontend/scroll_helper.ts
+++ b/ui/src/frontend/scroll_helper.ts
@@ -35,6 +35,9 @@
 
 // Given a start and end timestamp (in ns), move the viewport to center this
 // range and zoom if necessary:
+// - If [viewPercentage] is specified, the viewport will be zoomed so that
+//   the given time range takes up this percentage of the viewport.
+// The following scenarios assume [viewPercentage] is undefined.
 // - If the new range is more than 50% of the viewport, zoom out to a level
 // where
 //   the range is 1/5 of the viewport.
@@ -42,7 +45,8 @@
 // viewport
 //   to cover 1/5 of the viewport.
 // - Otherwise, preserve the zoom range.
-export function focusHorizontalRange(startTs: number, endTs: number) {
+export function focusHorizontalRange(
+    startTs: number, endTs: number, viewPercentage?: number) {
   const visibleDur = globals.frontendLocalState.visibleWindowTime.end -
       globals.frontendLocalState.visibleWindowTime.start;
   let selectDur = endTs - startTs;
@@ -52,6 +56,24 @@
     selectDur = INCOMPLETE_SLICE_TIME_S;
     endTs = startTs;
   }
+
+  if (viewPercentage !== undefined) {
+    if (viewPercentage <= 0.0 || viewPercentage > 1.0) {
+      console.warn(
+          'Invalid value for [viewPercentage]. ' +
+              'Value must be between 0.0 (exclusive) and 1.0 (inclusive).',
+      );
+      // Default to 50%.
+      viewPercentage = 0.5;
+    }
+    const paddingPercentage = 1.0 - viewPercentage;
+    const paddingTime = selectDur * paddingPercentage;
+    const halfPaddingTime = paddingTime / 2;
+    globals.frontendLocalState.updateVisibleTime(
+        new TimeSpan(startTs - halfPaddingTime, endTs + halfPaddingTime));
+    return;
+  }
+
   // If the range is too large to fit on the current zoom level, resize.
   if (selectDur > 0.5 * visibleDur) {
     globals.frontendLocalState.updateVisibleTime(
diff --git a/ui/src/frontend/search_handler.ts b/ui/src/frontend/search_handler.ts
index d911a45..1622a7e 100644
--- a/ui/src/frontend/search_handler.ts
+++ b/ui/src/frontend/search_handler.ts
@@ -19,13 +19,16 @@
 import {globals} from './globals';
 
 function setToPrevious(current: number) {
-  const index = Math.max(current - 1, 0);
+  let index = current - 1;
+  if (index < 0) {
+    index = globals.currentSearchResults.totalResults - 1;
+  }
   globals.dispatch(Actions.setSearchIndex({index}));
 }
 
 function setToNext(current: number) {
   const index =
-      Math.min(current + 1, globals.currentSearchResults.totalResults - 1);
+      (current + 1) % globals.currentSearchResults.totalResults;
   globals.dispatch(Actions.setSearchIndex({index}));
 }
 
@@ -35,6 +38,12 @@
   const endNs = toNs(globals.frontendLocalState.visibleWindowTime.end);
   const currentTs = globals.currentSearchResults.tsStarts[index];
 
+  // If the value of |globals.currentSearchResults.totalResults| is 0,
+  // it means that the query is in progress or no results are found.
+  if (globals.currentSearchResults.totalResults === 0) {
+    return;
+  }
+
   // If this is a new search or the currentTs is not in the viewport,
   // select the first/last item in the viewport.
   if (index === -1 || currentTs < startNs || currentTs > endNs) {
@@ -79,6 +88,8 @@
   if (source === 'cpu') {
     globals.dispatch(
         Actions.selectSlice({id: currentId, trackId, scroll: true}));
+  } else if (source === 'log') {
+    globals.dispatch(Actions.selectLog({id: currentId, trackId, scroll: true}));
   } else {
     // Search results only include slices from the slice table for now.
     // When we include annotations we need to pass the correct table.
diff --git a/ui/src/frontend/service_worker_controller.ts b/ui/src/frontend/service_worker_controller.ts
index 5a71476..ea0c285 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 {ignoreCacheUnactionableErrors} from '../common/errors';
 
 import {globals} from './globals';
 
@@ -27,6 +28,28 @@
 // IndexedDB (which would be overkill).
 const BYPASS_ID = 'BYPASS_SERVICE_WORKER';
 
+class BypassCache {
+  static async isBypassed(): Promise<boolean> {
+    try {
+      return await caches.has(BYPASS_ID);
+    } catch (e) {
+      return ignoreCacheUnactionableErrors(e, false);
+    }
+  }
+
+  static async setBypass(bypass: boolean): Promise<void> {
+    try {
+      if (bypass) {
+        await caches.open(BYPASS_ID);
+      } else {
+        await caches.delete(BYPASS_ID);
+      }
+    } catch (e) {
+      ignoreCacheUnactionableErrors(e, undefined);
+    }
+  }
+}
+
 export class ServiceWorkerController {
   private _initialWorker: ServiceWorker|null = null;
   private _bypassed = false;
@@ -37,12 +60,12 @@
     if (!('serviceWorker' in navigator)) return;  // Not supported.
     this._bypassed = bypass;
     if (bypass) {
-      await caches.open(BYPASS_ID);  // Create the entry.
+      await BypassCache.setBypass(true);  // Create the entry.
       for (const reg of await navigator.serviceWorker.getRegistrations()) {
         await reg.unregister();
       }
     } else {
-      await caches.delete(BYPASS_ID);
+      await BypassCache.setBypass(false);
       if (window.localStorage) {
         window.localStorage.setItem('bypassDisabled', '1');
       }
@@ -94,7 +117,7 @@
       await this.setBypass(true);  // Will cause the check below to bail out.
     }
 
-    if (await caches.has(BYPASS_ID)) {
+    if (await BypassCache.isBypassed()) {
       this._bypassed = true;
       console.log('Skipping service worker registration, disabled by the user');
       return;
diff --git a/ui/src/frontend/sidebar.ts b/ui/src/frontend/sidebar.ts
index dd9d538..185f43e 100644
--- a/ui/src/frontend/sidebar.ts
+++ b/ui/src/frontend/sidebar.ts
@@ -20,6 +20,12 @@
 import {TRACE_SUFFIX} from '../common/constants';
 import {ConversionJobStatus} from '../common/conversion_jobs';
 import {Engine} from '../common/engine';
+import {featureFlags} from '../common/feature_flags';
+import {
+  disableMetatracingAndGetTrace,
+  enableMetatracing,
+  isMetatracingEnabled,
+} from '../common/metatracing';
 import {EngineMode, TraceArrayBufferSource} from '../common/state';
 import * as version from '../gen/perfetto_version';
 
@@ -119,8 +125,15 @@
   }
 }
 
+const HIRING_BANNER_FLAG = featureFlags.register({
+  id: 'showHiringBanner',
+  name: 'Show hiring banner',
+  description: 'Show the "We\'re hiring" banner link in the side bar.',
+  defaultValue: false,
+});
+
 function shouldShowHiringBanner(): boolean {
-  return globals.isInternalUser;
+  return globals.isInternalUser && HIRING_BANNER_FLAG.get();
 }
 
 function createCannedQuery(query: string): (_: Event) => void {
@@ -137,7 +150,9 @@
   return (e: Event) => {
     e.preventDefault();
     globals.dispatch(Actions.addDebugTrack({
-      engineId: assertExists(globals.state.currentEngineId),
+      // The debug track will only be shown once we have a currentEngineId which
+      // is not undefined.
+      engineId: assertExists(globals.state.engine).id,
       name: 'Debug Slices',
     }));
   };
@@ -147,7 +162,7 @@
     'https://storage.googleapis.com/perfetto-misc/example_android_trace_15s';
 
 const EXAMPLE_CHROME_TRACE_URL =
-    'https://storage.googleapis.com/perfetto-misc/example_chrome_trace_4s_1.json';
+    'https://storage.googleapis.com/perfetto-misc/chrome_example_wikipedia.perfetto_trace.gz';
 
 interface SectionItem {
   t: string;
@@ -288,7 +303,12 @@
     title: 'Sample queries',
     summary: 'Compute summary statistics',
     items: [
-      {t: 'Show Debug Track', a: showDebugTrack(), i: 'view_day'},
+      {
+        t: 'Show Debug Track',
+        a: showDebugTrack(),
+        i: 'view_day',
+        isVisible: () => globals.state.engine !== undefined,
+      },
       {
         t: 'Record metatrace',
         a: recordMetatrace,
@@ -613,7 +633,11 @@
     fileName = url.split('/').slice(-1)[0];
   } else if (src.type === 'ARRAY_BUFFER') {
     const blob = new Blob([src.buffer], {type: 'application/octet-stream'});
-    if (src.fileName) {
+    const inputFileName =
+        window.prompt('Please enter a name for your file or leave blank');
+    if (inputFileName) {
+      fileName = `${inputFileName}.perfetto_trace.gz`;
+    } else if (src.fileName) {
       fileName = src.fileName;
     }
     url = URL.createObjectURL(blob);
@@ -668,6 +692,7 @@
           text: 'YES, record metatrace',
           primary: true,
           action: () => {
+            enableMetatracing();
             engine.enableMetatrace();
           },
         },
@@ -685,6 +710,8 @@
   e.preventDefault();
   globals.logging.logEvent('Trace Actions', 'Finalise metatrace');
 
+  const jsEvents = disableMetatracingAndGetTrace();
+
   const engine = getCurrentEngine();
   if (!engine) return;
 
@@ -693,7 +720,8 @@
     throw new Error(`Failed to read metatrace: ${result.error}`);
   }
 
-  const blob = new Blob([result.metatrace], {type: 'application/octet-stream'});
+  const blob = new Blob(
+      [result.metatrace, jsEvents], {type: 'application/octet-stream'});
   const url = URL.createObjectURL(blob);
 
   downloadUrl(url, 'metatrace');
@@ -708,10 +736,8 @@
     let failed = false;
     let mode: EngineMode|undefined;
 
-    // We are assuming we have at most one engine here.
-    const engines = Object.values(globals.state.engines);
-    assertTrue(engines.length <= 1);
-    for (const engine of engines) {
+    const engine = globals.state.engine;
+    if (engine !== undefined) {
       mode = engine.mode;
       if (engine.failed !== undefined) {
         cssClass += '.red';
@@ -886,14 +912,12 @@
           continue;
         }
         if (item.checkMetatracingEnabled || item.checkMetatracingDisabled) {
-          const engine = getCurrentEngine();
-          if (!engine) continue;
           if (item.checkMetatracingEnabled === true &&
-              !engine.isMetatracingEnabled()) {
+              !isMetatracingEnabled()) {
             continue;
           }
           if (item.checkMetatracingDisabled === true &&
-              engine.isMetatracingEnabled()) {
+              isMetatracingEnabled()) {
             continue;
           }
           if (item.checkMetatracingDisabled &&
@@ -917,24 +941,27 @@
             'li', m(`a${css}`, attrs, m('i.material-icons', item.i), item.t)));
       }
       if (section.appendOpenedTraceTitle) {
-        const engines = Object.values(globals.state.engines);
-        if (engines.length === 1) {
+        const engine = globals.state.engine;
+        if (engine !== undefined) {
           let traceTitle = '';
           let traceUrl = '';
-          switch (engines[0].source.type) {
+          switch (engine.source.type) {
             case 'FILE':
               // Split on both \ and / (because C:\Windows\paths\are\like\this).
-              traceTitle = engines[0].source.file.name.split(/[/\\]/).pop()!;
-              const fileSizeMB = Math.ceil(engines[0].source.file.size / 1e6);
+              traceTitle = engine.source.file.name.split(/[/\\]/).pop()!;
+              const fileSizeMB = Math.ceil(engine.source.file.size / 1e6);
               traceTitle += ` (${fileSizeMB} MB)`;
               break;
             case 'URL':
-              traceUrl = engines[0].source.url;
+              traceUrl = engine.source.url;
               traceTitle = traceUrl.split('/').pop()!;
               break;
             case 'ARRAY_BUFFER':
-              traceTitle = engines[0].source.title;
-              traceUrl = engines[0].source.url || '';
+              traceTitle = engine.source.title;
+              traceUrl = engine.source.url || '';
+              const arrayBufferSizeMB =
+                  Math.ceil(engine.source.buffer.byteLength / 1e6);
+              traceTitle += ` (${arrayBufferSizeMB} MB)`;
               break;
             case 'HTTP_RPC':
               traceTitle = 'External trace (RPC)';
diff --git a/ui/src/frontend/slice_details_panel.ts b/ui/src/frontend/slice_details_panel.ts
index 0521ea8..23e54d2 100644
--- a/ui/src/frontend/slice_details_panel.ts
+++ b/ui/src/frontend/slice_details_panel.ts
@@ -85,7 +85,7 @@
 
       return m(
           '.details-table',
-          m('table.half-width', tableRows),
+          m('table.half-width-panel', tableRows),
       );
     }
   }
diff --git a/ui/src/frontend/thread_state_panel.ts b/ui/src/frontend/thread_state_panel.ts
index 286235f..337832a 100644
--- a/ui/src/frontend/thread_state_panel.ts
+++ b/ui/src/frontend/thread_state_panel.ts
@@ -35,7 +35,7 @@
       return m(
           '.details-panel',
           m('.details-panel-heading', m('h2', 'Thread State')),
-          m('.details-table', [m('table.half-width', [
+          m('.details-table', [m('table', [
               m('tr',
                 m('th', `Start time`),
                 m('td', `${timeToCode(threadState.ts)}`)),
diff --git a/ui/src/frontend/tickmark_panel.ts b/ui/src/frontend/tickmark_panel.ts
index 6cc8037..aea1085 100644
--- a/ui/src/frontend/tickmark_panel.ts
+++ b/ui/src/frontend/tickmark_panel.ts
@@ -13,11 +13,16 @@
 // limitations under the License.
 
 import * as m from 'mithril';
+
 import {fromNs} from '../common/time';
 
 import {TRACK_SHELL_WIDTH} from './css_constants';
 import {globals} from './globals';
-import {gridlines} from './gridline_helper';
+import {
+  TickGenerator,
+  TickType,
+  timeScaleForVisibleWindow,
+} from './gridline_helper';
 import {Panel, PanelSize} from './panel';
 
 // This is used to display the summary of search results.
@@ -31,9 +36,11 @@
 
     ctx.fillStyle = '#999';
     ctx.fillRect(TRACK_SHELL_WIDTH - 2, 0, 2, size.height);
-    for (const xAndTime of gridlines(
-             size.width, visibleWindowTime, timeScale)) {
-      ctx.fillRect(xAndTime[0], 0, 1, size.height);
+    const relScale = timeScaleForVisibleWindow(TRACK_SHELL_WIDTH, size.width);
+    if (relScale.timeSpan.duration > 0 && relScale.widthPx > 0) {
+      for (const {type, position} of new TickGenerator(relScale)) {
+        if (type === TickType.MAJOR) ctx.fillRect(position, 0, 1, size.height);
+      }
     }
 
     const data = globals.searchSummary;
diff --git a/ui/src/frontend/time_axis_panel.ts b/ui/src/frontend/time_axis_panel.ts
index 2f39881..aeb396b 100644
--- a/ui/src/frontend/time_axis_panel.ts
+++ b/ui/src/frontend/time_axis_panel.ts
@@ -18,7 +18,11 @@
 
 import {TRACK_SHELL_WIDTH} from './css_constants';
 import {globals} from './globals';
-import {gridlines} from './gridline_helper';
+import {
+  TickGenerator,
+  TickType,
+  timeScaleForVisibleWindow,
+} from './gridline_helper';
 import {Panel, PanelSize} from './panel';
 
 export class TimeAxisPanel extends Panel {
@@ -27,27 +31,23 @@
   }
 
   renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize) {
-    const timeScale = globals.frontendLocalState.timeScale;
-    const range = globals.frontendLocalState.visibleWindowTime;
     ctx.fillStyle = '#999';
-
-    // Write trace offset time + line.
-    ctx.font = '12px Roboto Condensed';
-
-    ctx.textAlign = 'right';
-    const offsetTime =
-        timeToString(range.start - globals.state.traceTime.startSec);
-    ctx.fillText(offsetTime, TRACK_SHELL_WIDTH - 6, 11);
-
+    ctx.font = '10px Roboto Condensed';
     ctx.textAlign = 'left';
+
     const startTime = timeToString(globals.state.traceTime.startSec);
     ctx.fillText(startTime + ' +', 6, 11);
 
     // Draw time axis.
-    ctx.font = '10px Roboto Condensed';
-    for (const [x, time] of gridlines(size.width, range, timeScale)) {
-      ctx.fillRect(x, 0, 1, size.height);
-      ctx.fillText('+' + timeToString(time - range.start), x + 5, 10);
+    const timeScale = timeScaleForVisibleWindow(TRACK_SHELL_WIDTH, size.width);
+    if (timeScale.timeSpan.duration > 0 && timeScale.widthPx > 0) {
+      const tickGen = new TickGenerator(timeScale);
+      for (const {type, time, position} of tickGen) {
+        if (type === TickType.MAJOR) {
+          ctx.fillRect(position, 0, 1, size.height);
+          ctx.fillText(time.toFixed(tickGen.digits) + ' s', position + 5, 10);
+        }
+      }
     }
 
     ctx.fillRect(TRACK_SHELL_WIDTH - 2, 0, 2, size.height);
diff --git a/ui/src/frontend/time_scale.ts b/ui/src/frontend/time_scale.ts
index cdf3ca2..6f0307a 100644
--- a/ui/src/frontend/time_scale.ts
+++ b/ui/src/frontend/time_scale.ts
@@ -79,6 +79,14 @@
   get endPx(): number {
     return this._endPx;
   }
+
+  get widthPx(): number {
+    return this._endPx - this._startPx;
+  }
+
+  get timeSpan(): TimeSpan {
+    return this.timeBounds;
+  }
 }
 
 export function computeZoom(
diff --git a/ui/src/frontend/time_selection_panel.ts b/ui/src/frontend/time_selection_panel.ts
index f1391da..5fc8a9f 100644
--- a/ui/src/frontend/time_selection_panel.ts
+++ b/ui/src/frontend/time_selection_panel.ts
@@ -19,7 +19,11 @@
 
 import {TRACK_SHELL_WIDTH} from './css_constants';
 import {globals} from './globals';
-import {gridlines} from './gridline_helper';
+import {
+  TickGenerator,
+  TickType,
+  timeScaleForVisibleWindow,
+} from './gridline_helper';
 import {Panel, PanelSize} from './panel';
 
 export interface BBox {
@@ -120,13 +124,15 @@
   }
 
   renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize) {
-    const range = globals.frontendLocalState.visibleWindowTime;
-    const timeScale = globals.frontendLocalState.timeScale;
-
     ctx.fillStyle = '#999';
     ctx.fillRect(TRACK_SHELL_WIDTH - 2, 0, 2, size.height);
-    for (const xAndTime of gridlines(size.width, range, timeScale)) {
-      ctx.fillRect(xAndTime[0], 0, 1, size.height);
+    const scale = timeScaleForVisibleWindow(TRACK_SHELL_WIDTH, size.width);
+    if (scale.timeSpan.duration > 0 && scale.widthPx > 0) {
+      for (const {position, type} of new TickGenerator(scale)) {
+        if (type === TickType.MAJOR) {
+          ctx.fillRect(position, 0, 1, size.height);
+        }
+      }
     }
 
     const localArea = globals.frontendLocalState.selectedArea;
diff --git a/ui/src/frontend/topbar.ts b/ui/src/frontend/topbar.ts
index 1c76a8b..10fbb48 100644
--- a/ui/src/frontend/topbar.ts
+++ b/ui/src/frontend/topbar.ts
@@ -93,12 +93,10 @@
 
   view() {
     const msgTTL = globals.state.status.timestamp + 1 - Date.now() / 1e3;
-    let enginesAreBusy = false;
-    for (const engine of Object.values(globals.state.engines)) {
-      enginesAreBusy = enginesAreBusy || !engine.ready;
-    }
+    const engineIsBusy =
+        globals.state.engine !== undefined && !globals.state.engine.ready;
 
-    if (msgTTL > 0 || enginesAreBusy) {
+    if (msgTTL > 0 || engineIsBusy) {
       setTimeout(
           () => globals.rafScheduler.scheduleFullRedraw(), msgTTL * 1000);
       return m(
@@ -115,14 +113,16 @@
           placeholder: PLACEHOLDER[mode],
           oninput: (e: InputEvent) => {
             const value = (e.target as HTMLInputElement).value;
-            globals.frontendLocalState.setOmnibox(
-                value, commandMode ? 'COMMAND' : 'SEARCH');
+            globals.dispatch(Actions.setOmnibox({
+              omnibox: value,
+              mode: commandMode ? 'COMMAND' : 'SEARCH',
+            }));
             if (mode === SEARCH) {
               displayStepThrough = value.length >= 4;
               globals.dispatch(Actions.setSearchIndex({index: -1}));
             }
           },
-          value: globals.frontendLocalState.omnibox,
+          value: globals.state.omniboxState.omnibox,
         }),
         displayStepThrough ?
             m(
@@ -135,7 +135,6 @@
                               globals.currentSearchResults.totalResults}`}`),
                 m('button',
                   {
-                    disabled: globals.state.searchIndex <= 0,
                     onclick: () => {
                       executeSearch(true /* reverse direction */);
                     },
@@ -143,8 +142,6 @@
                   m('i.material-icons.left', 'keyboard_arrow_left')),
                 m('button',
                   {
-                    disabled: globals.state.searchIndex ===
-                        globals.currentSearchResults.totalResults - 1,
                     onclick: () => {
                       executeSearch();
                     },
@@ -233,6 +230,8 @@
 
 class TraceErrorIcon implements m.ClassComponent {
   view() {
+    if (globals.embeddedMode) return;
+
     const errors = globals.traceErrors;
     if (!errors && !globals.metricError || mode === COMMAND) return;
     const message = errors ? `${errors} import or data loss errors detected.` :
diff --git a/ui/src/frontend/trace_info_page.ts b/ui/src/frontend/trace_info_page.ts
index 327e528..61e2e46 100644
--- a/ui/src/frontend/trace_info_page.ts
+++ b/ui/src/frontend/trace_info_page.ts
@@ -209,6 +209,15 @@
       } else {
         batteryInterventions = 'Not supported';
       }
+      // Game mode numbers are defined in
+      // https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/GameManager.java;l=68
+      if (row.current_mode === 1) {
+        row.current_mode = 'Standard';
+      } else if (row.current_mode === 2) {
+        row.current_mode = 'Performance';
+      } else if (row.current_mode === 3) {
+        row.current_mode = 'Battery';
+      }
       tableRows.push(m(
           'tr',
           m('td.name', `${row.package_name}`),
@@ -308,7 +317,7 @@
           cssClass: '.errors',
           subTitle:
               `These counters are collected at trace recording time. The trace
-               data for one or more data sources was droppped and hence some
+               data for one or more data sources was dropped and hence some
                track contents will be incomplete.`,
           sqlConstraints: `severity = 'data_loss' and value > 0`,
         }),
diff --git a/ui/src/frontend/trace_url_handler.ts b/ui/src/frontend/trace_url_handler.ts
index 2c495b7..d343158 100644
--- a/ui/src/frontend/trace_url_handler.ts
+++ b/ui/src/frontend/trace_url_handler.ts
@@ -111,7 +111,8 @@
   // This early out prevents to re-trigger the openTraceFromXXX() action if the
   // URL changes (e.g. if the user navigates back/fwd) while the new trace is
   // being loaded.
-  for (const eng of Object.values(globals.state.engines)) {
+  if (globals.state.engine !== undefined) {
+    const eng = globals.state.engine;
     if (eng.source.type === 'ARRAY_BUFFER' && eng.source.uuid === traceUuid) {
       return;
     }
diff --git a/ui/src/frontend/track_group_panel.ts b/ui/src/frontend/track_group_panel.ts
index 34a7ec4..0c1df65 100644
--- a/ui/src/frontend/track_group_panel.ts
+++ b/ui/src/frontend/track_group_panel.ts
@@ -215,8 +215,6 @@
 
     drawGridLines(
         ctx,
-        globals.frontendLocalState.timeScale,
-        globals.frontendLocalState.visibleWindowTime,
         size.width,
         size.height);
 
diff --git a/ui/src/frontend/track_panel.ts b/ui/src/frontend/track_panel.ts
index 588900f..5ee1a55 100644
--- a/ui/src/frontend/track_panel.ts
+++ b/ui/src/frontend/track_panel.ts
@@ -364,8 +364,6 @@
 
     drawGridLines(
         ctx,
-        globals.frontendLocalState.timeScale,
-        globals.frontendLocalState.visibleWindowTime,
         size.width,
         size.height);
 
diff --git a/ui/src/service_worker/service_worker.ts b/ui/src/service_worker/service_worker.ts
index de185a9..08e32f3 100644
--- a/ui/src/service_worker/service_worker.ts
+++ b/ui/src/service_worker/service_worker.ts
@@ -217,8 +217,8 @@
     await cache.addAll(urlsToCache);
     console.log(LOG_TAG + 'installation completed for ' + version);
   } catch (err) {
-    await caches.delete(CACHE_NAME);
     console.error(LOG_TAG + `Installation failed for ${manifestUrl}`, err);
+    await caches.delete(CACHE_NAME);
     throw err;
   }
 }
diff --git a/ui/src/service_worker/tsconfig.json b/ui/src/service_worker/tsconfig.json
index 35ff5b6..1e77d1d 100644
--- a/ui/src/service_worker/tsconfig.json
+++ b/ui/src/service_worker/tsconfig.json
@@ -8,7 +8,7 @@
     "outDir": "../../out/tsc/service_worker",
     "lib": [
       "webworker",
-      "es2018",
+      "es2020",
     ],
     "types" : []
   }
diff --git a/ui/src/test/ui_integrationtest.ts b/ui/src/test/ui_integrationtest.ts
index 4794d8d..d95fe59 100644
--- a/ui/src/test/ui_integrationtest.ts
+++ b/ui/src/test/ui_integrationtest.ts
@@ -93,19 +93,6 @@
     });
     await waitForPerfettoIdle(page);
   });
-
-  // TODO(198431341): Test is flaky. We should de-flake and re-enable.
-  // test('search', async () => {
-  //  const page = await getPage();
-  //  const searchInput = '.omnibox input';
-  //  await page.focus(searchInput);
-  //  await page.keyboard.type('TrimMaps');
-  //  await waitForPerfettoIdle(page);
-  //  for (let i = 0; i < 10; i++) {
-  //    await page.keyboard.type('\n');
-  //  }
-  //  await waitForPerfettoIdle(page);
-  // });
 });
 
 describe('chrome_rendering_desktop', () => {
@@ -148,6 +135,26 @@
   });
 });
 
+// Tests that chrome traces with missing process/thread names still open
+// correctly in the UI.
+describe('chrome_missing_track_names', () => {
+  let page: puppeteer.Page;
+
+  beforeAll(async () => {
+    page = await getPage();
+    await page.goto('http://localhost:10000/?testing=1');
+    await waitForPerfettoIdle(page);
+  });
+
+  test('load', async () => {
+    const page = await getPage();
+    const file = await page.waitForSelector('input.trace_file');
+    const tracePath = getTestTracePath('chrome_missing_track_names.pb.gz');
+    assertExists(file).uploadFile(tracePath);
+    await waitForPerfettoIdle(page);
+  });
+});
+
 describe('routing', () => {
   describe('open_two_traces_then_go_back', () => {
     let page: puppeteer.Page;
diff --git a/ui/src/tracks/chrome_slices/index.ts b/ui/src/tracks/chrome_slices/index.ts
index 00c04f4..00c475a 100644
--- a/ui/src/tracks/chrome_slices/index.ts
+++ b/ui/src/tracks/chrome_slices/index.ts
@@ -15,7 +15,6 @@
 import {Actions} from '../../common/actions';
 import {cropText, drawIncompleteSlice} from '../../common/canvas_utils';
 import {colorForThreadIdleSlice, hslForSlice} from '../../common/colorizer';
-import {TRACE_MARGIN_TIME_S} from '../../common/constants';
 import {PluginContext} from '../../common/plugin_api';
 import {NUM, NUM_NULL, STR} from '../../common/query_result';
 import {fromNs, toNs} from '../../common/time';
@@ -43,7 +42,6 @@
   maxDepth: number;
   namespace: string;
   trackId: number;
-  isThreadSlice?: boolean;
 }
 
 export interface Data extends TrackData {
@@ -75,13 +73,7 @@
     // be an even number, so we can snap in the middle.
     const bucketNs = Math.max(Math.round(resolution * 1e9 * pxSize / 2) * 2, 1);
 
-    const isThreadSlice = this.config.isThreadSlice;
-    let tableName = this.namespaceTable('slice');
-    let threadDurQuery = ', dur';
-    if (isThreadSlice) {
-      tableName = this.namespaceTable('thread_slice');
-      threadDurQuery = ', iif(thread_dur IS NULL, dur, thread_dur)';
-    }
+    const tableName = this.namespaceTable('slice');
 
     if (this.maxDurNs === 0) {
       const query = `
@@ -107,8 +99,8 @@
         id as sliceId,
         ifnull(name, '[null]') as name,
         dur = 0 as isInstant,
-        dur = -1 as isIncomplete
-        ${threadDurQuery} as threadDur
+        dur = -1 as isIncomplete,
+        thread_dur as threadDur
       FROM ${tableName}
       WHERE track_id = ${this.config.trackId} AND
         ts >= (${startNs - this.maxDurNs}) AND
@@ -152,7 +144,7 @@
       name: STR,
       isInstant: NUM,
       isIncomplete: NUM,
-      threadDur: NUM,
+      threadDur: NUM_NULL,
     });
     for (let row = 0; it.valid(); it.next(), row++) {
       const startNsQ = it.tsq;
@@ -184,7 +176,7 @@
       slices.isIncomplete[row] = it.isIncomplete;
 
       let cpuTimeRatio = 1;
-      if (!isInstant && !it.isIncomplete) {
+      if (!isInstant && !it.isIncomplete && it.threadDur !== null) {
         // Rounding the CPU time ratio to two decimal places and ensuring
         // it is less than or equal to one, incase the thread duration exceeds
         // the total duration.
@@ -252,7 +244,6 @@
       const isIncomplete = data.isIncomplete[i];
       const title = data.strings[titleId];
       const colorOverride = data.colors && data.strings[data.colors[i]];
-      const isThreadSlice = this.config.isThreadSlice;
       if (isIncomplete) {  // incomplete slice
         tEnd = visibleWindowTime.end;
       }
@@ -322,7 +313,8 @@
 
       if (isIncomplete && rect.width > SLICE_HEIGHT / 4) {
         drawIncompleteSlice(ctx, rect.left, rect.top, rect.width, SLICE_HEIGHT);
-      } else if (isThreadSlice) {
+      } else if (
+          data.cpuTimeRatio !== undefined && data.cpuTimeRatio[i] < 1 - 1e-9) {
         // We draw two rectangles, representing the ratio between wall time and
         // time spent on cpu.
         const cpuTimeRatio = data.cpuTimeRatio![i];
@@ -445,7 +437,7 @@
       |undefined {
     const {timeScale, visibleWindowTime} = globals.frontendLocalState;
     const pxEnd = timeScale.timeToPx(visibleWindowTime.end);
-    const left = Math.max(timeScale.timeToPx(tStart), -TRACE_MARGIN_TIME_S);
+    const left = Math.max(timeScale.timeToPx(tStart), 0);
     const right = Math.min(timeScale.timeToPx(tEnd), pxEnd);
     return {
       left,
diff --git a/ui/src/tracks/counter/index.ts b/ui/src/tracks/counter/index.ts
index 52d1a77..52f1125 100644
--- a/ui/src/tracks/counter/index.ts
+++ b/ui/src/tracks/counter/index.ts
@@ -17,8 +17,14 @@
 import {searchSegment} from '../../base/binary_search';
 import {assertTrue} from '../../base/logging';
 import {Actions} from '../../common/actions';
-import {PluginContext} from '../../common/plugin_api';
-import {NUM, NUM_NULL} from '../../common/query_result';
+import {
+  EngineProxy,
+  NUM,
+  NUM_NULL,
+  PluginContext,
+  STR,
+  TrackInfo,
+} from '../../common/plugin_api';
 import {fromNs, toNs} from '../../common/time';
 import {TrackData} from '../../common/track_data';
 import {
@@ -524,9 +530,47 @@
   }
 }
 
+async function globalTrackProvider(engine: EngineProxy): Promise<TrackInfo[]> {
+  const result = await engine.query(`
+    select name, id
+    from (
+      select name, id
+      from counter_track
+      where type = 'counter_track'
+      union
+      select name, id
+      from gpu_counter_track
+      where name != 'gpufreq'
+    )
+    order by name
+  `);
+
+  // Add global or GPU counter tracks that are not bound to any pid/tid.
+  const it = result.iter({
+    name: STR,
+    id: NUM,
+  });
+
+  const tracks: TrackInfo[] = [];
+  for (; it.valid(); it.next()) {
+    const name = it.name;
+    const trackId = it.id;
+    tracks.push({
+      trackKind: COUNTER_TRACK_KIND,
+      name,
+      config: {
+        name,
+        trackId,
+      },
+    });
+  }
+  return tracks;
+}
+
 export function activate(ctx: PluginContext) {
   ctx.registerTrackController(CounterTrackController);
   ctx.registerTrack(CounterTrack);
+  ctx.registerTrackProvider(globalTrackProvider);
 }
 
 export const plugin = {
diff --git a/ui/src/tracks/cpu_profile/index.ts b/ui/src/tracks/cpu_profile/index.ts
index fd489ac..eee7b17 100644
--- a/ui/src/tracks/cpu_profile/index.ts
+++ b/ui/src/tracks/cpu_profile/index.ts
@@ -138,7 +138,7 @@
       // inclusive and within array bounds.
       let clusterEndIndex = clusterStartIndex;
       while (clusterEndIndex + 1 < data.tsStarts.length &&
-             data.callsiteId[clusterEndIndex] === callsiteId) {
+             data.callsiteId[clusterEndIndex + 1] === callsiteId) {
         clusterEndIndex++;
       }
 
diff --git a/ui/src/tracks/debug_slices/index.ts b/ui/src/tracks/debug_slices/index.ts
index c10b55f..46c1fdd 100644
--- a/ui/src/tracks/debug_slices/index.ts
+++ b/ui/src/tracks/debug_slices/index.ts
@@ -50,7 +50,7 @@
       Promise<Data> {
     const queryRes = await this.query(`select
       ifnull(id, -1) as id,
-      ifnull(name, '[null]') as name,
+      CAST(ifnull(name, '[null]') AS text) as name,
       ts,
       iif(dur = -1, (SELECT end_ts FROM trace_bounds) - ts, dur) as dur,
       ifnull(depth, 0) as depth
diff --git a/ui/tsconfig.json b/ui/tsconfig.json
index 5867aea..5fd4e3d 100644
--- a/ui/tsconfig.json
+++ b/ui/tsconfig.json
@@ -11,7 +11,7 @@
     "outDir": "./out/tsc",
     "lib": [
       "dom",                               // Need to be explicitly mentioned now since we're overriding default included libs.
-      "es2018",                            // Need this to use Object.values.
+      "es2020",                            // Need this to use Promise.allSettled.
     ],
     "paths": {
       "*" : ["*", "./node_modules/@tsundoku/micromodal_types/*"]